generated from metcoder95/lib_template
-
-
Notifications
You must be signed in to change notification settings - Fork 1
feat!: v1 #1
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
Merged
Merged
feat!: v1 #1
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
697adc3
chore: expand gitignore
metcoder95 560b4c4
feat!: implement first version
metcoder95 53c98f2
chore: update package metadata
metcoder95 5d0bc87
test: set decoration testing
metcoder95 7f44e91
refactor: validations
metcoder95 a641e6d
test: add testing for aborted request
metcoder95 8af28f3
refactor: cleanup of not used controllers
metcoder95 a73125e
refactor: implement refactor with proper tests
metcoder95 a049c54
fix: handle socket closed on Promise returns
metcoder95 9a80f94
refactor: leftover
metcoder95 74d821b
fix: support tests for nodev14
metcoder95 7861539
regressive: drop support for node14
metcoder95 74a04cf
feat: add ts types
metcoder95 255c283
refactor: apply refactoring to Abort event result
metcoder95 7619625
fix: error thrown
metcoder95 bd8081c
chore: replace fastify-error
metcoder95 9970f57
chore: add Node 18
metcoder95 c9062aa
refactor: migrate to weak maps
metcoder95 2428729
ci: remov node 17
metcoder95 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,3 +102,6 @@ dist | |
|
||
# TernJS port file | ||
.tern-port | ||
|
||
# Not necessary for libs | ||
package-lock.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,152 @@ | ||
'use strict' | ||
const fp = require('fastify-plugin') | ||
|
||
const { Errors } = require('./lib/index') | ||
|
||
module.exports = fp( | ||
function fastifyRacePlugin (fastify, globalOpts, next) { | ||
const controllers = new Map() | ||
let error | ||
|
||
if (globalOpts != null && typeof globalOpts !== 'object') { | ||
return next(new Errors.BAD_PARAMS('object', typeof globalOpts)) | ||
} | ||
|
||
globalOpts = Object.assign( | ||
{}, | ||
{ handleOnError: true, onRequestClosed: null }, | ||
globalOpts | ||
) | ||
|
||
if (typeof globalOpts.handleOnError !== 'boolean') { | ||
error = new Errors.BAD_PARAMS('boolean', typeof globalOpts.handleOnError) | ||
} else if ( | ||
globalOpts.onRequestClosed != null && | ||
typeof globalOpts.onRequestClosed !== 'function' | ||
) { | ||
error = new Errors.BAD_PARAMS( | ||
'function', | ||
typeof globalOpts.onRequestClosed | ||
) | ||
} | ||
|
||
fastify.decorateRequest('race', race) | ||
fastify.addHook('onResponse', onResponseCleaner) | ||
|
||
return next(error) | ||
|
||
function onResponseCleaner (request, _reply, done) { | ||
if (controllers.has(request.id)) { | ||
const { controller, cbs } = controllers.get(request.id) | ||
|
||
if (controller.signal.aborted === false) { | ||
for (const cb of cbs) { | ||
controller.signal.removeEventListener('abort', cb, { | ||
once: true | ||
}) | ||
} | ||
} | ||
|
||
controllers.delete(request.id) | ||
} | ||
|
||
done() | ||
} | ||
|
||
function race (opts = globalOpts) { | ||
const { raw, id: reqId } = this | ||
const handleError = typeof opts === 'function' ? true : opts.handleOnError | ||
const cb = typeof opts === 'function' ? opts : opts.onRequestClosed | ||
|
||
if (controllers.has(reqId)) { | ||
const { controller: ctrl, cbs } = controllers.get(reqId) | ||
|
||
if (cb != null) { | ||
// TODO: handle when socket is destroyed already | ||
ctrl.signal.addEventListener('abort', cb, { | ||
once: true | ||
}) | ||
|
||
raw.once('error', err => { | ||
if (controllers.has(reqId)) { | ||
const internalCtrl = controllers.get(reqId) | ||
if (internalCtrl.signal.aborted === false) internalCtrl.abort(err) | ||
} | ||
}) | ||
|
||
controllers.set(reqId, { controller: ctrl, cbs: cbs.concat(cb) }) | ||
} | ||
|
||
if (raw.socket.destroyed === true) { | ||
process.nextTick(() => ctrl.abort(Errors.SOCKET_CLOSED(reqId))) | ||
} | ||
|
||
return ctrl.signal | ||
} else { | ||
// eslint-disable-next-line no-undef | ||
const controller = new AbortController() | ||
|
||
if (cb != null) { | ||
controller.signal.addEventListener('abort', cb, { | ||
once: true | ||
}) | ||
} | ||
|
||
if (cb == null) controller.signal.then = theneable.bind(this) | ||
|
||
if (raw.socket.destroyed) { | ||
process.nextTick(() => controller.abort(Errors.SOCKET_CLOSED(reqId))) | ||
} else { | ||
raw.once('close', () => { | ||
if (controllers.has(reqId)) { | ||
const { controller: ctrl } = controllers.get(reqId) | ||
if (ctrl.signal.aborted === false) controller.abort() | ||
} | ||
}) | ||
|
||
if (handleError === true || cb != null) { | ||
raw.once('error', err => { | ||
if (controllers.has(reqId)) { | ||
const { controller: ctrl } = controllers.get(reqId) | ||
if (ctrl.signal.aborted === false) controller.abort(err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
controllers.set(reqId, { controller, cbs: cb != null ? [cb] : [] }) | ||
|
||
return controller.signal | ||
} | ||
|
||
function theneable (resolve, reject) { | ||
const { controller, cbs } = controllers.get(reqId) | ||
|
||
if (controller.signal.aborted === true) { | ||
return reject(Errors.ALREADY_ABORTED(reqId)) | ||
} | ||
|
||
try { | ||
controller.signal.addEventListener('abort', theneableHandler, { | ||
once: true | ||
}) | ||
|
||
controllers.set(reqId, { | ||
controller, | ||
cbs: cbs.concat(theneableHandler) | ||
}) | ||
} catch (err) { | ||
reject(err) | ||
} | ||
|
||
function theneableHandler (evt) { | ||
resolve(evt) | ||
} | ||
} | ||
} | ||
}, | ||
{ | ||
fastify: '>=3.24.1', | ||
name: 'fastify-racing' | ||
} | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const Errors = require('fastify-error') | ||
|
||
module.exports = { | ||
Errors: { | ||
BAD_PARAMS: Errors( | ||
'FST_PLUGIN_RACE_BAD_PARAM', | ||
'Invalid param, expected %s but received %s' | ||
), | ||
ALREADY_ABORTED: Errors( | ||
'FST_PLUGIN_RACE_ALREADY_ABORTED', | ||
"Request with ID '%s' already aborted" | ||
), | ||
SOCKET_CLOSED: Errors( | ||
'FST_PLUGIN_RACE_SOCKET_CLOSED', | ||
"Socket for request with ID '%s' already closed" | ||
) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
{ | ||
"name": "<lib_name>", | ||
"name": "fastify-racing", | ||
"version": "0.0.0", | ||
"description": "<description>", | ||
"description": "Cancel any running operation at the right time on your request handler", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"scripts": { | ||
|
@@ -16,24 +16,29 @@ | |
"keywords": [], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/metcoder95/<lib_name>.git" | ||
"url": "git+https://github.com/metcoder95/fastify-racing.git" | ||
}, | ||
"readme": "https://github.com/metcoder95/<lib_name>/blob/main/README.md", | ||
"readme": "https://github.com/metcoder95/fastify-racing/blob/main/README.md", | ||
"bugs": { | ||
"url": "https://github.com/metcoder95/<lib_name>/issues" | ||
"url": "https://github.com/metcoder95/fastify-racing/issues" | ||
}, | ||
"author": "metcoder95 <[email protected]>", | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@types/node": "^14.17.6", | ||
"@types/node": "^14.18.13", | ||
"fastify": "^3.28.0", | ||
"husky": "^7.0.2", | ||
"nodemon": "^2.0.15", | ||
"snazzy": "^9.0.0", | ||
"standard": "^16.0.3", | ||
"tap": "^15.0.10", | ||
"tsd": "^0.17.0", | ||
"typescript": "^4.4" | ||
"typescript": "^4.4", | ||
"undici": "^5.0.0" | ||
}, | ||
"dependencies": { | ||
"fastify-error": "^1.1.0", | ||
"fastify-plugin": "^3.0.1" | ||
}, | ||
"tsd": { | ||
"directory": "test" | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very leak-prone. Maybe use a WeakMap instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about that at the first iteration, but I wasn't sure under which circumstances the
onResponse
cannot be called so the Map is not cleaned properly. But I think for avoiding unexpected issues would be better to go with a WeakMap as you suggestedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, here is what do you recommend to use the key for the WeakMap? My initial thought is to take advantage of the request, as is ready to be gc'ed once the request is done. wdyt?