@atproto/repo 0.8.12 → 0.9.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 (105) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/block-map.d.ts +15 -16
  4. package/dist/block-map.d.ts.map +1 -1
  5. package/dist/block-map.js +9 -44
  6. package/dist/block-map.js.map +1 -1
  7. package/dist/car.d.ts +8 -8
  8. package/dist/car.d.ts.map +1 -1
  9. package/dist/car.js +107 -37
  10. package/dist/car.js.map +1 -1
  11. package/dist/cid-set.d.ts +8 -7
  12. package/dist/cid-set.d.ts.map +1 -1
  13. package/dist/cid-set.js +11 -4
  14. package/dist/cid-set.js.map +1 -1
  15. package/dist/data-diff.d.ts +10 -10
  16. package/dist/data-diff.d.ts.map +1 -1
  17. package/dist/data-diff.js.map +1 -1
  18. package/dist/error.d.ts +10 -10
  19. package/dist/error.d.ts.map +1 -1
  20. package/dist/error.js.map +1 -1
  21. package/dist/logger.d.ts.map +1 -1
  22. package/dist/logger.js +1 -0
  23. package/dist/logger.js.map +1 -1
  24. package/dist/mst/mst.d.ts +29 -29
  25. package/dist/mst/mst.d.ts.map +1 -1
  26. package/dist/mst/mst.js +12 -13
  27. package/dist/mst/mst.js.map +1 -1
  28. package/dist/mst/util.d.ts +2 -2
  29. package/dist/mst/util.d.ts.map +1 -1
  30. package/dist/mst/util.js +19 -40
  31. package/dist/mst/util.js.map +1 -1
  32. package/dist/parse.d.ts +6 -7
  33. package/dist/parse.d.ts.map +1 -1
  34. package/dist/parse.js +2 -3
  35. package/dist/parse.js.map +1 -1
  36. package/dist/readable-repo.d.ts +6 -7
  37. package/dist/readable-repo.d.ts.map +1 -1
  38. package/dist/readable-repo.js +0 -1
  39. package/dist/readable-repo.js.map +1 -1
  40. package/dist/repo.d.ts +4 -4
  41. package/dist/repo.d.ts.map +1 -1
  42. package/dist/repo.js +11 -10
  43. package/dist/repo.js.map +1 -1
  44. package/dist/storage/memory-blockstore.d.ts +9 -9
  45. package/dist/storage/memory-blockstore.d.ts.map +1 -1
  46. package/dist/storage/memory-blockstore.js.map +1 -1
  47. package/dist/storage/readable-blockstore.d.ts +11 -12
  48. package/dist/storage/readable-blockstore.d.ts.map +1 -1
  49. package/dist/storage/readable-blockstore.js +2 -36
  50. package/dist/storage/readable-blockstore.js.map +1 -1
  51. package/dist/storage/sync-storage.d.ts +5 -5
  52. package/dist/storage/sync-storage.d.ts.map +1 -1
  53. package/dist/storage/sync-storage.js.map +1 -1
  54. package/dist/storage/types.d.ts +25 -26
  55. package/dist/storage/types.d.ts.map +1 -1
  56. package/dist/storage/types.js +0 -1
  57. package/dist/storage/types.js.map +1 -1
  58. package/dist/sync/consumer.d.ts +3 -3
  59. package/dist/sync/consumer.d.ts.map +1 -1
  60. package/dist/sync/consumer.js +1 -1
  61. package/dist/sync/consumer.js.map +1 -1
  62. package/dist/sync/provider.d.ts +3 -3
  63. package/dist/sync/provider.d.ts.map +1 -1
  64. package/dist/sync/provider.js.map +1 -1
  65. package/dist/types.d.ts +127 -100
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js +53 -13
  68. package/dist/types.js.map +1 -1
  69. package/dist/util.d.ts +8 -3
  70. package/dist/util.d.ts.map +1 -1
  71. package/dist/util.js +33 -17
  72. package/dist/util.js.map +1 -1
  73. package/package.json +6 -7
  74. package/src/block-map.ts +31 -28
  75. package/src/car.ts +124 -46
  76. package/src/cid-set.ts +16 -10
  77. package/src/data-diff.ts +10 -10
  78. package/src/error.ts +6 -6
  79. package/src/logger.ts +1 -0
  80. package/src/mst/mst.ts +24 -28
  81. package/src/mst/util.ts +24 -11
  82. package/src/parse.ts +8 -10
  83. package/src/readable-repo.ts +6 -9
  84. package/src/repo.ts +13 -12
  85. package/src/storage/memory-blockstore.ts +8 -8
  86. package/src/storage/readable-blockstore.ts +12 -15
  87. package/src/storage/sync-storage.ts +4 -4
  88. package/src/storage/types.ts +25 -27
  89. package/src/sync/consumer.ts +5 -5
  90. package/src/sync/provider.ts +10 -7
  91. package/src/types.ts +82 -43
  92. package/src/util.ts +31 -16
  93. package/tests/_keys.ts +156 -156
  94. package/tests/_util.ts +28 -14
  95. package/tests/car.test.ts +31 -13
  96. package/tests/commit-proof-fixtures.json +79 -57
  97. package/tests/commit-proofs.test.ts +3 -3
  98. package/tests/covering-proofs.test.ts +6 -6
  99. package/tests/mst.test.ts +18 -19
  100. package/tests/proofs.test.ts +8 -8
  101. package/tests/repo.test.ts +1 -1
  102. package/tests/sync.test.ts +3 -6
  103. package/bench/mst.bench.ts +0 -165
  104. package/bench/repo.bench.ts +0 -48
  105. package/tsconfig.tests.tsbuildinfo +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAwB,MAAM,kBAAkB,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EACL,MAAM,EACN,cAAc,EACd,oBAAoB,EAEpB,UAAU,EAEV,mBAAmB,EACnB,cAAc,EAEf,MAAM,SAAS,CAAA;AAEhB,eAAO,MAAM,oBAAoB,GAC/B,MAAM,QAAQ,KACb,OAAO,CAAC,mBAAmB,EAAE,CA+B/B,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,WAAW,mBAAmB,EAAE,KAC/B,oBAAoB,EAUtB,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,UAI1C,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,YAAY,MAAM,EAAE,MAAM,MAAM,KAAG,MAEhE,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,OAEhD,CAAA;AAED,eAAO,MAAM,UAAU,GACrB,UAAU,cAAc,EACxB,SAAS,OAAO,KACf,OAAO,CAAC,MAAM,CAOhB,CAAA;AAED,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,QAAQ,MAAM,KACb,OAAO,CAAC,OAAO,CAIjB,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,KAAK,UAAU,KAAG,QAE3C,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,KAAK,UAAU,KAAG,UAMjD,CAAA;AAED,eAAO,MAAM,YAAY,GAAU,KAAK,QAAQ,4CAE/C,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,QAAQ,cAAc,GAAG,MAAM,KAAG,MAUhE,CAAA"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEzC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAiB,MAAM,mBAAmB,CAAA;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EACL,MAAM,EACN,cAAc,EACd,oBAAoB,EAEpB,UAAU,EAEV,mBAAmB,EACnB,cAAc,EAEf,MAAM,SAAS,CAAA;AAEhB,eAAO,MAAM,oBAAoB,GAC/B,MAAM,QAAQ,KACb,OAAO,CAAC,mBAAmB,EAAE,CA+B/B,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,WAAW,mBAAmB,EAAE,KAC/B,oBAAoB,EAUtB,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,UAI1C,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,YAAY,MAAM,EAAE,MAAM,MAAM,KAAG,MAEhE,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,OAEhD,CAAA;AAED,eAAO,MAAM,UAAU,GACrB,UAAU,cAAc,EACxB,SAAS,OAAO,KACf,OAAO,CAAC,MAAM,CAOhB,CAAA;AAED,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,QAAQ,MAAM,KACb,OAAO,CAAC,OAAO,CAIjB,CAAA;AAED,eAAO,MAAM,SAAS,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,QAAsB,CAAA;AAEnE,eAAO,MAAM,eAAe,GAAI,KAAK,UAAU,KAAG,MAMjD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC,GAAG,CAAkB,CAAA;AAE3E,eAAO,MAAM,cAAc,GAAI,QAAQ,cAAc,GAAG,MAAM,KAAG,MAUhE,CAAA;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,wCAIzE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,CAUrE"}
