Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V4 #307

Open
wants to merge 109 commits into
base: master
Choose a base branch
from
Open

V4 #307

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
1a110b3
Pass context via async storage
Rokt33r Jan 4, 2024
41d2cd2
Add alias for body selector factories
Rokt33r Jan 4, 2024
9ce33c4
Add Temp docs
Rokt33r Jan 4, 2024
d9be9c6
Remove skipCOntentTypeCheck
Rokt33r Jan 4, 2024
9da724d
Added search param selectors
Rokt33r Jan 5, 2024
eed8771
Restructure utils
Rokt33r Jan 6, 2024
57c89ff
Renew selector interface
Rokt33r Jan 6, 2024
c7696f9
Simplify type tests
Rokt33r Jan 6, 2024
9de0b50
Simplify middleware interface
Rokt33r Jan 6, 2024
03bb112
Remove PrismyPureMiddleware type
Rokt33r Jan 6, 2024
efc6646
Use weakmap instead of symbol
Rokt33r Jan 6, 2024
0c5a79d
Update v4 todo
Rokt33r Jan 6, 2024
4212e78
Refactor router
Rokt33r Jan 6, 2024
cb279c1
Introduce PrismyHandler class
Rokt33r Jan 6, 2024
2281fd7
Rename Promisable to MaybePromise
Rokt33r Jan 6, 2024
612a83d
Implement prefix and notFoundHandler
Rokt33r Jan 7, 2024
44bfa1d
Use PascalCase for factory method
Rokt33r Jan 7, 2024
6a00bb8
Use node-fetch instead of got
Rokt33r Jan 7, 2024
d09a259
Add testFetch heleper
Rokt33r Jan 7, 2024
c526829
Remove test-listen
Rokt33r Jan 7, 2024
4f1904d
Fix type error in bodyReader
Rokt33r Jan 7, 2024
eae88b2
Upgrade ts
Rokt33r Jan 7, 2024
45c764b
Update tsconfig for node 18
Rokt33r Jan 7, 2024
a2b7a59
Convert middleware into class PrismyMiddleware
Rokt33r Jan 7, 2024
6566523
Update v4 todo
Rokt33r Jan 7, 2024
0cda40a
Reconfigure jest for fast testing
Rokt33r Jan 8, 2024
21113e1
Remove res
Rokt33r Jan 8, 2024
dba32e3
Fix handler type
Rokt33r Jan 8, 2024
a251ef2
Export handler
Rokt33r Jan 8, 2024
6ccb33d
Use PascalCase for factory fn
Rokt33r Jan 8, 2024
eb56c90
Improve test tools
Rokt33r Jan 8, 2024
323c07b
Use node 18 types
Rokt33r Jan 8, 2024
e3e3998
Upgrade tslib
Rokt33r Jan 8, 2024
705496f
Update keyword
Rokt33r Jan 8, 2024
ba7a259
Reimplement type tests
Rokt33r Jan 8, 2024
6f461df
Improve type tests
Rokt33r Jan 8, 2024
2a4b1cd
Update v4 todo
Rokt33r Jan 8, 2024
22a8294
Fix route parm selector to select string only
Rokt33r Jan 8, 2024
11e4aa9
Remove ResObject
Rokt33r Jan 8, 2024
78854f5
Remove raw result
Rokt33r Jan 8, 2024
7a4af60
Add InjectSelector
Rokt33r Jan 8, 2024
9961147
Implement cookie handling
Rokt33r Jan 9, 2024
582f002
Fix typo in jsonBody
Rokt33r Jan 13, 2024
8b8dd5b
Add file upload temp example
Rokt33r Jan 14, 2024
e4aa3ea
clean up
Rokt33r Jan 14, 2024
048817c
Update todo
Rokt33r Jan 14, 2024
8c7d9fb
Fix basic interfaces
Rokt33r Jan 14, 2024
40a2c22
4.0.0-0
Rokt33r Jan 14, 2024
4d5e4af
Expose test tool
Rokt33r Jan 21, 2024
e59efa8
4.0.0-1
Rokt33r Jan 21, 2024
75bcbd8
Fix test config
Rokt33r Jan 21, 2024
ca03cfb
Remove main and types
Rokt33r Jan 21, 2024
c258b1d
4.0.0-2
Rokt33r Jan 21, 2024
a6fc163
Rollback main and types
Rokt33r Jan 21, 2024
33251c7
4.0.0-3
Rokt33r Jan 21, 2024
7a6df06
Export createPrismySelector
Rokt33r Jan 21, 2024
76042ee
4.0.0-4
Rokt33r Jan 21, 2024
6220670
Move async-listen from devDep to dep
Rokt33r Jan 21, 2024
d84e891
4.0.0-5
Rokt33r Jan 21, 2024
41d5d8c
Fix router typing
Rokt33r Jan 21, 2024
7773be1
4.0.0-6
Rokt33r Jan 21, 2024
5e4523d
test coverage:prismy.ts
Rokt33r Jan 22, 2024
506e3ae
Rename res to result
Rokt33r Jan 22, 2024
c63cebe
Fix result test coverage
Rokt33r Jan 22, 2024
8672fea
Fix router test coverage
Rokt33r Jan 22, 2024
dc60b09
Emit text coverage only
Rokt33r Jan 23, 2024
9d942aa
Fix cookie test coverage
Rokt33r Jan 24, 2024
7e5df26
Fix cookie test coverage
Rokt33r Jan 24, 2024
6d7c304
Remove logging
Rokt33r Jan 24, 2024
3e85d60
Fix test coverage of TestServer
Rokt33r Jan 24, 2024
261a4be
Refactor middleware test
Rokt33r Jan 24, 2024
e5d4c08
Refactor prismy tests
Rokt33r Jan 24, 2024
6fcb904
Refactor handler
Rokt33r Jan 24, 2024
f4f948c
Refactor result test
Rokt33r Jan 24, 2024
b589a0a
Refactor bodyReaders
Rokt33r Jan 26, 2024
e5a2707
Remove deprecated methods
Rokt33r Feb 12, 2024
71f62ec
Use camelcase for all selectors
Rokt33r Feb 12, 2024
f2f4a11
Fix coverage
Rokt33r Feb 12, 2024
86f8469
4.0.0-7
Rokt33r Feb 12, 2024
0cadf13
Rename handler methods for better testing DX
Rokt33r Mar 16, 2024
c11a7a4
Add result type generic arg to handler class
Rokt33r Mar 16, 2024
74527c9
4.0.0-8
Rokt33r Mar 16, 2024
10ffca8
Build es module
Rokt33r Mar 17, 2024
4d663f9
4.0.0-9
Rokt33r Mar 17, 2024
6aab8c6
Don't emit type defs when compiling es modules
Rokt33r Mar 17, 2024
0a5974c
Make selector can use selectors
Rokt33r Apr 3, 2024
7f9c76d
Use selectors arg for searchparamselector
Rokt33r Apr 3, 2024
a0439e7
Can omit selectors for Handler and Route
Rokt33r Apr 3, 2024
cd31735
4.0.0-10
Rokt33r Apr 3, 2024
d61dd44
Add PrismyErrorResult
Rokt33r Apr 6, 2024
5f8938d
4.0.0-11
Rokt33r Apr 6, 2024
a0e7f48
Make RedirectResult distinguishable
Rokt33r Apr 6, 2024
37736b0
Add tests for ErrorResult utils
Rokt33r Apr 6, 2024
5f9063e
Add tests for RedirectResult utils
Rokt33r Apr 6, 2024
6284190
4.0.0-12
Rokt33r Apr 6, 2024
e71cd19
Move default error handlers from PrismyHandler to prismy fn
Rokt33r Apr 14, 2024
a89bc24
Simplify middleware api
Rokt33r Apr 14, 2024
3365fef
Remove utils and restruct selector resolving module
Rokt33r Apr 14, 2024
0c3da97
Make middleware selectors optional
Rokt33r Apr 14, 2024
9fcd0e4
Update v4 todo
Rokt33r Apr 14, 2024
5d10fdf
4.0.0-13
Rokt33r Apr 14, 2024
8613f13
Export PrismyError
Rokt33r Apr 14, 2024
e5e2e36
4.0.0-14
Rokt33r Apr 14, 2024
12775ad
Improve error message of assertion methods
Rokt33r Apr 23, 2024
73bbac9
Make PrismyResult throwable
Rokt33r Apr 23, 2024
df92499
Fix coverage
Rokt33r Apr 23, 2024
0117b45
4.0.0-15
Rokt33r Apr 23, 2024
355d9f8
Add OptionalRouteParamSelector
Rokt33r May 6, 2024
932e139
4.0.0-16
Rokt33r May 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions examples/basic-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { prismy, res, router } from 'prismy'
import { prismy, Result, router } from 'prismy'

