Skip to content

Commit

Permalink
Merge pull request #105 from dandeduck/main
Browse files Browse the repository at this point in the history
Unify parsing into a util and add support for Date inside object
  • Loading branch information
SaltyAom committed Jun 21, 2024
2 parents 41003b6 + f117750 commit 37da8db
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 136 deletions.
26 changes: 2 additions & 24 deletions src/fetch/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import type { Elysia } from 'elysia'

import { isFormalDate, isISO8601, isShortenDate } from '../treaty2'

import { EdenFetchError } from '../errors'
import type { EdenFetch } from './types'
import { isNumericString } from '../treaty/utils'
import { parseStringifiedValue } from '../utils/parsingUtils'

export type { EdenFetch } from './types'

Expand Down Expand Up @@ -75,27 +73,7 @@ export const edenFetch =
break

default:
data = await response.text().then((data) => {
if (isNumericString(data)) return +data
if (data === 'true') return true
if (data === 'false') return false

if (!data) return data

// Remove quote from stringified date
const temp = data.replace(/"/g, '')

if (
isISO8601.test(temp) ||
isFormalDate.test(temp) ||
isShortenDate.test(temp)
) {
const date = new Date(temp)
if (!Number.isNaN(date.getTime())) return date
}

return data
})
data = await response.text().then(parseStringifiedValue)
}

if (response.status > 300)
Expand Down
3 changes: 2 additions & 1 deletion src/treaty/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import type { Elysia, InputSchema } from 'elysia'

import { EdenFetchError } from '../errors'

import { composePath, isNumericString } from './utils'
import { composePath } from './utils'
import type { EdenTreaty } from './types'
import { isNumericString } from '../utils/parsingUtils'

export type { EdenTreaty } from './types'

Expand Down
3 changes: 0 additions & 3 deletions src/treaty/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,3 @@ export const composePath = (

return `${domain}${path}?${q.slice(0, -1)}`
}

export const isNumericString = (message: string) =>
message.trim().length !== 0 && !Number.isNaN(Number(message))
146 changes: 44 additions & 102 deletions src/treaty2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import type { Elysia } from 'elysia'
import type { Treaty } from './types'

import { composePath, isNumericString } from '../treaty/utils'
import { EdenFetchError } from '../errors'
import { EdenWS } from './ws'
import { parseStringifiedValue } from '../utils/parsingUtils'

