
Research
Malicious fezbox npm Package Steals Browser Passwords from Cookies via Innovative QR Code Steganographic Technique
A malicious package uses a QR code as steganography in an innovative technique.
literium-router
Advanced tools
This project is part of Literium WEB-framework but can be used standalone.
The ordinary routers is not type-safe, so when you change route or corresponding interface you need track compliance between it manually. The compiler cannot help you in cases of breaking that compliance.
For example, your app may expect arguments which actual router don't provide. Or viceversa the router may require some arguments which missing in your application.
This leads us to a question: what can we do to make type system to keep the routes and corresponding interfaces in compliance? Many frameworks in other languages with type inference, like Haskell and PureScript, solves this question. So why TypeScript cannot do same too.
Each route have corresponds object with typed fields i.e. arguments. The routes without arguments corresponds to empty object. The type of this arguments object is inferred automatically by constructing the route. You can construct complex routes using simple routing algebra.
In simplest case you can use path string to create routes.
import { dir, match, build } from 'literium-router';
const blog = dir('/blog');
// => Route<{}>
match(blog, '/blog') // => {}
match(blog, '/blog/1') // => undefined
match(blog, '/other') // => undefined
build(blog, {}) // => '/blog'
build(blog, {id: 1}) // error (extra 'id' property)
This route corresponds to path /blog and empty object.
When you want handle some data in the path string you can create route with the typed argument.
import { num, arg, match, build } from 'literium-router';
const blog_id = arg({id: num});
// => Route<{id: number}>
match(blog_id, '1') // => {id:1}
match(blog_id, 'other') // => undefined
match(blog_id, 'blog/1') // => undefined
build(blog_id, {}) // error (missing 'id' property)
build(blog_id, {id: 1}) // => '1'
The first argument of arg()
is the object with property name as key and with property type-tag as value.
The available type-tags and corresponding value types is defined by the second argument of arg()
.
To deal with query string you can use query()
.
import { num, query, match, build } from 'literium-router';
const blog_posts = query({offset: num, length: num});
// => Route<{offset: number, length: number}>
match(blog_posts, '?offset=10&length=5') // => {offset:10,length:5}
match(blog_posts, '?length=5&offset=10') // => {offset:10,length:5}
match(blog_posts, '?length=5') // => undefined (no length arg)
build(blog_posts, {length:5}) // error (missing 'offset' property)
build(blog_posts, {offset:10, length:5}) // => '?offset=10&length=5'
When you need process different query strings you can alternate queryes using variants.
To build complex routes you can use seq()
to sequentially combine sub-routes.
import { num, dir, arg, seq, match, build } from 'literium-router';
const blog_by_id = seq(dir('/blog/'), arg({id: num});
// => Route<{id: number}>
match(blog_by_id, '/blog/1') // => {id:1}
match(blog_by_id, '/blog/1/') // => undefined
match(blog_by_id, '/other') // => undefined
match(blog_by_id, '1') // => undefined
build(blog_by_id, {}) // error (missing 'id' property)
build(blog_by_id, {id: 1}) // => '/blog/1'
And of course you can combine some number of routes which can be used alternatively.
import { str, num, dir, arg, seq, alt, match, build } from 'literium-router';
const blogs_by_tag = seq(dir('/blog/tag-'), arg({id: str}));
// => Route<{id: string}>
const by_author = seq(dir('/author-'), arg({user: num}));
// => Route<{user: number}>
const blogs_by_tag_opt_by_author = seq(blogs_by_tag, alt(by_author, dir('')));
// => Route<{tag: string} | {tag: string} & {user: number}>
match(blogs_by_tag_opt_by_author, '/blog/tag-js') // => {tag:"js"}
match(blogs_by_tag_opt_by_author, '/blog/tag-js/author-3') // => {tag:"js",user:3}
match(blogs_by_tag_opt_by_author, '/blog/tag-js/') // => undefined
match(blogs_by_tag_opt_by_author, '/blog/tag-js/author-') // => undefined
match(blogs_by_tag_opt_by_author, '/other') // => undefined
build(blogs_by_tag_opt_by_author, {}) // error (missing 'tag' property)
build(blogs_by_tag_opt_by_author, {tag:"git"}) // => '/blog/tag-git'
build(blogs_by_tag_opt_by_author, {user:3}) // error (missing 'tag' property)
build(blogs_by_tag_opt_by_author, {tag:"git",user:3}) // => '/blog/tag-git/author-3'
NOTE: Keep in mind the more complex variant must precede simple because in else case the simple routes may become unreachable to match. This assumption is accepted in order to simplify and speedup route matching algorithm.
One of the more helpful feature of this router is the possibility to define your own argument types. For example, suppose we have enum type Order which has two values: Asc and Desc. We can simply use it in our routes as arguments when we defined corresponding TypeApi.
import { dir, arg, seq, match, build, Route } from 'literium-router';
// Our enum type
export const enum Order { Asc, Desc }
// The implementation of Route
export const ord: Route<Order> = {
// The parser function (path => value)
p: path => {
const m = path.match(/^(asc|desc)(.*)$/);
if (m) return [m[1] == 'asc' ? Order.Asc : Order.Desc, m[2]];
},
// The builder function (value => path)
b: arg => arg === Order.Asc || arg === Order.Desc ?
`${arg == Order.Asc ? 'asc' : 'desc'}` : undefined
};
const blogs_by_date = seq(dir('/blog/date-'), arg({sort: ord}));
// => Route<{sort: Order}>
match(blogs_by_date, '/blog/date-asc') // => {sort:Order.Asc}
match(blogs_by_date, '/blog/date-desc') // => {sort:Order.Desc}
match(blogs_by_date, '/blog/date-') // => undefined
match(blogs_by_date, '/blog/date-other') // => undefined
build(blogs_by_date, {sort:Order.Asc}) // => '/blog/date-asc'
build(blogs_by_date, {sort:Order.Desc}) // => '/blog/date-desc'
build(blogs_by_date, {}) // error (missing 'sort' property)
build(blogs_by_date, {sort:'asc'}) // error (type mismatch of 'sort' property)
Usually in real-life applications we don't like to operate with routes separately.
One of ways to works with all defined routes together is using of the methods matchs()
and builds()
.
This methods gets the object with routes names as keys and routes itself as values. It operates with the complex state which represents object with routes names as keys and corresponding route arguments objects as values.
import { num, str, dir, arg, seq, matchs, builds } from 'literium-router';
const root = dir('/');
const blog_by_id = seq(root, dir('blog/'), arg({id:num}));
const blogs_by_tag = seq(root, dir('blog/tag-'), arg({tag:str}));
const routes = { root, blogs_by_tag, blog_by_id };
matchs(routes, '/') // => {root:{}}
matchs(routes, '/blog/2') // => {blog_by_id:{id:2}}
matchs(routes, '/blog/tag-es6') // => {blogs_by_tag:{tag:"es6"}}
builds(routes, {root:{}}) // => '/'
builds(routes, {blog_by_id:{id:2}}) // => '/blog/2'
builds(routes, {blogs_by_tag:{tag:"es6"}}) // => '/blog/tag-es6'
The TypeScript 2.7.x have an issue which breaks the type inference of object values, so I strongly recommend to use TypeScript 2.6.x until TypeScript 2.8.x has been released.
FAQs
Type-safe router module for Literium web-framework.
We found that literium-router 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
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.
Application Security
/Research
/Security News
Socket detected multiple compromised CrowdStrike npm packages, continuing the "Shai-Hulud" supply chain attack that has now impacted nearly 500 packages.