Skip to content

Commit d8bfb62

Browse files
authored
Merge pull request #164 from planetscale/sanitize-buffer
Support binary data
2 parents 7b49f94 + 218b85c commit d8bfb62

File tree

6 files changed

+44
-8
lines changed

6 files changed

+44
-8
lines changed

__tests__/index.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,11 @@ describe('cast', () => {
626626
expect(cast({ name: 'test', type: 'FLOAT64' }, '2.32')).toEqual(2.32)
627627
})
628628

629+
test('casts binary data to array of 8-bit unsigned integers', () => {
630+
expect(cast({ name: 'test', type: 'BLOB' }, '')).toEqual(new Uint8Array([]))
631+
expect(cast({ name: 'test', type: 'BLOB' }, 'Å')).toEqual(new Uint8Array([197]))
632+
})
633+
629634
test('casts JSON string to JSON object', () => {
630635
expect(cast({ name: 'test', type: 'JSON' }, '{ "foo": "bar" }')).toStrictEqual({ foo: 'bar' })
631636
})

__tests__/text.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { decode, hex } from '../src/text'
1+
import { decode, hex, uint8Array, uint8ArrayToHex } from '../src/text'
22

33
describe('text', () => {
44
describe('decode', () => {
@@ -32,4 +32,17 @@ describe('text', () => {
3232
expect(hex('aa')).toEqual('0x6161')
3333
})
3434
})
35+
36+
describe('uint8Array', () => {
37+
test('converts to an array of 8-bit unsigned integers', () => {
38+
expect(uint8Array('')).toEqual(new Uint8Array([]))
39+
expect(uint8Array('Å')).toEqual(new Uint8Array([197]))
40+
})
41+
})
42+
43+
describe('uint8ArrayToHex', () => {
44+
test('converts an array of 8-bit unsigned integers to hex', () => {
45+
expect(uint8ArrayToHex(new Uint8Array([197]))).toEqual('0xc5')
46+
})
47+
})
3548
})

jest.config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export default {
1616
testEnvironment: 'node',
1717

1818
// Automatically clear mock calls, instances, contexts and results before every test
19-
clearMocks: true
19+
clearMocks: true,
2020

2121
// Indicates whether the coverage information should be collected while executing the test
2222
// collectCoverage: false,
@@ -90,7 +90,9 @@ export default {
9090
// ],
9191

9292
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
93-
// moduleNameMapper: {},
93+
moduleNameMapper: {
94+
'./text.js': './text'
95+
},
9496

9597
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
9698
// modulePathIgnorePatterns: [],

src/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { format } from './sanitization.js'
22
export { format } from './sanitization.js'
3+
import { decode, uint8Array } from './text.js'
34
export { hex } from './text.js'
4-
import { decode } from './text.js'
55
import { Version } from './version.js'
66

77
type Row<T extends ExecuteAs = 'object'> = T extends 'array' ? any[] : T extends 'object' ? Record<string, any> : never
@@ -379,7 +379,7 @@ function decodeRow(row: QueryResultRow): Array<string | null> {
379379
}
380380

381381
export function cast(field: Field, value: string | null): any {
382-
if (value === '' || value == null) {
382+
if (value == null) {
383383
return value
384384
}
385385

@@ -404,14 +404,15 @@ export function cast(field: Field, value: string | null): any {
404404
case 'TIME':
405405
case 'DATETIME':
406406
case 'TIMESTAMP':
407+
return value
407408
case 'BLOB':
408409
case 'BIT':
409410
case 'VARBINARY':
410411
case 'BINARY':
411412
case 'GEOMETRY':
412-
return value
413+
return uint8Array(value)
413414
case 'JSON':
414-
return JSON.parse(decode(value))
415+
return value ? JSON.parse(decode(value)) : value
415416
default:
416417
return decode(value)
417418
}

src/sanitization.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { uint8ArrayToHex } from './text.js'
2+
13
type Stringable = { toString: () => string }
24
type Value = null | undefined | number | boolean | string | Array<Value> | Date | Stringable
35

@@ -47,6 +49,10 @@ function sanitize(value: Value): string {
4749
return quote(value.toISOString().slice(0, -1))
4850
}
4951

52+
if (value instanceof Uint8Array) {
53+
return uint8ArrayToHex(value)
54+
}
55+
5056
return quote(value.toString())
5157
}
5258

src/text.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
const decoder = new TextDecoder('utf-8')
22

33
export function decode(text: string | null | undefined): string {
4-
return text ? decoder.decode(Uint8Array.from(bytes(text))) : ''
4+
return text ? decoder.decode(uint8Array(text)) : ''
55
}
66

77
export function hex(text: string): string {
88
const digits = bytes(text).map((b) => b.toString(16).padStart(2, '0'))
99
return `0x${digits.join('')}`
1010
}
1111

12+
export function uint8Array(text: string): Uint8Array {
13+
return Uint8Array.from(bytes(text))
14+
}
15+
16+
export function uint8ArrayToHex(uint8: Uint8Array): string {
17+
const digits = Array.from(uint8).map((i) => i.toString(16).padStart(2, '0'))
18+
return `0x${digits.join('')}`
19+
}
20+
1221
function bytes(text: string): number[] {
1322
return text.split('').map((c) => c.charCodeAt(0))
1423
}

0 commit comments

Comments
 (0)