export const rootHandler = prismy([], () => {
return res(
return Result(
[
'<!DOCTYPE html>',
'<body>',
'<h1>Root Page</h1>',
'<a href="/test">Go to /test</a>',
'</body>',
].join('')
].join(''),
)
})

const testHandler = prismy([], () => {
return res(
return Result(
[
'<!DOCTYPE html>',
'<body>',
'<h1>Test Page</h1>',
'<a href="/">Go to Root</a>',
'</body>',
].join('')
].join(''),
)
})

Expand Down
226 changes: 226 additions & 0 deletions examples/file-upload/specs/file.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import { testServerManager } from '../../../specs/helpers'
import {
ErrorResult,
getPrismyContext,
Middleware,
prismy,
Result,
} from '../../../src'
import { MultipartBodySelector } from './file'
import path from 'path'
import { File } from 'buffer'
import fs from 'fs'
import { nanoid } from 'nanoid'
import Formidable from 'formidable'

const testBoundary = `------test-boundary-${nanoid()}`
const testUploadRoot = path.join(process.cwd(), 'file-test-dest')

beforeAll(async () => {
await testServerManager.start()
})

afterAll(async () => {
await testServerManager.close()
fs.rmSync(testUploadRoot, { recursive: true })
fs.mkdirSync(testUploadRoot)
})

