@fireproof/core 0.19.8-dev-global → 0.19.8-dev-series-2

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 (64) hide show
  1. package/README.md +34 -0
  2. package/chunk-7EWIAXTM.js +7 -0
  3. package/chunk-7EWIAXTM.js.map +1 -0
  4. package/chunk-OFGPKRCM.js +290 -0
  5. package/chunk-OFGPKRCM.js.map +1 -0
  6. package/chunk-PB4BKL4O.js +7 -0
  7. package/chunk-PB4BKL4O.js.map +1 -0
  8. package/chunk-WS3YRPIA.js +75 -0
  9. package/chunk-WS3YRPIA.js.map +1 -0
  10. package/deno.json +22 -0
  11. package/gateway-5FCWPX5W.js +144 -0
  12. package/gateway-5FCWPX5W.js.map +1 -0
  13. package/{store-indexdb-WLRSICCB.js → gateway-H7UD6TNB.js} +49 -82
  14. package/gateway-H7UD6TNB.js.map +1 -0
  15. package/index.cjs +2365 -1875
  16. package/index.cjs.map +1 -1
  17. package/index.d.cts +663 -535
  18. package/index.d.ts +663 -535
  19. package/index.global.js +26742 -20717
  20. package/index.global.js.map +1 -1
  21. package/index.js +1691 -1094
  22. package/index.js.map +1 -1
  23. package/key-bag-file-WADZBHYG.js +54 -0
  24. package/key-bag-file-WADZBHYG.js.map +1 -0
  25. package/key-bag-indexdb-PGVAI3FJ.js +50 -0
  26. package/key-bag-indexdb-PGVAI3FJ.js.map +1 -0
  27. package/mem-filesystem-YPPJV7Q2.js +41 -0
  28. package/mem-filesystem-YPPJV7Q2.js.map +1 -0
  29. package/metafile-cjs.json +1 -1
  30. package/metafile-esm.json +1 -1
  31. package/metafile-iife.json +1 -1
  32. package/node-filesystem-INX4ZTHE.js +45 -0
  33. package/node-filesystem-INX4ZTHE.js.map +1 -0
  34. package/package.json +14 -8
  35. package/tests/blockstore/fragment-gateway.test.ts +107 -0
  36. package/tests/blockstore/keyed-crypto.test.ts +332 -0
  37. package/tests/blockstore/loader.test.ts +24 -19
  38. package/tests/blockstore/store.test.ts +51 -40
  39. package/tests/blockstore/transaction.test.ts +19 -15
  40. package/tests/fireproof/all-gateway.test.ts +394 -0
  41. package/tests/fireproof/cars/bafkreidxwt2nhvbl4fnqfw3ctlt6zbrir4kqwmjo5im6rf4q5si27kgo2i.car +0 -0
  42. package/tests/fireproof/cars/bafkreidxwt2nhvbl4fnqfw3ctlt6zbrir4kqwmjo5im6rf4q5si27kgo2i.ts +316 -0
  43. package/tests/fireproof/config.test.ts +94 -78
  44. package/tests/fireproof/crdt.test.ts +34 -28
  45. package/tests/fireproof/database.test.ts +22 -14
  46. package/tests/fireproof/fireproof.test.fixture.ts +133 -0
  47. package/tests/fireproof/fireproof.test.ts +331 -219
  48. package/tests/fireproof/hello.test.ts +34 -18
  49. package/tests/fireproof/indexer.test.ts +34 -27
  50. package/tests/fireproof/utils.test.ts +84 -0
  51. package/tests/helpers.ts +28 -57
  52. package/tests/www/todo-local.html +1 -1
  53. package/tests/www/todo.html +12 -15
  54. package/utils-QO2HIWGI.js +14 -0
  55. package/utils-QO2HIWGI.js.map +1 -0
  56. package/chunk-BNL4PVBF.js +0 -314
  57. package/chunk-BNL4PVBF.js.map +0 -1
  58. package/chunk-JW2QT6BF.js +0 -184
  59. package/chunk-JW2QT6BF.js.map +0 -1
  60. package/node-sys-container-MIEX6ELJ.js +0 -29
  61. package/node-sys-container-MIEX6ELJ.js.map +0 -1
  62. package/store-file-VJ6BI4II.js +0 -191
  63. package/store-file-VJ6BI4II.js.map +0 -1
  64. package/store-indexdb-WLRSICCB.js.map +0 -1
