Skip to content

Commit b283771

Browse files
committed
feat: safeDeserialize, safeSerialize
1 parent 80f6812 commit b283771

File tree

6 files changed

+73
-31
lines changed

6 files changed

+73
-31
lines changed

packages/trpc-webext/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"build": "tsc",
3939
"lint": "eslint src",
4040
"format": "prettier --write .",
41-
"test": "vitest",
41+
"test": "vitest run",
4242
"test:watch": "vitest --watch"
4343
},
4444
"peerDependencies": {

packages/trpc-webext/src/adapter/index.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import { initTRPC, TRPCError } from '@trpc/server'
2+
import { observable } from '@trpc/server/observable'
13
import {
4+
afterEach,
5+
beforeEach,
26
describe,
3-
it,
47
expect,
5-
vi,
6-
beforeEach,
7-
afterEach,
8+
it,
89
type Mock,
10+
vi,
911
} from 'vitest'
10-
import { initTRPC, TRPCError } from '@trpc/server'
11-
import { observable } from '@trpc/server/observable'
1212
import { z } from 'zod'
13+
1314
import { createWebExtHandler } from './'
15+
1416
import type { Runtime } from 'webextension-polyfill'
1517

1618
// Mock runtime

packages/trpc-webext/src/adapter/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
TRPCError,
77
} from '@trpc/server/unstable-core-do-not-import'
88

9+
import { safeDeserialize, safeSerialize } from '../utils'
10+
911
import type { Unsubscribable } from '@trpc/server/observable'
1012
import type {
1113
AnyRouter,
@@ -172,7 +174,7 @@ async function handleRegularProcedure<TRouter extends AnyRouter>(
172174
})
173175
}
174176

