UNPKG

fp-ts-routing

Version:

A type-safe routing library for TypeScript

189 lines 5.88 kB
/** * @since 0.6.0 */ import * as E from 'fp-ts/es6/Either'; import { identity } from 'fp-ts/es6/function'; import * as O from 'fp-ts/es6/Option'; // This `pipe` version is deprecated, but provided by `fp-ts` v2.0.1 and higher. import { pipe } from 'fp-ts/es6/pipeable'; import { failure, Int, string, success, Type } from 'io-ts'; import { Formatter } from './formatter'; import { Parser } from './parser'; import { Route } from './route'; /** * @category matchers * @since 0.4.0 */ var Match = /** @class */ (function () { function Match(parser, formatter) { this.parser = parser; this.formatter = formatter; } /** * @since 0.4.0 */ Match.prototype.imap = function (f, g) { return new Match(this.parser.map(f), this.formatter.contramap(g)); }; /** * @since 0.4.0 */ Match.prototype.then = function (that) { return new Match(this.parser.then(that.parser), this.formatter.then(that.formatter)); }; return Match; }()); export { Match }; /** * @category matchers * @since 0.5.1 */ export var imap = function (f, g) { return function (ma) { return ma.imap(f, g); }; }; /** * @category matchers * @since 0.5.1 */ export var then = function (mb) { return function (ma) { return ma.then(mb); }; }; var singleton = function (k, v) { var _a; return (_a = {}, _a[k] = v, _a); }; /** * `succeed` matches everything but consumes nothing * * @category matchers * @since 0.4.0 */ export var succeed = function (a) { return new Match(new Parser(function (r) { return O.some([a, r]); }), new Formatter(identity)); }; /** * `end` matches the end of a route * * @category matchers * @since 0.4.0 */ export var end = new Match(new Parser(function (r) { return (Route.isEmpty(r) ? O.some([{}, r]) : O.none); }), new Formatter(identity)); /** * `type` matches any io-ts type path component * * @example * import * as t from 'io-ts' * import { lit, type, Route } from 'fp-ts-routing' * import { some, none } from 'fp-ts/es6/Option' * * const T = t.keyof({ * a: null, * b: null * }) * * const match = lit('search').then(type('topic', T)) * * assert.deepStrictEqual(match.parser.run(Route.parse('/search/a')), some([{ topic: 'a' }, Route.empty])) * assert.deepStrictEqual(match.parser.run(Route.parse('/search/b')), some([{ topic: 'b' }, Route.empty])) * assert.deepStrictEqual(match.parser.run(Route.parse('/search/')), none) * * @category matchers * @since 0.4.0 */ export var type = function (k, type) { return new Match(new Parser(function (r) { if (r.parts.length === 0) { return O.none; } return pipe(type.decode(r.parts[0]), O.fromEither, O.map(function (a) { return [singleton(k, a), new Route(r.parts.slice(1), r.query)]; })); }), new Formatter(function (r, o) { return new Route(r.parts.concat(type.encode(o[k])), r.query); })); }; /** * `str` matches any string path component * * @example * import { str, Route } from 'fp-ts-routing' * import { some, none } from 'fp-ts/es6/Option' * * assert.deepStrictEqual(str('id').parser.run(Route.parse('/abc')), some([{ id: 'abc' }, new Route([], {})])) * assert.deepStrictEqual(str('id').parser.run(Route.parse('/')), none) * * @category matchers * @since 0.4.0 */ export var str = function (k) { return type(k, string); }; /** * @category matchers * @since 0.4.2 */ export var IntegerFromString = new Type('IntegerFromString', function (u) { return Int.is(u); }, function (u, c) { return pipe(string.validate(u, c), E.chain(function (s) { var n = +s; return isNaN(n) || !Number.isInteger(n) ? failure(s, c) : success(n); })); }, String); /** * `int` matches any integer path component * * @example * import { int, Route } from 'fp-ts-routing' * import { some, none } from 'fp-ts/es6/Option' * * assert.deepStrictEqual(int('id').parser.run(Route.parse('/1')), some([{ id: 1 }, new Route([], {})])) * assert.deepStrictEqual(int('id').parser.run(Route.parse('/a')), none) * * @category matchers * @since 0.4.0 */ export var int = function (k) { return type(k, IntegerFromString); }; /** * `lit(x)` will match exactly the path component `x` * * @example * import { lit, Route } from 'fp-ts-routing' * import { some, none } from 'fp-ts/es6/Option' * * assert.deepStrictEqual(lit('subview').parser.run(Route.parse('/subview/')), some([{}, new Route([], {})])) * assert.deepStrictEqual(lit('subview').parser.run(Route.parse('/')), none) * * @category matchers * @since 0.4.0 */ export var lit = function (literal) { return new Match(new Parser(function (r) { if (r.parts.length === 0) { return O.none; } return r.parts[0] === literal ? O.some([{}, new Route(r.parts.slice(1), r.query)]) : O.none; }), new Formatter(function (r) { return new Route(r.parts.concat(literal), r.query); })); }; /** * Will match a querystring. * * * **Note**. Use `io-ts`'s `strict` instead of `type` otherwise excess properties won't be removed. * * @example * import * as t from 'io-ts' * import { lit, str, query, Route } from 'fp-ts-routing' * * const route = lit('accounts') * .then(str('accountId')) * .then(lit('files')) * .then(query(t.strict({ pathparam: t.string }))) * .formatter.run(Route.empty, { accountId: 'testId', pathparam: '123' }) * .toString() * * assert.strictEqual(route, '/accounts/testId/files?pathparam=123') * * @category matchers * @since 0.4.0 */ export var query = function (type) { return new Match(new Parser(function (r) { return pipe(type.decode(r.query), O.fromEither, O.map(function (query) { return [query, new Route(r.parts, {})]; })); }), new Formatter(function (r, query) { return new Route(r.parts, type.encode(query)); })); }; //# sourceMappingURL=matcher.js.map