@@ -0,0 +1,45 @@
1
+ import {
2
+ toArrayBuffer
3
+ } from "./chunk-WS3YRPIA.js";
4
+ import "./chunk-OFGPKRCM.js";
5
+
6
+ // src/runtime/gateways/file/node-filesystem.ts
7
+ import { runtimeFn } from "@adviser/cement";
8
+ var NodeFileSystem = class {
9
+ async start() {
10
+ this.fs = await import("node:fs/promises");
11
+ return this;
12
+ }
13
+ async mkdir(path, options) {
14
+ return this.fs?.mkdir(path, options);
15
+ }
16
+ async readdir(path, options) {
17
+ return this.fs?.readdir(path, options);
18
+ }
19
+ async rm(path, options) {
20
+ return this.fs?.rm(path, options);
21
+ }
22
+ async copyFile(source, destination) {
23
+ return this.fs?.copyFile(source, destination);
24
+ }
25
+ async readfile(path, options) {
26
+ const ret = await this.fs?.readFile(path, options);
27
+ return toArrayBuffer(ret);
28
+ }
29
+ stat(path) {
30
+ return this.fs?.stat(path);
31
+ }
32
+ async unlink(path) {
33
+ return this.fs?.unlink(path);
34
+ }
35
+ async writefile(path, data) {
36
+ if (runtimeFn().isDeno) {
37
+ return this.fs?.writeFile(path, data);
38
+ }
39
+ return this.fs?.writeFile(path, Buffer.from(data));
40
+ }
41
+ };
42
+ export {
43
+ NodeFileSystem
44
+ };
45
+ //# sourceMappingURL=node-filesystem-INX4ZTHE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/runtime/gateways/file/node-filesystem.ts"],"sourcesContent":["import type { PathLike, MakeDirectoryOptions, Stats, ObjectEncodingOptions } from \"node:fs\";\nimport type { mkdir, readdir, rm, copyFile, readFile, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { toArrayBuffer } from \"./utils.js\";\nimport { SysFileSystem } from \"../../../types.js\";\nimport { runtimeFn } from \"@adviser/cement\";\n\nexport class NodeFileSystem implements SysFileSystem {\n fs?: {\n mkdir: typeof mkdir;\n readdir: typeof readdir;\n rm: typeof rm;\n copyFile: typeof copyFile;\n readFile: typeof readFile;\n stat: typeof stat;\n unlink: typeof unlink;\n writeFile: typeof writeFile;\n };\n\n async start(): Promise<SysFileSystem> {\n this.fs = await import(\"node:fs/promises\");\n return this;\n }\n async mkdir(path: PathLike, options?: { recursive: boolean }): Promise<string | undefined> {\n return this.fs?.mkdir(path, options);\n }\n async readdir(path: PathLike, options?: ObjectEncodingOptions): Promise<string[]> {\n return this.fs?.readdir(path, options) as Promise<string[]>;\n }\n async rm(path: PathLike, options?: MakeDirectoryOptions & { recursive: boolean }): Promise<void> {\n return this.fs?.rm(path, options);\n }\n async copyFile(source: PathLike, destination: PathLike): Promise<void> {\n return this.fs?.copyFile(source, destination);\n }\n async readfile(path: PathLike, options?: { encoding: BufferEncoding; flag?: string }): Promise<Uint8Array> {\n const ret = (await this.fs?.readFile(path, options)) as Buffer;\n return toArrayBuffer(ret);\n }\n stat(path: PathLike): Promise<Stats> {\n return this.fs?.stat(path) as Promise<Stats>;\n }\n async unlink(path: PathLike): Promise<void> {\n return this.fs?.unlink(path);\n }\n async writefile(path: PathLike, data: Uint8Array | string): Promise<void> {\n if (runtimeFn().isDeno) {\n return this.fs?.writeFile(path, data);\n }\n return this.fs?.writeFile(path, Buffer.from(data));\n }\n}\n\n// import { type NodeMap, join } from \"../../sys-container.js\";\n// import type { ObjectEncodingOptions, PathLike } from \"fs\";\n// import * as fs from \"fs/promises\";\n// import * as path from \"path\";\n// import * as os from \"os\";\n// import * as url from \"url\";\n// import { toArrayBuffer } from \"./utils.js\";\n\n// export async function createNodeSysContainer(): Promise<NodeMap> {\n// // const nodePath = \"node:path\";\n// // const nodeOS = \"node:os\";\n// // const nodeURL = \"node:url\";\n// // const nodeFS = \"node:fs\";\n// // const fs = (await import(\"node:fs\")).promises;\n// // const assert = \"assert\";\n// // const path = await import(\"node:path\");\n// return {\n// state: \"node\",\n// ...path,\n// // ...(await import(\"node:os\")),\n// // ...(await import(\"node:url\")),\n// ...os,\n// ...url,\n// ...fs,\n// join,\n// stat: fs.stat as NodeMap[\"stat\"],\n// readdir: fs.readdir as NodeMap[\"readdir\"],\n// readfile: async (path: PathLike, options?: ObjectEncodingOptions): Promise<Uint8Array> => {\n// const rs = await fs.readFile(path, options);\n// return toArrayBuffer(rs);\n// },\n// writefile: fs.writeFile as NodeMap[\"writefile\"],\n// };\n// }\n"],"mappings":";;;;;;AAIA,SAAS,iBAAiB;AAEnB,IAAM,iBAAN,MAA8C;AAAA,EAYnD,MAAM,QAAgC;AACpC,SAAK,KAAK,MAAM,OAAO,kBAAkB;AACzC,WAAO;AAAA,EACT;AAAA,EACA,MAAM,MAAM,MAAgB,SAA+D;AACzF,WAAO,KAAK,IAAI,MAAM,MAAM,OAAO;AAAA,EACrC;AAAA,EACA,MAAM,QAAQ,MAAgB,SAAoD;AAChF,WAAO,KAAK,IAAI,QAAQ,MAAM,OAAO;AAAA,EACvC;AAAA,EACA,MAAM,GAAG,MAAgB,SAAwE;AAC/F,WAAO,KAAK,IAAI,GAAG,MAAM,OAAO;AAAA,EAClC;AAAA,EACA,MAAM,SAAS,QAAkB,aAAsC;AACrE,WAAO,KAAK,IAAI,SAAS,QAAQ,WAAW;AAAA,EAC9C;AAAA,EACA,MAAM,SAAS,MAAgB,SAA4E;AACzG,UAAM,MAAO,MAAM,KAAK,IAAI,SAAS,MAAM,OAAO;AAClD,WAAO,cAAc,GAAG;AAAA,EAC1B;AAAA,EACA,KAAK,MAAgC;AACnC,WAAO,KAAK,IAAI,KAAK,IAAI;AAAA,EAC3B;AAAA,EACA,MAAM,OAAO,MAA+B;AAC1C,WAAO,KAAK,IAAI,OAAO,IAAI;AAAA,EAC7B;AAAA,EACA,MAAM,UAAU,MAAgB,MAA0C;AACxE,QAAI,UAAU,EAAE,QAAQ;AACtB,aAAO,KAAK,IAAI,UAAU,MAAM,IAAI;AAAA,IACtC;AACA,WAAO,KAAK,IAAI,UAAU,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,EACnD;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fireproof/core",
3
- "version": "0.19.8-dev-global",
3
+ "version": "0.19.8-dev-series-2",
4
4
  "description": "Live database for the web.",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -46,17 +46,23 @@