describe('MultipartBodySelector', () => {
it('parse a file and a field (FormData)', async () => {
const handler = MultipartTestHandler()

const formData = new FormData()
const fileBuffer = fs.readFileSync(
path.join(process.cwd(), 'specs/dummyFiles/smallFile.txt'),
)
const fileBlob = new Blob([fileBuffer])
const file = new File([fileBlob], 'smallFile.txt')
formData.append('testFile', file)
formData.append('testField', 'testValue')

const response = await testServerManager.loadRequestListenerAndCall(
handler,
'/',
{
method: 'post',
body: formData,
},
)

expect(response).toMatchObject({
statusCode: 200,
})
expect(JSON.parse(response.body)).toMatchObject({
fields: {
testField: 'testValue',
},
files: {
testFile: [
{
filepath: expect.stringMatching(
new RegExp(testUploadRoot + '/[A-z0-9]+'),
),
mimetype: 'application/octet-stream',
newFilename: expect.stringMatching('[A-z0-9]+'),
originalFilename: 'smallFile.txt',
size: file.size,
},
],
},
})
})

it('parse a file and a field (Raw)', async () => {
const handler = MultipartTestHandler()

const response = await testServerManager.loadRequestListenerAndCall(
handler,
'/',
{
method: 'post',
headers: {
'content-type': `multipart/form-data; boundary=${testBoundary}`,
},
body: [
'--' + testBoundary,
'Content-Disposition: form-data; name="file_name_0"',
'',
'super alpha file',
'--' + testBoundary,
'Content-Disposition: form-data; ' +
'name="upload_file_0"; filename="1k_a.dat"',
'Content-Type: application/octet-stream',
'',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'--' + testBoundary + '--',
].join('\r\n'),
},
)

expect(JSON.parse(response.body)).toMatchObject({
fields: [
{
name: 'file_name_0',
value: 'super alpha file',
info: {
encoding: '7bit',
mimeType: 'text/plain',
nameTruncated: false,
valueTruncated: false,
},
},
],
files: [
{
filePath: expect.stringMatching(
new RegExp(
`${testUploadRoot}/prismy-temp-dir-[A-z0-9_-]+/prismy-upload-[A-z0-9_-]+`,
),
),
info: {
encoding: '7bit',
filename: '1k_a.dat',
mimeType: 'application/octet-stream',
},
name: 'upload_file_0',
},
],
})
expect(response).toMatchObject({
statusCode: 200,
})
})

it('throws when body is empty', async () => {
const handler = MultipartTestHandler()

const response = await testServerManager.loadRequestListenerAndCall(
handler,
'/',
{
method: 'post',
headers: {
'content-type': `multipart/form-data; boundary=${testBoundary}`,
},
body: '',
},
)

expect(JSON.parse(response.body)).toMatchObject({
error: expect.stringContaining('Error: Unexpected end of form'),
})
expect(response).toMatchObject({
statusCode: 400,
})
})

it('throws if field size hits limit', async () => {
const handler = MultipartTestHandler({
maxFileSize: 13,
maxFieldsSize: 3,
})

const response = await testServerManager.loadRequestListenerAndCall(
handler,
'/',
{
method: 'post',
headers: {
'content-type': `multipart/form-data; boundary=${testBoundary}`,
},
body: [
'--' + testBoundary,
'Content-Disposition: form-data; name="file_name_0"',
'',
'super alpha file',
'--' + testBoundary,
'Content-Disposition: form-data; ' +
'name="upload_file_0"; filename="1k_a.dat"',
'Content-Type: application/octet-stream',
'',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'--' + testBoundary + '--',
].join('\r\n'),
},
)

const jsonBody = JSON.parse(response.body)
expect(jsonBody).toMatchObject({
error: expect.stringContaining('Error: options.maxFieldsSize'),
})
expect(response).toMatchObject({
statusCode: 413,
})
})
})

