@atproto/xrpc-server 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,11 +7,11 @@ export declare const messageFrameHeader: z.ZodObject<{
7
7
  op: z.ZodLiteral<FrameType.Message>;
8
8
  t: z.ZodOptional<z.ZodString>;
9
9
  }, "strip", z.ZodTypeAny, {
10
- t?: string | undefined;
11
10
  op: FrameType.Message;
12
- }, {
13
11
  t?: string | undefined;
12
+ }, {
14
13
  op: FrameType.Message;
14
+ t?: string | undefined;
15
15
  }>;
16
16
  export declare type MessageFrameHeader = z.infer<typeof messageFrameHeader>;
17
17
  export declare const errorFrameHeader: z.ZodObject<{
@@ -25,11 +25,11 @@ export declare const errorFrameBody: z.ZodObject<{
25
25
  error: z.ZodString;
26
26
  message: z.ZodOptional<z.ZodString>;
27
27
  }, "strip", z.ZodTypeAny, {
28
- message?: string | undefined;
29
28
  error: string;
30
- }, {
31
29
  message?: string | undefined;
30
+ }, {
32
31
  error: string;
32
+ message?: string | undefined;
33
33
  }>;
34
34
  export declare type ErrorFrameHeader = z.infer<typeof errorFrameHeader>;
35
35
  export declare type ErrorFrameBody<T extends string = string> = {
@@ -39,11 +39,11 @@ export declare const frameHeader: z.ZodUnion<[z.ZodObject<{
39
39
  op: z.ZodLiteral<FrameType.Message>;
40
40
  t: z.ZodOptional<z.ZodString>;
41
41
  }, "strip", z.ZodTypeAny, {
42
- t?: string | undefined;
43
42
  op: FrameType.Message;
44
- }, {
45
43
  t?: string | undefined;
44
+ }, {
46
45
  op: FrameType.Message;
46
+ t?: string | undefined;
47
47
  }>, z.ZodObject<{
48
48
  op: z.ZodLiteral<FrameType.Error>;
49
49
  }, "strip", z.ZodTypeAny, {
package/dist/types.d.ts CHANGED
@@ -18,11 +18,11 @@ export declare const handlerInput: zod.ZodObject<{
18
18
  encoding: zod.ZodString;
19
19
  body: zod.ZodAny;
20
20
  }, "strip", zod.ZodTypeAny, {
21
- body?: any;
22
21
  encoding: string;
23
- }, {
24
22
  body?: any;
23
+ }, {
25
24
  encoding: string;
25
+ body?: any;
26
26
  }>;
27
27
  export declare type HandlerInput = zod.infer<typeof handlerInput>;
28
28
  export declare const handlerAuth: zod.ZodObject<{
@@ -39,12 +39,15 @@ export declare type HandlerAuth = zod.infer<typeof handlerAuth>;
39
39
  export declare const handlerSuccess: zod.ZodObject<{
40
40
  encoding: zod.ZodString;
41
41
  body: zod.ZodAny;
42
+ headers: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodString>>;
42
43
  }, "strip", zod.ZodTypeAny, {
43
- body?: any;
44
44
  encoding: string;
45
- }, {
46
45
  body?: any;
46
+ headers?: Record<string, string> | undefined;
47
+ }, {
47
48
  encoding: string;
49
+ body?: any;
50
+ headers?: Record<string, string> | undefined;
48
51
  }>;
49
52
  export declare type HandlerSuccess = zod.infer<typeof handlerSuccess>;
50
53
  export declare const handlerError: zod.ZodObject<{
@@ -52,13 +55,13 @@ export declare const handlerError: zod.ZodObject<{
52
55
  error: zod.ZodOptional<zod.ZodString>;
53
56
  message: zod.ZodOptional<zod.ZodString>;
54
57
  }, "strip", zod.ZodTypeAny, {
58
+ status: number;
55
59
  error?: string | undefined;
56
60
  message?: string | undefined;
57
- status: number;
58
61
  }, {
62
+ status: number;
59
63
  error?: string | undefined;
60
64
  message?: string | undefined;
61
- status: number;
62
65
  }>;
63
66
  export declare type HandlerError = zod.infer<typeof handlerError>;
64
67
  export declare type HandlerOutput = HandlerSuccess | HandlerError;
package/dist/util.d.ts CHANGED
@@ -9,3 +9,18 @@ export declare function validateOutput(nsid: string, def: LexXrpcProcedure | Lex
9
9
  export declare function normalizeMime(v: string): any;
10
10
  export declare function hasBody(req: express.Request): string | true | undefined;
11
11
  export declare function processBodyAsBytes(req: express.Request): Promise<Uint8Array>;
12
+ export declare function serverTimingHeader(timings: ServerTiming[]): string;
13
+ export declare class ServerTimer implements ServerTiming {
14
+ name: string;
15
+ description?: string | undefined;
16
+ duration?: number;
17
+ private startMs?;
18
+ constructor(name: string, description?: string | undefined);
19
+ start(): this;
20
+ stop(): this;
21
+ }
22
+ export interface ServerTiming {
23
+ name: string;
24
+ duration?: number;
25
+ description?: string;
26
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@atproto/xrpc-server",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "test": "jest",
7
- "prettier": "prettier --check src/",
8
- "prettier:fix": "prettier --write src/",
7
+ "prettier": "prettier --check src/ tests/",
8
+ "prettier:fix": "prettier --write src/ tests/",
9
9
  "lint": "eslint . --ext .ts,.tsx",
10
10
  "lint:fix": "yarn lint --fix",
11
11
  "verify": "run-p prettier lint",
@@ -33,7 +33,7 @@
33
33
  "mime-types": "^2.1.35",
34
34
  "uint8arrays": "3.0.0",
35
35
  "ws": "^8.12.0",
36
- "zod": "^3.14.2"
36
+ "zod": "^3.21.4"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@atproto/crypto": "*",
package/src/auth.ts CHANGED
@@ -44,7 +44,7 @@ const jsonToB64Url = (json: Record<string, unknown>): string => {
44
44
 
45
45
  export const verifyJwt = async (
46
46
  jwtStr: string,
47
- ownDid: string,
47
+ ownDid: string | null, // null indicates to skip the audience check
48
48
  getSigningKey: (did: string) => Promise<string>,
49
49
  ): Promise<string> => {
50
50
  const parts = jwtStr.split('.')
@@ -57,7 +57,7 @@ export const verifyJwt = async (
57
57
  if (Date.now() / 1000 > payload.exp) {
58
58
  throw new AuthRequiredError('jwt expired', 'JwtExpired')
59
59
  }
60
- if (payload.aud !== ownDid) {
60
+ if (ownDid !== null && payload.aud !== ownDid) {
61
61
  throw new AuthRequiredError(
62
62
  'jwt audience does not match service did',
63
63
  'BadJwtAudience',
package/src/index.ts CHANGED
@@ -2,3 +2,6 @@ export * from './types'
2
2
  export * from './auth'
3
3
  export * from './server'
4
4
  export * from './stream'
5
+
6
+ export type { ServerTiming } from './util'
7
+ export { serverTimingHeader, ServerTimer } from './util'
package/src/server.ts CHANGED
@@ -219,6 +219,12 @@ export class Server {
219
219
  if (!outputUnvalidated || isHandlerSuccess(outputUnvalidated)) {
220
220
  // validate response
221
221
  const output = validateResOutput(outputUnvalidated)
222
+ // set headers
223
+ if (output?.headers) {
224
+ Object.entries(output.headers).forEach(([name, val]) => {
225
+ res.header(name, val)
226
+ })
227
+ }
222
228
  // send response
223
229
  if (
224
230
  output?.encoding === 'application/json' ||
@@ -229,6 +235,7 @@ export class Server {
229
235
  } else if (output?.body instanceof Readable) {
230
236
  res.header('Content-Type', output.encoding)
231
237
  res.status(200)
238
+ res.once('error', (err) => res.destroy(err))
232
239
  forwardStreamErrors(output.body, res)
233
240
  output.body.pipe(res)
234
241
  } else if (output) {
package/src/types.ts CHANGED
@@ -37,6 +37,7 @@ export type HandlerAuth = zod.infer<typeof handlerAuth>
37
37
  export const handlerSuccess = zod.object({
38
38
  encoding: zod.string(),
39
39
  body: zod.any(),
40
+ headers: zod.record(zod.string()).optional(),
40
41
  })
41
42
  export type HandlerSuccess = zod.infer<typeof handlerSuccess>
42
43
 
package/src/util.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import assert from 'assert'
1
2
  import { Readable, Transform } from 'stream'
2
3
  import { createDeflate, createGunzip } from 'zlib'
3
4
  import express from 'express'
@@ -266,3 +267,35 @@ function decodeBodyStream(
266
267
 
267
268
  return stream
268
269
  }
270
+
271
+ export function serverTimingHeader(timings: ServerTiming[]) {
272
+ return timings
273
+ .map((timing) => {
274
+ let header = timing.name
275
+ if (timing.duration) header += `;dur=${timing.duration}`
276
+ if (timing.description) header += `;desc="${timing.description}"`
277
+ return header
278
+ })
279
+ .join(', ')
280
+ }
281
+
282
+ export class ServerTimer implements ServerTiming {
283
+ public duration?: number
284
+ private startMs?: number
285
+ constructor(public name: string, public description?: string) {}
286
+ start() {
287
+ this.startMs = Date.now()
288
+ return this
289
+ }
290
+ stop() {
291
+ assert(this.startMs, "timer hasn't been started")
292
+ this.duration = Date.now() - this.startMs
293
+ return this
294
+ }
295
+ }
296
+
297
+ export interface ServerTiming {
298
+ name: string
299
+ duration?: number
300
+ description?: string
301
+ }
@@ -43,7 +43,7 @@ const LEXICONS = [
43
43
  },
44
44
  {
45
45
  lexicon: 1,
46
- id: 'io.example.validationTest2',
46
+ id: 'io.example.validationTestTwo',
47
47
  defs: {
48
48
  main: {
49
49
  type: 'query',
@@ -101,7 +101,7 @@ describe('Bodies', () => {
101
101
  body: ctx.input?.body,
102
102
  }),
103
103
  )
104
- server.method('io.example.validationTest2', () => ({
104
+ server.method('io.example.validationTestTwo', () => ({
105
105
  encoding: 'json',
106
106
  body: { wrong: 'data' },
107
107
  }))
@@ -175,7 +175,7 @@ describe('Bodies', () => {
175
175
  return logger.error(obj, ...args)
176
176
  }
177
177
 
178
- await expect(client.call('io.example.validationTest2')).rejects.toThrow(
178
+ await expect(client.call('io.example.validationTestTwo')).rejects.toThrow(
179
179
  'Internal Server Error',
180
180
  )
181
181
  expect(error).toEqual(`Output must have the property "foo"`)
@@ -8,7 +8,7 @@ import * as xrpcServer from '../src'
8
8
  const LEXICONS = [
9
9
  {
10
10
  lexicon: 1,
11
- id: 'io.example.ping1',
11
+ id: 'io.example.pingOne',
12
12
  defs: {
13
13
  main: {
14
14
  type: 'procedure',
@@ -26,7 +26,7 @@ const LEXICONS = [
26
26
  },
27
27
  {
28
28
  lexicon: 1,
29
- id: 'io.example.ping2',
29
+ id: 'io.example.pingTwo',
30
30
  defs: {
31
31
  main: {
32
32
  type: 'procedure',
@@ -41,7 +41,7 @@ const LEXICONS = [
41
41
  },
42
42
  {
43
43
  lexicon: 1,
44
- id: 'io.example.ping3',
44
+ id: 'io.example.pingThree',
45
45
  defs: {
46
46
  main: {
47
47
  type: 'procedure',
@@ -56,7 +56,7 @@ const LEXICONS = [
56
56
  },
57
57
  {
58
58
  lexicon: 1,
59
- id: 'io.example.ping4',
59
+ id: 'io.example.pingFour',
60
60
  defs: {
61
61
  main: {
62
62
  type: 'procedure',
@@ -84,17 +84,17 @@ const LEXICONS = [
84
84
  describe('Procedures', () => {
85
85
  let s: http.Server
86
86
  const server = xrpcServer.createServer(LEXICONS)
87
- server.method('io.example.ping1', (ctx: { params: xrpcServer.Params }) => {
87
+ server.method('io.example.pingOne', (ctx: { params: xrpcServer.Params }) => {
88
88
  return { encoding: 'text/plain', body: ctx.params.message }
89
89
  })
90
90
  server.method(
91
- 'io.example.ping2',
91
+ 'io.example.pingTwo',
92
92
  (ctx: { params: xrpcServer.Params; input?: xrpcServer.HandlerInput }) => {
93
93
  return { encoding: 'text/plain', body: ctx.input?.body }
94
94
  },
95
95
  )
96
96
  server.method(
97
- 'io.example.ping3',
97
+ 'io.example.pingThree',
98
98
  async (ctx: {
99
99
  params: xrpcServer.Params
100
100
  input?: xrpcServer.HandlerInput
@@ -112,7 +112,7 @@ describe('Procedures', () => {
112
112
  },
113
113
  )
114
114
  server.method(
115
- 'io.example.ping4',
115
+ 'io.example.pingFour',
116
116
  (ctx: { params: xrpcServer.Params; input?: xrpcServer.HandlerInput }) => {
117
117
  return {
118
118
  encoding: 'application/json',
@@ -133,14 +133,14 @@ describe('Procedures', () => {
133
133
  })
134
134
 
135
135
  it('serves requests', async () => {
136
- const res1 = await client.call('io.example.ping1', {
136
+ const res1 = await client.call('io.example.pingOne', {
137
137
  message: 'hello world',
138
138
  })
139
139
  expect(res1.success).toBeTruthy()
140
140
  expect(res1.headers['content-type']).toBe('text/plain; charset=utf-8')
141
141
  expect(res1.data).toBe('hello world')
142
142
 
143
- const res2 = await client.call('io.example.ping2', {}, 'hello world', {
143
+ const res2 = await client.call('io.example.pingTwo', {}, 'hello world', {
144
144
  encoding: 'text/plain',
145
145
  })
146
146
  expect(res2.success).toBeTruthy()
@@ -148,7 +148,7 @@ describe('Procedures', () => {
148
148
  expect(res2.data).toBe('hello world')
149
149
 
150
150
  const res3 = await client.call(
151
- 'io.example.ping3',
151
+ 'io.example.pingThree',
152
152
  {},
153
153
  new TextEncoder().encode('hello world'),
154
154
  { encoding: 'application/octet-stream' },
@@ -158,7 +158,7 @@ describe('Procedures', () => {
158
158
  expect(new TextDecoder().decode(res3.data)).toBe('hello world')
159
159
 
160
160
  const res4 = await client.call(
161
- 'io.example.ping4',
161
+ 'io.example.pingFour',
162
162
  {},
163
163
  { message: 'hello world' },
164
164
  )
@@ -7,7 +7,7 @@ import * as xrpcServer from '../src'
7
7
  const LEXICONS = [
8
8
  {
9
9
  lexicon: 1,
10
- id: 'io.example.ping1',
10
+ id: 'io.example.pingOne',
11
11
  defs: {
12
12
  main: {
13
13
  type: 'query',
@@ -25,7 +25,7 @@ const LEXICONS = [
25
25
  },
26
26
  {
27
27
  lexicon: 1,
28
- id: 'io.example.ping2',
28
+ id: 'io.example.pingTwo',
29
29
  defs: {
30
30
  main: {
31
31
  type: 'query',
@@ -43,7 +43,7 @@ const LEXICONS = [
43
43
  },
44
44
  {
45
45
  lexicon: 1,
46
- id: 'io.example.ping3',
46
+ id: 'io.example.pingThree',
47
47
  defs: {
48
48
  main: {
49
49
  type: 'query',
@@ -69,21 +69,25 @@ const LEXICONS = [
69
69
  describe('Queries', () => {
70
70
  let s: http.Server
71
71
  const server = xrpcServer.createServer(LEXICONS)
72
- server.method('io.example.ping1', (ctx: { params: xrpcServer.Params }) => {
72
+ server.method('io.example.pingOne', (ctx: { params: xrpcServer.Params }) => {
73
73
  return { encoding: 'text/plain', body: ctx.params.message }
74
74
  })
75
- server.method('io.example.ping2', (ctx: { params: xrpcServer.Params }) => {
75
+ server.method('io.example.pingTwo', (ctx: { params: xrpcServer.Params }) => {
76
76
  return {
77
77
  encoding: 'application/octet-stream',
78
78
  body: new TextEncoder().encode(String(ctx.params.message)),
79
79
  }
80
80
  })
81
- server.method('io.example.ping3', (ctx: { params: xrpcServer.Params }) => {
82
- return {
83
- encoding: 'application/json',
84
- body: { message: ctx.params.message },
85
- }
86
- })
81
+ server.method(
82
+ 'io.example.pingThree',
83
+ (ctx: { params: xrpcServer.Params }) => {
84
+ return {
85
+ encoding: 'application/json',
86
+ body: { message: ctx.params.message },
87
+ headers: { 'x-test-header-name': 'test-value' },
88
+ }
89
+ },
90
+ )
87
91
  xrpc.addLexicons(LEXICONS)
88
92
 
89
93
  let client: ServiceClient
@@ -97,25 +101,26 @@ describe('Queries', () => {
97
101
  })
98
102
 
99
103
  it('serves requests', async () => {
100
- const res1 = await client.call('io.example.ping1', {
104
+ const res1 = await client.call('io.example.pingOne', {
101
105
  message: 'hello world',
102
106
  })
103
107
  expect(res1.success).toBeTruthy()
104
108
  expect(res1.headers['content-type']).toBe('text/plain; charset=utf-8')
105
109
  expect(res1.data).toBe('hello world')
106
110
 
107
- const res2 = await client.call('io.example.ping2', {
111
+ const res2 = await client.call('io.example.pingTwo', {
108
112
  message: 'hello world',
109
113
  })
110
114
  expect(res2.success).toBeTruthy()
111
115
  expect(res2.headers['content-type']).toBe('application/octet-stream')
112
116
  expect(new TextDecoder().decode(res2.data)).toBe('hello world')
113
117
 
114
- const res3 = await client.call('io.example.ping3', {
118
+ const res3 = await client.call('io.example.pingThree', {
115
119
  message: 'hello world',
116
120
  })
117
121
  expect(res3.success).toBeTruthy()
118
122
  expect(res3.headers['content-type']).toBe('application/json; charset=utf-8')
119
123
  expect(res3.data?.message).toBe('hello world')
124
+ expect(res3.headers['x-test-header-name']).toEqual('test-value')
120
125
  })
121
126
  })
@@ -0,0 +1,77 @@
1
+ import * as http from 'http'
2
+ import getPort from 'get-port'
3
+ import xrpc, { ServiceClient } from '@atproto/xrpc'
4
+ import { byteIterableToStream } from '@atproto/common'
5
+ import { createServer, closeServer } from './_util'
6
+ import * as xrpcServer from '../src'
7
+
8
+ const LEXICONS = [
9
+ {
10
+ lexicon: 1,
11
+ id: 'io.example.readableStream',
12
+ defs: {
13
+ main: {
14
+ type: 'query',
15
+ parameters: {
16
+ type: 'params',
17
+ properties: {
18
+ shouldErr: { type: 'boolean' },
19
+ },
20
+ },
21
+ output: {
22
+ encoding: 'application/vnd.ipld.car',
23
+ },
24
+ },
25
+ },
26
+ },
27
+ ]
28
+
29
+ describe('Responses', () => {
30
+ let s: http.Server
31
+ const server = xrpcServer.createServer(LEXICONS)
32
+ server.method(
33
+ 'io.example.readableStream',
34
+ async (ctx: { params: xrpcServer.Params }) => {
35
+ async function* iter(): AsyncIterable<Uint8Array> {
36
+ for (let i = 0; i < 5; i++) {
37
+ yield new Uint8Array([i])
38
+ }
39
+ if (ctx.params.shouldErr) {
40
+ throw new Error('error')
41
+ }
42
+ }
43
+ return {
44
+ encoding: 'application/vnd.ipld.car',
45
+ body: byteIterableToStream(iter()),
46
+ }
47
+ },
48
+ )
49
+ xrpc.addLexicons(LEXICONS)
50
+
51
+ let client: ServiceClient
52
+ let url: string
53
+ beforeAll(async () => {
54
+ const port = await getPort()
55
+ s = await createServer(port, server)
56
+ url = `http://localhost:${port}`
57
+ client = xrpc.service(url)
58
+ })
59
+ afterAll(async () => {
60
+ await closeServer(s)
61
+ })
62
+
63
+ it('returns readable streams of bytes', async () => {
64
+ const res = await client.call('io.example.readableStream', {
65
+ shouldErr: false,
66
+ })
67
+ const expected = new Uint8Array([0, 1, 2, 3, 4])
68
+ expect(res.data).toEqual(expected)
69
+ })
70
+
71
+ it('handles errs on readable streams of bytes', async () => {
72
+ const attempt = client.call('io.example.readableStream', {
73
+ shouldErr: true,
74
+ })
75
+ await expect(attempt).rejects.toThrow()
76
+ })
77
+ })
@@ -14,7 +14,7 @@ import * as xrpcServer from '../src'
14
14
  const LEXICONS = [
15
15
  {
16
16
  lexicon: 1,
17
- id: 'io.example.stream1',
17
+ id: 'io.example.streamOne',
18
18
  defs: {
19
19
  main: {
20
20
  type: 'subscription',
@@ -37,7 +37,7 @@ const LEXICONS = [
37
37
  },
38
38
  {
39
39
  lexicon: 1,
40
- id: 'io.example.stream2',
40
+ id: 'io.example.streamTwo',
41
41
  defs: {
42
42
  main: {
43
43
  type: 'subscription',
@@ -84,7 +84,7 @@ describe('Subscriptions', () => {
84
84
  const server = xrpcServer.createServer(LEXICONS)
85
85
  const lex = server.lex
86
86
 
87
- server.streamMethod('io.example.stream1', async function* ({ params }) {
87
+ server.streamMethod('io.example.streamOne', async function* ({ params }) {
88
88
  const countdown = Number(params.countdown ?? 0)
89
89
  for (let i = countdown; i >= 0; i--) {
90
90
  await wait(0)
@@ -92,11 +92,11 @@ describe('Subscriptions', () => {
92
92
  }
93
93
  })
94
94
 
95
- server.streamMethod('io.example.stream2', async function* ({ params }) {
95
+ server.streamMethod('io.example.streamTwo', async function* ({ params }) {
96
96
  const countdown = Number(params.countdown ?? 0)
97
97
  for (let i = countdown; i >= 0; i--) {
98
98
  yield {
99
- $type: i % 2 === 0 ? '#even' : 'io.example.stream2#odd',
99
+ $type: i % 2 === 0 ? '#even' : 'io.example.streamTwo#odd',
100
100
  count: i,
101
101
  }
102
102
  }
@@ -124,7 +124,7 @@ describe('Subscriptions', () => {
124
124
 
125
125
  it('streams messages', async () => {
126
126
  const ws = new WebSocket(
127
- `ws://localhost:${port}/xrpc/io.example.stream1?countdown=5`,
127
+ `ws://localhost:${port}/xrpc/io.example.streamOne?countdown=5`,
128
128
  )
129
129
 
130
130
  const frames: Frame[] = []
@@ -144,7 +144,7 @@ describe('Subscriptions', () => {
144
144
 
145
145
  it('streams messages in a union', async () => {
146
146
  const ws = new WebSocket(
147
- `ws://localhost:${port}/xrpc/io.example.stream2?countdown=5`,
147
+ `ws://localhost:${port}/xrpc/io.example.streamTwo?countdown=5`,
148
148
  )
149
149
 
150
150
  const frames: Frame[] = []
@@ -192,7 +192,7 @@ describe('Subscriptions', () => {
192
192
  })
193
193
 
194
194
  it('errors immediately on bad parameter', async () => {
195
- const ws = new WebSocket(`ws://localhost:${port}/xrpc/io.example.stream1`)
195
+ const ws = new WebSocket(`ws://localhost:${port}/xrpc/io.example.streamOne`)
196
196
 
197
197
  const frames: Frame[] = []
198
198
  for await (const frame of byFrame(ws)) {
@@ -245,11 +245,11 @@ describe('Subscriptions', () => {
245
245
  it('receives messages w/ skips', async () => {
246
246
  const sub = new Subscription({
247
247
  service: `ws://localhost:${port}`,
248
- method: 'io.example.stream1',
248
+ method: 'io.example.streamOne',
249
249
  getParams: () => ({ countdown: 5 }),
250
250
  validate: (obj) => {
251
251
  const result = lex.assertValidXrpcMessage<{ count: number }>(
252
- 'io.example.stream1',
252
+ 'io.example.streamOne',
253
253
  obj,
254
254
  )
255
255
  if (!result.count || result.count % 2) {
@@ -276,12 +276,12 @@ describe('Subscriptions', () => {
276
276
  let reconnects = 0
277
277
  const sub = new Subscription({
278
278
  service: `ws://localhost:${port}`,
279
- method: 'io.example.stream1',
279
+ method: 'io.example.streamOne',
280
280
  onReconnectError: () => reconnects++,
281
281
  getParams: () => ({ countdown }),
282
282
  validate: (obj) => {
283
283
  return lex.assertValidXrpcMessage<{ count: number }>(
284
- 'io.example.stream1',
284
+ 'io.example.streamOne',
285
285
  obj,
286
286
  )
287
287
  },
@@ -307,12 +307,12 @@ describe('Subscriptions', () => {
307
307
  const abortController = new AbortController()
308
308
  const sub = new Subscription({
309
309
  service: `ws://localhost:${port}`,
310
- method: 'io.example.stream1',
310
+ method: 'io.example.streamOne',
311
311
  signal: abortController.signal,
312
312
  getParams: () => ({ countdown: 10 }),
313
313
  validate: (obj) => {
314
314
  const result = lex.assertValidXrpcMessage<{ count: number }>(
315
- 'io.example.stream1',
315
+ 'io.example.streamOne',
316
316
  obj,
317
317
  )
318
318
  return result