@atproto/pds 0.4.68 → 0.4.70

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 (77) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/api/app/bsky/feed/getPostThread.d.ts.map +1 -1
  3. package/dist/api/app/bsky/feed/getPostThread.js +11 -6
  4. package/dist/api/app/bsky/feed/getPostThread.js.map +1 -1
  5. package/dist/config/config.d.ts +1 -0
  6. package/dist/config/config.d.ts.map +1 -1
  7. package/dist/config/config.js +3 -0
  8. package/dist/config/config.js.map +1 -1
  9. package/dist/config/env.d.ts +1 -0
  10. package/dist/config/env.d.ts.map +1 -1
  11. package/dist/config/env.js +1 -0
  12. package/dist/config/env.js.map +1 -1
  13. package/dist/context.d.ts +2 -2
  14. package/dist/context.d.ts.map +1 -1
  15. package/dist/context.js +8 -1
  16. package/dist/context.js.map +1 -1
  17. package/dist/lexicon/index.d.ts +13 -0
  18. package/dist/lexicon/index.d.ts.map +1 -1
  19. package/dist/lexicon/index.js +36 -1
  20. package/dist/lexicon/index.js.map +1 -1
  21. package/dist/lexicon/lexicons.d.ts +396 -0
  22. package/dist/lexicon/lexicons.d.ts.map +1 -1
  23. package/dist/lexicon/lexicons.js +439 -3
  24. package/dist/lexicon/lexicons.js.map +1 -1
  25. package/dist/lexicon/types/app/bsky/unspecced/getConfig.d.ts +34 -0
  26. package/dist/lexicon/types/app/bsky/unspecced/getConfig.d.ts.map +1 -0
  27. package/dist/lexicon/types/app/bsky/unspecced/getConfig.js +3 -0
  28. package/dist/lexicon/types/app/bsky/unspecced/getConfig.js.map +1 -0
  29. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +60 -2
  30. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  31. package/dist/lexicon/types/tools/ozone/moderation/defs.js +50 -0
  32. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  33. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +1 -1
  34. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  35. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +10 -0
  36. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
  37. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts +20 -0
  38. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts.map +1 -0
  39. package/dist/lexicon/types/tools/ozone/setting/defs.js +15 -0
  40. package/dist/lexicon/types/tools/ozone/setting/defs.js.map +1 -0
  41. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts +43 -0
  42. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts.map +1 -0
  43. package/dist/lexicon/types/tools/ozone/setting/listOptions.js +3 -0
  44. package/dist/lexicon/types/tools/ozone/setting/listOptions.js.map +1 -0
  45. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts +40 -0
  46. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts.map +1 -0
  47. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js +3 -0
  48. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js.map +1 -0
  49. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts +45 -0
  50. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts.map +1 -0
  51. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js +3 -0
  52. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js.map +1 -0
  53. package/dist/pipethrough.d.ts +0 -1
  54. package/dist/pipethrough.d.ts.map +1 -1
  55. package/dist/pipethrough.js +11 -10
  56. package/dist/pipethrough.js.map +1 -1
  57. package/example.env +1 -1
  58. package/package.json +11 -11
  59. package/src/api/app/bsky/feed/getPostThread.ts +15 -4
  60. package/src/config/config.ts +5 -0
  61. package/src/config/env.ts +2 -0
  62. package/src/context.ts +11 -3
  63. package/src/lexicon/index.ts +58 -0
  64. package/src/lexicon/lexicons.ts +449 -3
  65. package/src/lexicon/types/app/bsky/unspecced/getConfig.ts +43 -0
  66. package/src/lexicon/types/tools/ozone/moderation/defs.ts +132 -0
  67. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +3 -0
  68. package/src/lexicon/types/tools/ozone/moderation/queryStatuses.ts +10 -0
  69. package/src/lexicon/types/tools/ozone/setting/defs.ts +37 -0
  70. package/src/lexicon/types/tools/ozone/setting/listOptions.ts +53 -0
  71. package/src/lexicon/types/tools/ozone/setting/removeOptions.ts +49 -0
  72. package/src/lexicon/types/tools/ozone/setting/upsertOption.ts +58 -0
  73. package/src/pipethrough.ts +13 -12
  74. package/tests/proxied/__snapshots__/admin.test.ts.snap +12 -0
  75. package/tests/proxied/proxy-catchall.test.ts +1 -1
  76. package/tests/proxied/read-after-write.test.ts +20 -1
  77. package/tsconfig.build.tsbuildinfo +1 -1