46
46
  "react": ">=18.0.0"
47
47
  },
48
48
  "dependencies": {
49
- "react": "^18.3.1",
50
- "@adviser/cement": "^0.2.2",
49
+ "@adviser/cement": "^0.2.31",
50
+ "multiformats": "^13.3.0",
51
+ "@ipld/unixfs": "^3.0.0",
52
+ "@ipld/car": "^5.3.2",
53
+ "@ipld/dag-json": "^10.2.2",
54
+ "@ipld/dag-cbor": "^9.2.1",
55
+ "@web3-storage/pail": "^0.6.0",
56
+ "cborg": "^4.2.4",
51
57
  "charwise": "^3.0.1",
52
58
  "prolly-trees": "^1.0.4",
53
59
  "idb": "^8.0.0",
54
- "ipfs-unixfs-exporter": "^13.5.0",
55
- "uuidv7": "^1.0.0",
60
+ "ipfs-unixfs-exporter": "^13.6.1",
61
+ "memfs": "^4.12.0",
56
62
  "p-limit": "^6.1.0",
57
- "@web3-storage/pail": "^0.6.0",
58
- "@ipld/unixfs": "^3.0.0",
59
- "@ipld/car": "^5.3.2"
63
+ "react": "^18.3.1",
64
+ "p-map": "^7.0.2",
65
+ "p-retry": "^6.2.0"
60
66
  },
61
67
  "scripts": {}
62
68
  }