const errorDataMap = new WeakMap()
function MultipartTestHandler(options: Formidable.Options = {}) {
const multipartBodySelector = MultipartBodySelector({
uploadDir: testUploadRoot,
...options,
})
return prismy(
[multipartBodySelector],
(body) => {
return Result(body)
},
[
Middleware([], (next) => async () => {
try {
return await next()
} catch (error: any) {
const context = getPrismyContext()
return ErrorResult(
error.statusCode != null ? error.statusCode : 500,
{
error: error.stack,
data: errorDataMap.get(context),
},
)
}
}),
],
)
}
93 changes: 93 additions & 0 deletions examples/file-upload/specs/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { getPrismyContext } from '../prismy'
import { createPrismySelector, PrismySelector } from './createSelector'
import { createError } from '../error'
import Formidable, { errors as formidableErrors, File } from 'formidable'

function createMultipartBodyReader<MVF extends string>(
options: Formidable.Options & { multivaluedFields?: MVF[] } = {},
): () => Promise<{
fields: Omit<{ [key: string]: undefined | string }, MVF> & {
[key in MVF]: string[] | undefined
}
files: { [key: string]: File[] | undefined }
}> {
return async () => {
const context = getPrismyContext()
const contentType = context.req.headers['content-type']
if (!isContentTypeMultipart(contentType)) {
throw createError(
400,
`Content type must be multipart/form-data. (Current: ${contentType})`,
)
}
const { multivaluedFields = [], ...otherOptions } = options
const form = Formidable(otherOptions)
try {
const [fields, files] = await form.parse(context.req)
return {
fields: Object.entries(fields).reduce(
(obj, [key, value]) => {
if (value == null) {
return obj
}
if (multivaluedFields.indexOf(key as any) > -1) {
obj[key] = value
} else {
obj[key] = value[0]
}

return obj
},
{} as Omit<{ [key: string]: undefined | string }, MVF> & {
[key in MVF]: string[] | undefined
},
),
files,
}
} catch (error: any) {
switch (error.code) {
case formidableErrors.biggerThanMaxFileSize:
throw createError(413, error.message, error)
}
console.log(error)
throw error
}
}
}

export function MultipartBodySelector<MVF extends string>(
options: Formidable.Options & { multivaluedFields?: MVF[] } = {},
): PrismySelector<{
fields: Omit<{ [key: string]: undefined | string }, MVF> & {
[key in MVF]: string[] | undefined
}
files: { [key: string]: File[] | undefined }
}> {
return createPrismySelector(async () => {
return createMultipartBodyReader(options)()
})
}
export function MultipartBodyReaderSelector<MVF extends string>(
options: Formidable.Options & { multivaluedFields?: MVF[] } = {},
): PrismySelector<
() => Promise<{
fields: Omit<{ [key: string]: undefined | string }, MVF> & {
[key in MVF]: string[] | undefined
}
files: { [key: string]: File[] | undefined }
}>
> {
return createPrismySelector(async () => {
return createMultipartBodyReader(options)
})
}

function isContentTypeMultipart(contentType: string | undefined) {
if (contentType == null) {
return false
}
if (contentType.startsWith('multipart/form-data')) {
return true
}
return false
}
7 changes: 7 additions & 0 deletions examples/file-upload/specs/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { MultipartBodySelector } from '../../../src/selectors/file'
import { expectType } from '../../../specs/helpers'

const selector = MultipartBodySelector({ multivaluedFields: ['hello'] })
const body = await selector.resolve()
expectType<string[] | undefined>(body.fields.hello)
expectType<string | undefined>(body.fields.noHello)
Loading