@atproto/pds 0.5.27 → 0.5.28

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 (84) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/actor-store/blob/transactor.d.ts +1 -5
  3. package/dist/actor-store/blob/transactor.d.ts.map +1 -1
  4. package/dist/actor-store/blob/transactor.js +72 -43
  5. package/dist/actor-store/blob/transactor.js.map +1 -1
  6. package/dist/api/com/atproto/repo/uploadBlob.d.ts.map +1 -1
  7. package/dist/api/com/atproto/repo/uploadBlob.js +1 -1
  8. package/dist/api/com/atproto/repo/uploadBlob.js.map +1 -1
  9. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.d.ts +3 -0
  10. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.d.ts.map +1 -0
  11. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.d.ts +66 -0
  12. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.d.ts.map +1 -0
  13. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.js +26 -0
  14. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.js.map +1 -0
  15. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.js +6 -0
  16. package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.js.map +1 -0
  17. package/dist/lexicons/app/bsky/actor.d.ts +1 -0
  18. package/dist/lexicons/app/bsky/actor.d.ts.map +1 -1
  19. package/dist/lexicons/app/bsky/actor.js +1 -0
  20. package/dist/lexicons/app/bsky/actor.js.map +1 -1
  21. package/dist/lexicons/app/bsky/authCreatePosts.defs.d.ts.map +1 -1
  22. package/dist/lexicons/app/bsky/authCreatePosts.defs.js +5 -0
  23. package/dist/lexicons/app/bsky/authCreatePosts.defs.js.map +1 -1
  24. package/dist/lexicons/app/bsky/authFullApp.defs.d.ts.map +1 -1
  25. package/dist/lexicons/app/bsky/authFullApp.defs.js +5 -0
  26. package/dist/lexicons/app/bsky/authFullApp.defs.js.map +1 -1
  27. package/dist/lexicons/app/bsky/video/abortUpload.d.ts +3 -0
  28. package/dist/lexicons/app/bsky/video/abortUpload.d.ts.map +1 -0
  29. package/dist/lexicons/app/bsky/video/abortUpload.defs.d.ts +53 -0
  30. package/dist/lexicons/app/bsky/video/abortUpload.defs.d.ts.map +1 -0
  31. package/dist/lexicons/app/bsky/video/abortUpload.defs.js +26 -0
  32. package/dist/lexicons/app/bsky/video/abortUpload.defs.js.map +1 -0
  33. package/dist/lexicons/app/bsky/video/abortUpload.js +6 -0
  34. package/dist/lexicons/app/bsky/video/abortUpload.js.map +1 -0
  35. package/dist/lexicons/app/bsky/video/defs.defs.d.ts +1 -1
  36. package/dist/lexicons/app/bsky/video/defs.defs.d.ts.map +1 -1
  37. package/dist/lexicons/app/bsky/video/defs.defs.js.map +1 -1
  38. package/dist/lexicons/app/bsky/video/finishUpload.d.ts +3 -0
  39. package/dist/lexicons/app/bsky/video/finishUpload.d.ts.map +1 -0
  40. package/dist/lexicons/app/bsky/video/finishUpload.defs.d.ts +42 -0
  41. package/dist/lexicons/app/bsky/video/finishUpload.defs.d.ts.map +1 -0
  42. package/dist/lexicons/app/bsky/video/finishUpload.defs.js +30 -0
  43. package/dist/lexicons/app/bsky/video/finishUpload.defs.js.map +1 -0
  44. package/dist/lexicons/app/bsky/video/finishUpload.js +6 -0
  45. package/dist/lexicons/app/bsky/video/finishUpload.js.map +1 -0
  46. package/dist/lexicons/app/bsky/video/getUploadStatus.d.ts +3 -0
  47. package/dist/lexicons/app/bsky/video/getUploadStatus.d.ts.map +1 -0
  48. package/dist/lexicons/app/bsky/video/getUploadStatus.defs.d.ts +73 -0
  49. package/dist/lexicons/app/bsky/video/getUploadStatus.defs.d.ts.map +1 -0
  50. package/dist/lexicons/app/bsky/video/getUploadStatus.defs.js +30 -0
  51. package/dist/lexicons/app/bsky/video/getUploadStatus.defs.js.map +1 -0
  52. package/dist/lexicons/app/bsky/video/getUploadStatus.js +6 -0
  53. package/dist/lexicons/app/bsky/video/getUploadStatus.js.map +1 -0
  54. package/dist/lexicons/app/bsky/video/startUpload.d.ts +3 -0
  55. package/dist/lexicons/app/bsky/video/startUpload.d.ts.map +1 -0
  56. package/dist/lexicons/app/bsky/video/startUpload.defs.d.ts +63 -0
  57. package/dist/lexicons/app/bsky/video/startUpload.defs.d.ts.map +1 -0
  58. package/dist/lexicons/app/bsky/video/startUpload.defs.js +36 -0
  59. package/dist/lexicons/app/bsky/video/startUpload.defs.js.map +1 -0
  60. package/dist/lexicons/app/bsky/video/startUpload.js +6 -0
  61. package/dist/lexicons/app/bsky/video/startUpload.js.map +1 -0
  62. package/dist/lexicons/app/bsky/video/uploadPart.d.ts +3 -0
  63. package/dist/lexicons/app/bsky/video/uploadPart.d.ts.map +1 -0
  64. package/dist/lexicons/app/bsky/video/uploadPart.defs.d.ts +37 -0
  65. package/dist/lexicons/app/bsky/video/uploadPart.defs.d.ts.map +1 -0
  66. package/dist/lexicons/app/bsky/video/uploadPart.defs.js +31 -0
  67. package/dist/lexicons/app/bsky/video/uploadPart.defs.js.map +1 -0
  68. package/dist/lexicons/app/bsky/video/uploadPart.js +6 -0
  69. package/dist/lexicons/app/bsky/video/uploadPart.js.map +1 -0
  70. package/dist/lexicons/app/bsky/video.d.ts +5 -0
  71. package/dist/lexicons/app/bsky/video.d.ts.map +1 -1
  72. package/dist/lexicons/app/bsky/video.js +5 -0
  73. package/dist/lexicons/app/bsky/video.js.map +1 -1
  74. package/dist/mailer/templates/confirm-email.js +44 -1198
  75. package/dist/mailer/templates/confirm-email.js.map +1 -7
  76. package/dist/mailer/templates/delete-account.js +25 -1190
  77. package/dist/mailer/templates/delete-account.js.map +1 -7
  78. package/dist/mailer/templates/plc-operation.js +25 -1190
  79. package/dist/mailer/templates/plc-operation.js.map +1 -7
  80. package/dist/mailer/templates/reset-password.js +33 -1190
  81. package/dist/mailer/templates/reset-password.js.map +1 -7
  82. package/dist/mailer/templates/update-email.js +25 -1190
  83. package/dist/mailer/templates/update-email.js.map +1 -7
  84. package/package.json +19 -19
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @atproto/pds
2
2
 
