@web3-storage/pail 0.6.0 → 0.6.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 (106) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +208 -0
  4. package/dist/scripts/randomcid.d.ts +2 -0
  5. package/dist/scripts/randomcid.d.ts.map +1 -0
  6. package/dist/scripts/randomcid.js +10 -0
  7. package/dist/scripts/words/gen.d.ts +2 -0
  8. package/dist/scripts/words/gen.d.ts.map +1 -0
  9. package/dist/scripts/words/gen.js +51 -0
  10. package/dist/src/api.d.ts +4 -2
  11. package/dist/src/api.d.ts.map +1 -1
  12. package/dist/src/api.js +1 -0
  13. package/dist/src/batch/api.d.ts +2 -2
  14. package/dist/src/batch/api.d.ts.map +1 -1
  15. package/dist/src/batch/api.js +1 -0
  16. package/dist/src/batch/index.d.ts +1 -53
  17. package/dist/src/batch/index.d.ts.map +1 -1
  18. package/dist/src/batch/index.js +241 -0
  19. package/dist/src/batch/shard.d.ts +1 -1
  20. package/dist/src/batch/shard.d.ts.map +1 -1
  21. package/dist/src/batch/shard.js +12 -0
  22. package/dist/src/block.d.ts +2 -2
  23. package/dist/src/block.d.ts.map +1 -1
  24. package/dist/src/block.js +66 -0
  25. package/dist/src/clock/api.d.ts +1 -1
  26. package/dist/src/clock/api.d.ts.map +1 -1
  27. package/dist/src/clock/api.js +1 -0
  28. package/dist/src/clock/index.d.ts +2 -2
  29. package/dist/src/clock/index.d.ts.map +1 -1
  30. package/dist/src/clock/index.js +199 -0
  31. package/dist/src/crdt/api.d.ts +1 -1
  32. package/dist/src/crdt/api.d.ts.map +1 -1
  33. package/dist/src/crdt/api.js +1 -0
  34. package/dist/src/crdt/batch/api.d.ts +1 -1
  35. package/dist/src/crdt/batch/api.d.ts.map +1 -1
  36. package/dist/src/crdt/batch/api.js +1 -0
  37. package/dist/src/crdt/batch/index.d.ts.map +1 -1
  38. package/dist/src/crdt/batch/index.js +140 -0
  39. package/dist/src/crdt/index.d.ts +2 -2
  40. package/dist/src/crdt/index.d.ts.map +1 -1
  41. package/dist/src/crdt/index.js +344 -0
  42. package/dist/src/diff.d.ts +3 -3
  43. package/dist/src/diff.d.ts.map +1 -1
  44. package/dist/src/diff.js +151 -0
  45. package/dist/src/index.d.ts +1 -1
  46. package/dist/src/index.d.ts.map +1 -1
  47. package/dist/src/index.js +356 -0
  48. package/dist/src/merge.d.ts.map +1 -1
  49. package/dist/src/merge.js +42 -0
  50. package/dist/src/shard.d.ts +4 -4
  51. package/dist/src/shard.d.ts.map +1 -1
  52. package/dist/src/shard.js +166 -0
  53. package/dist/test/batch.test.d.ts +2 -0
  54. package/dist/test/batch.test.d.ts.map +1 -0
  55. package/dist/test/batch.test.js +126 -0
  56. package/dist/test/clock.test.d.ts +2 -0
  57. package/dist/test/clock.test.d.ts.map +1 -0
  58. package/dist/test/clock.test.js +244 -0
  59. package/dist/test/crdt.test.d.ts +2 -0
  60. package/dist/test/crdt.test.d.ts.map +1 -0
  61. package/dist/test/crdt.test.js +271 -0
  62. package/dist/test/del.test.d.ts +2 -0
  63. package/dist/test/del.test.d.ts.map +1 -0
  64. package/dist/test/del.test.js +21 -0
  65. package/dist/test/diff.test.d.ts +2 -0
  66. package/dist/test/diff.test.d.ts.map +1 -0
  67. package/dist/test/diff.test.js +38 -0
  68. package/dist/test/entries.test.d.ts +2 -0
  69. package/dist/test/entries.test.d.ts.map +1 -0
  70. package/dist/test/entries.test.js +210 -0
  71. package/dist/test/get.test.d.ts +2 -0
  72. package/dist/test/get.test.d.ts.map +1 -0
  73. package/dist/test/get.test.js +26 -0
  74. package/dist/test/helpers.d.ts +35 -0
  75. package/dist/test/helpers.d.ts.map +1 -0
  76. package/dist/test/helpers.js +168 -0
  77. package/dist/test/put.test.d.ts +2 -0
  78. package/dist/test/put.test.d.ts.map +1 -0
  79. package/dist/test/put.test.js +165 -0
  80. package/dist/tsconfig.tsbuildinfo +1 -1
  81. package/dist/vitest.config.d.ts +3 -0
  82. package/dist/vitest.config.d.ts.map +1 -0
  83. package/dist/vitest.config.js +13 -0
  84. package/package.json +49 -94
  85. package/scripts/propernames/gen.sh +3 -0
  86. package/cli.js +0 -218
  87. package/src/api.js +0 -1
  88. package/src/api.ts +0 -90
  89. package/src/batch/api.js +0 -1
  90. package/src/batch/api.ts +0 -59
  91. package/src/batch/index.js +0 -258
  92. package/src/batch/shard.js +0 -13
  93. package/src/block.js +0 -75
  94. package/src/clock/api.js +0 -1
  95. package/src/clock/api.ts +0 -12
  96. package/src/clock/index.js +0 -182
  97. package/src/crdt/api.js +0 -1
  98. package/src/crdt/api.ts +0 -33
  99. package/src/crdt/batch/api.js +0 -1
  100. package/src/crdt/batch/api.ts +0 -30
  101. package/src/crdt/batch/index.js +0 -155
  102. package/src/crdt/index.js +0 -354
  103. package/src/diff.js +0 -151
  104. package/src/index.js +0 -406
  105. package/src/merge.js +0 -43
  106. package/src/shard.js +0 -180