@@ -0,0 +1,107 @@
1
+ import { Result, URI } from "@adviser/cement";
2
+ import { mockSuperThis } from "../helpers";
3
+ import { bs } from "@fireproof/core";
4
+
5
+ class TraceGateway implements bs.Gateway {
6
+ readonly buildUrlFn = vitest.fn();
7
+
8
+ readonly fragSize: number;
9
+ constructor(fragSize = 0) {
10
+ this.fragSize = fragSize;
11
+ }
12
+
13
+ buildUrl(baseUrl: URI, key: string): Promise<Result<URI>> {
14
+ this.buildUrlFn(baseUrl, key);
15
+ return Promise.resolve(Result.Ok(baseUrl.build().setParam("key", key).URI()));
16
+ }
17
+ readonly startFn = vitest.fn();
18
+ start(baseUrl: URI): Promise<Result<URI>> {
19
+ this.startFn(baseUrl);
20
+ const burl = baseUrl.build();
21
+ if (this.fragSize) {
22
+ burl.setParam("fragSize", this.fragSize.toString());
23
+ }
24
+ return Promise.resolve(Result.Ok(burl.URI()));
25
+ }
26
+ readonly closeFn = vitest.fn();
27
+ close(baseUrl: URI): Promise<bs.VoidResult> {
28
+ this.closeFn(baseUrl);
29
+ return Promise.resolve(Result.Ok(undefined));
30
+ }
31
+ readonly destroyFn = vitest.fn();
32
+ destroy(baseUrl: URI): Promise<bs.VoidResult> {
33
+ this.destroyFn(baseUrl);
34
+ return Promise.resolve(Result.Ok(undefined));
35
+ }
36
+ readonly getFn = vitest.fn();
37
+ putCalls = 0;
38
+ async get(url: URI): Promise<Result<Uint8Array>> {
39
+ const idx = this.putCalls++;
40
+ this.getFn(url);
41
+ return Result.Ok(this.putFn.mock.calls[idx][1]);
42
+ }
43
+ readonly putFn = vitest.fn();
44
+ async put(url: URI, data: Uint8Array): Promise<Result<void>> {
45
+ this.putFn(url, data);
46
+ return Result.Ok(undefined);
47
+ }
48
+ readonly deleteFn = vitest.fn();
49
+ async delete(url: URI): Promise<Result<void>> {
50
+ this.deleteFn(url);
51
+ return Result.Ok(undefined);
52
+ }
53
+ }
54
+
55
+ describe("FragmentGateway", () => {
56
+ const sthis = mockSuperThis();
57
+ it("passthrough", async () => {
58
+ const innerGW = new TraceGateway();
59
+ const fgw = new bs.FragmentGateway(sthis, innerGW);
60
+ const url = URI.from("http://example.com?key=3333");
61
+
62
+ expect(await fgw.put(url, new Uint8Array([1, 2, 3, 4]))).toEqual(Result.Ok(undefined));
63
+ expect(innerGW.putFn).toHaveBeenCalledWith(url, new Uint8Array([1, 2, 3, 4]));
64
+
65
+ expect(await fgw.get(url)).toEqual(Result.Ok(new Uint8Array([1, 2, 3, 4])));
66
+ expect(innerGW.getFn).toHaveBeenCalledWith(url);
67
+ });
68
+
69
+ function slice(total: number, headerSize: number, fragSize: number): { len?: string; ofs: string }[] {
70
+ const res = [];
71
+ for (let ofs = 0; ofs < total; ofs += fragSize - headerSize) {
72
+ res.push({ len: total.toString(), ofs: ofs.toString() });
73
+ }
74
+ return res;
75
+ }
76
+
77
+ it("enable frag", async () => {
78
+ const innerGW = new TraceGateway(128);
79
+ const fgw = new bs.FragmentGateway(sthis, innerGW);
80
+ const url = (await fgw.start(URI.from("http://example.com?key=3333"))).Ok();
81
+ const buf = new Uint8Array(1024).fill(1).map((_, i) => i);
82
+
83
+ expect(await fgw.put(url, buf)).toEqual(Result.Ok(undefined));
84
+
85
+ const ref = slice(1024, fgw.headerSize, 128);
86
+
87
+ expect(
88
+ innerGW.putFn.mock.calls.map((i) => {
89
+ return {
90
+ len: i[0].getParam("len"),
91
+ ofs: i[0].getParam("ofs"),
92
+ };
93
+ }),
94
+ ).toEqual(ref);
95
+
96
+ expect((await fgw.get(url)).Ok()).toEqual(buf);
97
+ ref[0].len = undefined;
98
+ expect(
99
+ innerGW.getFn.mock.calls.map((i) => {
100
+ return {
101
+ len: i[0].getParam("len"),
102
+ ofs: i[0].getParam("ofs"),
103
+ };
104
+ }),
105
+ ).toEqual(ref);
106
+ });
107
+ });
@@ -0,0 +1,332 @@
1
+ import { bs, rt } from "@fireproof/core";
2
+ import { BuildURI, Logger, MockLogger, runtimeFn, toCryptoRuntime, URI } from "@adviser/cement";
3
+ import { base58btc } from "multiformats/bases/base58";
4
+ import { sha256 as hasher } from "multiformats/hashes/sha2";
5
+ import * as dagCodec from "@ipld/dag-cbor";
6
+ import { MockSuperThis, mockSuperThis } from "../helpers";
7
+
8
+ describe("KeyBag", () => {
9
+ let url: URI;
10
+ let sthis: MockSuperThis;
11
+
12
+ beforeEach(async () => {
13
+ sthis = mockSuperThis();
14
+ await sthis.start();
15
+ if (runtimeFn().isBrowser) {
16
+ url = URI.from("indexdb://fp-keybag");
17
+ } else {
18
+ url = URI.merge(`file://./dist/tests/key.bag`, sthis.env.get("FP_KEYBAG_URL"));
19
+ }
20
+ });
21
+ it("default-path", async () => {
22
+ const old = sthis.env.get("FP_KEYBAG_URL");
23
+ sthis.env.delete("FP_KEYBAG_URL");
24
+ const kb = await rt.kb.getKeyBag(sthis);
25
+ if (runtimeFn().isBrowser) {
26
+ expect(kb.rt.url.toString()).toBe(`indexdb://fp-keybag`);
27
+ } else {
28
+ expect(kb.rt.url.toString()).toBe(`file://${sthis.env.get("HOME")}/.fireproof/keybag`);
29
+ }
30
+ sthis.env.set("FP_KEYBAG_URL", old);
31
+ });
32
+ it("from env", async () => {
33
+ const old = sthis.env.get("FP_KEYBAG_URL");
34
+ sthis.env.set("FP_KEYBAG_URL", url.toString());
35
+ const kb = await rt.kb.getKeyBag(sthis);
36
+ expect(kb.rt.url.toString()).toBe(url.toString());
37
+ sthis.env.set("FP_KEYBAG_URL", old);
38
+ });
39
+
40
+ it("extract keyMaterial", async () => {
41
+ const dkb = await rt.kb.getKeyBag(sthis);
42
+ const old = sthis.env.get("FP_KEYBAG_URL");
43
+ sthis.env.set("FP_KEYBAG_URL", BuildURI.from(dkb.rt.url).setParam("extractKey", "_deprecated_internal_api").toString());
44
+ const kb = await rt.kb.getKeyBag(sthis);
45
+ const key = kb.rt.crypto.randomBytes(kb.rt.keyLength);
46
+ const keyStr = base58btc.encode(key);
47
+ const res = await kb.setNamedKey("extract.test", keyStr);
48
+ expect(res.isOk()).toBeTruthy();
49
+ const gkb = await kb.getNamedExtractableKey("extract.test", true);
50
+ expect(gkb.isOk()).toBeTruthy();
51
+ expect(await gkb.Ok().extract()).toEqual({
52
+ key,
53
+ keyStr,
54
+ });
55
+ sthis.env.set("FP_KEYBAG_URL", old);
56
+ await sthis.logger.Flush();
57
+ expect(sthis.logCollector.Logs()).toEqual([
58
+ {
59
+ level: "warn",
60
+ module: "KeyBag",
61
+ msg: "extractKey is enabled via _deprecated_internal_api --- handle keys safely!!!",
62
+ },
63
+ ]);
64
+ });
65
+
66
+ it("simple add", async () => {
67
+ const kb = await rt.kb.getKeyBag(sthis, {
68
+ url: url.toString(),
69
+ crypto: toCryptoRuntime({
70
+ randomBytes: (size) => new Uint8Array(size).map((_, i) => i),
71
+ }),
72
+ });
73
+ const name = "setkey" + Math.random();
74
+ expect((await kb.getNamedKey(name, true)).isErr()).toBeTruthy();
75
+
76
+ const key = base58btc.encode(kb.rt.crypto.randomBytes(kb.rt.keyLength));
77
+ const res = await kb.setNamedKey(name, key);
78
+ expect(res.isOk()).toBeTruthy();
79
+ expect((await kb.getNamedKey(name, true)).Ok()).toEqual(res.Ok());
80
+
81
+ const name2 = "implicit";
82
+ const created = await kb.getNamedKey(name2);
83
+ expect(created.isOk()).toBeTruthy();
84
+
85
+ expect((await kb.getNamedKey(name2)).Ok()).toEqual(created.Ok());
86
+
87
+ let diskBag: rt.kb.KeyItem;
88
+ let diskBag2: rt.kb.KeyItem;
89
+ const provider = await kb.rt.getBag();
90
+ if (runtimeFn().isBrowser) {
91
+ const p = provider as rt.kb.KeyBagProviderIndexDB;
92
+ diskBag = await p._prepare().then((db) => db.get("bag", name));
93
+ diskBag2 = await p._prepare().then((db) => db.get("bag", name2));
94
+ } else {
95
+ const p = provider as rt.kb.KeyBagProviderFile;
96
+ const { sysFS } = await p._prepare(name);
97
+
98
+ diskBag = await sysFS.readfile((await p._prepare(name)).fName).then((data) => {
99
+ return JSON.parse(new TextDecoder().decode(data)) as rt.kb.KeyItem;
100
+ });
101
+ diskBag2 = await sysFS.readfile((await p._prepare(name2)).fName).then((data) => {
102
+ return JSON.parse(new TextDecoder().decode(data)) as rt.kb.KeyItem;
103
+ });
104
+ }
105
+ expect(await kb.toKeyWithFingerPrint(diskBag.key)).toEqual(res);
106
+ expect(await kb.toKeyWithFingerPrint(diskBag2.key)).toEqual(created);
107
+ const algo = {
108
+ name: "AES-GCM",
109
+ iv: kb.rt.crypto.randomBytes(12),
110
+ tagLength: 128,
111
+ };
112
+ const data = kb.rt.crypto.randomBytes(122);
113
+ expect(await kb.rt.crypto.encrypt(algo, res.Ok().key, data)).toEqual(await kb.rt.crypto.encrypt(algo, created.Ok().key, data));
114
+ expect(await kb.rt.crypto.encrypt(algo, await kb.subtleKey(diskBag.key), data)).toEqual(
115
+ await kb.rt.crypto.encrypt(algo, created.Ok().key, data),
116
+ );
117
+ expect(await kb.rt.crypto.encrypt(algo, await kb.subtleKey(diskBag2.key), data)).toEqual(
118
+ await kb.rt.crypto.encrypt(algo, created.Ok().key, data),
119
+ );
120
+ });
121
+ });
122
+
123
+ describe("KeyedCryptoStore", () => {
124
+ let kb: rt.kb.KeyBag;
125
+ let logger: Logger;
126
+ let baseUrl: URI;
127
+ const sthis = mockSuperThis();
128
+ beforeEach(async () => {
129
+ await sthis.start();
130
+ logger = MockLogger().logger;
131
+ let kbUrl: URI;
132
+ if (runtimeFn().isBrowser) {
133
+ kbUrl = URI.from("indexdb://fp-keybag");
134
+ baseUrl = URI.from("indexdb://fp-keyed-crypto-store");
135
+ } else {
136
+ kbUrl = URI.merge(`file://./dist/tests/key.bag`, sthis.env.get("FP_KEYBAG_URL"));
137
+ // baseUrl = URI.merge("file://./dist/tests/keyed-crypto-store", sthis.env.get("FP_STORAGE_URL"));
138
+ baseUrl = URI.from(sthis.env.get("FP_STORAGE_URL"));
139
+ }
140
+ kb = await rt.kb.getKeyBag(sthis, {
141
+ url: kbUrl,
142
+ });
143
+ });
144
+ it("no crypto", async () => {
145
+ const loader = {
146
+ sthis,
147
+ name: "test",
148
+ ebOpts: {
149
+ keyBag: {
150
+ keyRuntime: kb.rt,
151
+ },
152
+ store: {
153
+ stores: {
154
+ base: baseUrl.build().setParam("storekey", "insecure"),
155
+ },
156
+ },
157
+ },
158
+ } as unknown as bs.Loadable;
159
+ const strt = bs.toStoreRuntime({}, sthis);
160
+
161
+ for (const pstore of [strt.makeDataStore(loader), strt.makeMetaStore(loader), strt.makeWALStore(loader)]) {
162
+ const store = await pstore;
163
+ // await store.start();
164
+ const kc = await store.keyedCrypto();
165
+ expect(kc.constructor.name).toBe("noCrypto");
166
+ // expect(kc.isEncrypting).toBe(false);
167
+ }
168
+ });
169
+
170
+ it("create key", async () => {
171
+ const loader = {
172
+ sthis,
173
+ name: "test",
174
+ ebOpts: {
175
+ keyBag: {
176
+ keyRuntime: kb.rt,
177
+ },
178
+ store: {
179
+ stores: {
180
+ base: baseUrl,
181
+ },
182
+ },
183
+ },
184
+ } as unknown as bs.Loadable;
185
+ const strt = bs.toStoreRuntime({}, sthis);
186
+ for (const pstore of [strt.makeDataStore(loader), strt.makeMetaStore(loader), strt.makeWALStore(loader)]) {
187
+ const store = await bs.ensureStart(await pstore, logger);
188
+ const kc = await store.keyedCrypto();
189
+ expect(kc.constructor.name).toBe("keyedCrypto");
190
+ // expect(kc.isEncrypting).toBe(true);
191
+ expect(store.url().getParam("storekey")).toBe(`@test:${store.url().getParam("store")}@`);
192
+ }
193
+ });
194
+
195
+ it("key ref keybag", async () => {
196
+ const key = base58btc.encode(kb.rt.crypto.randomBytes(kb.rt.keyLength));
197
+ const genKey = await kb.setNamedKey("@heute@", key);
198
+ const loader = {
199
+ sthis,
200
+ name: "test",
201
+ ebOpts: {
202
+ keyBag: {
203
+ keyRuntime: kb.rt,
204
+ },
205
+ store: {
206
+ stores: {
207
+ base: baseUrl.build().setParam("storekey", "@heute@").URI(),
208
+ },
209
+ },
210
+ },
211
+ } as unknown as bs.Loadable;
212
+ const strt = bs.toStoreRuntime({}, sthis);
213
+ for (const pstore of [strt.makeDataStore(loader), strt.makeMetaStore(loader), strt.makeWALStore(loader)]) {
214
+ const store = await pstore;
215
+ // await store.start();
216
+ expect(store.url().getParam("storekey")).toBe(`@heute@`);
217
+ const kc = await store.keyedCrypto();
218
+ expect(kc.constructor.name).toBe("keyedCrypto");
219
+ const testData = kb.rt.crypto.randomBytes(1024);
220
+ const iv = kb.rt.crypto.randomBytes(12);
221
+ const blk = await kc._encrypt({ bytes: testData, iv });
222
+ expect(blk).not.toEqual(testData);
223
+ const fpkey = genKey.Ok();
224
+ expect(fpkey.fingerPrint).toEqual(await kc.fingerPrint());
225
+ const dec = new Uint8Array(await kc.crypto.decrypt(kc.algo(iv), fpkey.key, blk));
226
+ expect(dec).toEqual(testData);
227
+ }
228
+ });
229
+
230
+ it("key", async () => {
231
+ const key = base58btc.encode(kb.rt.crypto.randomBytes(kb.rt.keyLength));
232
+ const loader = {
233
+ sthis,
234
+ name: "test",
235
+ ebOpts: {
236
+ keyBag: {
237
+ keyRuntime: kb.rt,
238
+ },
239
+ store: {
240
+ stores: {
241
+ base: BuildURI.from(baseUrl).setParam("storekey", key),
242
+ },
243
+ },
244
+ },
245
+ } as unknown as bs.Loadable;
246
+ const strt = bs.toStoreRuntime({}, sthis);
247
+ for (const pstore of [strt.makeDataStore(loader), strt.makeMetaStore(loader), strt.makeWALStore(loader)]) {
248
+ const store = await pstore;
249
+ // await store.start();
250
+ expect(store.url().getParam("storekey")).toBe(key);
251
+ const kc = await store.keyedCrypto();
252
+ expect(kc.constructor.name).toBe("keyedCrypto");
253
+ const testData = kb.rt.crypto.randomBytes(1024);
254
+ const iv = kb.rt.crypto.randomBytes(12);
255
+ const blk = await kc._encrypt({ bytes: testData, iv });
256
+ expect(blk).not.toEqual(testData);
257
+ const dec = await kc._decrypt({ bytes: blk, iv });
258
+ expect(dec).toEqual(testData);
259
+ }
260
+ });
261
+ });
262
+
263
+ describe("KeyedCrypto", () => {
264
+ let kb: rt.kb.KeyBag;
265
+ let kycr: bs.KeyedCrypto;
266
+ let keyStr: string;
267
+ const sthis = mockSuperThis();
268
+ beforeEach(async () => {
269
+ let url: URI;
270
+ if (runtimeFn().isBrowser) {
271
+ url = URI.from("indexdb://fp-keybag");
272
+ } else {
273
+ url = URI.merge(`file://./dist/tests/key.bag`, sthis.env.get("FP_KEYBAG_URL"));
274
+ }
275
+ kb = await rt.kb.getKeyBag(sthis, {
276
+ url,
277
+ });
278
+ keyStr = base58btc.encode(kb.rt.crypto.randomBytes(kb.rt.keyLength));
279
+ kycr = await rt.kc.keyedCryptoFactory(URI.from(`test://bla?storekey=${keyStr}`), kb, sthis);
280
+ });
281
+ it("codec explict iv", async () => {
282
+ const testData = kb.rt.crypto.randomBytes(1024);
283
+ const iv = kb.rt.crypto.randomBytes(12);
284
+ const codec = kycr.codec(iv, { noIVVerify: true });
285
+ const blk = (await codec.encode(testData)) as Uint8Array;
286
+ const myDec = await rt.mf.block.decode<bs.IvKeyIdData, number, number>({ bytes: blk, hasher, codec: dagCodec });
287
+ expect(myDec.value.iv).toEqual(iv);
288
+ expect(base58btc.encode(myDec.value.keyId)).toEqual(await kycr.fingerPrint());
289
+ const dec = await codec.decode(blk);
290
+ expect(dec).toEqual(testData);
291
+ });
292
+
293
+ it("codec implict iv", async () => {
294
+ const testData = kb.rt.crypto.randomBytes(1024);
295
+ const codec = kycr.codec();
296
+ const blk = await codec.encode(testData);
297
+ expect(blk.length).toBeGreaterThanOrEqual(12 + testData.length);
298
+ const dec = await codec.decode(blk);
299
+ expect(dec).toEqual(testData);
300
+ });
301
+
302
+ it("codec implict iv same for multiple clients", async () => {
303
+ const testData = kb.rt.crypto.randomBytes(1024);
304
+ const codec = kycr.codec();
305
+ const blk = await codec.encode(testData);
306
+ const blk2 = await codec.encode(testData);
307
+ expect(blk).toEqual(blk2);
308
+ });
309
+ });
310
+
311
+ // describe("KeyedCryptoStore RunLength", () => {
312
+ // const logger = MockLogger().logger;
313
+ // it("de/encode", () => {
314
+ // for (const data of [
315
+ // new Uint8Array(),
316
+ // new Uint8Array(10).fill(10),
317
+ // new Uint8Array(127).fill(127),
318
+ // new Uint8Array(128).fill(128),
319
+ // new Uint8Array(1024).fill(17),
320
+ // ]) {
321
+ // const res = rt.kc.encodeRunLength(data, logger);
322
+ // expect(res.length).toBeLessThanOrEqual(data.length + (data.length > 127 ? 4 : 1));
323
+ // for (let ofs = 0; ofs < 1024; ofs += 61) {
324
+ // const ofsRes = new Uint8Array([...new Uint8Array(ofs).fill(23), ...res]);
325
+ // const dec = rt.kc.decodeRunLength(ofsRes, ofs, logger);
326
+ // expect(dec.data).toEqual(data);
327
+ // expect(dec.data.length).toBe(data.length);
328
+ // expect(dec.next).toBe(ofs + data.length + (data.length > 127 ? 4 : 1));
329
+ // }
330
+ // }
331
+ // });
332
+ // });
@@ -1,18 +1,20 @@
1
1
  import * as codec from "@ipld/dag-cbor";