3
+ ## 0.5.28
4
+
5
+ ### Patch Changes
6
+
7
+ - [#5382](https://github.com/bluesky-social/atproto/pull/5382) [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update `file-type` dependency
8
+
9
+ - [#5371](https://github.com/bluesky-social/atproto/pull/5371) [`c7f39c5`](https://github.com/bluesky-social/atproto/commit/c7f39c527758ffd5b21b896fac494ecf7e69aa37) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove "esbuild" dependency
10
+
11
+ - [#5382](https://github.com/bluesky-social/atproto/pull/5382) [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Propagate back-pressure when uploading blobs (avoiding high memory pressure)
12
+
13
+ - [#5382](https://github.com/bluesky-social/atproto/pull/5382) [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove unused `CidNotFound` error class
14
+
15
+ - Updated dependencies [[`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`4e18a00`](https://github.com/bluesky-social/atproto/commit/4e18a00b2de97e36b9b00a7fcb8713efd87964d1)]:
16
+ - @atproto/common@0.8.0
17
+ - @atproto/oauth-provider@0.22.2
18
+ - @atproto/aws@0.3.13
19
+ - @atproto/repo@0.10.11
20
+ - @atproto/xrpc-server@0.12.4
21
+ - @atproto-labs/xrpc-utils@0.1.17
22
+ - @atproto/lex@0.3.6
23
+
3
24
  ## 0.5.27
4
25
 
5
26
  ### Patch Changes
@@ -19,7 +19,7 @@ export declare class BlobTransactor extends BlobReader {
19
19
  backgroundQueue: BackgroundQueue;
20
20
  constructor(db: ActorDb, blobstore: BlobStore, backgroundQueue: BackgroundQueue);
21
21
  insertBlobs(recordUri: string, blobs: Iterable<BlobRef>): Promise<void>;
22
- uploadBlobAndGetMetadata(userSuggestedMime: string, blobStream: stream.Readable): Promise<BlobMetadata>;
22
+ uploadBlobAndGetMetadata(blobStream: stream.Readable, fallbackMime?: `${string}/${string}`, maxSize?: number): Promise<BlobMetadata>;
23
23
  trackUntetheredBlob(metadata: BlobMetadata): Promise<TypedBlobRef>;
24
24
  processWriteBlobs(rev: string, writes: PreparedWrite[]): Promise<void>;
25
25
  updateBlobTakedownStatus(cid: Cid, takedown: com.atproto.admin.defs.StatusAttr): Promise<void>;
@@ -28,8 +28,4 @@ export declare class BlobTransactor extends BlobReader {
28
28
  insertBlobMetadata(blob: TypedBlobRef): Promise<void>;
29
29
  associateBlob(blob: TypedBlobRef, recordUri: AtUri): Promise<void>;
30
30
  }
31
- export declare class CidNotFound extends Error {
32
- cid: Cid;
33
- constructor(cid: Cid);
34
- }
35
31
  //# sourceMappingURL=transactor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transactor.d.ts","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAMrC,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,YAAY,EAIlB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAqB,KAAK,SAAS,EAAiB,MAAM,eAAe,CAAA;AAChF,OAAO,EAAE,KAAK,KAAK,EAAyB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAA;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,gBAAgB,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,GAAG,CAAA;IACR,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,qBAAa,cAAe,SAAQ,UAAU;IAEnC,EAAE,EAAE,OAAO;IACX,SAAS,EAAE,SAAS;IACpB,eAAe,EAAE,eAAe;IAHzC,YACS,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,eAAe,EAGxC;IAEK,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,iBAa5D;IAEK,wBAAwB,CAC5B,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,CAAC,QAAQ,GAC1B,OAAO,CAAC,YAAY,CAAC,CAcvB;IAEK,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAoCvE;IAEK,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,iBAqC3D;IAEK,wBAAwB,CAC5B,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,iBA8B5C;IAEK,uBAAuB,CAC3B,MAAM,EAAE,aAAa,EAAE,EACvB,aAAa,CAAC,EAAE,OAAO,iBAuDxB;IAEK,0BAA0B,CAC9B,IAAI,EAAE,YAAY,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Cf;IAEK,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAW1D;IAEK,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CASvE;CACF;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,GAAG,EAAE,GAAG,CAAA;IACR,YAAY,GAAG,EAAE,GAAG,EAGnB;CACF"}
1
+ {"version":3,"file":"transactor.d.ts","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAerC,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,YAAY,EAIlB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAqB,KAAK,SAAS,EAAiB,MAAM,eAAe,CAAA;AAChF,OAAO,EAAE,KAAK,KAAK,EAAyB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAA;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,gBAAgB,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,GAAG,CAAA;IACR,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,qBAAa,cAAe,SAAQ,UAAU;IAEnC,EAAE,EAAE,OAAO;IACX,SAAS,EAAE,SAAS;IACpB,eAAe,EAAE,eAAe;IAHzC,YACS,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,eAAe,EAGxC;IAEK,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,iBAa5D;IAEK,wBAAwB,CAC5B,UAAU,EAAE,MAAM,CAAC,QAAQ,EAC3B,YAAY,GAAE,GAAG,MAAM,IAAI,MAAM,EAA+B,EAChE,OAAO,SAAW,GACjB,OAAO,CAAC,YAAY,CAAC,CAoCvB;IAEK,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAoCvE;IAEK,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,iBAqC3D;IAEK,wBAAwB,CAC5B,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,iBA8B5C;IAEK,uBAAuB,CAC3B,MAAM,EAAE,aAAa,EAAE,EACvB,aAAa,CAAC,EAAE,OAAO,iBAuDxB;IAEK,0BAA0B,CAC9B,IAAI,EAAE,YAAY,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Cf;IAEK,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAW1D;IAEK,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CASvE;CACF"}
@@ -1,9 +1,9 @@
1
- import crypto from 'node:crypto';
2
- import * as FileTypeModule from 'file-type';
3
- const FileType = ((m) => m.default ?? m)(FileTypeModule);
4
- const { fromStream: fileTypeFromStream } = FileType;
1
+ import assert from 'node:assert';
2
+ import { PassThrough, pipeline, } from 'node:stream';
3
+ import { FileTypeParser, } from 'file-type';
5
4
  import PQueue from 'p-queue';
6
- import { SECOND, cloneStream, streamSize } from '@atproto/common';
5
+ import { fromStream } from 'strtok3';
6
+ import { HashPassThrough, MaxSizeChecker, SECOND, Tee } from '@atproto/common';
7
7
  import { cidForRawHash, getBlobCidString, parseCid, } from '@atproto/lex-data';
8
8
  import { BlobNotFoundError, WriteOpAction } from '@atproto/repo';
9
9
  import { currentDatetimeString } from '@atproto/syntax';
@@ -30,19 +30,35 @@ export class BlobTransactor extends BlobReader {
30
30
  .execute();
31
31
  }
32
32
  }
33
- async uploadBlobAndGetMetadata(userSuggestedMime, blobStream) {
34
- const [tempKey, size, sha256, sniffedMime] = await Promise.all([
35
- this.blobstore.putTemp(cloneStream(blobStream)),
36
- streamSize(cloneStream(blobStream)),
37
- sha256Stream(cloneStream(blobStream)),
38
- mimeTypeFromStream(cloneStream(blobStream)),
39
- ]);
40
- return {
41
- tempKey,
42
- size,
43
- cid: cidForRawHash(sha256),
44
- mimeType: sniffedMime || userSuggestedMime,
45
- };
33
+ async uploadBlobAndGetMetadata(blobStream, fallbackMime = 'application/octet-stream', maxSize = Infinity) {
34
+ try {
35
+ const hashDuplex = new HashPassThrough('sha256');
36
+ const sizeDuplex = new MaxSizeChecker(maxSize);
37
+ const typeDuplex = new FileTypePassThrough();
38
+ // @NOTE a pipeline of duplex streams is used to ensure that backpressure
39
+ // is properly propagated to the input stream. using inputStream.pipe()
40
+ // with several destinations does **not** propagate backpressure, and
41
+ // **will** high memory consumption (hash and size duplexes consume the
42
+ // stream faster than the blobstore can process it).
43
+ const streams = [blobStream, hashDuplex, sizeDuplex, typeDuplex];
44
+ const blobStoreStream = pipeline(streams, (_err) => {
45
+ // errors will be propagated to the streams, and handled (rethrown) by
46
+ // the blobStore.
47
+ }); // pipeline() returns the last stream
48
+ // Start the pipeline by reading its output
49
+ const tempKey = await this.blobstore.putTemp(blobStoreStream);
50
+ // Fool-proof against faulty blobstore implementations
51
+ assert(streams.every((s) => s.readableEnded), 'blobstore did not fully consume the stream');
52
+ return {
53
+ tempKey,
54
+ size: sizeDuplex.totalSize,
55
+ cid: cidForRawHash(hashDuplex.digest),
56
+ mimeType: typeDuplex.fileTypeResult?.mime || fallbackMime,
57
+ };
58
+ }
59
+ finally {
60
+ blobStream.destroy();
61
+ }
46
62
  }
47
63
  async trackUntetheredBlob(metadata) {
48
64
  const { tempKey, size, cid, mimeType } = metadata;
@@ -238,34 +254,47 @@ export class BlobTransactor extends BlobReader {
238
254
  .execute();
239
255
  }
240
256
  }
241
- export class CidNotFound extends Error {
242
- constructor(cid) {
243
- super(`cid not found: ${cid.toString()}`);
244
- this.cid = cid;
245
- }
246
- }
247
- async function sha256Stream(toHash) {
248
- const hash = crypto.createHash('sha256');
249
- try {
250
- for await (const chunk of toHash) {
251
- hash.write(chunk);
252
- }
257
+ // "file-type" does not provide a duplex implementation so we create one here
258
+ class FileTypePassThrough extends Tee {
259
+ #fileTypeResult;
260
+ get fileTypeResult() {
261
+ const result = this.#fileTypeResult;
262
+ if (result)
263
+ return result.status === 'fulfilled' ? result.value : undefined;
264
+ throw new Error('FileTypePassThrough result is not yet available');
253
265
  }
254
- catch (err) {
255
- hash.end();
256
- throw err;
266
+ constructor(options) {
267
+ const branch = new PassThrough();
268
+ super(branch);
269
+ const parser = new FileTypeParser(options);
270
+ // @NOTE file-type does not support NodeJS Readable and recommends wrapping
271
+ // into a web steam. We use strtok3 to convert the NodeJS Readable into a
272
+ // tokenizer, bypassing that limitation.
273
+ this.fileTypePromise = fromStream(branch)
274
+ .then((tokenizer) => parser.fromTokenizer(tokenizer))
275
+ // file-type won't destroy() the stream. We need to destroy to allow
276
+ // the Tee's main stream to flow freely.
277
+ .finally(() => branch.destroy());
278
+ // avoids unhandled rejections (might be awaited later)
279
+ this.fileTypePromise.then((value) => {
280
+ this.#fileTypeResult = { status: 'fulfilled', value };
281
+ }, (reason) => {
282
+ this.#fileTypeResult = { status: 'rejected', reason };
283
+ });
257
284
  }
258
- hash.end();
259
- return hash.read();
260
- }
261
- async function mimeTypeFromStream(blobStream) {
262
- try {
263
- const fileType = await fileTypeFromStream(blobStream);
264
- return fileType?.mime;
285
+ _final(cb) {
286
+ // propagate the result promise to the final callback, so that the stream is
287
+ // not considered finished until the file type has been determined.
288
+ super._final((err) => {
289
+ this.fileTypePromise.then(() => cb(err), () => cb(err));
290
+ });
265
291
  }
266
- finally {
267
- // @NOTE Draining avoids a Node pipe cleanup bug that can pause other clones.
268
- blobStream.resume();
292
+ _destroy(err, cb) {
293
+ // propagate the result promise to the destroy callback, so that the stream
294
+ // is not considered finished until the file type has been determined.
295
+ super._destroy(err, (err) => {
296
+ this.fileTypePromise.then(() => cb(err), () => cb(err));
297
+ });
269
298
  }
270
299
  }
271
300
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"transactor.js","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from 'node:crypto'\nimport type stream from 'node:stream'\nimport * as FileTypeModule from 'file-type'\nconst FileType = ((m) => m.default ?? m)(FileTypeModule)\nconst { fromStream: fileTypeFromStream } = FileType\nimport PQueue from 'p-queue'\nimport { SECOND, cloneStream, streamSize } from '@atproto/common'\nimport {\n type BlobRef,\n type Cid,\n type TypedBlobRef,\n cidForRawHash,\n getBlobCidString,\n parseCid,\n} from '@atproto/lex-data'\nimport { BlobNotFoundError, type BlobStore, WriteOpAction } from '@atproto/repo'\nimport { type AtUri, currentDatetimeString } from '@atproto/syntax'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport type { BackgroundQueue } from '../../background.js'\nimport type { com } from '../../lexicons/index.js'\nimport { blobStoreLogger as log } from '../../logger.js'\nimport type { PreparedWrite } from '../../repo/types.js'\nimport type { ActorDb, Blob as BlobTable } from '../db/index.js'\nimport { BlobReader } from './reader.js'\n\nexport type BlobMetadata = {\n tempKey: string\n size: number\n cid: Cid\n mimeType: string\n}\n\nexport class BlobTransactor extends BlobReader {\n constructor(\n public db: ActorDb,\n public blobstore: BlobStore,\n public backgroundQueue: BackgroundQueue,\n ) {\n super(db, blobstore)\n }\n\n async insertBlobs(recordUri: string, blobs: Iterable<BlobRef>) {\n const values = Array.from(blobs, (blob) => ({\n recordUri,\n blobCid: getBlobCidString(blob),\n }))\n\n if (values.length) {\n await this.db.db\n .insertInto('record_blob')\n .values(values)\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n }\n\n async uploadBlobAndGetMetadata(\n userSuggestedMime: string,\n blobStream: stream.Readable,\n ): Promise<BlobMetadata> {\n const [tempKey, size, sha256, sniffedMime] = await Promise.all([\n this.blobstore.putTemp(cloneStream(blobStream)),\n streamSize(cloneStream(blobStream)),\n sha256Stream(cloneStream(blobStream)),\n mimeTypeFromStream(cloneStream(blobStream)),\n ])\n\n return {\n tempKey,\n size,\n cid: cidForRawHash(sha256),\n mimeType: sniffedMime || userSuggestedMime,\n }\n }\n\n async trackUntetheredBlob(metadata: BlobMetadata): Promise<TypedBlobRef> {\n const { tempKey, size, cid, mimeType } = metadata\n const cidStr = cid.toString()\n\n const found = await this.db.db\n .selectFrom('blob')\n .selectAll()\n .where('cid', '=', cidStr)\n .executeTakeFirst()\n if (found?.takedownRef) {\n throw new InvalidRequestError('Blob has been takendown, cannot re-upload')\n }\n\n await this.db.db\n .insertInto('blob')\n .values({\n cid: cidStr,\n mimeType,\n size,\n tempKey,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) =>\n oc\n .column('cid')\n .doUpdateSet({ tempKey })\n .where('blob.tempKey', 'is not', null),\n )\n .execute()\n\n return {\n $type: 'blob',\n ref: cid,\n mimeType,\n size,\n }\n }\n\n async processWriteBlobs(rev: string, writes: PreparedWrite[]) {\n await this.deleteDereferencedBlobs(writes)\n\n const ac = new AbortController()\n\n // Limit the number of parallel requests made to the BlobStore by using a\n // a queue with concurrency management.\n type Task = () => Promise<void>\n const tasks: Task[] = []\n\n for (const write of writes) {\n if (isCreate(write) || isUpdate(write)) {\n for (const blob of write.blobs) {\n tasks.push(async () => {\n if (ac.signal.aborted) return\n await this.associateBlob(blob, write.uri)\n await this.verifyBlobAndMakePermanent(blob, ac.signal)\n })\n }\n }\n }\n\n try {\n const queue = new PQueue({\n concurrency: 20,\n // The blob store should already limit the time of every operation. We\n // add a timeout here as an extra precaution.\n timeout: 60 * SECOND,\n throwOnTimeout: true,\n })\n\n // Will reject as soon as any task fails, causing the \"finally\" block\n // below to run, aborting every other pending tasks.\n await queue.addAll(tasks)\n } finally {\n ac.abort()\n }\n }\n\n async updateBlobTakedownStatus(\n cid: Cid,\n takedown: com.atproto.admin.defs.StatusAttr,\n ) {\n const takedownRef = takedown.applied\n ? (takedown.ref ?? currentDatetimeString())\n : null\n await this.db.db\n .updateTable('blob')\n .set({ takedownRef })\n .where('cid', '=', cid.toString())\n .executeTakeFirst()\n\n try {\n // @NOTE find a way to not perform i/o operations during the transaction\n // (typically by using a state in the \"blob\" table, and another process to\n // handle the actual i/o)\n if (takedown.applied) {\n await this.blobstore.quarantine(cid)\n } else {\n await this.blobstore.unquarantine(cid)\n }\n } catch (err) {\n if (!(err instanceof BlobNotFoundError)) {\n log.error(\n { err, cid: cid.toString() },\n 'could not update blob takedown status',\n )\n\n throw err\n }\n }\n }\n\n async deleteDereferencedBlobs(\n writes: PreparedWrite[],\n skipBlobStore?: boolean,\n ) {\n const deletes = writes.filter(isDelete)\n const updates = writes.filter(isUpdate)\n const uris = [...deletes, ...updates].map((w) => w.uri.toString())\n if (uris.length === 0) return\n\n const deletedRepoBlobs = await this.db.db\n .deleteFrom('record_blob')\n .where('recordUri', 'in', uris)\n .returning('blobCid')\n .execute()\n if (deletedRepoBlobs.length === 0) return\n\n const deletedRepoBlobCids = deletedRepoBlobs.map((row) => row.blobCid)\n const duplicateCids = await this.db.db\n .selectFrom('record_blob')\n .where('blobCid', 'in', deletedRepoBlobCids)\n .select('blobCid')\n .execute()\n\n const newBlobCids = writes\n .filter((w) => isUpdate(w) || isCreate(w))\n .flatMap((w) => w.blobs.map((b) => b.ref.toString()))\n\n const cidsToKeep = [\n ...newBlobCids,\n ...duplicateCids.map((row) => row.blobCid),\n ]\n\n const cidsToDelete = deletedRepoBlobCids.filter(\n (cid) => !cidsToKeep.includes(cid),\n )\n if (cidsToDelete.length === 0) return\n\n await this.db.db\n .deleteFrom('blob')\n .where('cid', 'in', cidsToDelete)\n .execute()\n\n if (!skipBlobStore) {\n this.db.onCommit(() => {\n this.backgroundQueue.add(async () => {\n try {\n const cids = cidsToDelete.map((cid) => parseCid(cid))\n await this.blobstore.deleteMany(cids)\n } catch (err) {\n log.error(\n { err, cids: cidsToDelete },\n 'could not delete blobs from blobstore',\n )\n }\n })\n })\n }\n }\n\n async verifyBlobAndMakePermanent(\n blob: TypedBlobRef,\n signal?: AbortSignal,\n ): Promise<void> {\n const found = await this.db.db\n .selectFrom('blob')\n .select(['tempKey', 'size', 'mimeType'])\n .where('cid', '=', blob.ref.toString())\n .where('takedownRef', 'is', null)\n .executeTakeFirst()\n\n signal?.throwIfAborted()\n\n if (!found) {\n throw new InvalidRequestError(\n `Could not find blob: ${blob.ref.toString()}`,\n 'BlobNotFound',\n )\n }\n\n if (found.tempKey) {\n verifyBlob(blob, found)\n\n // @NOTE it is less than ideal to perform async (i/o) operations during a\n // transaction. Especially since there have been instances of the actor-db\n // being locked, requiring to kick the processes.\n\n // The better solution would be to update the blob state in the database\n // (e.g. \"makeItPermanent\") and to process those updates outside of the\n // transaction.\n\n await this.blobstore\n .makePermanent(found.tempKey, blob.ref)\n .catch((err) => {\n log.error(\n { err, cid: blob.ref.toString() },\n 'could not make blob permanent',\n )\n\n throw err\n })\n\n signal?.throwIfAborted()\n\n await this.db.db\n .updateTable('blob')\n .set({ tempKey: null })\n .where('tempKey', '=', found.tempKey)\n .execute()\n }\n }\n\n async insertBlobMetadata(blob: TypedBlobRef): Promise<void> {\n await this.db.db\n .insertInto('blob')\n .values({\n cid: blob.ref.toString(),\n mimeType: blob.mimeType,\n size: blob.size,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n\n async associateBlob(blob: TypedBlobRef, recordUri: AtUri): Promise<void> {\n await this.db.db\n .insertInto('record_blob')\n .values({\n blobCid: blob.ref.toString(),\n recordUri: recordUri.toString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n}\n\nexport class CidNotFound extends Error {\n cid: Cid\n constructor(cid: Cid) {\n super(`cid not found: ${cid.toString()}`)\n this.cid = cid\n }\n}\n\nasync function sha256Stream(toHash: stream.Readable): Promise<Uint8Array> {\n const hash = crypto.createHash('sha256')\n try {\n for await (const chunk of toHash) {\n hash.write(chunk)\n }\n } catch (err) {\n hash.end()\n throw err\n }\n hash.end()\n return hash.read()\n}\n\nasync function mimeTypeFromStream(\n blobStream: stream.Readable,\n): Promise<string | undefined> {\n try {\n const fileType = await fileTypeFromStream(blobStream)\n return fileType?.mime\n } finally {\n // @NOTE Draining avoids a Node pipe cleanup bug that can pause other clones.\n blobStream.resume()\n }\n}\n\n/**\n * Ensures that the blob referenced in the record matches the stored blob.\n */\nfunction verifyBlob(\n blob: TypedBlobRef,\n found: Pick<BlobTable, 'size' | 'mimeType'>,\n) {\n if (blob.mimeType !== found.mimeType) {\n throw new InvalidRequestError(\n `Referenced Mimetype does not match stored blob. Expected: ${found.mimeType}, Got: ${blob.mimeType}`,\n 'InvalidMimeType',\n )\n }\n\n if (blob.size !== found.size) {\n throw new InvalidRequestError(\n `Referenced Size does not match stored blob. Expected: ${found.size}, Got: ${blob.size}`,\n 'InvalidSize',\n )\n }\n}\n\nfunction isCreate(write: PreparedWrite) {\n return write.action === WriteOpAction.Create\n}\nfunction isUpdate(write: PreparedWrite) {\n return write.action === WriteOpAction.Update\n}\nfunction isDelete(write: PreparedWrite) {\n return write.action === WriteOpAction.Delete\n}\n"]}
1
+ {"version":3,"file":"transactor.js","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'node:assert'\nimport type stream from 'node:stream'\nimport {\n type Duplex,\n PassThrough,\n type TransformCallback,\n pipeline,\n} from 'node:stream'\nimport {\n type FileTypeOptions,\n FileTypeParser,\n type FileTypeResult,\n} from 'file-type'\nimport PQueue from 'p-queue'\nimport { fromStream } from 'strtok3'\nimport { HashPassThrough, MaxSizeChecker, SECOND, Tee } from '@atproto/common'\nimport {\n type BlobRef,\n type Cid,\n type TypedBlobRef,\n cidForRawHash,\n getBlobCidString,\n parseCid,\n} from '@atproto/lex-data'\nimport { BlobNotFoundError, type BlobStore, WriteOpAction } from '@atproto/repo'\nimport { type AtUri, currentDatetimeString } from '@atproto/syntax'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport type { BackgroundQueue } from '../../background.js'\nimport type { com } from '../../lexicons/index.js'\nimport { blobStoreLogger as log } from '../../logger.js'\nimport type { PreparedWrite } from '../../repo/types.js'\nimport type { ActorDb, Blob as BlobTable } from '../db/index.js'\nimport { BlobReader } from './reader.js'\n\nexport type BlobMetadata = {\n tempKey: string\n size: number\n cid: Cid\n mimeType: string\n}\n\nexport class BlobTransactor extends BlobReader {\n constructor(\n public db: ActorDb,\n public blobstore: BlobStore,\n public backgroundQueue: BackgroundQueue,\n ) {\n super(db, blobstore)\n }\n\n async insertBlobs(recordUri: string, blobs: Iterable<BlobRef>) {\n const values = Array.from(blobs, (blob) => ({\n recordUri,\n blobCid: getBlobCidString(blob),\n }))\n\n if (values.length) {\n await this.db.db\n .insertInto('record_blob')\n .values(values)\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n }\n\n async uploadBlobAndGetMetadata(\n blobStream: stream.Readable,\n fallbackMime: `${string}/${string}` = 'application/octet-stream',\n maxSize = Infinity,\n ): Promise<BlobMetadata> {\n try {\n const hashDuplex = new HashPassThrough('sha256')\n const sizeDuplex = new MaxSizeChecker(maxSize)\n const typeDuplex = new FileTypePassThrough()\n\n // @NOTE a pipeline of duplex streams is used to ensure that backpressure\n // is properly propagated to the input stream. using inputStream.pipe()\n // with several destinations does **not** propagate backpressure, and\n // **will** high memory consumption (hash and size duplexes consume the\n // stream faster than the blobstore can process it).\n const streams = [blobStream, hashDuplex, sizeDuplex, typeDuplex]\n\n const blobStoreStream = pipeline(streams, (_err) => {\n // errors will be propagated to the streams, and handled (rethrown) by\n // the blobStore.\n }) as Duplex // pipeline() returns the last stream\n\n // Start the pipeline by reading its output\n const tempKey = await this.blobstore.putTemp(blobStoreStream)\n\n // Fool-proof against faulty blobstore implementations\n assert(\n streams.every((s) => s.readableEnded),\n 'blobstore did not fully consume the stream',\n )\n\n return {\n tempKey,\n size: sizeDuplex.totalSize,\n cid: cidForRawHash(hashDuplex.digest),\n mimeType: typeDuplex.fileTypeResult?.mime || fallbackMime,\n }\n } finally {\n blobStream.destroy()\n }\n }\n\n async trackUntetheredBlob(metadata: BlobMetadata): Promise<TypedBlobRef> {\n const { tempKey, size, cid, mimeType } = metadata\n const cidStr = cid.toString()\n\n const found = await this.db.db\n .selectFrom('blob')\n .selectAll()\n .where('cid', '=', cidStr)\n .executeTakeFirst()\n if (found?.takedownRef) {\n throw new InvalidRequestError('Blob has been takendown, cannot re-upload')\n }\n\n await this.db.db\n .insertInto('blob')\n .values({\n cid: cidStr,\n mimeType,\n size,\n tempKey,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) =>\n oc\n .column('cid')\n .doUpdateSet({ tempKey })\n .where('blob.tempKey', 'is not', null),\n )\n .execute()\n\n return {\n $type: 'blob',\n ref: cid,\n mimeType,\n size,\n }\n }\n\n async processWriteBlobs(rev: string, writes: PreparedWrite[]) {\n await this.deleteDereferencedBlobs(writes)\n\n const ac = new AbortController()\n\n // Limit the number of parallel requests made to the BlobStore by using a\n // a queue with concurrency management.\n type Task = () => Promise<void>\n const tasks: Task[] = []\n\n for (const write of writes) {\n if (isCreate(write) || isUpdate(write)) {\n for (const blob of write.blobs) {\n tasks.push(async () => {\n if (ac.signal.aborted) return\n await this.associateBlob(blob, write.uri)\n await this.verifyBlobAndMakePermanent(blob, ac.signal)\n })\n }\n }\n }\n\n try {\n const queue = new PQueue({\n concurrency: 20,\n // The blob store should already limit the time of every operation. We\n // add a timeout here as an extra precaution.\n timeout: 60 * SECOND,\n throwOnTimeout: true,\n })\n\n // Will reject as soon as any task fails, causing the \"finally\" block\n // below to run, aborting every other pending tasks.\n await queue.addAll(tasks)\n } finally {\n ac.abort()\n }\n }\n\n async updateBlobTakedownStatus(\n cid: Cid,\n takedown: com.atproto.admin.defs.StatusAttr,\n ) {\n const takedownRef = takedown.applied\n ? (takedown.ref ?? currentDatetimeString())\n : null\n await this.db.db\n .updateTable('blob')\n .set({ takedownRef })\n .where('cid', '=', cid.toString())\n .executeTakeFirst()\n\n try {\n // @NOTE find a way to not perform i/o operations during the transaction\n // (typically by using a state in the \"blob\" table, and another process to\n // handle the actual i/o)\n if (takedown.applied) {\n await this.blobstore.quarantine(cid)\n } else {\n await this.blobstore.unquarantine(cid)\n }\n } catch (err) {\n if (!(err instanceof BlobNotFoundError)) {\n log.error(\n { err, cid: cid.toString() },\n 'could not update blob takedown status',\n )\n\n throw err\n }\n }\n }\n\n async deleteDereferencedBlobs(\n writes: PreparedWrite[],\n skipBlobStore?: boolean,\n ) {\n const deletes = writes.filter(isDelete)\n const updates = writes.filter(isUpdate)\n const uris = [...deletes, ...updates].map((w) => w.uri.toString())\n if (uris.length === 0) return\n\n const deletedRepoBlobs = await this.db.db\n .deleteFrom('record_blob')\n .where('recordUri', 'in', uris)\n .returning('blobCid')\n .execute()\n if (deletedRepoBlobs.length === 0) return\n\n const deletedRepoBlobCids = deletedRepoBlobs.map((row) => row.blobCid)\n const duplicateCids = await this.db.db\n .selectFrom('record_blob')\n .where('blobCid', 'in', deletedRepoBlobCids)\n .select('blobCid')\n .execute()\n\n const newBlobCids = writes\n .filter((w) => isUpdate(w) || isCreate(w))\n .flatMap((w) => w.blobs.map((b) => b.ref.toString()))\n\n const cidsToKeep = [\n ...newBlobCids,\n ...duplicateCids.map((row) => row.blobCid),\n ]\n\n const cidsToDelete = deletedRepoBlobCids.filter(\n (cid) => !cidsToKeep.includes(cid),\n )\n if (cidsToDelete.length === 0) return\n\n await this.db.db\n .deleteFrom('blob')\n .where('cid', 'in', cidsToDelete)\n .execute()\n\n if (!skipBlobStore) {\n this.db.onCommit(() => {\n this.backgroundQueue.add(async () => {\n try {\n const cids = cidsToDelete.map((cid) => parseCid(cid))\n await this.blobstore.deleteMany(cids)\n } catch (err) {\n log.error(\n { err, cids: cidsToDelete },\n 'could not delete blobs from blobstore',\n )\n }\n })\n })\n }\n }\n\n async verifyBlobAndMakePermanent(\n blob: TypedBlobRef,\n signal?: AbortSignal,\n ): Promise<void> {\n const found = await this.db.db\n .selectFrom('blob')\n .select(['tempKey', 'size', 'mimeType'])\n .where('cid', '=', blob.ref.toString())\n .where('takedownRef', 'is', null)\n .executeTakeFirst()\n\n signal?.throwIfAborted()\n\n if (!found) {\n throw new InvalidRequestError(\n `Could not find blob: ${blob.ref.toString()}`,\n 'BlobNotFound',\n )\n }\n\n if (found.tempKey) {\n verifyBlob(blob, found)\n\n // @NOTE it is less than ideal to perform async (i/o) operations during a\n // transaction. Especially since there have been instances of the actor-db\n // being locked, requiring to kick the processes.\n\n // The better solution would be to update the blob state in the database\n // (e.g. \"makeItPermanent\") and to process those updates outside of the\n // transaction.\n\n await this.blobstore\n .makePermanent(found.tempKey, blob.ref)\n .catch((err) => {\n log.error(\n { err, cid: blob.ref.toString() },\n 'could not make blob permanent',\n )\n\n throw err\n })\n\n signal?.throwIfAborted()\n\n await this.db.db\n .updateTable('blob')\n .set({ tempKey: null })\n .where('tempKey', '=', found.tempKey)\n .execute()\n }\n }\n\n async insertBlobMetadata(blob: TypedBlobRef): Promise<void> {\n await this.db.db\n .insertInto('blob')\n .values({\n cid: blob.ref.toString(),\n mimeType: blob.mimeType,\n size: blob.size,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n\n async associateBlob(blob: TypedBlobRef, recordUri: AtUri): Promise<void> {\n await this.db.db\n .insertInto('record_blob')\n .values({\n blobCid: blob.ref.toString(),\n recordUri: recordUri.toString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n}\n\n// \"file-type\" does not provide a duplex implementation so we create one here\nclass FileTypePassThrough extends Tee {\n readonly fileTypePromise: Promise<FileTypeResult | undefined>\n\n #fileTypeResult?: PromiseSettledResult<FileTypeResult | undefined>\n get fileTypeResult(): FileTypeResult | undefined {\n const result = this.#fileTypeResult\n if (result) return result.status === 'fulfilled' ? result.value : undefined\n throw new Error('FileTypePassThrough result is not yet available')\n }\n\n constructor(options?: FileTypeOptions) {\n const branch = new PassThrough()\n super(branch)\n\n const parser = new FileTypeParser(options)\n\n // @NOTE file-type does not support NodeJS Readable and recommends wrapping\n // into a web steam. We use strtok3 to convert the NodeJS Readable into a\n // tokenizer, bypassing that limitation.\n this.fileTypePromise = fromStream(branch)\n .then((tokenizer) => parser.fromTokenizer(tokenizer))\n // file-type won't destroy() the stream. We need to destroy to allow\n // the Tee's main stream to flow freely.\n .finally(() => branch.destroy())\n\n // avoids unhandled rejections (might be awaited later)\n this.fileTypePromise.then(\n (value) => {\n this.#fileTypeResult = { status: 'fulfilled', value }\n },\n (reason) => {\n this.#fileTypeResult = { status: 'rejected', reason }\n },\n )\n }\n\n _final(cb: TransformCallback) {\n // propagate the result promise to the final callback, so that the stream is\n // not considered finished until the file type has been determined.\n super._final((err) => {\n this.fileTypePromise.then(\n () => cb(err),\n () => cb(err),\n )\n })\n }\n\n _destroy(err: Error | null, cb: (err?: Error | null) => void) {\n // propagate the result promise to the destroy callback, so that the stream\n // is not considered finished until the file type has been determined.\n super._destroy(err, (err) => {\n this.fileTypePromise.then(\n () => cb(err),\n () => cb(err),\n )\n })\n }\n}\n\n/**\n * Ensures that the blob referenced in the record matches the stored blob.\n */\nfunction verifyBlob(\n blob: TypedBlobRef,\n found: Pick<BlobTable, 'size' | 'mimeType'>,\n) {\n if (blob.mimeType !== found.mimeType) {\n throw new InvalidRequestError(\n `Referenced Mimetype does not match stored blob. Expected: ${found.mimeType}, Got: ${blob.mimeType}`,\n 'InvalidMimeType',\n )\n }\n\n if (blob.size !== found.size) {\n throw new InvalidRequestError(\n `Referenced Size does not match stored blob. Expected: ${found.size}, Got: ${blob.size}`,\n 'InvalidSize',\n )\n }\n}\n\nfunction isCreate(write: PreparedWrite) {\n return write.action === WriteOpAction.Create\n}\nfunction isUpdate(write: PreparedWrite) {\n return write.action === WriteOpAction.Update\n}\nfunction isDelete(write: PreparedWrite) {\n return write.action === WriteOpAction.Delete\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"uploadBlob.d.ts","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA2CvD"}
1
+ {"version":3,"file":"uploadBlob.d.ts","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA+CvD"}
@@ -18,7 +18,7 @@ export default function (server, ctx) {
18
18
  const requester = auth.credentials.did;
19
19
  const blob = await ctx.actorStore.writeNoTransaction(requester, async (store) => {
20
20
  const metadata = await store.repo.blob
21
- .uploadBlobAndGetMetadata(input.encoding, input.body)
21
+ .uploadBlobAndGetMetadata(input.body, input.encoding, ctx.cfg.service.blobUploadLimit)
22
22
  .catch(throwAbortAsUpstreamError);
23
23
  return store.transact(async (actorTxn) => {
24
24
  const blobRef = await actorTxn.repo.blob.trackUntetheredBlob(metadata);
@@ -1 +1 @@
1
- {"version":3,"file":"uploadBlob.js","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAEL,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAEnD,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE;QACtC,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,8BAA8B,CAAC;YACpD,aAAa,EAAE,IAAI;YACnB,SAAS,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAClC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;gBACtC,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC5C,CAAC;SACF,CAAC;QACF,SAAS,EAAE;YACT,UAAU,EAAE,GAAG;YACf,MAAM,EAAE,IAAI;SACb;QACD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;YACjC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YAEtC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAClD,SAAS,EACT,KAAK,EAAE,KAAK,EAAE,EAAE;gBACd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI;qBACnC,wBAAwB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC;qBACpD,KAAK,CAAC,yBAAyB,CAAC,CAAA;gBAEnC,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;oBACvC,MAAM,OAAO,GACX,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAA;oBAExD,qEAAqE;oBACrE,IAAI,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC5D,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAA;oBAC9D,CAAC;oBAED,OAAO,OAAO,CAAA;gBAChB,CAAC,CAAC,CAAA;YACJ,CAAC,CACF,CAAA;YAED,OAAO;gBACL,QAAQ,EAAE,kBAA2B;gBACrC,IAAI,EAAE,EAAE,IAAI,EAAE;aACf,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,yBAAyB,CAAC,GAAY;IAC7C,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,qDAAqD;IACrD,KAAK,IAAI,CAAC,GAAG,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,YAAY,KAAK,IAAI,KAAK,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;QACvE,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzD,MAAM,IAAI,oBAAoB,CAAC,wCAAwC,CAAC,CAAA;QAC1E,CAAC;QACD,CAAC,GAAG,CAAC,CAAC,KAAK,CAAA;IACb,CAAC;IACD,MAAM,GAAG,CAAA;AACX,CAAC","sourcesContent":["import { DAY } from '@atproto/common'\nimport {\n type Server,\n UpstreamTimeoutError,\n parseReqEncoding,\n} from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport { com } from '../../../../lexicons/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n server.add(com.atproto.repo.uploadBlob, {\n auth: ctx.authVerifier.authorizationOrUserServiceAuth({\n checkTakedown: true,\n authorize: (permissions, { req }) => {\n const encoding = parseReqEncoding(req)\n permissions.assertBlob({ mime: encoding })\n },\n }),\n rateLimit: {\n durationMs: DAY,\n points: 1000,\n },\n handler: async ({ auth, input }) => {\n const requester = auth.credentials.did\n\n const blob = await ctx.actorStore.writeNoTransaction(\n requester,\n async (store) => {\n const metadata = await store.repo.blob\n .uploadBlobAndGetMetadata(input.encoding, input.body)\n .catch(throwAbortAsUpstreamError)\n\n return store.transact(async (actorTxn) => {\n const blobRef =\n await actorTxn.repo.blob.trackUntetheredBlob(metadata)\n\n // make the blob permanent if an associated record is already indexed\n if (await actorTxn.repo.blob.hasRecordsForBlob(blobRef.ref)) {\n await actorTxn.repo.blob.verifyBlobAndMakePermanent(blobRef)\n }\n\n return blobRef\n })\n },\n )\n\n return {\n encoding: 'application/json' as const,\n body: { blob },\n }\n },\n })\n}\n\nfunction throwAbortAsUpstreamError(err: unknown): never {\n // Blob store timeouts surface as AbortError (total upload timeout) or\n // TimeoutError (stalled connection), possibly wrapped as an error cause\n // (e.g. S3BlobStore's \"Blob upload timed out\" error). The depth limit\n // guards against (pathological) cyclic cause chains.\n for (let e = err, depth = 0; e instanceof Error && depth < 10; depth++) {\n if (e.name === 'AbortError' || e.name === 'TimeoutError') {\n throw new UpstreamTimeoutError('Operation timed out, please try again.')\n }\n e = e.cause\n }\n throw err\n}\n"]}
1
+ {"version":3,"file":"uploadBlob.js","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAEL,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAEnD,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE;QACtC,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,8BAA8B,CAAC;YACpD,aAAa,EAAE,IAAI;YACnB,SAAS,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAClC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;gBACtC,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC5C,CAAC;SACF,CAAC;QACF,SAAS,EAAE;YACT,UAAU,EAAE,GAAG;YACf,MAAM,EAAE,IAAI;SACb;QACD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;YACjC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YAEtC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAClD,SAAS,EACT,KAAK,EAAE,KAAK,EAAE,EAAE;gBACd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI;qBACnC,wBAAwB,CACvB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,QAAQ,EACd,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAChC;qBACA,KAAK,CAAC,yBAAyB,CAAC,CAAA;gBAEnC,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;oBACvC,MAAM,OAAO,GACX,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAA;oBAExD,qEAAqE;oBACrE,IAAI,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC5D,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAA;oBAC9D,CAAC;oBAED,OAAO,OAAO,CAAA;gBAChB,CAAC,CAAC,CAAA;YACJ,CAAC,CACF,CAAA;YAED,OAAO;gBACL,QAAQ,EAAE,kBAA2B;gBACrC,IAAI,EAAE,EAAE,IAAI,EAAE;aACf,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,yBAAyB,CAAC,GAAY;IAC7C,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,qDAAqD;IACrD,KAAK,IAAI,CAAC,GAAG,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,YAAY,KAAK,IAAI,KAAK,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;QACvE,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzD,MAAM,IAAI,oBAAoB,CAAC,wCAAwC,CAAC,CAAA;QAC1E,CAAC;QACD,CAAC,GAAG,CAAC,CAAC,KAAK,CAAA;IACb,CAAC;IACD,MAAM,GAAG,CAAA;AACX,CAAC","sourcesContent":["import { DAY } from '@atproto/common'\nimport {\n type Server,\n UpstreamTimeoutError,\n parseReqEncoding,\n} from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport { com } from '../../../../lexicons/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n server.add(com.atproto.repo.uploadBlob, {\n auth: ctx.authVerifier.authorizationOrUserServiceAuth({\n checkTakedown: true,\n authorize: (permissions, { req }) => {\n const encoding = parseReqEncoding(req)\n permissions.assertBlob({ mime: encoding })\n },\n }),\n rateLimit: {\n durationMs: DAY,\n points: 1000,\n },\n handler: async ({ auth, input }) => {\n const requester = auth.credentials.did\n\n const blob = await ctx.actorStore.writeNoTransaction(\n requester,\n async (store) => {\n const metadata = await store.repo.blob\n .uploadBlobAndGetMetadata(\n input.body,\n input.encoding,\n ctx.cfg.service.blobUploadLimit,\n )\n .catch(throwAbortAsUpstreamError)\n\n return store.transact(async (actorTxn) => {\n const blobRef =\n await actorTxn.repo.blob.trackUntetheredBlob(metadata)\n\n // make the blob permanent if an associated record is already indexed\n if (await actorTxn.repo.blob.hasRecordsForBlob(blobRef.ref)) {\n await actorTxn.repo.blob.verifyBlobAndMakePermanent(blobRef)\n }\n\n return blobRef\n })\n },\n )\n\n return {\n encoding: 'application/json' as const,\n body: { blob },\n }\n },\n })\n}\n\nfunction throwAbortAsUpstreamError(err: unknown): never {\n // Blob store timeouts surface as AbortError (total upload timeout) or\n // TimeoutError (stalled connection), possibly wrapped as an error cause\n // (e.g. S3BlobStore's \"Blob upload timed out\" error). The depth limit\n // guards against (pathological) cyclic cause chains.\n for (let e = err, depth = 0; e instanceof Error && depth < 10; depth++) {\n if (e.name === 'AbortError' || e.name === 'TimeoutError') {\n throw new UpstreamTimeoutError('Operation timed out, please try again.')\n }\n e = e.cause\n }\n throw err\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './contentVisibilityDeclaration.defs.js';
2
+ export { main as default } from './contentVisibilityDeclaration.defs.js';
3
+ //# sourceMappingURL=contentVisibilityDeclaration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentVisibilityDeclaration.d.ts","sourceRoot":"","sources":["../../../../../src/lexicons/app/bsky/actor/contentVisibilityDeclaration.ts"],"names":[],"mappings":"AAIA,cAAc,wCAAwC,CAAA;AACtD,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,wCAAwC,CAAA"}
@@ -0,0 +1,66 @@
1
+ import { l } from '@atproto/lex';
2
+ declare const $nsid = "app.bsky.actor.contentVisibilityDeclaration";
3
+ type $nsid = typeof $nsid;
4
+ export { $nsid };
5
+ /** A declaration of an account's preferences for appearing in content discovery surfaces. */
6
+ type Main = {
7
+ $type: 'app.bsky.actor.contentVisibilityDeclaration';
8
+ /**
9
+ * Whether the account requests that its posts be hidden from algorithmic recommendations. Consumers must treat a missing record as false.
10
+ */
11
+ hideFromAlgorithmicRecommendations: boolean;
12
+ };
13
+ export type { Main };
14
+ /** A declaration of an account's preferences for appearing in content discovery surfaces. */
15
+ declare const main: l.RecordSchema<"literal:self", "app.bsky.actor.contentVisibilityDeclaration", l.Validator<Omit<Main, "$type">, Omit<Main, "$type">>>;
16
+ export { main };
17
+ declare const $type = "app.bsky.actor.contentVisibilityDeclaration";
18
+ type $type = typeof $type;
19
+ export { $type };
20
+ export declare const $isTypeOf: <TValue extends {
21
+ $type?: unknown;
22
+ }>(value: TValue) => value is l.TypedRecord<"app.bsky.actor.contentVisibilityDeclaration", TValue>;
23
+ export declare const $build: {
24
+ (input: Omit<Omit<Main, "$type">, "$type">): {
25
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
26
+ hideFromAlgorithmicRecommendations: boolean;
27
+ };
28
+ (input: Omit<Omit<Main, "$type">, "$type">): {
29
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
30
+ hideFromAlgorithmicRecommendations: boolean;
31
+ };
32
+ };
33
+ export declare const $assert: (input: unknown, options?: l.ValidateOptions) => asserts input is {
34
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
35
+ hideFromAlgorithmicRecommendations: boolean;
36
+ };
37
+ export declare const $check: (input: unknown, options?: l.ValidateOptions) => void;
38
+ export declare const $cast: <I>(input: I, options?: l.ValidateOptions) => I & {
39
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
40
+ hideFromAlgorithmicRecommendations: boolean;
41
+ };
42
+ export declare const $ifMatches: <I>(input: I, options?: l.ValidateOptions) => (I & {
43
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
44
+ hideFromAlgorithmicRecommendations: boolean;
45
+ }) | undefined;
46
+ export declare const $matches: <I>(input: I, options?: l.ValidateOptions) => input is I & {
47
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
48
+ hideFromAlgorithmicRecommendations: boolean;
49
+ };
50
+ export declare const $parse: (input: unknown, options?: l.ParseOptions) => {
51
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
52
+ hideFromAlgorithmicRecommendations: boolean;
53
+ };
54
+ export declare const $safeParse: (input: unknown, options?: l.ParseOptions) => l.ValidationResult<{
55
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
56
+ hideFromAlgorithmicRecommendations: boolean;
57
+ }>;
58
+ export declare const $validate: <I>(input: I, options?: l.ValidateOptions) => I & {
59
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
60
+ hideFromAlgorithmicRecommendations: boolean;
61
+ };
62
+ export declare const $safeValidate: <I>(input: I, options?: l.ValidateOptions) => l.ValidationResult<I & {
63
+ $type: "app.bsky.actor.contentVisibilityDeclaration";
64
+ hideFromAlgorithmicRecommendations: boolean;
65
+ }>;
66
+ //# sourceMappingURL=contentVisibilityDeclaration.defs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentVisibilityDeclaration.defs.d.ts","sourceRoot":"","sources":["../../../../../src/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,QAAA,MAAM,KAAK,gDAAgD,CAAA;AAE3D,KAAK,KAAK,GAAG,OAAO,KAAK,CAAA;AAEzB,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,6FAA6F;AAC7F,KAAK,IAAI,GAAG;IACV,KAAK,EAAE,6CAA6C,CAAA;IAEpD;;OAEG;IACH,kCAAkC,EAAE,OAAO,CAAA;CAC5C,CAAA;AAED,YAAY,EAAE,IAAI,EAAE,CAAA;AAEpB,6FAA6F;AAC7F,QAAA,MAAM,IAAI,sIAMT,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA;AAEf,QAAA,MAAM,KAAK,gDAAQ,CAAA;AAEnB,KAAK,KAAK,GAAG,OAAO,KAAK,CAAA;AAEzB,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,eAAO,MAAM,SAAS;;kGAAyC,CAAA;AAC/D,eAAO,MAAM,MAAM;;;4CAvBmB,OAAO;;;;4CAAP,OAAO;;CAuBY,CAAA;AACzD,eAAO,MAAM,OAAO;;wCAxBkB,OAAO;CAwBc,CAAA;AAC3D,eAAO,MAAM,MAAM,uDAAsC,CAAA;AACzD,eAAO,MAAM,KAAK;;wCA1BoB,OAAO;CA0BU,CAAA;AACvD,eAAO,MAAM,UAAU;;wCA3Be,OAAO;cA2BoB,CAAA;AACjE,eAAO,MAAM,QAAQ;;wCA5BiB,OAAO;CA4BgB,CAAA;AAC7D,eAAO,MAAM,MAAM;;wCA7BmB,OAAO;CA6BY,CAAA;AACzD,eAAO,MAAM,UAAU;;wCA9Be,OAAO;EA8BoB,CAAA;AACjE,eAAO,MAAM,SAAS;;wCA/BgB,OAAO;CA+BkB,CAAA;AAC/D,eAAO,MAAM,aAAa;;wCAhCY,OAAO;EAgC0B,CAAA"}
@@ -0,0 +1,26 @@
1
+ /*
2
+ * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
+ */
4
+ import { l } from '@atproto/lex';
5
+ const $nsid = 'app.bsky.actor.contentVisibilityDeclaration';
6
+ export { $nsid };
7
+ /** A declaration of an account's preferences for appearing in content discovery surfaces. */
8
+ const main = /*#__PURE__*/ l.record('literal:self', $nsid,
9
+ /*#__PURE__*/ l.object({
10
+ hideFromAlgorithmicRecommendations: /*#__PURE__*/ l.boolean(),
11
+ }));
12
+ export { main };
13
+ const $type = $nsid;
14
+ export { $type };
15
+ export const $isTypeOf = /*#__PURE__*/ main.isTypeOf.bind(main);
16
+ export const $build = /*#__PURE__*/ main.build.bind(main);
17
+ export const $assert = /*#__PURE__*/ main.assert.bind(main);
18
+ export const $check = /*#__PURE__*/ main.check.bind(main);
19
+ export const $cast = /*#__PURE__*/ main.cast.bind(main);
20
+ export const $ifMatches = /*#__PURE__*/ main.ifMatches.bind(main);
21
+ export const $matches = /*#__PURE__*/ main.matches.bind(main);
22
+ export const $parse = /*#__PURE__*/ main.parse.bind(main);
23
+ export const $safeParse = /*#__PURE__*/ main.safeParse.bind(main);
24
+ export const $validate = /*#__PURE__*/ main.validate.bind(main);
25
+ export const $safeValidate = /*#__PURE__*/ main.safeValidate.bind(main);
26
+ //# sourceMappingURL=contentVisibilityDeclaration.defs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentVisibilityDeclaration.defs.js","sourceRoot":"","sources":["../../../../../src/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,MAAM,KAAK,GAAG,6CAA6C,CAAA;AAI3D,OAAO,EAAE,KAAK,EAAE,CAAA;AAchB,6FAA6F;AAC7F,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,MAAM,CACjC,cAAc,EACd,KAAK;AACL,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrB,kCAAkC,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,EAAE;CAC9D,CAAC,CACH,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA;AAEf,MAAM,KAAK,GAAG,KAAK,CAAA;AAInB,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC/D,MAAM,CAAC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzD,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC3D,MAAM,CAAC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzD,MAAM,CAAC,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACvD,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACjE,MAAM,CAAC,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC7D,MAAM,CAAC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACjE,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC/D,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nimport { l } from '@atproto/lex'\n\nconst $nsid = 'app.bsky.actor.contentVisibilityDeclaration'\n\ntype $nsid = typeof $nsid\n\nexport { $nsid }\n\n/** A declaration of an account's preferences for appearing in content discovery surfaces. */\ntype Main = {\n $type: 'app.bsky.actor.contentVisibilityDeclaration'\n\n /**\n * Whether the account requests that its posts be hidden from algorithmic recommendations. Consumers must treat a missing record as false.\n */\n hideFromAlgorithmicRecommendations: boolean\n}\n\nexport type { Main }\n\n/** A declaration of an account's preferences for appearing in content discovery surfaces. */\nconst main = /*#__PURE__*/ l.record<'literal:self', Main>(\n 'literal:self',\n $nsid,\n /*#__PURE__*/ l.object({\n hideFromAlgorithmicRecommendations: /*#__PURE__*/ l.boolean(),\n }),\n)\n\nexport { main }\n\nconst $type = $nsid\n\ntype $type = typeof $type\n\nexport { $type }\n\nexport const $isTypeOf = /*#__PURE__*/ main.isTypeOf.bind(main)\nexport const $build = /*#__PURE__*/ main.build.bind(main)\nexport const $assert = /*#__PURE__*/ main.assert.bind(main)\nexport const $check = /*#__PURE__*/ main.check.bind(main)\nexport const $cast = /*#__PURE__*/ main.cast.bind(main)\nexport const $ifMatches = /*#__PURE__*/ main.ifMatches.bind(main)\nexport const $matches = /*#__PURE__*/ main.matches.bind(main)\nexport const $parse = /*#__PURE__*/ main.parse.bind(main)\nexport const $safeParse = /*#__PURE__*/ main.safeParse.bind(main)\nexport const $validate = /*#__PURE__*/ main.validate.bind(main)\nexport const $safeValidate = /*#__PURE__*/ main.safeValidate.bind(main)\n"]}
@@ -0,0 +1,6 @@
1
+ /*
2
+ * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
+ */
4
+ export * from './contentVisibilityDeclaration.defs.js';
5
+ export { main as default } from './contentVisibilityDeclaration.defs.js';
6
+ //# sourceMappingURL=contentVisibilityDeclaration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentVisibilityDeclaration.js","sourceRoot":"","sources":["../../../../../src/lexicons/app/bsky/actor/contentVisibilityDeclaration.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,wCAAwC,CAAA;AACtD,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,wCAAwC,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * from './contentVisibilityDeclaration.defs.js'\nexport { main as default } from './contentVisibilityDeclaration.defs.js'\n"]}
@@ -1,3 +1,4 @@
1
+ export * as contentVisibilityDeclaration from './actor/contentVisibilityDeclaration.js';
1
2
  export * as defs from './actor/defs.js';
2
3
  export * as getPreferences from './actor/getPreferences.js';
3
4
  export * as getProfile from './actor/getProfile.js';
@@ -1 +1 @@
1
- {"version":3,"file":"actor.d.ts","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/actor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAA;AACvC,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAA;AACnD,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,YAAY,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,qBAAqB,MAAM,kCAAkC,CAAA;AACzE,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"actor.d.ts","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/actor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,4BAA4B,MAAM,yCAAyC,CAAA;AACvF,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAA;AACvC,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAA;AACnD,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,YAAY,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,qBAAqB,MAAM,kCAAkC,CAAA;AACzE,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  /*
2
2
  * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT.
3
3
  */
4
+ export * as contentVisibilityDeclaration from './actor/contentVisibilityDeclaration.js';
4
5
  export * as defs from './actor/defs.js';
5
6
  export * as getPreferences from './actor/getPreferences.js';
6
7
  export * as getProfile from './actor/getProfile.js';
@@ -1 +1 @@
1
- {"version":3,"file":"actor.js","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/actor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAA;AACvC,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAA;AACnD,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,YAAY,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,qBAAqB,MAAM,kCAAkC,CAAA;AACzE,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * as defs from './actor/defs.js'\nexport * as getPreferences from './actor/getPreferences.js'\nexport * as getProfile from './actor/getProfile.js'\nexport * as getProfiles from './actor/getProfiles.js'\nexport * as getSuggestions from './actor/getSuggestions.js'\nexport * as profile from './actor/profile.js'\nexport * as putPreferences from './actor/putPreferences.js'\nexport * as searchActors from './actor/searchActors.js'\nexport * as searchActorsTypeahead from './actor/searchActorsTypeahead.js'\nexport * as status from './actor/status.js'\n"]}
1
+ {"version":3,"file":"actor.js","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/actor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,4BAA4B,MAAM,yCAAyC,CAAA;AACvF,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAA;AACvC,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAA;AACnD,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,YAAY,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,qBAAqB,MAAM,kCAAkC,CAAA;AACzE,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nexport * as contentVisibilityDeclaration from './actor/contentVisibilityDeclaration.js'\nexport * as defs from './actor/defs.js'\nexport * as getPreferences from './actor/getPreferences.js'\nexport * as getProfile from './actor/getProfile.js'\nexport * as getProfiles from './actor/getProfiles.js'\nexport * as getSuggestions from './actor/getSuggestions.js'\nexport * as profile from './actor/profile.js'\nexport * as putPreferences from './actor/putPreferences.js'\nexport * as searchActors from './actor/searchActors.js'\nexport * as searchActorsTypeahead from './actor/searchActorsTypeahead.js'\nexport * as status from './actor/status.js'\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"authCreatePosts.defs.d.ts","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/authCreatePosts.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,QAAA,MAAM,KAAK,6BAA6B,CAAA;AAExC,KAAK,KAAK,GAAG,OAAO,KAAK,CAAA;AAEzB,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,QAAA,MAAM,IAAI,uHA0BT,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
1
+ {"version":3,"file":"authCreatePosts.defs.d.ts","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/authCreatePosts.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,QAAA,MAAM,KAAK,6BAA6B,CAAA;AAExC,KAAK,KAAK,GAAG,OAAO,KAAK,CAAA;AAEzB,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,QAAA,MAAM,IAAI,uHA+BT,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
@@ -11,6 +11,11 @@ const main = /*#__PURE__*/ l.permissionSet($nsid, [
11
11
  'app.bsky.video.uploadVideo',
12
12
  'app.bsky.video.getJobStatus',
13
13
  'app.bsky.video.getUploadLimits',
14
+ 'app.bsky.video.startUpload',
15
+ 'app.bsky.video.uploadPart',
16
+ 'app.bsky.video.finishUpload',
17
+ 'app.bsky.video.abortUpload',
18
+ 'app.bsky.video.getUploadStatus',
14
19
  ],
15
20
  }),
16
21
  /*#__PURE__*/ l.permission('repo', {
@@ -1 +1 @@
1
- {"version":3,"file":"authCreatePosts.defs.js","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/authCreatePosts.defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,MAAM,KAAK,GAAG,0BAA0B,CAAA;AAIxC,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,aAAa,CACxC,KAAK,EACL;IACE,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE;QAChC,UAAU,EAAE,IAAI;QAChB,GAAG,EAAE;YACH,4BAA4B;YAC5B,6BAA6B;YAC7B,gCAAgC;SACjC;KACF,CAAC;IACF,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE;QACjC,MAAM,EAAE,CAAC,QAAQ,CAAC;QAClB,UAAU,EAAE;YACV,oBAAoB;YACpB,wBAAwB;YACxB,0BAA0B;SAC3B;KACF,CAAC;CACH,EACD;IACE,KAAK,EAAE,sBAAsB;IAC7B,YAAY,EAAE,EAAE;IAChB,MAAM,EAAE,iCAAiC;IACzC,aAAa,EAAE,EAAE;CAClB,CACF,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nimport { l } from '@atproto/lex'\n\nconst $nsid = 'app.bsky.authCreatePosts'\n\ntype $nsid = typeof $nsid\n\nexport { $nsid }\n\nconst main = /*#__PURE__*/ l.permissionSet(\n $nsid,\n [\n /*#__PURE__*/ l.permission('rpc', {\n inheritAud: true,\n lxm: [\n 'app.bsky.video.uploadVideo',\n 'app.bsky.video.getJobStatus',\n 'app.bsky.video.getUploadLimits',\n ],\n }),\n /*#__PURE__*/ l.permission('repo', {\n action: ['create'],\n collection: [\n 'app.bsky.feed.post',\n 'app.bsky.feed.postgate',\n 'app.bsky.feed.threadgate',\n ],\n }),\n ],\n {\n title: 'Create Bluesky Posts',\n 'title:lang': {},\n detail: 'Can not update or delete posts.',\n 'detail:lang': {},\n },\n)\n\nexport { main }\n"]}
1
+ {"version":3,"file":"authCreatePosts.defs.js","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/authCreatePosts.defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,MAAM,KAAK,GAAG,0BAA0B,CAAA;AAIxC,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,aAAa,CACxC,KAAK,EACL;IACE,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE;QAChC,UAAU,EAAE,IAAI;QAChB,GAAG,EAAE;YACH,4BAA4B;YAC5B,6BAA6B;YAC7B,gCAAgC;YAChC,4BAA4B;YAC5B,2BAA2B;YAC3B,6BAA6B;YAC7B,4BAA4B;YAC5B,gCAAgC;SACjC;KACF,CAAC;IACF,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE;QACjC,MAAM,EAAE,CAAC,QAAQ,CAAC;QAClB,UAAU,EAAE;YACV,oBAAoB;YACpB,wBAAwB;YACxB,0BAA0B;SAC3B;KACF,CAAC;CACH,EACD;IACE,KAAK,EAAE,sBAAsB;IAC7B,YAAY,EAAE,EAAE;IAChB,MAAM,EAAE,iCAAiC;IACzC,aAAa,EAAE,EAAE;CAClB,CACF,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA","sourcesContent":["/*\n * THIS FILE WAS GENERATED BY \"@atproto/lex\". DO NOT EDIT.\n */\n\nimport { l } from '@atproto/lex'\n\nconst $nsid = 'app.bsky.authCreatePosts'\n\ntype $nsid = typeof $nsid\n\nexport { $nsid }\n\nconst main = /*#__PURE__*/ l.permissionSet(\n $nsid,\n [\n /*#__PURE__*/ l.permission('rpc', {\n inheritAud: true,\n lxm: [\n 'app.bsky.video.uploadVideo',\n 'app.bsky.video.getJobStatus',\n 'app.bsky.video.getUploadLimits',\n 'app.bsky.video.startUpload',\n 'app.bsky.video.uploadPart',\n 'app.bsky.video.finishUpload',\n 'app.bsky.video.abortUpload',\n 'app.bsky.video.getUploadStatus',\n ],\n }),\n /*#__PURE__*/ l.permission('repo', {\n action: ['create'],\n collection: [\n 'app.bsky.feed.post',\n 'app.bsky.feed.postgate',\n 'app.bsky.feed.threadgate',\n ],\n }),\n ],\n {\n title: 'Create Bluesky Posts',\n 'title:lang': {},\n detail: 'Can not update or delete posts.',\n 'detail:lang': {},\n },\n)\n\nexport { main }\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"authFullApp.defs.d.ts","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/authFullApp.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,QAAA,MAAM,KAAK,yBAAyB,CAAA;AAEpC,KAAK,KAAK,GAAG,OAAO,KAAK,CAAA;AAEzB,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,QAAA,MAAM,IAAI,mHAkIT,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
1
+ {"version":3,"file":"authFullApp.defs.d.ts","sourceRoot":"","sources":["../../../../src/lexicons/app/bsky/authFullApp.defs.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAEhC,QAAA,MAAM,KAAK,yBAAyB,CAAA;AAEpC,KAAK,KAAK,GAAG,OAAO,KAAK,CAAA;AAEzB,OAAO,EAAE,KAAK,EAAE,CAAA;AAEhB,QAAA,MAAM,IAAI,mHAuIT,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
@@ -103,6 +103,11 @@ const main = /*#__PURE__*/ l.permissionSet($nsid, [
103
103
  'app.bsky.video.getJobStatus',
104
104
  'app.bsky.video.getUploadLimits',
105
105
  'app.bsky.video.uploadVideo',
106
+ 'app.bsky.video.startUpload',
107
+ 'app.bsky.video.uploadPart',
108
+ 'app.bsky.video.finishUpload',
109
+ 'app.bsky.video.abortUpload',
110
+ 'app.bsky.video.getUploadStatus',
106
111
  ],
107
112
  }),
108
113
  /*#__PURE__*/ l.permission('repo', {