175-
const input = context.transformer.input.deserialize(trpc.params.input)
177+
const input = safeDeserialize(context.transformer.input, trpc.params.input)
176178
const ctx = await context.createContext?.({
177179
req: port,
178180
res: undefined,
@@ -187,7 +189,7 @@ async function handleRegularProcedure<TRouter extends AnyRouter>(
187189
signal: undefined,
188190
})
189191

190-
const serializedData = context.transformer.output.serialize(result)
192+
const serializedData = safeSerialize(context.transformer.output, result)
191193
sendResponse({
192194
result: {
193195
type: 'data',
@@ -211,7 +213,7 @@ async function handleSubscription<TRouter extends AnyRouter>(
211213
})
212214
}
213215

214-
const input = context.transformer.input.deserialize(trpc.params.input)
216+
const input = safeDeserialize(context.transformer.input, trpc.params.input)
215217
const ctx = await context.createContext?.({
216218
req: port,
217219
res: undefined,

packages/trpc-webext/src/link/index.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import { TRPCClientError } from '@trpc/client'
12
import {
3+
afterEach,
4+
beforeEach,
25
describe,
3-
it,
46
expect,
5-
vi,
6-
beforeEach,
7-
afterEach,
7+
it,
88
type Mock,
9+
vi,
910
} from 'vitest'
10-
import { TRPCClientError } from '@trpc/client'
11+
1112
import { webExtensionLink } from './'
13+
14+
import type { Operation, TRPCClientRuntime } from '@trpc/client'
1215
import type { Runtime } from 'webextension-polyfill'
13-
import type { Operation } from '@trpc/client'
1416

1517
// Mock runtime
1618
const mockRuntime = {
@@ -147,7 +149,7 @@ describe('webExtensionLink', () => {
147149
runtime: mockRuntime,
148150
transformer: mockTransformer,
149151
})
150-
operationLink = link()
152+
operationLink = link({} as TRPCClientRuntime)
151153
})
152154

153155
it('should serialize input before sending', () => {
@@ -253,7 +255,7 @@ describe('webExtensionLink', () => {
253255
runtime: mockRuntime,
254256
transformer: mockTransformer,
255257
})
256-
operationLink = link()
258+
operationLink = link({} as TRPCClientRuntime)
257259
})
258260

259261
it('should ignore non-tRPC messages', () => {
@@ -525,7 +527,7 @@ describe('webExtensionLink', () => {
525527
runtime: mockRuntime,
526528
transformer: mockTransformer,
527529
})
528-
operationLink = link()
530+
operationLink = link({} as TRPCClientRuntime)
529531
})
530532

531533
it('should not complete subscription on data', () => {
@@ -642,7 +644,7 @@ describe('webExtensionLink', () => {
642644
transformer: mockTransformer,
643645
timeoutMS: 1000,
644646
})
645-
operationLink = link()
647+
operationLink = link({} as TRPCClientRuntime)
646648
})
647649

648650
afterEach(() => {
@@ -752,10 +754,8 @@ describe('webExtensionLink', () => {
752754
})
753755

754756
describe('Port Disconnection', () => {
755-
let link: ReturnType<typeof webExtensionLink>
756-
757757
beforeEach(() => {
758-
link = webExtensionLink({
758+
webExtensionLink({
759759
runtime: mockRuntime,
760760
transformer: mockTransformer,
761761
})
@@ -777,7 +777,7 @@ describe('webExtensionLink', () => {
777777
;(mockRuntime.connect as Mock).mockReturnValue(newMockPort)
778778

779779
// Create new link - should reconnect
780-
const newLink = webExtensionLink({
780+
webExtensionLink({
781781
runtime: mockRuntime,
782782
transformer: mockTransformer,
783783
})
@@ -795,7 +795,7 @@ describe('webExtensionLink', () => {
795795
runtime: mockRuntime,
796796
transformer: mockTransformer,
797797
})
798-
operationLink = link()
798+
operationLink = link({} as TRPCClientRuntime)
799799
})
800800

801801
it('should handle transformer that returns undefined', () => {

packages/trpc-webext/src/link/index.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { TRPCClientError } from '@trpc/client'
22
import { observable } from '@trpc/server/observable'
33

4+
import { safeDeserialize, safeSerialize } from '../utils'
5+
46
import type { Operation, OperationResultEnvelope, TRPCLink } from '@trpc/client'
57
import type { AnyTRPCRouter } from '@trpc/server'
68
import type { Observer } from '@trpc/server/observable'
79
import type { TRPCResponseMessage } from '@trpc/server/rpc'
8-
import type { DataTransformer } from '@trpc/server/unstable-core-do-not-import'
10+
import type {
11+
DataTransformer,
12+
TRPCResult,
13+
} from '@trpc/server/unstable-core-do-not-import'
914
import type { Runtime } from 'webextension-polyfill'
1015

1116
export interface WebExtensionLinkOptions {
@@ -45,7 +50,7 @@ function isBackgroundMessage(message: unknown): message is BackgroundMessage {
4550
typeof message === 'object' &&
4651
message !== null &&
4752
'trpc' in message &&
48-
typeof (message as any).trpc === 'object'
53+
typeof (message as BackgroundMessage).trpc === 'object'
4954
)
5055
}
5156

@@ -77,7 +82,7 @@ function createPortMessageHandler(
7782
if ('error' in trpc) {
7883
// Handle error response
7984
const error = shouldDeserialize(trpc.error)
80-
? transformer.deserialize(trpc.error)
85+
? safeDeserialize(transformer, trpc.error)
8186
: trpc.error
8287

8388
observer.error(TRPCClientError.from({ ...trpc, error }))
@@ -90,9 +95,9 @@ function createPortMessageHandler(
9095
...trpc.result,
9196
...((!trpc.result?.type || trpc.result.type === 'data') && {
9297
type: 'data' as const,
93-
data: transformer.deserialize(trpc.result?.data || {}),
98+
data: safeDeserialize(transformer, trpc.result?.data || {}),
9499
}),
95-
},
100+
} as TRPCResult<unknown>,
96101
})
97102

98103
// Complete for non-subscription or stopped subscription
@@ -159,7 +164,7 @@ export function webExtensionLink(
159164
return ({ op }) => {
160165
const { id, type, path, input } = op
161166

162-
const serializedInput = transformer.serialize(input) ?? input
167+
const serializedInput = safeSerialize(transformer, input) ?? input
163168

164169
const trpcPayload = {
165170
id,

packages/trpc-webext/src/utils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { TRPCError } from '@trpc/server'
2+
3+
import type { DataTransformer } from '@trpc/server/unstable-core-do-not-import'
4+
5+
export function safeDeserialize<T>(
6+
transformer: DataTransformer,
7+
data: unknown,
8+
): T {
9+
try {
10+
return transformer.deserialize(data)
11+
} catch (error) {
12+
throw new TRPCError({
13+
code: 'BAD_REQUEST',
14+
message: 'Failed to deserialize input data',
15+
cause: error,
16+
})
17+
}
18+
}
19+
20+
export function safeSerialize<T>(
21+
transformer: DataTransformer,
22+
data: T,
23+
): unknown {
24+
try {
25+
return transformer.serialize(data)
26+
} catch (error) {
27+
throw new TRPCError({
28+
code: 'BAD_REQUEST',
29+
message: 'Failed to serialize output data',
30+
cause: error,
31+
})
32+
}
33+
}

0 commit comments

Comments
 (0)