2
2
  import { sha256 as hasher } from "multiformats/hashes/sha2";
3
3
  import { BlockView } from "multiformats";
4
- import { encode } from "multiformats/block";
5
4
  import { CID } from "multiformats/cid";
6
5
  import { MemoryBlockstore } from "@web3-storage/pail/block";
7
- import { CRDTMeta, IndexTransactionMeta, bs, rt } from "@fireproof/core";
6
+ import { CRDTMeta, IndexTransactionMeta, SuperThis, bs, rt } from "@fireproof/core";
7
+ import { mockSuperThis } from "../helpers";
8
8
 
9
9
  class MyMemoryBlockStore extends bs.EncryptedBlockstore {
10
10
  readonly memblock = new MemoryBlockstore();
11
- constructor() {
11
+ loader: bs.Loader;
12
+ constructor(sthis: SuperThis) {
12
13
  const ebOpts = {
13
14
  name: "MyMemoryBlockStore",
14
15
  };
15
- super(ebOpts);
16
+ super(sthis, ebOpts);
17
+ this.loader = new bs.Loader("MyMemoryBlockStore", {}, sthis);
16
18
  }
17
19
  ready(): Promise<void> {
18
20
  return Promise.resolve();
@@ -20,7 +22,6 @@ class MyMemoryBlockStore extends bs.EncryptedBlockstore {
20
22
  close(): Promise<void> {
21
23
  return this.loader.close();
22
24
  }
23
- loader = new bs.Loader("MyMemoryBlockStore", {});
24
25
  readonly transactions = new Set<bs.CarTransaction>();
25
26
  // readonly lastTxMeta?: TransactionMeta;
26
27
  readonly compacting: boolean = false;
@@ -50,6 +51,7 @@ describe("basic Loader simple", function () {
50
51
  let loader: bs.Loader;
51
52
  let block: BlockView;
52
53
  let t: bs.CarTransaction;
54
+ const sthis = mockSuperThis();
53
55
 
54
56
  afterEach(async function () {
55
57
  await loader.close();
@@ -58,12 +60,12 @@ describe("basic Loader simple", function () {
58
60
 
59
61
  beforeEach(async function () {
60
62
  const testDbName = "test-loader-commit";
61
- await rt.SysContainer.start();
62
- const mockM = new MyMemoryBlockStore();
63
+ await sthis.start();
64
+ const mockM = new MyMemoryBlockStore(sthis);
63
65
  t = new bs.CarTransaction(mockM as bs.EncryptedBlockstore);
64
- loader = new bs.Loader(testDbName, { public: true });
66
+ loader = new bs.Loader(testDbName, { public: true }, sthis);
65
67
  await loader.ready();
66
- block = await encode({
68
+ block = await rt.mf.block.encode({
67
69
  value: { hello: "world" },
68
70
  hasher,
69
71
  codec,
@@ -97,17 +99,19 @@ describe("basic Loader with two commits", function () {
97
99
  let carCid: bs.CarGroup;
98
100
  let carCid0: bs.CarGroup;
99
101
 
102
+ const sthis = mockSuperThis();
103
+
100
104
  afterEach(async function () {
101
105
  await loader.close();
102
106
  await loader.destroy();
103
107
  });
104
108
 
105
109
  beforeEach(async function () {
106
- await rt.SysContainer.start();
107
- const mockM = new MyMemoryBlockStore();
110
+ await sthis.start();
111
+ const mockM = new MyMemoryBlockStore(sthis);
108
112
  t = new bs.CarTransaction(mockM);
109
- loader = new bs.Loader("test-loader-two-commit", { public: true });
110
- block = await encode({
113
+ loader = new bs.Loader("test-loader-two-commit", { public: true }, sthis);
114
+ block = await rt.mf.block.encode({
111
115
  value: { hello: "world" },
112
116
  hasher,
113
117
  codec,
@@ -115,7 +119,7 @@ describe("basic Loader with two commits", function () {
115
119
  await t.put(block.cid, block.bytes);
116
120
  carCid0 = await loader.commit(t, { head: [block.cid] });
117
121
 
118
- block2 = await encode({
122
+ block2 = await rt.mf.block.encode({
119
123
  value: { hello: "universe" },
120
124
  hasher,
121
125
  codec,
@@ -123,14 +127,14 @@ describe("basic Loader with two commits", function () {
123
127
  await t.put(block2.cid, block2.bytes);
124
128
  carCid = await loader.commit(t, { head: [block2.cid] });
125
129
 
126
- block3 = await encode({
130
+ block3 = await rt.mf.block.encode({
127
131
  value: { hello: "multiverse" },
128
132
  hasher,
129
133
  codec,
130
134
  });
131
135
  await t.put(block3.cid, block3.bytes);
132
136
 
133
- block4 = await encode({
137
+ block4 = await rt.mf.block.encode({
134
138
  value: { hello: "megaverse" },
135
139
  hasher,
136
140
  codec,
@@ -197,6 +201,7 @@ describe("basic Loader with index commits", function () {
197
201
  let indexerResult: IndexTransactionMeta;
198
202
  let cid: CID;
199
203
  // let indexMap: Map<string, CID>;
204
+ const sthis = mockSuperThis();
200
205
 
201
206
  afterEach(async function () {
202
207
  await ib.close();
@@ -205,10 +210,10 @@ describe("basic Loader with index commits", function () {
205
210
 
206
211
  beforeEach(async function () {
207
212
  const name = "test-loader-index" + Math.random();
208
- await rt.SysContainer.start();
213
+ await sthis.start();
209
214
  // t = new CarTransaction()
210
- ib = new bs.EncryptedBlockstore({ name });
211
- block = await encode({
215
+ ib = new bs.EncryptedBlockstore(sthis, { name });
216
+ block = await rt.mf.block.encode({
212
217
  value: { hello: "world" },
213
218
  hasher,
214
219
  codec,