package/dist/util.js CHANGED
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable import/no-deprecated */
3
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
3
  if (k2 === undefined) k2 = k;
5
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -35,10 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
35
34
  })();
36
35
  Object.defineProperty(exports, "__esModule", { value: true });
37
36
  exports.ensureV3Commit = exports.cidForRecord = exports.cborToLexRecord = exports.cborToLex = exports.verifyCommitSig = exports.signCommit = exports.metaEqual = exports.formatDataKey = exports.parseDataKey = exports.ensureCreates = exports.diffToWriteDescripts = void 0;
38
- const cbor = __importStar(require("@ipld/dag-cbor"));
39
- const common_1 = require("@atproto/common");
37
+ exports.concatBytesAsync = concatBytesAsync;
38
+ exports.concatBytes = concatBytes;
39
+ const common_web_1 = require("@atproto/common-web");
40
40
  const crypto = __importStar(require("@atproto/crypto"));
41
- const lexicon_1 = require("@atproto/lexicon");
41
+ const cbor = __importStar(require("@atproto/lex-cbor"));
42
+ const lex_data_1 = require("@atproto/lex-data");
42
43
  const types_1 = require("./types");
43
44
  const diffToWriteDescripts = (diff) => {
44
45
  return Promise.all([
@@ -87,10 +88,10 @@ const ensureCreates = (descripts) => {
87
88
  };
88
89
  exports.ensureCreates = ensureCreates;
89
90
  const parseDataKey = (key) => {
90
- const parts = key.split('/');
91
- if (parts.length !== 2)
91
+ const { length, 0: collection, 1: rkey } = key.split('/');
92
+ if (length !== 2)
92
93
  throw new Error(`Invalid record key: ${key}`);
93
- return { collection: parts[0], rkey: parts[1] };
94
+ return { collection, rkey };
94
95
  };
95
96
  exports.parseDataKey = parseDataKey;
96
97
  const formatDataKey = (collection, rkey) => {
@@ -116,22 +117,16 @@ const verifyCommitSig = async (commit, didKey) => {
116
117
  return crypto.verifySignature(didKey, encoded, sig);
117
118
  };
118
119
  exports.verifyCommitSig = verifyCommitSig;
119
- const cborToLex = (val) => {
120
- return (0, lexicon_1.ipldToLex)((0, common_1.cborDecode)(val));
121
- };
122
- exports.cborToLex = cborToLex;
120
+ exports.cborToLex = cbor.decode;
123
121
  const cborToLexRecord = (val) => {
124
122
  const parsed = (0, exports.cborToLex)(val);
125
- if (!common_1.check.is(parsed, common_1.schema.map)) {
123
+ if (!(0, lex_data_1.isPlainObject)(parsed)) {
126
124
  throw new Error('lexicon records be a json object');
127
125
  }
128
126
  return parsed;
129
127
  };
130
128
  exports.cborToLexRecord = cborToLexRecord;
131
- const cidForRecord = async (val) => {
132
- return (0, common_1.cidForCbor)((0, lexicon_1.lexToIpld)(val));
133
- };
134
- exports.cidForRecord = cidForRecord;
129
+ exports.cidForRecord = cbor.cidForLex;
135
130
  const ensureV3Commit = (commit) => {
136
131
  if (commit.version === 3) {
137
132
  return commit;
@@ -140,9 +135,30 @@ const ensureV3Commit = (commit) => {
140
135
  return {
141
136
  ...commit,
142
137
  version: 3,
143
- rev: commit.rev ?? common_1.TID.nextStr(),
138
+ rev: commit.rev ?? common_web_1.TID.nextStr(),
144
139
  };
145
140
  }
146
141
  };
147
142
  exports.ensureV3Commit = ensureV3Commit;
143
+ async function concatBytesAsync(iterable) {
144
+ const chunks = [];
145
+ for await (const chunk of iterable)
146
+ chunks.push(chunk);
147
+ return concatBytes(chunks);
148
+ }
149
+ /**
150
+ * This is the same as {@link Buffer.concat}, without the `totalLength` argument.
151
+ */
152
+ function concatBytes(chunks) {
153
+ let totalLength = 0;
154
+ for (const chunk of chunks)
155
+ totalLength += chunk.byteLength;
156
+ const result = new Uint8Array(totalLength);
157
+ let offset = 0;
158
+ for (const chunk of chunks) {
159
+ result.set(chunk, offset);
160
+ offset += chunk.byteLength;
161
+ }
162
+ return result;
163
+ }
148
164
  //# sourceMappingURL=util.js.map
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable import/no-deprecated */\n\nimport * as cbor from '@ipld/dag-cbor'\nimport { TID, cborDecode, check, cidForCbor, schema } from '@atproto/common'\nimport * as crypto from '@atproto/crypto'\nimport { Keypair } from '@atproto/crypto'\nimport { LexValue, RepoRecord, ipldToLex, lexToIpld } from '@atproto/lexicon'\nimport { DataDiff } from './data-diff'\nimport {\n Commit,\n LegacyV2Commit,\n RecordCreateDescript,\n RecordDeleteDescript,\n RecordPath,\n RecordUpdateDescript,\n RecordWriteDescript,\n UnsignedCommit,\n WriteOpAction,\n} from './types'\n\nexport const diffToWriteDescripts = (\n diff: DataDiff,\n): Promise<RecordWriteDescript[]> => {\n return Promise.all([\n ...diff.addList().map(async (add) => {\n const { collection, rkey } = parseDataKey(add.key)\n return {\n action: WriteOpAction.Create,\n collection,\n rkey,\n cid: add.cid,\n } as RecordCreateDescript\n }),\n ...diff.updateList().map(async (upd) => {\n const { collection, rkey } = parseDataKey(upd.key)\n return {\n action: WriteOpAction.Update,\n collection,\n rkey,\n cid: upd.cid,\n prev: upd.prev,\n } as RecordUpdateDescript\n }),\n ...diff.deleteList().map((del) => {\n const { collection, rkey } = parseDataKey(del.key)\n return {\n action: WriteOpAction.Delete,\n collection,\n rkey,\n cid: del.cid,\n } as RecordDeleteDescript\n }),\n ])\n}\n\nexport const ensureCreates = (\n descripts: RecordWriteDescript[],\n): RecordCreateDescript[] => {\n const creates: RecordCreateDescript[] = []\n for (const descript of descripts) {\n if (descript.action !== WriteOpAction.Create) {\n throw new Error(`Unexpected action: ${descript.action}`)\n } else {\n creates.push(descript)\n }\n }\n return creates\n}\n\nexport const parseDataKey = (key: string): RecordPath => {\n const parts = key.split('/')\n if (parts.length !== 2) throw new Error(`Invalid record key: ${key}`)\n return { collection: parts[0], rkey: parts[1] }\n}\n\nexport const formatDataKey = (collection: string, rkey: string): string => {\n return collection + '/' + rkey\n}\n\nexport const metaEqual = (a: Commit, b: Commit): boolean => {\n return a.did === b.did && a.version === b.version\n}\n\nexport const signCommit = async (\n unsigned: UnsignedCommit,\n keypair: Keypair,\n): Promise<Commit> => {\n const encoded = cbor.encode(unsigned)\n const sig = await keypair.sign(encoded)\n return {\n ...unsigned,\n sig,\n }\n}\n\nexport const verifyCommitSig = async (\n commit: Commit,\n didKey: string,\n): Promise<boolean> => {\n const { sig, ...rest } = commit\n const encoded = cbor.encode(rest)\n return crypto.verifySignature(didKey, encoded, sig)\n}\n\nexport const cborToLex = (val: Uint8Array): LexValue => {\n return ipldToLex(cborDecode(val))\n}\n\nexport const cborToLexRecord = (val: Uint8Array): RepoRecord => {\n const parsed = cborToLex(val)\n if (!check.is(parsed, schema.map)) {\n throw new Error('lexicon records be a json object')\n }\n return parsed\n}\n\nexport const cidForRecord = async (val: LexValue) => {\n return cidForCbor(lexToIpld(val))\n}\n\nexport const ensureV3Commit = (commit: LegacyV2Commit | Commit): Commit => {\n if (commit.version === 3) {\n return commit\n } else {\n return {\n ...commit,\n version: 3,\n rev: commit.rev ?? TID.nextStr(),\n }\n }\n}\n"]}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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","sourcesContent":["import { TID } from '@atproto/common-web'\nimport * as crypto from '@atproto/crypto'\nimport { Keypair } from '@atproto/crypto'\nimport * as cbor from '@atproto/lex-cbor'\nimport { Cid, LexMap, LexValue, isPlainObject } from '@atproto/lex-data'\nimport { DataDiff } from './data-diff'\nimport {\n Commit,\n LegacyV2Commit,\n RecordCreateDescript,\n RecordDeleteDescript,\n RecordPath,\n RecordUpdateDescript,\n RecordWriteDescript,\n UnsignedCommit,\n WriteOpAction,\n} from './types'\n\nexport const diffToWriteDescripts = (\n diff: DataDiff,\n): Promise<RecordWriteDescript[]> => {\n return Promise.all([\n ...diff.addList().map(async (add) => {\n const { collection, rkey } = parseDataKey(add.key)\n return {\n action: WriteOpAction.Create,\n collection,\n rkey,\n cid: add.cid,\n } as RecordCreateDescript\n }),\n ...diff.updateList().map(async (upd) => {\n const { collection, rkey } = parseDataKey(upd.key)\n return {\n action: WriteOpAction.Update,\n collection,\n rkey,\n cid: upd.cid,\n prev: upd.prev,\n } as RecordUpdateDescript\n }),\n ...diff.deleteList().map((del) => {\n const { collection, rkey } = parseDataKey(del.key)\n return {\n action: WriteOpAction.Delete,\n collection,\n rkey,\n cid: del.cid,\n } as RecordDeleteDescript\n }),\n ])\n}\n\nexport const ensureCreates = (\n descripts: RecordWriteDescript[],\n): RecordCreateDescript[] => {\n const creates: RecordCreateDescript[] = []\n for (const descript of descripts) {\n if (descript.action !== WriteOpAction.Create) {\n throw new Error(`Unexpected action: ${descript.action}`)\n } else {\n creates.push(descript)\n }\n }\n return creates\n}\n\nexport const parseDataKey = (key: string): RecordPath => {\n const { length, 0: collection, 1: rkey } = key.split('/')\n if (length !== 2) throw new Error(`Invalid record key: ${key}`)\n return { collection, rkey }\n}\n\nexport const formatDataKey = (collection: string, rkey: string): string => {\n return collection + '/' + rkey\n}\n\nexport const metaEqual = (a: Commit, b: Commit): boolean => {\n return a.did === b.did && a.version === b.version\n}\n\nexport const signCommit = async (\n unsigned: UnsignedCommit,\n keypair: Keypair,\n): Promise<Commit> => {\n const encoded = cbor.encode(unsigned)\n const sig = await keypair.sign(encoded)\n return {\n ...unsigned,\n sig,\n }\n}\n\nexport const verifyCommitSig = async (\n commit: Commit,\n didKey: string,\n): Promise<boolean> => {\n const { sig, ...rest } = commit\n const encoded = cbor.encode(rest)\n return crypto.verifySignature(didKey, encoded, sig)\n}\n\nexport const cborToLex: (val: Uint8Array) => LexValue = cbor.decode\n\nexport const cborToLexRecord = (val: Uint8Array): LexMap => {\n const parsed = cborToLex(val)\n if (!isPlainObject(parsed)) {\n throw new Error('lexicon records be a json object')\n }\n return parsed\n}\n\nexport const cidForRecord: (val: LexValue) => Promise<Cid> = cbor.cidForLex\n\nexport const ensureV3Commit = (commit: LegacyV2Commit | Commit): Commit => {\n if (commit.version === 3) {\n return commit\n } else {\n return {\n ...commit,\n version: 3,\n rev: commit.rev ?? TID.nextStr(),\n }\n }\n}\n\nexport async function concatBytesAsync(iterable: AsyncIterable<Uint8Array>) {\n const chunks: Uint8Array[] = []\n for await (const chunk of iterable) chunks.push(chunk)\n return concatBytes(chunks)\n}\n\n/**\n * This is the same as {@link Buffer.concat}, without the `totalLength` argument.\n */\nexport function concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n let totalLength = 0\n for (const chunk of chunks) totalLength += chunk.byteLength\n const result = new Uint8Array(totalLength)\n let offset = 0\n for (const chunk of chunks) {\n result.set(chunk, offset)\n offset += chunk.byteLength\n }\n return result\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/repo",
3
- "version": "0.8.12",
3
+ "version": "0.9.0",
4
4
  "license": "MIT",
5
5
  "description": "atproto repo and MST implementation",
6
6
  "keywords": [
@@ -19,15 +19,14 @@
19
19
  "main": "dist/index.js",
20
20
  "types": "dist/index.d.ts",
21
21
  "dependencies": {
22
- "@ipld/dag-cbor": "^7.0.0",
23
- "multiformats": "^9.9.0",
24
- "uint8arrays": "3.0.0",
25
22
  "varint": "^6.0.0",
26
23
  "zod": "^3.23.8",
27
- "@atproto/common": "^0.5.3",
28
- "@atproto/common-web": "^0.4.7",
24
+ "@atproto/common": "^0.5.15",
25
+ "@atproto/common-web": "^0.4.19",
29
26
  "@atproto/crypto": "^0.4.5",
30
- "@atproto/lexicon": "^0.6.0"
27
+ "@atproto/lex-cbor": "^0.0.15",
28
+ "@atproto/lex-data": "^0.0.14",
29
+ "@atproto/syntax": "^0.5.2"
31
30
  },
32
31
  "devDependencies": {
33
32
  "jest": "^28.1.2",
package/src/block-map.ts CHANGED
@@ -1,14 +1,16 @@
1
- /* eslint-disable import/no-deprecated */
2
-
3
- import { CID } from 'multiformats/cid'
4
- import * as uint8arrays from 'uint8arrays'
5
- import { dataToCborBlock } from '@atproto/common'
6
- import { LexValue, lexToIpld } from '@atproto/lexicon'
7
-
8
- export class BlockMap implements Iterable<[cid: CID, bytes: Uint8Array]> {
1
+ import { encode } from '@atproto/lex-cbor'
2
+ import {
3
+ Cid,
4
+ LexValue,
5
+ cidForCbor,
6
+ parseCid,
7
+ ui8Equals,
8
+ } from '@atproto/lex-data'
9
+
10
+ export class BlockMap implements Iterable<[cid: Cid, bytes: Uint8Array]> {
9
11
  private map: Map<string, Uint8Array> = new Map()
10
12
 
11
- constructor(entries?: Iterable<readonly [cid: CID, bytes: Uint8Array]>) {
13
+ constructor(entries?: Iterable<readonly [cid: Cid, bytes: Uint8Array]>) {
12
14
  if (entries) {
13
15
  for (const [cid, bytes] of entries) {
14
16
  this.set(cid, bytes)
@@ -16,28 +18,29 @@ export class BlockMap implements Iterable<[cid: CID, bytes: Uint8Array]> {
16
18
  }
17
19
  }
18
20
 
19
- async add(value: LexValue): Promise<CID> {
20
- const block = await dataToCborBlock(lexToIpld(value))
21
- this.set(block.cid, block.bytes)
22
- return block.cid
21
+ async add(value: LexValue): Promise<Cid> {
22
+ const bytes = encode(value)
23
+ const cid = await cidForCbor(bytes)
24
+ this.set(cid, bytes)
25
+ return cid
23
26
  }
24
27
 
25
- set(cid: CID, bytes: Uint8Array): BlockMap {
28
+ set(cid: Cid, bytes: Uint8Array): BlockMap {
26
29
  this.map.set(cid.toString(), bytes)
27
30
  return this
28
31
  }
29
32
 
30
- get(cid: CID): Uint8Array | undefined {
33
+ get(cid: Cid): Uint8Array | undefined {
31
34
  return this.map.get(cid.toString())
32
35
  }
33
36
 
34
- delete(cid: CID): BlockMap {
37
+ delete(cid: Cid): BlockMap {
35
38
  this.map.delete(cid.toString())
36
39
  return this
37
40
  }
38
41
 
39
- getMany(cids: CID[]): { blocks: BlockMap; missing: CID[] } {
40
- const missing: CID[] = []
42
+ getMany(cids: Cid[]): { blocks: BlockMap; missing: Cid[] } {
43
+ const missing: Cid[] = []
41
44
  const blocks = new BlockMap()
42
45
  for (const cid of cids) {
43
46
  const got = this.map.get(cid.toString())
@@ -50,7 +53,7 @@ export class BlockMap implements Iterable<[cid: CID, bytes: Uint8Array]> {
50
53
  return { blocks, missing }
51
54
  }
52
55
 
53
- has(cid: CID): boolean {
56
+ has(cid: Cid): boolean {
54
57
  return this.map.has(cid.toString())
55
58
  }
56
59
 
@@ -58,7 +61,7 @@ export class BlockMap implements Iterable<[cid: CID, bytes: Uint8Array]> {
58
61
  this.map.clear()
59
62
  }
60
63
 
61
- forEach(cb: (bytes: Uint8Array, cid: CID) => void): void {
64
+ forEach(cb: (bytes: Uint8Array, cid: Cid) => void): void {
62
65
  for (const [cid, bytes] of this) cb(bytes, cid)
63
66
  }
64
67
 
@@ -66,7 +69,7 @@ export class BlockMap implements Iterable<[cid: CID, bytes: Uint8Array]> {
66
69
  return Array.from(this, toEntry)
67
70
  }
68
71
 
69
- cids(): CID[] {
72
+ cids(): Cid[] {
70
73
  return Array.from(this.keys())
71
74
  }
72
75
 
@@ -92,16 +95,16 @@ export class BlockMap implements Iterable<[cid: CID, bytes: Uint8Array]> {
92
95
  for (const [cid, bytes] of this) {
93
96
  const otherBytes = other.get(cid)
94
97
  if (!otherBytes) return false
95
- if (!uint8arrays.equals(bytes, otherBytes)) {
98
+ if (!ui8Equals(bytes, otherBytes)) {
96
99
  return false
97
100
  }
98
101
  }
99
102
  return true
100
103
  }
101
104
 
102
- *keys(): Generator<CID, void, unknown> {
105
+ *keys(): Generator<Cid, void, unknown> {
103
106
  for (const key of this.map.keys()) {
104
- yield CID.parse(key)
107
+ yield parseCid(key)
105
108
  }
106
109
  }
107
110
 
@@ -109,19 +112,19 @@ export class BlockMap implements Iterable<[cid: CID, bytes: Uint8Array]> {
109
112
  yield* this.map.values()
110
113
  }
111
114
 
112
- *[Symbol.iterator](): Generator<[CID, Uint8Array], void, unknown> {
115
+ *[Symbol.iterator](): Generator<[Cid, Uint8Array], void, unknown> {
113
116
  for (const [key, value] of this.map) {
114
- yield [CID.parse(key), value]
117
+ yield [parseCid(key), value]
115
118
  }
116
119
  }
117
120
  }
118
121
 
119
- function toEntry([cid, bytes]: readonly [CID, Uint8Array]): Entry {
122
+ function toEntry([cid, bytes]: readonly [Cid, Uint8Array]): Entry {
120
123
  return { cid, bytes }
121
124
  }
122
125
 
123
126
  type Entry = {
124
- cid: CID
127
+ cid: Cid
125
128
  bytes: Uint8Array
126
129
  }
127
130
 
package/src/car.ts CHANGED
@@ -1,22 +1,13 @@
1
- /* eslint-disable import/no-deprecated */
2
-
3
1
  import { setImmediate } from 'node:timers/promises'
4
- import * as cbor from '@ipld/dag-cbor'
5
- import { CID } from 'multiformats/cid'
6
- import * as ui8 from 'uint8arrays'
7
2
  import * as varint from 'varint'
8
- import {
9
- check,
10
- parseCidFromBytes,
11
- schema,
12
- streamToBuffer,
13
- verifyCidForBytes,
14
- } from '@atproto/common'
3
+ import * as cbor from '@atproto/lex-cbor'
4
+ import { Cid, decodeCid, isCidForBytes } from '@atproto/lex-data'
15
5
  import { BlockMap } from './block-map'
16
- import { CarBlock } from './types'
6
+ import { CarBlock, schema } from './types'
7
+ import { concatBytesAsync } from './util'
17
8
 
18
9
  export async function* writeCarStream(
19
- root: CID | null,
10
+ root: Cid | null,
20
11
  blocks: AsyncIterable<CarBlock>,
21
12
  ): AsyncIterable<Uint8Array> {
22
13
  const header = new Uint8Array(
@@ -36,16 +27,15 @@ export async function* writeCarStream(
36
27
  }
37
28
  }
38
29
 
39
- export const blocksToCarFile = (
40
- root: CID | null,
30
+ export async function blocksToCarFile(
31
+ root: Cid | null,
41
32
  blocks: BlockMap,
42
- ): Promise<Uint8Array> => {
43
- const carStream = blocksToCarStream(root, blocks)
44
- return streamToBuffer(carStream)
33
+ ): Promise<Uint8Array> {
34
+ return concatBytesAsync(blocksToCarStream(root, blocks))
45
35
  }
46
36
 
47
37
  export const blocksToCarStream = (
48
- root: CID | null,
38
+ root: Cid | null,
49
39
  blocks: BlockMap,
50
40
  ): AsyncIterable<Uint8Array> => {
51
41
  return writeCarStream(root, iterateBlocks(blocks))
@@ -67,7 +57,7 @@ export type ReadCarOptions = {
67
57
  export const readCar = async (
68
58
  bytes: Uint8Array,
69
59
  opts?: ReadCarOptions,
70
- ): Promise<{ roots: CID[]; blocks: BlockMap }> => {
60
+ ): Promise<{ roots: Cid[]; blocks: BlockMap }> => {
71
61
  const { roots, blocks } = await readCarReader(new Ui8Reader(bytes), opts)
72
62
  const blockMap = new BlockMap()
73
63
  for await (const block of blocks) {
@@ -79,7 +69,7 @@ export const readCar = async (
79
69
  export const readCarWithRoot = async (
80
70
  bytes: Uint8Array,
81
71
  opts?: ReadCarOptions,
82
- ): Promise<{ root: CID; blocks: BlockMap }> => {
72
+ ): Promise<{ root: Cid; blocks: BlockMap }> => {
83
73
  const { roots, blocks } = await readCar(bytes, opts)
84
74
  if (roots.length !== 1) {
85
75
  throw new Error(`Expected one root, got ${roots.length}`)
@@ -98,7 +88,7 @@ export const readCarStream = async (
98
88
  car: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,
99
89
  opts?: ReadCarOptions,
100
90
  ): Promise<{
101
- roots: CID[]
91
+ roots: Cid[]
102
92
  blocks: CarBlockIterable
103
93
  }> => {
104
94
  return readCarReader(new BufferedReader(car), opts)
@@ -108,7 +98,7 @@ export const readCarReader = async (
108
98
  reader: BytesReader,
109
99
  opts?: ReadCarOptions,
110
100
  ): Promise<{
111
- roots: CID[]
101
+ roots: Cid[]
112
102
  blocks: CarBlockIterable
113
103
  }> => {
114
104
  try {
@@ -118,11 +108,12 @@ export const readCarReader = async (
118
108
  }
119
109
  const headerBytes = await reader.read(headerSize)
120
110
  const header = cbor.decode(headerBytes)
121
- if (!check.is(header, schema.carHeader)) {
122
- throw new Error('Could not parse CAR header')
111
+ const result = schema.carHeader.safeParse(header)
112
+ if (!result.success) {
113
+ throw new Error('Could not parse CAR header', { cause: result.error })
123
114
  }
124
115
  return {
125
- roots: header.roots,
116
+ roots: result.data.roots,
126
117
  blocks: readCarBlocksIter(reader, opts),
127
118
  }
128
119
  } catch (err) {
@@ -165,7 +156,7 @@ async function* readCarBlocksIterGenerator(
165
156
  break
166
157
  }
167
158
  const blockBytes = await reader.read(blockSize)
168
- const cid = parseCidFromBytes(blockBytes.subarray(0, 36))
159
+ const cid = decodeCid(blockBytes.subarray(0, 36))
169
160
  const bytes = blockBytes.subarray(36)
170
161
  yield { cid, bytes }
171
162
 
@@ -185,7 +176,9 @@ export async function* verifyIncomingCarBlocks(
185
176
  car: AsyncIterable<CarBlock>,
186
177
  ): AsyncGenerator<CarBlock, void, unknown> {
187
178
  for await (const block of car) {
188
- await verifyCidForBytes(block.cid, block.bytes)
179
+ if (!(await isCidForBytes(block.cid, block.bytes))) {
180
+ throw new Error(`Not a valid CID for bytes (${block.cid.toString()})`)
181
+ }
189
182
  yield block
190
183
  }
191
184
  }
@@ -207,7 +200,7 @@ const readVarint = async (reader: BytesReader): Promise<number | null> => {
207
200
  done = true
208
201
  }
209
202
  }
210
- const concatted = ui8.concat(bytes)
203
+ const concatted = Buffer.concat(bytes)
211
204
  return varint.decode(concatted)
212
205
  }
213
206
 
@@ -235,11 +228,18 @@ class Ui8Reader implements BytesReader {
235
228
  async close(): Promise<void> {}
236
229
  }
237
230
 
231
+ /**
232
+ * This code was optimized for performance. See
233
+ * {@link https://github.com/bluesky-social/atproto/pull/4729 #4729} for more details
234
+ * and benchmarks.
235
+ */
238
236
  class BufferedReader implements BytesReader {
239
- buffer: Uint8Array = new Uint8Array()
240
237
  iterator: Iterator<Uint8Array> | AsyncIterator<Uint8Array>
241
238
  isDone = false
242
239
 
240
+ /** fifo list of chunks to consume */
241
+ private chunks: Uint8Array[] = []
242
+
243
243
  constructor(stream: Iterable<Uint8Array> | AsyncIterable<Uint8Array>) {
244
244
  this.iterator =
245
245
  Symbol.asyncIterator in stream
@@ -247,32 +247,110 @@ class BufferedReader implements BytesReader {
247
247
  : stream[Symbol.iterator]()
248
248
  }
249
249
 
250
- async read(bytesToRead: number): Promise<Uint8Array> {
251
- await this.readUntilBuffered(bytesToRead)
252
- const value = this.buffer.subarray(0, bytesToRead)
253
- this.buffer = this.buffer.subarray(bytesToRead)
254
- return value
250
+ /** Number of bytes currently buffered and available for reading */
251
+ get bufferedByteLength() {
252
+ let total = 0
253
+ for (let i = 0; i < this.chunks.length; i++) {
254
+ total += this.chunks[i].byteLength
255
+ }
256
+ return total
255
257
  }
256
258
 
257
- private async readUntilBuffered(bytesToRead: number) {
258
- if (this.isDone) {
259
- return
259
+ /**
260
+ * @note concurrent reads are **NOT** supported by the current implementation
261
+ * and would require call to readUntilBuffered to be using a fifo lock for
262
+ * read()s to be processed in fifo order.
263
+ */
264
+ async read(bytesToRead: number): Promise<Uint8Array> {
265
+ const bytesNeeded = bytesToRead - this.bufferedByteLength
266
+ if (bytesNeeded > 0 && !this.isDone) {
267
+ await this.readUntilBuffered(bytesNeeded)
260
268
  }
261
- while (this.buffer.length < bytesToRead) {
269
+
270
+ const resultLength = Math.min(bytesToRead, this.bufferedByteLength)
271
+ if (resultLength <= 0) return new Uint8Array()
272
+
273
+ const firstChunk = this.consumeChunk(resultLength)
274
+ if (firstChunk.byteLength === resultLength) {
275
+ // If the data consumed from the first chunk contains all we need, return
276
+ // it as-is. This allows to avoid any copy operation.
277
+ return firstChunk
278
+ }
279
+
280
+ // The first chunk does not have all the data we need. We have to copy
281
+ // multiple chunks into a larger buffer
282
+ const result = new Uint8Array(resultLength)
283
+ let resultWriteIndex = 0
284
+
285
+ // Copy the first chunk into the result buffer
286
+ result.set(firstChunk, resultWriteIndex)
287
+ resultWriteIndex += firstChunk.byteLength
288
+
289
+ // Copy more chunks as needed (we use do-while because we *know* we need
290
+ // more than one chunk)
291
+ do {
292
+ const missingLength = resultLength - resultWriteIndex
293
+ const currentChunk = this.consumeChunk(missingLength)
294
+
295
+ result.set(currentChunk, resultWriteIndex)
296
+ resultWriteIndex += currentChunk.byteLength
297
+ } while (resultWriteIndex < resultLength)
298
+
299
+ return result
300
+ }
301
+
302
+ private async readUntilBuffered(bytesNeeded: number) {
303
+ let bytesRead = 0
304
+ while (bytesRead < bytesNeeded) {
262
305
  const next = await this.iterator.next()
263
306
  if (next.done) {
264
307
  this.isDone = true
265
- return
308
+ break
309
+ } else {
310
+ this.chunks.push(next.value)
311
+ bytesRead += next.value.byteLength
266
312
  }
267
- this.buffer = ui8.concat([this.buffer, next.value])
313
+ }
314
+ return bytesRead
315
+ }
316
+
317
+ private consumeChunk(bytesToConsume: number) {
318
+ const firstChunk = this.chunks[0]!
319
+ if (bytesToConsume < firstChunk.byteLength) {
320
+ // return a sub-view of the data being read and replace the first chunk
321
+ // with a sub-view that does not contain that data.
322
+
323
+ // @NOTE for some reason, subarray() revealed to be 7-8% slower in NodeJS
324
+ // benchmarks.
325
+
326
+ // this.chunks[0] = firstChunk.subarray(bytesToConsume)
327
+ // return firstChunk.subarray(0, bytesToConsume)
328
+
329
+ this.chunks[0] = new Uint8Array(
330
+ firstChunk.buffer,
331
+ firstChunk.byteOffset + bytesToConsume,
332
+ firstChunk.byteLength - bytesToConsume,
333
+ )
334
+ return new Uint8Array(
335
+ firstChunk.buffer,
336
+ firstChunk.byteOffset,
337
+ bytesToConsume,
338
+ )
339
+ } else {
340
+ // First chunk is being read in full, discard it
341
+ this.chunks.shift()
342
+ return firstChunk
268
343
  }
269
344
  }
270
345
 
271
346
  async close(): Promise<void> {
272
- if (!this.isDone && this.iterator.return) {
273
- await this.iterator.return()
347
+ try {
348
+ if (!this.isDone && this.iterator.return) {
349
+ await this.iterator.return()
350
+ }
351
+ } finally {
352
+ this.isDone = true
353
+ this.chunks.length = 0
274
354
  }
275
- this.isDone = true
276
- this.buffer = new Uint8Array()
277
355
  }
278
356
  }
package/src/cid-set.ts CHANGED
@@ -1,34 +1,34 @@
1
- import { CID } from 'multiformats'
1
+ import { Cid, parseCid } from '@atproto/lex-data'
2
2
 
3
- export class CidSet {
3
+ export class CidSet implements Iterable<Cid> {
4
4
  private set: Set<string>
5
5
 
6
- constructor(arr: CID[] = []) {
6
+ constructor(arr: Cid[] = []) {
7
7
  const strArr = arr.map((c) => c.toString())
8
8
  this.set = new Set(strArr)
9
9
  }
10
10
 
11
- add(cid: CID): CidSet {
11
+ add(cid: Cid): CidSet {
12
12
  this.set.add(cid.toString())
13
13
  return this
14
14
  }
15
15
 
16
16
  addSet(toMerge: CidSet): CidSet {
17
- toMerge.toList().map((c) => this.add(c))
17
+ for (const c of toMerge.set) this.set.add(c)
18
18
  return this
19
19
  }
20
20
 
21
21
  subtractSet(toSubtract: CidSet): CidSet {
22
- toSubtract.toList().map((c) => this.delete(c))
22
+ for (const c of toSubtract.set) this.set.delete(c)
23
23
  return this
24
24
  }
25
25
 
26
- delete(cid: CID) {
26
+ delete(cid: Cid) {
27
27
  this.set.delete(cid.toString())
28
28
  return this
29
29
  }
30
30
 
31
- has(cid: CID): boolean {
31
+ has(cid: Cid): boolean {
32
32
  return this.set.has(cid.toString())
33
33
  }
34
34
 
@@ -41,8 +41,14 @@ export class CidSet {
41
41
  return this
42
42
  }
43
43
 
44
- toList(): CID[] {
45
- return [...this.set].map((c) => CID.parse(c))
44
+ toList(): Cid[] {
45
+ return Array.from(this)
46
+ }
47
+
48
+ *[Symbol.iterator](): Generator<Cid, void, unknown> {
49
+ for (const c of this.set) {
50
+ yield parseCid(c)
51
+ }
46
52
  }
47
53
  }
48
54