@@ -1,3 +1,3 @@
1
- export function create(init?: API.BatcherShardInit | undefined): API.BatcherShard;
1
+ export function create(init?: API.BatcherShardInit): API.BatcherShard;
2
2
  import * as API from './api.js';
3
3
  //# sourceMappingURL=shard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shard.d.ts","sourceRoot":"","sources":["../../../src/batch/shard.js"],"names":[],"mappings":"AAQO,iEAFM,IAAI,YAAY,CAM3B;qBAXmB,UAAU"}
1
+ {"version":3,"file":"shard.d.ts","sourceRoot":"","sources":["../../../src/batch/shard.js"],"names":[],"mappings":"AAQO,8BAHI,GAAG,CAAC,gBAAgB,GAClB,GAAG,CAAC,YAAY,CAM3B;qBAXmB,UAAU"}
@@ -0,0 +1,12 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import * as API from './api.js';
3
+ import { configure } from '../shard.js';
4
+ /**
5
+ * @param {API.BatcherShardInit} [init]
6
+ * @returns {API.BatcherShard}
7
+ */
8
+ export const create = init => ({
9
+ base: init?.base,
10
+ entries: [...init?.entries ?? []],
11
+ ...configure(init)
12
+ });
@@ -3,7 +3,7 @@ export class MemoryBlockstore implements API.BlockFetcher {
3
3
  /**
4
4
  * @param {Array<import('multiformats').Block>} [blocks]
5
5
  */
6
- constructor(blocks?: API.Block<any, number, number, 1>[] | undefined);
6
+ constructor(blocks?: Array<import("multiformats").Block>);
7
7
  get<T = unknown, C extends number = number, A extends number = number, V extends API.Version = 1>(link: API.Link<T, C, A, V>): Promise<API.Block<T, C, A, V> | undefined>;
8
8
  /**
9
9
  * @param {API.UnknownLink} cid
@@ -21,7 +21,7 @@ export class MemoryBlockstore implements API.BlockFetcher {
21
21
  deleteSync(cid: API.UnknownLink): void;
22
22
  entries(): Generator<{
23
23
  cid: API.Link<any, number, number, API.Version>;
24
- bytes: Uint8Array;
24
+ bytes: Uint8Array<ArrayBufferLike>;
25
25
  }, void, unknown>;
26
26
  #private;
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../../src/block.js"],"names":[],"mappings":"AAIA,qCAAqC;AACrC,yCADiB,GAAG,CAAC,YAAY;IAK/B;;OAEG;IACH,sEAIC;IAgCsB,0KAUI;IAjC3B;;;OAGG;IACH,SAHW,IAAI,WAAW,SACf,UAAU,iBAIpB;IAED;;;OAGG;IACH,aAHW,IAAI,WAAW,SACf,UAAU,QAIpB;IAED,mCAAmC;IACnC,YADY,IAAI,WAAW,iBAG1B;IAED,mCAAmC;IACnC,gBADY,IAAI,WAAW,QAG1B;IAED;;;sBAIC;;CACF;AAED;IAIE,2CAA2C;IAC3C,yBADY,IAAI,YAAY,EAAE,EAG7B;IAjBsB,0KAUI;;CAgB5B;qBAzEoB,UAAU"}
1
+ {"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../../src/block.js"],"names":[],"mappings":"AAIA,qCAAqC;AACrC,yCADiB,GAAG,CAAC,YAAY;IAK/B;;OAEG;IACH,qBAFW,KAAK,CAAC,OAAO,cAAc,EAAE,KAAK,CAAC,EAM7C;;IASD;;;OAGG;IACH,SAHW,GAAG,CAAC,WAAW,SACf,UAAU,iBAIpB;IAED;;;OAGG;IACH,aAHW,GAAG,CAAC,WAAW,SACf,UAAU,QAIpB;IAED,mCAAmC;IACnC,YADY,GAAG,CAAC,WAAW,iBAG1B;IAED,mCAAmC;IACnC,gBADY,GAAG,CAAC,WAAW,QAG1B;IAED;;;sBAIC;;CACF;AAED;IAIE,2CAA2C;IAC3C,yBADY,GAAG,CAAC,YAAY,EAAE,EAG7B;;;CASF;qBAzEoB,UAAU"}
@@ -0,0 +1,66 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import * as API from './api.js';
3
+ import { parse } from 'multiformats/link';
4
+ /** @implements {API.BlockFetcher} */
5
+ export class MemoryBlockstore {
6
+ /** @type {Map<string, Uint8Array>} */
7
+ #blocks = new Map();
8
+ /**
9
+ * @param {Array<import('multiformats').Block>} [blocks]
10
+ */
11
+ constructor(blocks) {
12
+ if (blocks) {
13
+ this.#blocks = new Map(blocks.map(b => [b.cid.toString(), b.bytes]));
14
+ }
15
+ }
16
+ /** @type {API.BlockFetcher['get']} */
17
+ async get(cid) {
18
+ const bytes = this.#blocks.get(cid.toString());
19
+ if (!bytes)
20
+ return;
21
+ return { cid, bytes };
22
+ }
23
+ /**
24
+ * @param {API.UnknownLink} cid
25
+ * @param {Uint8Array} bytes
26
+ */
27
+ async put(cid, bytes) {
28
+ this.#blocks.set(cid.toString(), bytes);
29
+ }
30
+ /**
31
+ * @param {API.UnknownLink} cid
32
+ * @param {Uint8Array} bytes
33
+ */
34
+ putSync(cid, bytes) {
35
+ this.#blocks.set(cid.toString(), bytes);
36
+ }
37
+ /** @param {API.UnknownLink} cid */
38
+ async delete(cid) {
39
+ this.#blocks.delete(cid.toString());
40
+ }
41
+ /** @param {API.UnknownLink} cid */
42
+ deleteSync(cid) {
43
+ this.#blocks.delete(cid.toString());
44
+ }
45
+ *entries() {
46
+ for (const [str, bytes] of this.#blocks) {
47
+ yield { cid: parse(str), bytes };
48
+ }
49
+ }
50
+ }
51
+ export class MultiBlockFetcher {
52
+ /** @type {API.BlockFetcher[]} */
53
+ #fetchers;
54
+ /** @param {API.BlockFetcher[]} fetchers */
55
+ constructor(...fetchers) {
56
+ this.#fetchers = fetchers;
57
+ }
58
+ /** @type {API.BlockFetcher['get']} */
59
+ async get(link) {
60
+ for (const f of this.#fetchers) {
61
+ const v = await f.get(link);
62
+ if (v)
63
+ return v;
64
+ }
65
+ }
66
+ }
@@ -2,7 +2,7 @@ import { Link, BlockView } from 'multiformats';
2
2
  export { BlockFetcher } from '../api.js';
3
3
  export type EventLink<T> = Link<EventView<T>>;
4
4
  export interface EventView<T> {
5
- parents: EventLink<T>[];
5
+ parents: Array<EventLink<T>>;
6
6
  data: T;
7
7
  }
8
8
  export interface EventBlockView<T> extends BlockView<EventView<T>> {
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/clock/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;AAE7C,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,IAAI,EAAE,CAAC,CAAA;CACR;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;CAAG"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/clock/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;AAE7C,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5B,IAAI,EAAE,CAAC,CAAA;CACR;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;CAAG"}
@@ -0,0 +1 @@
1
+ export {};
@@ -10,7 +10,7 @@ export class EventBlock<T> extends Block<API.EventView<T>, 113, 18, 1> implement
10
10
  * @param {T} data
11
11
  * @param {API.EventLink<T>[]} [parents]
12
12
  */
13
- static create<T_1>(data: T_1, parents?: API.EventLink<T_1>[] | undefined): Promise<API.EventBlockView<T_1>>;
13
+ static create<T_1>(data: T_1, parents?: API.EventLink<T_1>[]): Promise<API.EventBlockView<T_1>>;
14
14
  /**
15
15
  * @param {object} config
16
16
  * @param {API.EventLink<T>} config.cid
@@ -42,7 +42,7 @@ export function encodeEventBlock<T>(value: API.EventView<T>): Promise<API.EventB
42
42
  export function decodeEventBlock<T>(bytes: Uint8Array): Promise<API.EventBlockView<T>>;
43
43
  export function vis<T>(blocks: API.BlockFetcher, head: API.EventLink<T>[], options?: {
44
44
  renderNodeLabel?: ((b: API.EventBlockView<T>) => string) | undefined;
45
- } | undefined): AsyncGenerator<string, void, unknown>;
45
+ }): AsyncGenerator<string, void, unknown>;
46
46
  import * as API from './api.js';
47
47
  import { Block } from 'multiformats/block';
48
48
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/clock/index.js"],"names":[],"mappings":"AAcO,mCAJI,IAAI,YAAY,kFA8B1B;AAED;;;;GAIG;AACH,kFAFgB,GAAG,CAAC,cAAc,CAAC,CAAC;IAgBlC;;;;OAIG;IACH,4GAEC;IApBD;;;;;;OAMG;IACH;QALoC,GAAG,EAA5B,IAAI,SAAS,CAAC,CAAC,CAAC;QACF,KAAK,EAAnB,KAAK;QACc,KAAK,EAAxB,UAAU;QACK,MAAM,EAArB,MAAM;OAMhB;IADC,eAAoB;CAWvB;AAED,kBAAkB;AAClB;IACE,uCAAuC;IACvC,oBADY,IAAI,YAAY,EAI3B;IAFC,eAAe;IACf,gBAAqB;IAGvB;;;OAGG;IACH,UAHW,IAAI,SAAS,CAAC,CAAC,CAAC,GACd,QAAQ,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC,CAM1C;CACF;AAOM,6FAKN;AAOM,2CAHI,UAAU,kCAOpB;AAoCM,+BALI,IAAI,YAAY;qDAGc,MAAM;sDAiC9C;qBA9KoB,UAAU;sBAJO,oBAAoB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/clock/index.js"],"names":[],"mappings":"AAcO,wBALM,CAAC,UACH,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAClB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,+BA4B1B;AAED;;;;GAIG;AACH,wBAJa,CAAC,yDAEE,GAAG,CAAC,cAAc,CAAC,CAAC;IAgBlC;;;;OAIG;IACH,yBAHW,GAAC,YACD,GAAG,CAAC,SAAS,CAAC,GAAC,CAAC,EAAE,oCAI5B;IApBD;;;;;;OAMG;IACH,2CALG;QAAiC,GAAG,EAA5B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QACF,KAAK,EAAnB,KAAK;QACc,KAAK,EAAxB,UAAU;QACK,MAAM,EAArB,MAAM;KAChB,EAKA;IADC,eAAoB;CAWvB;AAED,kBAAkB;AAClB,0BADc,CAAC;IAEb,uCAAuC;IACvC,oBADY,GAAG,CAAC,YAAY,EAI3B;IAFC,eAAe;IACf,gBAAqB;IAGvB;;;OAGG;IACH,UAHW,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GACd,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAM1C;CACF;AAOM,iCAJM,CAAC,SACH,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GACd,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAW1C;AAqBM,iCAJM,CAAC,SACH,UAAU,GACR,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAM1C;AAoCM,oBANM,CAAC,UACH,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,YAE1B;IAAuD,eAAe,QAA1D,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,MAAM;CAA2B,yCAiCzE;qBAhMoB,UAAU;sBAJO,oBAAoB"}
@@ -0,0 +1,199 @@
1
+ import { Block, encode, decode } from 'multiformats/block';
2
+ import { sha256 } from 'multiformats/hashes/sha2';
3
+ import * as cbor from '@ipld/dag-cbor';
4
+ // eslint-disable-next-line no-unused-vars
5
+ import * as API from './api.js';
6
+ /**
7
+ * Advance the clock by adding an event.
8
+ *
9
+ * @template T
10
+ * @param {API.BlockFetcher} blocks Block storage.
11
+ * @param {API.EventLink<T>[]} head The head of the clock.
12
+ * @param {API.EventLink<T>} event The event to add.
13
+ */
14
+ export const advance = async (blocks, head, event) => {
15
+ const events = new EventFetcher(blocks);
16
+ const headmap = new Map(head.map(cid => [cid.toString(), cid]));
17
+ if (headmap.has(event.toString()))
18
+ return head;
19
+ // does event contain the clock?
20
+ let changed = false;
21
+ for (const cid of head) {
22
+ if (await contains(events, event, cid)) {
23
+ headmap.delete(cid.toString());
24
+ headmap.set(event.toString(), event);
25
+ changed = true;
26
+ }
27
+ }
28
+ if (changed) {
29
+ return [...headmap.values()];
30
+ }
31
+ // does clock contain the event?
32
+ for (const p of head) {
33
+ if (await contains(events, p, event)) {
34
+ return head;
35
+ }
36
+ }
37
+ return head.concat(event);
38
+ };
39
+ /**
40
+ * @template T
41
+ * @extends {Block<API.EventView<T>, typeof cbor.code, typeof sha256.code, 1>}
42
+ * @implements {API.EventBlockView<T>}
43
+ */
44
+ export class EventBlock extends Block {
45
+ /**
46
+ * @param {object} config
47
+ * @param {API.EventLink<T>} config.cid
48
+ * @param {Event} config.value
49
+ * @param {Uint8Array} config.bytes
50
+ * @param {string} config.prefix
51
+ */
52
+ constructor({ cid, value, bytes, prefix }) {
53
+ // @ts-expect-error
54
+ super({ cid, value, bytes });
55
+ this.prefix = prefix;
56
+ }
57
+ /**
58
+ * @template T
59
+ * @param {T} data
60
+ * @param {API.EventLink<T>[]} [parents]
61
+ */
62
+ static create(data, parents) {
63
+ return encodeEventBlock({ data, parents: parents ?? [] });
64
+ }
65
+ }
66
+ /** @template T */
67
+ export class EventFetcher {
68
+ /** @param {API.BlockFetcher} blocks */
69
+ constructor(blocks) {
70
+ /** @private */
71
+ this._blocks = blocks;
72
+ }
73
+ /**
74
+ * @param {API.EventLink<T>} link
75
+ * @returns {Promise<API.EventBlockView<T>>}
76
+ */
77
+ async get(link) {
78
+ const block = await this._blocks.get(link);
79
+ if (!block)
80
+ throw new Error(`missing block: ${link}`);
81
+ return decodeEventBlock(block.bytes);
82
+ }
83
+ }
84
+ /**
85
+ * @template T
86
+ * @param {API.EventView<T>} value
87
+ * @returns {Promise<API.EventBlockView<T>>}
88
+ */
89
+ export const encodeEventBlock = async (value) => {
90
+ if (typeof value.data === 'undefined' || !Array.isArray(value.parents)) {
91
+ throw new Error('invalid event block structure');
92
+ }
93
+ const { data } = value;
94
+ const parents = [...value.parents].sort((a, b) => compareBytes(a.bytes, b.bytes));
95
+ const { cid, bytes } = await encode({ value: { data, parents }, codec: cbor, hasher: sha256 });
96
+ // @ts-expect-error
97
+ return new Block({ cid, value, bytes });
98
+ };
99
+ /**
100
+ * @param {Uint8Array} a
101
+ * @param {Uint8Array} b
102
+ */
103
+ const compareBytes = (a, b) => {
104
+ for (let i = 0; i < a.byteLength; i++) {
105
+ if (a[i] < b[i])
106
+ return -1;
107
+ if (a[i] > b[i])
108
+ return 1;
109
+ }
110
+ if (a.byteLength > b.byteLength)
111
+ return 1;
112
+ if (a.byteLength < b.byteLength)
113
+ return -1;
114
+ return 0;
115
+ };
116
+ /**
117
+ * @template T
118
+ * @param {Uint8Array} bytes
119
+ * @returns {Promise<API.EventBlockView<T>>}
120
+ */
121
+ export const decodeEventBlock = async (bytes) => {
122
+ const { cid, value } = await decode({ bytes, codec: cbor, hasher: sha256 });
123
+ // @ts-expect-error
124
+ return new Block({ cid, value, bytes });
125
+ };
126
+ /**
127
+ * Returns true if event "a" contains event "b". Breadth first search.
128
+ * @template T
129
+ * @param {EventFetcher<T>} events
130
+ * @param {API.EventLink<T>} a
131
+ * @param {API.EventLink<T>} b
132
+ */
133
+ const contains = async (events, a, b) => {
134
+ if (a.toString() === b.toString())
135
+ return true;
136
+ const [{ value: aevent }, { value: bevent }] = await Promise.all([events.get(a), events.get(b)]);
137
+ const links = [...aevent.parents];
138
+ const seen = new Set();
139
+ while (links.length) {
140
+ const link = links.shift();
141
+ if (!link)
142
+ break;
143
+ if (link.toString() === b.toString())
144
+ return true;
145
+ // if any of b's parents are this link, then b cannot exist in any of the
146
+ // tree below, since that would create a cycle.
147
+ if (bevent.parents.some(p => link.toString() === p.toString()))
148
+ continue;
149
+ if (seen.has(link.toString()))
150
+ continue;
151
+ seen.add(link.toString());
152
+ const { value: event } = await events.get(link);
153
+ links.push(...event.parents);
154
+ }
155
+ return false;
156
+ };
157
+ /**
158
+ * @template T
159
+ * @param {API.BlockFetcher} blocks Block storage.
160
+ * @param {API.EventLink<T>[]} head
161
+ * @param {object} [options]
162
+ * @param {(b: API.EventBlockView<T>) => string} [options.renderNodeLabel]
163
+ */
164
+ export const vis = async function* (blocks, head, options = {}) {
165
+ const renderNodeLabel = options.renderNodeLabel ?? (b => shortLink(b.cid));
166
+ const events = new EventFetcher(blocks);
167
+ yield 'digraph clock {';
168
+ yield ' node [shape=point fontname="Courier"]; head;';
169
+ const hevents = await Promise.all(head.map(link => events.get(link)));
170
+ /** @type {import('multiformats').Link<API.EventView<any>>[]} */
171
+ const links = [];
172
+ const nodes = new Set();
173
+ for (const e of hevents) {
174
+ nodes.add(e.cid.toString());
175
+ yield ` node [shape=oval fontname="Courier"]; ${e.cid} [label="${renderNodeLabel(e)}"];`;
176
+ yield ` head -> ${e.cid};`;
177
+ for (const p of e.value.parents) {
178
+ yield ` ${e.cid} -> ${p};`;
179
+ }
180
+ links.push(...e.value.parents);
181
+ }
182
+ while (links.length) {
183
+ const link = links.shift();
184
+ if (!link)
185
+ break;
186
+ if (nodes.has(link.toString()))
187
+ continue;
188
+ nodes.add(link.toString());
189
+ const block = await events.get(link);
190
+ yield ` node [shape=oval]; ${link} [label="${renderNodeLabel(block)}" fontname="Courier"];`;
191
+ for (const p of block.value.parents) {
192
+ yield ` ${link} -> ${p};`;
193
+ }
194
+ links.push(...block.value.parents);
195
+ }
196
+ yield '}';
197
+ };
198
+ /** @param {import('multiformats').UnknownLink} l */
199
+ const shortLink = l => `${String(l).slice(0, 4)}..${String(l).slice(-4)}`;
@@ -4,7 +4,7 @@ export { BlockFetcher, UnknownLink, ShardBlockView, ShardDiff, ShardLink, Entrie
4
4
  export { EventBlockView, EventLink } from '../clock/api.js';
5
5
  export interface Result extends ShardDiff {
6
6
  root: ShardLink;
7
- head: EventLink<Operation>[];
7
+ head: Array<EventLink<Operation>>;
8
8
  event?: EventBlockView<Operation>;
9
9
  }
10
10
  export type Operation = (PutOperation | DeleteOperation | BatchOperation) & {
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/crdt/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAE3D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC3G,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,MAAO,SAAQ,SAAS;IACvC,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,EAAE,CAAA;IAC5B,KAAK,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;CAClC;AAED,MAAM,MAAM,SAAS,GAAG,CACpB,YAAY,GACZ,eAAe,GACf,cAAc,CACjB,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAEvB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,WAAW,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,KAAK,CAAC,YAAY,GAAC,eAAe,CAAC,CAAA;CACzC"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/crdt/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAE3D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC3G,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,MAAO,SAAQ,SAAS;IACvC,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAA;IACjC,KAAK,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;CAClC;AAED,MAAM,MAAM,SAAS,GAAG,CACpB,YAAY,GACZ,eAAe,GACf,cAAc,CACjB,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAEvB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,KAAK,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,WAAW,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,KAAK,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,KAAK,CAAC,YAAY,GAAG,eAAe,CAAC,CAAA;CAC3C"}
@@ -0,0 +1 @@
1
+ export {};
@@ -6,6 +6,6 @@ export interface CRDTBatcher extends Batcher {
6
6
  * Encode all altered shards in the batch and return the new root CID, new
7
7
  * clock head, the new clock event and the difference blocks.
8
8
  */
9
- commit(): Promise<Result>;
9
+ commit: () => Promise<Result>;
10
10
  }
11
11
  //# sourceMappingURL=api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/crdt/batch/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,SAAS,EACT,WAAW,EACZ,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAExE,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,SAAS,EACT,WAAW,EACX,SAAS,EACT,cAAc,EACd,SAAS,EACT,MAAM,EACP,CAAA;AAED,MAAM,WAAW,WAAY,SAAQ,OAAO;IAC1C;;;OAGG;IACH,MAAM,IAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC3B"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/crdt/batch/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,SAAS,EACT,WAAW,EACZ,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAExE,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,SAAS,EACT,WAAW,EACX,SAAS,EACT,cAAc,EACd,SAAS,EACT,MAAM,EACP,CAAA;AAED,MAAM,WAAW,WAAY,SAAQ,OAAO;IAC1C;;;OAGG;IACH,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CAC9B"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/crdt/batch/index.js"],"names":[],"mappings":";AA0JO,+BAJI,IAAI,YAAY,QAChB,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE,GAC5B,QAAQ,IAAI,WAAW,CAAC,CAEmC;oCArJpC,sBAAsB;qBAJrC,UAAU"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/crdt/batch/index.js"],"names":[],"mappings":";AA0JO,+BAJI,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,GAC5B,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAEmC;oCArJpC,sBAAsB;qBAJrC,UAAU"}
@@ -0,0 +1,140 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ import * as API from './api.js';
3
+ import * as Shard from '../../shard.js';
4
+ import { ShardFetcher, ShardBlock } from '../../shard.js';
5
+ import * as Batch from '../../batch/index.js';
6
+ import { BatchCommittedError } from '../../batch/index.js';
7
+ import * as CRDT from '../index.js';
8
+ import * as Clock from '../../clock/index.js';
9
+ import { EventBlock } from '../../clock/index.js';
10
+ import { MemoryBlockstore, MultiBlockFetcher } from '../../block.js';
11
+ export { BatchCommittedError };
12
+ /** @implements {API.CRDTBatcher} */
13
+ class Batcher {
14
+ #committed = false;
15
+ /**
16
+ * @param {object} init
17
+ * @param {API.BlockFetcher} init.blocks Block storage.
18
+ * @param {API.EventLink<API.Operation>[]} init.head Merkle clock head.
19
+ * @param {API.BatcherShardEntry[]} init.entries The entries in this shard.
20
+ * @param {string} init.prefix Key prefix.
21
+ * @param {number} init.version Shard compatibility version.
22
+ * @param {string} init.keyChars Characters allowed in keys, referring to a known character set.
23
+ * @param {number} init.maxKeySize Max key size in bytes.
24
+ * @param {API.ShardBlockView} init.base Original shard this batcher is based on.
25
+ * @param {API.ShardBlockView[]} init.additions Additions to include in the committed batch.
26
+ * @param {API.ShardBlockView[]} init.removals Removals to include in the committed batch.
27
+ */
28
+ constructor({ blocks, head, entries, prefix, version, keyChars, maxKeySize, base, additions, removals }) {
29
+ this.blocks = blocks;
30
+ this.head = head;
31
+ this.prefix = prefix;
32
+ this.entries = [...entries];
33
+ this.base = base;
34
+ this.version = version;
35
+ this.keyChars = keyChars;
36
+ this.maxKeySize = maxKeySize;
37
+ this.additions = additions;
38
+ this.removals = removals;
39
+ /** @type {API.BatchOperation['ops']} */
40
+ this.ops = [];
41
+ }
42
+ /**
43
+ * @param {string} key The key of the value to put.
44
+ * @param {API.UnknownLink} value The value to put.
45
+ * @returns {Promise<void>}
46
+ */
47
+ async put(key, value) {
48
+ if (this.#committed)
49
+ throw new BatchCommittedError();
50
+ await Batch.put(this.blocks, this, key, value);
51
+ this.ops.push({ type: 'put', key, value });
52
+ }
53
+ async commit() {
54
+ if (this.#committed)
55
+ throw new BatchCommittedError();
56
+ this.#committed = true;
57
+ const res = await Batch.commit(this);
58
+ /** @type {API.Operation} */
59
+ const data = { type: 'batch', ops: this.ops, root: res.root };
60
+ const event = await EventBlock.create(data, this.head);
61
+ const mblocks = new MemoryBlockstore();
62
+ const blocks = new MultiBlockFetcher(mblocks, this.blocks);
63
+ mblocks.putSync(event.cid, event.bytes);
64
+ const head = await Clock.advance(blocks, this.head, event.cid);
65
+ /** @type {Map<string, API.ShardBlockView>} */
66
+ const additions = new Map();
67
+ /** @type {Map<string, API.ShardBlockView>} */
68
+ const removals = new Map();
69
+ for (const a of this.additions) {
70
+ additions.set(a.cid.toString(), a);
71
+ }
72
+ for (const r of this.removals) {
73
+ removals.set(r.cid.toString(), r);
74
+ }
75
+ for (const a of res.additions) {
76
+ if (removals.has(a.cid.toString())) {
77
+ removals.delete(a.cid.toString());
78
+ }
79
+ additions.set(a.cid.toString(), a);
80
+ }
81
+ for (const r of res.removals) {
82
+ if (additions.has(r.cid.toString())) {
83
+ additions.delete(r.cid.toString());
84
+ }
85
+ else {
86
+ removals.set(r.cid.toString(), r);
87
+ }
88
+ }
89
+ return {
90
+ head,
91
+ event,
92
+ root: res.root,
93
+ additions: [...additions.values()],
94
+ removals: [...removals.values()]
95
+ };
96
+ }
97
+ /**
98
+ * @param {object} init
99
+ * @param {API.BlockFetcher} init.blocks Block storage.
100
+ * @param {API.EventLink<API.Operation>[]} init.head Merkle clock head.
101
+ */
102
+ static async create({ blocks, head }) {
103
+ const mblocks = new MemoryBlockstore();
104
+ blocks = new MultiBlockFetcher(mblocks, blocks);
105
+ if (!head.length) {
106
+ const base = await ShardBlock.create();
107
+ mblocks.putSync(base.cid, base.bytes);
108
+ return new Batcher({
109
+ blocks,
110
+ head,
111
+ entries: [],
112
+ base,
113
+ additions: [base],
114
+ removals: [],
115
+ ...Shard.configure(base.value)
116
+ });
117
+ }
118
+ const { root, additions, removals } = await CRDT.root(blocks, head);
119
+ for (const a of additions) {
120
+ mblocks.putSync(a.cid, a.bytes);
121
+ }
122
+ const shards = new ShardFetcher(blocks);
123
+ const base = await shards.get(root);
124
+ return new Batcher({
125
+ blocks,
126
+ head,
127
+ entries: base.value.entries,
128
+ base,
129
+ additions,
130
+ removals,
131
+ ...Shard.configure(base.value)
132
+ });
133
+ }
134
+ }
135
+ /**
136
+ * @param {API.BlockFetcher} blocks Bucket block storage.
137
+ * @param {API.EventLink<API.Operation>[]} head Merkle clock head.
138
+ * @returns {Promise<API.CRDTBatcher>}
139
+ */
140
+ export const create = (blocks, head) => Batcher.create({ blocks, head });
@@ -1,9 +1,9 @@
1
1
  export function put(blocks: API.BlockFetcher, head: API.EventLink<API.Operation>[], key: string, value: API.UnknownLink): Promise<API.Result>;
2
- export function del(blocks: API.BlockFetcher, head: API.EventLink<API.Operation>[], key: string, options?: object | undefined): Promise<API.Result>;
2
+ export function del(blocks: API.BlockFetcher, head: API.EventLink<API.Operation>[], key: string, options?: object): Promise<API.Result>;
3
3
  export function root(blocks: API.BlockFetcher, head: API.EventLink<API.Operation>[]): Promise<{
4
4
  root: API.ShardLink;
5
5
  } & API.ShardDiff>;
6
6
  export function get(blocks: API.BlockFetcher, head: API.EventLink<API.Operation>[], key: string): Promise<import("multiformats").Link<unknown, number, number, import("multiformats").Version> | undefined>;
7
- export function entries(blocks: API.BlockFetcher, head: API.EventLink<API.Operation>[], options?: API.EntriesOptions | undefined): AsyncGenerator<import("../api.js").ShardValueEntry, void, undefined>;
7
+ export function entries(blocks: API.BlockFetcher, head: API.EventLink<API.Operation>[], options?: API.EntriesOptions): AsyncGenerator<import("../api.js").ShardValueEntry, void, any>;
8
8
  import * as API from './api.js';
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/crdt/index.js"],"names":[],"mappings":"AAmBO,4BANI,IAAI,YAAY,QAChB,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE,OAC9B,MAAM,SACN,IAAI,WAAW,GACb,QAAQ,IAAI,MAAM,CAAC,CAoG/B;AAYM,4BANI,IAAI,YAAY,QAChB,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE,OAC9B,MAAM,iCAEJ,QAAQ,IAAI,MAAM,CAAC,CAI/B;AAYM,6BAJI,IAAI,YAAY,QAChB,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE;UACZ,IAAI,SAAS;mBAsEzC;AAOM,4BAJI,IAAI,YAAY,QAChB,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE,OAC9B,MAAM,6GAShB;AAOM,gCAJI,IAAI,YAAY,QAChB,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,EAAE,kHAUxC;qBA9OoB,UAAU"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/crdt/index.js"],"names":[],"mappings":"AAmBO,4BANI,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAC9B,MAAM,SACN,GAAG,CAAC,WAAW,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAoG/B;AAYM,4BANI,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAC9B,MAAM,YACN,MAAM,GACJ,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAI/B;AAYM,6BAJI,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,GAC5B,OAAO,CAAC;IAAE,IAAI,EAAE,GAAG,CAAC,SAAS,CAAA;CAAE,GAAG,GAAG,CAAC,SAAS,CAAC,CAsE5D;AAOM,4BAJI,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAC9B,MAAM,6GAShB;AAOM,gCAJI,GAAG,CAAC,YAAY,QAChB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,YAC9B,GAAG,CAAC,cAAc,kEAS5B;qBA9OoB,UAAU"}