Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
A real action hero for backends - heavily inspired by CerebralJS
Arnie wants action! Actions are structured in tasks and you compose them like legos to make things happen. Arnie is meant to be used with Koa2, but the core will work without it. The API is currently likely to change, so be adventures when using Arnie :)
Here is what a task looks like.
export default [
loadWishlist({shared: true}), {
error: [
fail(404, 'Wish list not found.')
]
},
createTempFolder('wishlist-'),
copyPreviewTemplate(),
loadProductImages({maxImages: 8}), {
error: [
fail(404, 'Product images not found.')
]
},
createPreviewMarkup(),
renderPreviewMarkup(),
send('input.previewImage'),
removeTempFolder()
]
Guess you can somewhat grasp what this task does just from reading it and that's the whole point here. Arnie is not about writing magic code with super powers - he is all about expressing complex things with simple words - and this is totally stolen from CerebralJS.
This sample structure will be used throughout the docs and is a good way to start structuring your project.
src/
├── actions/
│ ├── loadWishlist.js
│ ├── createTempFolder.js
│ ├── copyPreviewTemplate.js
│ ├── ...
│ └── send.js
├── tasks/
│ └── showPreviewImage.js
└── app.js
Tasks are defined in arrays an consist of three things
Yes tasks can contain other tasks :)
Enough with the smart talk - let's get coding.
// src/tasks/showPreviewImage.js
import {fail} from 'arnie/addons'
import copyPreviewTemplate from '../actions/copyPreviewTemplate'
import createTempFolder from '../actions/createTempFolder'
import createPreviewMarkup from '../actions/createPreviewMarkup'
import loadProductImages from '../actions/loadProductImages'
import loadWishlist from '../actions/loadWishlist'
import removeTempFolder from '../actions/createTempFolder'
import renderPreviewMarkup from '../actions/renderPreviewMarkup'
import send from '../actions/send'
export default [
loadWishlist({shared: true}), {
error: [
fail(404, 'Wish list not found.')
]
},
createTempFolder('wishlist-'),
copyPreviewTemplate(),
loadProductImages({maxImages: 8}), {
error: [
fail(404, 'Product images not found.')
]
},
createPreviewMarkup(),
renderPreviewMarkup(),
send('input.previewImage', 31536000),
removeTempFolder()
]
This is a sample tasks - it's an array at core and holds a mix of functions, objects and other arrays. Each of those functions calls are factories for actions, they create a preconfigured function that get will get called when the tasks is run. The objects define paths, which can be triggered at the end of an action. When a path gets triggered another tasks is run - those are the other arrays in the example above.
Actions are simple functions. They get one argument (it's called context) and they return an object (let's call it output). That's about it. When using tasks as Koa2 middle the conext object will be the Koa conext.
// src/actions/createTempFolder.js
import fs from 'mz/fs'
import shortid from 'shortid'
export default (prefix) => {
return async function createTempFolder (ctx) {
const tempFolder = `/tmp/${prefix}${shortid.generate()}`
await fs.mkdir(tempFolder)
return {
tempFolder
}
}
}
This action factory creates the actual createTempFolder()
action by returning it as a function. This function will be called within the task. It's handy to use a factory, because they allow you configure your actions when building tasks.
In this example the action is an async function, but this is entirely up to you. I personally tend to use them a lot on the server, thanks to Koa2. It's needless to say that you will have to transpile your code when using these kind of features.
The action's output will be an Object containing the folders path e.g. {tempFolder: /tmp/wishlist-d4th1s}
.
You have already seen a path definition and trigger in the examples before - let's take a closer look.
export default [
loadWishlist({shared: true}), {
wishlist: [
send('input.wishlist')
],
error: [
fail(404, 'Wish list not found.')
]
}
]
In the example the defined paths are wishlist
and error
. Each path defines a tasks that gets run when the path is triggered. A path gets triggered when an action returns an object with a key that matches a path.
export default ({id = 'input.wishlistId', shared = false}) => {
return async function loadWishlist (ctx) {
const wishlistId = get(ctx, id)
const wishlist = await db.collection('wishlists').findOne({
permaId: ObjectId(wishlistId),
shared
})
if (!wishlist) {
return {
error: true
}
}
return {
wishlist
}
}
}
This action could trigger two paths wishlist
and error
. There is one other scenario that could trigger a path - it's when an exceptions is thrown. An uncaught exception would also trigger the path error
.
When an actions returns output, the output will be shallow merged into context.input
. That allows actions to pass along data, which can then be used by other actions. The context.input
object should only be read and never directly mutated.
Arnie can take tasks and run them as Koa2 middlware. All tasks will be executed on every request and pass the Koa conext to your actions.
// src/app.js
import Koa from 'koa'
import Arnie from 'arnie'
import {route, restEndpoint, show, status} from 'arnie/addons'
import showPreviewImage from './tasks/showPreviewImage'
const app = new Koa()
const arnie = Arnie()
const tasks = {
wishlistPreview: [
route({
'/wishlist-preview/health': [
status(200),
show('request.path')
],
'/wishlist-preview/:wishlistId?': [
restEndpoint({
show: showPreviewImage
})
]
})
]
}
app.use(arnie.koa(tasks))
app.listen(80)
You can see that Arnie comes with addons that will help you with the most common use cases.
Arnie comes with a bunch off legos, which are supposed to assist you in building your next backend application. Each addon will be explained with a short task definition - look into the tests to find out more.
export default [
accepts({
json: [
sendSomeJSON()
],
otherwiss: [fail(406)]
})
]
export default [
cors('get, post', '*')
]
export default [
loadWishlist(), {
error: fail(404, 'Wishlist not found.')
}
]
export default [
header({
'Cache-Control': 'no-cache'
})
]
export default [
method({
get: [
showWishlist()
],
delete: [
removeWishlist()
],
otherwise: [
fail(405)
]
})
]
export default [
redirect('/redirect-target')
]
Is required to be used with route.
export default [
route('/resource/:id?', [
restEndpoint({
list: [], // GET /resource
show: [], // GET /resource/123
create: [], // POST /resource
update: [], // POST /resource/123
clear: [], // DELETE /resource
remove: [] // DELETE /resource/123
})
])
]
Note to self: it should probably be combined in to:
export default [
restEndpoint('/resource/:id?', {
list: [], // GET /resource
show: [], // GET /resource/123
create: [], // POST /resource
update: [], // POST /resource/123
clear: [], // DELETE /resource
remove: [] // DELETE /resource/123
})
]
The route addon uses path-to-regexp and merges the url params into context.input
.
export default [
route({
'/route/abc': [],
'/with/:param/in-path': [],
otherwise: [
fail(404, 'Route not found.')
]
})
]
export default [
createWishlist(),
show('input.wishlist')
]
export default [
createWishlist(),
status(201)
]
export default [
when('request.query.foo', {
bar: [],
otherwise: []
})
]
FAQs
Action hero for backends
We found that arnie demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.