@@ -30,6 +30,9 @@ export interface ModEventView {
30
30
  | ModEventResolveAppeal
31
31
  | ModEventDivert
32
32
  | ModEventTag
33
+ | AccountEvent
34
+ | IdentityEvent
35
+ | RecordEvent
33
36
  | { $type: string; [k: string]: unknown }
34
37
  subject:
35
38
  | ComAtprotoAdminDefs.RepoRef
@@ -74,6 +77,9 @@ export interface ModEventViewDetail {
74
77
  | ModEventResolveAppeal
75
78
  | ModEventDivert
76
79
  | ModEventTag
80
+ | AccountEvent
81
+ | IdentityEvent
82
+ | RecordEvent
77
83
  | { $type: string; [k: string]: unknown }
78
84
  subject:
79
85
  | RepoView
@@ -105,6 +111,10 @@ export interface SubjectStatusView {
105
111
  | ComAtprotoAdminDefs.RepoRef
106
112
  | ComAtprotoRepoStrongRef.Main
107
113
  | { $type: string; [k: string]: unknown }
114
+ hosting?:
115
+ | AccountHosting
116
+ | RecordHosting
117
+ | { $type: string; [k: string]: unknown }
108
118
  subjectBlobCids?: string[]
109
119
  subjectRepoHandle?: string
110
120
  /** Timestamp referencing when the last update was made to the moderation status of the subject */
@@ -472,6 +482,78 @@ export function validateModEventTag(v: unknown): ValidationResult {
472
482
  return lexicons.validate('tools.ozone.moderation.defs#modEventTag', v)
473
483
  }
474
484
 
485
+ /** Logs account status related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */
486
+ export interface AccountEvent {
487
+ comment?: string
488
+ /** Indicates that the account has a repository which can be fetched from the host that emitted this event. */
489
+ active: boolean
490
+ status?:
491
+ | 'unknown'
492
+ | 'deactivated'
493
+ | 'deleted'
494
+ | 'takendown'
495
+ | 'suspended'
496
+ | 'tombstoned'
497
+ | (string & {})
498
+ timestamp: string
499
+ [k: string]: unknown
500
+ }
501
+
502
+ export function isAccountEvent(v: unknown): v is AccountEvent {
503
+ return (
504
+ isObj(v) &&
505
+ hasProp(v, '$type') &&
506
+ v.$type === 'tools.ozone.moderation.defs#accountEvent'
507
+ )
508
+ }
509
+
510
+ export function validateAccountEvent(v: unknown): ValidationResult {
511
+ return lexicons.validate('tools.ozone.moderation.defs#accountEvent', v)
512
+ }
513
+
514
+ /** Logs identity related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */
515
+ export interface IdentityEvent {
516
+ comment?: string
517
+ handle?: string
518
+ pdsHost?: string
519
+ tombstone?: boolean
520
+ timestamp: string
521
+ [k: string]: unknown
522
+ }
523
+
524
+ export function isIdentityEvent(v: unknown): v is IdentityEvent {
525
+ return (
526
+ isObj(v) &&
527
+ hasProp(v, '$type') &&
528
+ v.$type === 'tools.ozone.moderation.defs#identityEvent'
529
+ )
530
+ }
531
+
532
+ export function validateIdentityEvent(v: unknown): ValidationResult {
533
+ return lexicons.validate('tools.ozone.moderation.defs#identityEvent', v)
534
+ }
535
+
536
+ /** Logs lifecycle event on a record subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */
537
+ export interface RecordEvent {
538
+ comment?: string
539
+ op: 'create' | 'update' | 'delete' | (string & {})
540
+ cid?: string
541
+ timestamp: string
542
+ [k: string]: unknown
543
+ }
544
+
545
+ export function isRecordEvent(v: unknown): v is RecordEvent {
546
+ return (
547
+ isObj(v) &&
548
+ hasProp(v, '$type') &&
549
+ v.$type === 'tools.ozone.moderation.defs#recordEvent'
550
+ )
551
+ }
552
+
553
+ export function validateRecordEvent(v: unknown): ValidationResult {
554
+ return lexicons.validate('tools.ozone.moderation.defs#recordEvent', v)
555
+ }
556
+
475
557
  export interface RepoView {
476
558
  did: string
477
559
  handle: string
@@ -483,6 +565,7 @@ export interface RepoView {
483
565
  invitesDisabled?: boolean
484
566
  inviteNote?: string
485
567
  deactivatedAt?: string
568
+ threatSignatures?: ComAtprotoAdminDefs.ThreatSignature[]
486
569
  [k: string]: unknown
487
570
  }
488
571
 
@@ -512,6 +595,7 @@ export interface RepoViewDetail {
512
595
  inviteNote?: string
513
596
  emailConfirmedAt?: string
514
597
  deactivatedAt?: string
598
+ threatSignatures?: ComAtprotoAdminDefs.ThreatSignature[]
515
599
  [k: string]: unknown
516
600
  }
517
601
 
@@ -703,3 +787,51 @@ export function isVideoDetails(v: unknown): v is VideoDetails {
703
787
  export function validateVideoDetails(v: unknown): ValidationResult {
704
788
  return lexicons.validate('tools.ozone.moderation.defs#videoDetails', v)
705
789
  }
790
+
791
+ export interface AccountHosting {
792
+ status:
793
+ | 'takendown'
794
+ | 'suspended'
795
+ | 'deleted'
796
+ | 'deactivated'
797
+ | 'unknown'
798
+ | (string & {})
799
+ updatedAt?: string
800
+ createdAt?: string
801
+ deletedAt?: string
802
+ deactivatedAt?: string
803
+ reactivatedAt?: string
804
+ [k: string]: unknown
805
+ }
806
+
807
+ export function isAccountHosting(v: unknown): v is AccountHosting {
808
+ return (
809
+ isObj(v) &&
810
+ hasProp(v, '$type') &&
811
+ v.$type === 'tools.ozone.moderation.defs#accountHosting'
812
+ )
813
+ }
814
+
815
+ export function validateAccountHosting(v: unknown): ValidationResult {
816
+ return lexicons.validate('tools.ozone.moderation.defs#accountHosting', v)
817
+ }
818
+
819
+ export interface RecordHosting {
820
+ status: 'deleted' | 'unknown' | (string & {})
821
+ updatedAt?: string
822
+ createdAt?: string
823
+ deletedAt?: string
824
+ [k: string]: unknown
825
+ }
826
+
827
+ export function isRecordHosting(v: unknown): v is RecordHosting {
828
+ return (
829
+ isObj(v) &&
830
+ hasProp(v, '$type') &&
831
+ v.$type === 'tools.ozone.moderation.defs#recordHosting'
832
+ )
833
+ }
834
+
835
+ export function validateRecordHosting(v: unknown): ValidationResult {
836
+ return lexicons.validate('tools.ozone.moderation.defs#recordHosting', v)
837
+ }
@@ -29,6 +29,9 @@ export interface InputSchema {
29
29
  | ToolsOzoneModerationDefs.ModEventResolveAppeal
30
30
  | ToolsOzoneModerationDefs.ModEventEmail
31
31
  | ToolsOzoneModerationDefs.ModEventTag
32
+ | ToolsOzoneModerationDefs.AccountEvent
33
+ | ToolsOzoneModerationDefs.IdentityEvent
34
+ | ToolsOzoneModerationDefs.RecordEvent
32
35
  | { $type: string; [k: string]: unknown }
33
36
  subject:
34
37
  | ComAtprotoAdminDefs.RepoRef
@@ -22,6 +22,16 @@ export interface QueryParams {
22
22
  reportedBefore?: string
23
23
  /** Search subjects reviewed after a given timestamp */
24
24
  reviewedAfter?: string
25
+ /** Search subjects where the associated record/account was deleted after a given timestamp */
26
+ hostingDeletedAfter?: string
27
+ /** Search subjects where the associated record/account was deleted before a given timestamp */
28
+ hostingDeletedBefore?: string
29
+ /** Search subjects where the associated record/account was updated after a given timestamp */
30
+ hostingUpdatedAfter?: string
31
+ /** Search subjects where the associated record/account was updated before a given timestamp */
32
+ hostingUpdatedBefore?: string
33
+ /** Search subjects by the status of the associated record/account */
34
+ hostingStatuses?: string[]
25
35
  /** Search subjects reviewed before a given timestamp */
26
36
  reviewedBefore?: string
27
37
  /** By default, we don't include muted subjects in the results. Set this to true to include them. */
@@ -0,0 +1,37 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
5
+ import { lexicons } from '../../../../lexicons'
6
+ import { isObj, hasProp } from '../../../../util'
7
+ import { CID } from 'multiformats/cid'
8
+
9
+ export interface Option {
10
+ key: string
11
+ did: string
12
+ value: {}
13
+ description?: string
14
+ createdAt?: string
15
+ updatedAt?: string
16
+ managerRole?:
17
+ | 'tools.ozone.team.defs#roleModerator'
18
+ | 'tools.ozone.team.defs#roleTriage'
19
+ | 'tools.ozone.team.defs#roleAdmin'
20
+ | (string & {})
21
+ scope: 'instance' | 'personal' | (string & {})
22
+ createdBy: string
23
+ lastUpdatedBy: string
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export function isOption(v: unknown): v is Option {
28
+ return (
29
+ isObj(v) &&
30
+ hasProp(v, '$type') &&
31
+ v.$type === 'tools.ozone.setting.defs#option'
32
+ )
33
+ }
34
+
35
+ export function validateOption(v: unknown): ValidationResult {
36
+ return lexicons.validate('tools.ozone.setting.defs#option', v)
37
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneSettingDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ limit: number
14
+ cursor?: string
15
+ scope: 'instance' | 'personal' | (string & {})
16
+ /** Filter keys by prefix */
17
+ prefix?: string
18
+ /** Filter for only the specified keys. Ignored if prefix is provided */
19
+ keys?: string[]
20
+ }
21
+
22
+ export type InputSchema = undefined
23
+
24
+ export interface OutputSchema {
25
+ cursor?: string
26
+ options: ToolsOzoneSettingDefs.Option[]
27
+ [k: string]: unknown
28
+ }
29
+
30
+ export type HandlerInput = undefined
31
+
32
+ export interface HandlerSuccess {
33
+ encoding: 'application/json'
34
+ body: OutputSchema
35
+ headers?: { [key: string]: string }
36
+ }
37
+
38
+ export interface HandlerError {
39
+ status: number
40
+ message?: string
41
+ }
42
+
43
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
44
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
45
+ auth: HA
46
+ params: QueryParams
47
+ input: HandlerInput
48
+ req: express.Request
49
+ res: express.Response
50
+ }
51
+ export type Handler<HA extends HandlerAuth = never> = (
52
+ ctx: HandlerReqCtx<HA>,
53
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,49 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+
11
+ export interface QueryParams {}
12
+
13
+ export interface InputSchema {
14
+ keys: string[]
15
+ scope: 'instance' | 'personal' | (string & {})
16
+ [k: string]: unknown
17
+ }
18
+
19
+ export interface OutputSchema {
20
+ [k: string]: unknown
21
+ }
22
+
23
+ export interface HandlerInput {
24
+ encoding: 'application/json'
25
+ body: InputSchema
26
+ }
27
+
28
+ export interface HandlerSuccess {
29
+ encoding: 'application/json'
30
+ body: OutputSchema
31
+ headers?: { [key: string]: string }
32
+ }
33
+
34
+ export interface HandlerError {
35
+ status: number
36
+ message?: string
37
+ }
38
+
39
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
40
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
41
+ auth: HA
42
+ params: QueryParams
43
+ input: HandlerInput
44
+ req: express.Request
45
+ res: express.Response
46
+ }
47
+ export type Handler<HA extends HandlerAuth = never> = (
48
+ ctx: HandlerReqCtx<HA>,
49
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,58 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneSettingDefs from './defs'
11
+
12
+ export interface QueryParams {}
13
+
14
+ export interface InputSchema {
15
+ key: string
16
+ scope: 'instance' | 'personal' | (string & {})
17
+ value: {}
18
+ description?: string
19
+ managerRole?:
20
+ | 'tools.ozone.team.defs#roleModerator'
21
+ | 'tools.ozone.team.defs#roleTriage'
22
+ | 'tools.ozone.team.defs#roleAdmin'
23
+ | (string & {})
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export interface OutputSchema {
28
+ option: ToolsOzoneSettingDefs.Option
29
+ [k: string]: unknown
30
+ }
31
+
32
+ export interface HandlerInput {
33
+ encoding: 'application/json'
34
+ body: InputSchema
35
+ }
36
+
37
+ export interface HandlerSuccess {
38
+ encoding: 'application/json'
39
+ body: OutputSchema
40
+ headers?: { [key: string]: string }
41
+ }
42
+
43
+ export interface HandlerError {
44
+ status: number
45
+ message?: string
46
+ }
47
+
48
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
49
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
50
+ auth: HA
51
+ params: QueryParams
52
+ input: HandlerInput
53
+ req: express.Request
54
+ res: express.Response
55
+ }
56
+ export type Handler<HA extends HandlerAuth = never> = (
57
+ ctx: HandlerReqCtx<HA>,
58
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -357,9 +357,9 @@ async function pipethroughRequest(
357
357
 
358
358
  function handleUpstreamRequestError(
359
359
  err: unknown,
360
- message = 'pipethrough network error',
360
+ message = 'Upstream service unreachable',
361
361
  ): never {
362
- httpLogger.warn({ err }, message)
362
+ httpLogger.error({ err }, message)
363
363
  throw new XRPCServerError(ResponseType.UpstreamFailure, message, undefined, {
364
364
  cause: err,
365
365
  })
@@ -520,18 +520,11 @@ async function tryParsingError(
520
520
  }
521
521
  }
522
522
 
523
- export async function bufferUpstreamResponse(
523
+ async function bufferUpstreamResponse(
524
524
  readable: Readable,
525
525
  contentEncoding?: string | string[],
526
526
  ): Promise<Buffer> {
527
527
  try {
528
- // Needed for type-safety (should never happen irl)
529
- if (Array.isArray(contentEncoding)) {
530
- throw new TypeError(
531
- 'upstream service returned multiple content-encoding headers',
532
- )
533
- }
534
-
535
528
  return await streamToNodeBuffer(decodeStream(readable, contentEncoding))
536
529
  } catch (err) {
537
530
  if (!readable.destroyed) readable.destroy()
@@ -561,7 +554,11 @@ export async function asPipeThroughBuffer(
561
554
  // Response parsing/forwarding
562
555
  // -------------------
563
556
 
564
- const RES_HEADERS_TO_FORWARD = ['atproto-repo-rev', 'atproto-content-labelers']
557
+ const RES_HEADERS_TO_FORWARD = [
558
+ 'atproto-repo-rev',
559
+ 'atproto-content-labelers',
560
+ 'retry-after',
561
+ ]
565
562
 
566
563
  function* responseHeaders(
567
564
  headers: IncomingHttpHeaders,
@@ -584,7 +581,11 @@ function* responseHeaders(
584
581
  for (let i = 0; i < RES_HEADERS_TO_FORWARD.length; i++) {
585
582
  const name = RES_HEADERS_TO_FORWARD[i]
586
583
  const val = headers[name]
587
- if (typeof val === 'string') yield [name, val]
584
+
585
+ if (val != null) {
586
+ const value: string = Array.isArray(val) ? val.join(',') : val
587
+ yield [name, value]
588
+ }
588
589
  }
589
590
  }
590
591
 
@@ -122,6 +122,10 @@ Object {
122
122
  "moderation": Object {
123
123
  "subjectStatus": Object {
124
124
  "createdAt": "1970-01-01T00:00:00.000Z",
125
+ "hosting": Object {
126
+ "$type": "tools.ozone.moderation.defs#accountHosting",
127
+ "status": "unknown",
128
+ },
125
129
  "id": 1,
126
130
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
127
131
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -214,6 +218,10 @@ Object {
214
218
  "moderation": Object {
215
219
  "subjectStatus": Object {
216
220
  "createdAt": "1970-01-01T00:00:00.000Z",
221
+ "hosting": Object {
222
+ "$type": "tools.ozone.moderation.defs#recordHosting",
223
+ "status": "unknown",
224
+ },
217
225
  "id": 4,
218
226
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
219
227
  "lastReviewedBy": "user(1)",
@@ -271,6 +279,10 @@ Object {
271
279
  "moderation": Object {
272
280
  "subjectStatus": Object {
273
281
  "createdAt": "1970-01-01T00:00:00.000Z",
282
+ "hosting": Object {
283
+ "$type": "tools.ozone.moderation.defs#accountHosting",
284
+ "status": "unknown",
285
+ },
274
286
  "id": 1,
275
287
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
276
288
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -116,7 +116,7 @@ describe('proxy header', () => {
116
116
  for (const lex of lexicons) client.lex.add(lex)
117
117
 
118
118
  await expect(client.call('com.example.ok')).rejects.toThrow(
119
- 'pipethrough network error',
119
+ 'Upstream service unreachable',
120
120
  )
121
121
  })
122
122
 
@@ -101,12 +101,22 @@ describe('proxy read after write', () => {
101
101
  { uri: sc.posts[alice][0].ref.uriStr },
102
102
  { headers: { ...sc.getHeaders(alice) } },
103
103
  )
104
- const layerOne = res.data.thread.replies as ThreadViewPost[]
104
+ const thread = res.data.thread as ThreadViewPost
105
+ const layerOne = thread.replies as ThreadViewPost[]
105
106
  expect(layerOne.length).toBe(1)
106
107
  expect(layerOne[0].post.uri).toEqual(reply1.ref.uriStr)
107
108
  const layerTwo = layerOne[0].replies as ThreadViewPost[]
108
109
  expect(layerTwo.length).toBe(1)
109
110
  expect(layerTwo[0].post.uri).toEqual(reply2.ref.uriStr)
111
+
112
+ const aliceHandle = sc.accounts[alice].handle
113
+ const handleUriStr = thread.post.uri.replace(alice, aliceHandle)
114
+ expect(handleUriStr).not.toEqual(thread.post.uri)
115
+ const handleRes = await agent.api.app.bsky.feed.getPostThread(
116
+ { uri: handleUriStr },
117
+ { headers: { ...sc.getHeaders(alice) } },
118
+ )
119
+ expect(handleRes.data.thread).toEqual(res.data.thread)
110
120
  })
111
121
 
112
122
  it('handles read after write on a thread that is not found on appview', async () => {
@@ -123,6 +133,15 @@ describe('proxy read after write', () => {
123
133
  expect((thread.replies?.at(0) as ThreadViewPost).post.uri).toEqual(
124
134
  replyRef2.uriStr,
125
135
  )
136
+
137
+ const aliceHandle = sc.accounts[alice].handle
138
+ const handleUriStr = thread.post.uri.replace(alice, aliceHandle)
139
+ expect(handleUriStr).not.toEqual(thread.post.uri)
140
+ const handleRes = await agent.api.app.bsky.feed.getPostThread(
141
+ { uri: handleUriStr },
142
+ { headers: { ...sc.getHeaders(alice) } },
143
+ )
144
+ expect(handleRes.data.thread).toEqual(res.data.thread)
126
145
  })
127
146
 
128
147
  it('handles read after write on threads with record embeds', async () => {