const method = [
'get',
Expand All @@ -22,13 +22,6 @@ const locals = ['localhost', '127.0.0.1', '0.0.0.0']

const isServer = typeof FileList === 'undefined'

export const isISO8601 =
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/
export const isFormalDate =
/(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{2}\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT(?:\+|-)\d{4}\s\([^)]+\)/
export const isShortenDate =
/^(?:(?:(?:(?:0?[1-9]|[12][0-9]|3[01])[/\s-](?:0?[1-9]|1[0-2])[/\s-](?:19|20)\d{2})|(?:(?:19|20)\d{2}[/\s-](?:0?[1-9]|1[0-2])[/\s-](?:0?[1-9]|[12][0-9]|3[01]))))(?:\s(?:1[012]|0?[1-9]):[0-5][0-9](?::[0-5][0-9])?(?:\s[AP]M)?)?$/

const isFile = (v: any) => {
if (isServer) return v instanceof Blob

Expand Down Expand Up @@ -130,46 +123,7 @@ export async function* streamResponse(response: Response) {

const data = decoder.decode(value)

if (isNumericString(data)) {
yield +data
continue
}

if (data === 'true') {
yield true
continue
}

if (data === 'false') {
yield false
continue
}

const start = data.charCodeAt(0)
const end = data.charCodeAt(data.length - 1)

if ((start === 123 && end === 125) || (start === 91 && end === 93))
try {
yield JSON.parse(data)

continue
} catch {}

// Remove quote from stringified date
const temp = data.replace(/"/g, '')

if (
isISO8601.test(temp) ||
isFormalDate.test(temp) ||
isShortenDate.test(temp)
) {
const date = new Date(temp)
if (!Number.isNaN(date.getTime())) yield date

continue
}

yield data
yield parseStringifiedValue(data)
}
} finally {
reader.releaseLock()
Expand Down Expand Up @@ -431,62 +385,50 @@ const createProxy = (
}
}

if (data === null) {
switch (
response.headers.get('Content-Type')?.split(';')[0]
) {
case 'text/event-stream':
data = streamResponse(response)
break

case 'application/json':
data = await response.json()
break

case 'application/octet-stream':
data = await response.arrayBuffer()
break

case 'multipart/form-data':
const temp = await response.formData()

data = {}
temp.forEach((value, key) => {
// @ts-ignore
data[key] = value
})

break

default:
data = await response.text().then((data) => {
if (isNumericString(data)) return +data
if (data === 'true') return true
if (data === 'false') return false

if (!data) return data

// Remove quote from stringified date
const temp = data.replace(/"/g, '')

if (
isISO8601.test(temp) ||
isFormalDate.test(temp) ||
isShortenDate.test(temp)
) {
const date = new Date(temp)
if (!Number.isNaN(date.getTime()))
return date
}

return data
})
if (data !== null) {
return {
data,
error,
response,
status: response.status,
headers: response.headers
}
}

if (response.status >= 300 || response.status < 200) {
error = new EdenFetchError(response.status, data)
data = null
}
switch (
response.headers.get('Content-Type')?.split(';')[0]
) {
case 'text/event-stream':
data = streamResponse(response)
break

case 'application/json':
data = await response.json()
break
case 'application/octet-stream':
data = await response.arrayBuffer()
break

case 'multipart/form-data':
const temp = await response.formData()

data = {}
temp.forEach((value, key) => {
// @ts-ignore
data[key] = value
})

break

default:
data = await response
.text()
.then(parseStringifiedValue) // Since this change, if the string contains a stringified JSOn, it will be parsed as such. Is it OK?
}

if (response.status >= 300 || response.status < 200) {
error = new EdenFetchError(response.status, data)
data = null
}

return {
Expand Down
17 changes: 11 additions & 6 deletions src/treaty2/ws.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { InputSchema } from 'elysia'
import type { Treaty } from './types'
import { isNumericString } from '../treaty/utils'
import { isNumericString } from '../utils/parsingUtils'

const ISO8601DateString = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/

Expand Down Expand Up @@ -67,23 +67,28 @@ export class EdenWS<in out Schema extends InputSchema<any> = {}> {
if (start === 91 || start === 123)
try {
data = JSON.parse(data, (key, value) => {
if(typeof value === 'string' && ISO8601DateString.test(value)) {
if (
typeof value === 'string' &&
ISO8601DateString.test(value)
) {
const d = new Date(value)
if(!Number.isNaN(d.getTime()))
return d
if (!Number.isNaN(d.getTime())) return d
}
return value
})
} catch {
// Not Empty
}

else if (isNumericString(data)) data = +data
else if (data === 'true') data = true
else if (data === 'false') data = false
else if (data === 'null') data = null
// Remove " before parsing
else if (start === 34 && end === 34 && ISO8601DateString.test(data))
else if (
start === 34 &&
end === 34 &&
ISO8601DateString.test(data)
)
data = new Date(data.substring(1, data.length - 1))

listener({
Expand Down
82 changes: 82 additions & 0 deletions src/utils/parsingUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const isISO8601 =
/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/
const isFormalDate =
/(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{2}\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT(?:\+|-)\d{4}\s\([^)]+\)/
const isShortenDate =
/^(?:(?:(?:(?:0?[1-9]|[12][0-9]|3[01])[/\s-](?:0?[1-9]|1[0-2])[/\s-](?:19|20)\d{2})|(?:(?:19|20)\d{2}[/\s-](?:0?[1-9]|1[0-2])[/\s-](?:0?[1-9]|[12][0-9]|3[01]))))(?:\s(?:1[012]|0?[1-9]):[0-5][0-9](?::[0-5][0-9])?(?:\s[AP]M)?)?$/

export const isNumericString = (message: string) =>
message.trim().length !== 0 && !Number.isNaN(Number(message))

export const parseStringifiedDate = (value: any) => {
if (typeof value !== 'string') {
return null
}

// Remove quote from stringified date
const temp = value.replace(/"/g, '')

if (
isISO8601.test(temp) ||
isFormalDate.test(temp) ||
isShortenDate.test(temp)
) {
const date = new Date(temp)

if (!Number.isNaN(date.getTime())) {
return date
}
}

return null
}

export const isStringifiedObject = (value: string) => {
const start = value.charCodeAt(0)
const end = value.charCodeAt(value.length - 1)

return (start === 123 && end === 125) || (start === 91 && end === 93)
}

export const parseStringifiedObject = (data: string) =>
JSON.parse(data, (_, value) => {
const date = parseStringifiedDate(value)

if (date) {
return date
}

return value
})

export const parseStringifiedValue = (value: any) => {
if (!value) {
return value
}

if (isNumericString(value)) {
return +value
}

if (value === 'true') {
return true
}

if (value === 'false') {
return false
}

const date = parseStringifiedDate(value)

if (date) {
return date
}

if (isStringifiedObject(value)) {
try {
return parseStringifiedObject(value)
} catch {}
}

return value
}
7 changes: 7 additions & 0 deletions test/treaty2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const app = new Elysia()
date: t.Date()
})
})
.get('/dateObject', () => ({ date: new Date() }))
.get(
'/redirect',
({ set }) => (set.redirect = 'http://localhost:8083/true')
Expand Down Expand Up @@ -190,6 +191,12 @@ describe('Treaty2', () => {
expect(data).toEqual(false)
})

it.todo('parse object with date', async () => {
const { data } = await client.dateObject.get()

expect(data?.date).toBeInstanceOf(Date)
})

it('post array', async () => {
const { data } = await client.array.post(['a', 'b'])

Expand Down

0 comments on commit 37da8db

Please sign in to comment.