Updating pouchdb keys based on an updated version of docuri.

This commit is contained in:
vdv committed 2022-11-13 20:02:11 +01:00
1 parent 12f0a8a10d
commit 00ad6285d0
22 files changed
+344 -181

No files matched your search

+137
View File
@@ -0,0 +1,137 @@
/*
* DocURI: Rich document ids for CouchDB.
*
* Copyright (c) 2014 null2 GmbH Berlin
* Licensed under the MIT license.
*/
// type/id/subtype/index/version
var docuri = module.exports = exports = {};
// Cached regular expressions for matching named param parts and splatted parts
// of route strings.
// http://backbonejs.org/docs/backbone.html#section-158
var optionalParam = /\((.*?)\)/g;
var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
var paramKeys = /[*:]\w+/g;
// Convert a route string into a regular expression,
// with named regular expressions for named arguments.
// http://backbonejs.org/docs/backbone.html#section-165
function routeToRegExp(src) {
var keys = [], match;
while ( ( match = paramKeys.exec( src ) ) !== null )
{
keys.push( match[0] );
}
var route = src.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) {
return optional ? match : '([^/?]+)';
})
.replace(splatParam, '([^?]*?)');
keys = keys.reduce(function(memo, key) {
var value = '\\' + key;
memo[key] = new RegExp(value + '(\\/|\\)|\\(|$)');
return memo;
}, {});
return {
src: src,
exp: new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'),
keys: keys
}
}
// Given a route and a DocURI return an object of extracted parameters.
// Unmatched DocURIs will be treated as false.
// http://backbonejs.org/docs/backbone.html#section-166
function extractParameters(route, fragment, coding) {
var params = route.exp.exec(fragment);
if (!params) {
return false;
}
params = params.slice(1);
return Object.keys(route.keys).reduce(function(memo, key, i) {
var param = params[i];
if (param) {
var k = key.substr(1);
var decoder = (coding[k] ?? {decoder:decodeURIComponent}).decoder;
if (key[0] === '*') {
param = param.split('/').map(decoder);
} else {
param = decoder(param);
}
memo[key.substr(1)] = param;
}
return memo;
}, {});
}
// Insert named parameters from object.
function insertParameters(route, obj, coding) {
var str = route.src;
Object.keys(route.keys).forEach(function(key) {
var k = key.substr(1);
var value = (obj[k] !== undefined) ? obj[k] : '';
var encoder = (coding[k] ?? {encoder:encodeURIComponent}).encoder;
if (Array.isArray(value)) {
value = value.map(encoder).join('/');
} else {
value = encoder(value);
}
str = str.replace(route.keys[key], value + '$1');
});
// massage optional parameter
return str
.replace(/\(\/\)/g, '')
.replace(/[)(]/g, '');
}
docuri.route = function(route, coding={}) {
route = routeToRegExp(route);
return function(source, target) {
source = source || {};
if (target) {
source = extractParameters(route, source, coding);
Object.keys(target).forEach(function(key) {
source[key] = target[key];
});
}
if (typeof source === 'object') {
return insertParameters(route, source, coding);
}
if (typeof source === 'string') {
return extractParameters(route, source, coding);
}
};
};
+39 -35
View File
@@ -1,4 +1,4 @@
import { route } from 'docuri';
import { route } from './docuri';
import uri from './ids';
describe('Checking some DocURI features', () => {
@@ -43,64 +43,68 @@ describe('Checking a multilevel route with optional part', () => {
});
describe('Checking gpx ids', () => {
const id = {
gpx: 12345678901234567n,
};
const key = 'gpx/12345678901234567';
test(', vice', () => {
const gpx = uri('gpx', { gpx: 'id' });
expect(gpx).toBe('gpx/id');
const gpx = uri('gpx', id);
expect(gpx).toBe(key);
});
test(', and versa', () => {
const gpx = uri('gpx', 'gpx/id');
expect(gpx).toMatchObject({ gpx: 'id' });
const gpx = uri('gpx', key);
expect(gpx).toMatchObject(id);
});
});
describe('Checking trk ids', () => {
const id = {
gpx: 12345678901234567n,
trk: 123456,
};
const key = 'gpx/12345678901234567/2trk/123456';
test(', vice', () => {
const rte = uri('trk', { gpx: 'gpxid', trk: 'trkid' });
expect(rte).toBe('gpx/gpxid/2trk/trkid');
const rte = uri('trk', id);
expect(rte).toBe(key);
});
test(', and versa', () => {
const rte = uri('trk', 'gpx/gpxid/2trk/trkid');
expect(rte).toMatchObject({ gpx: 'gpxid', trk: 'trkid' });
const rte = uri('trk', key);
expect(rte).toMatchObject(id);
});
});
describe('Checking trkseg ids', () => {
const id = {
gpx: 111n,
trk: 0,
trkseg: 3,
};
const key = 'gpx/00000000000000111/2trk/000000/000003';
test(', vice', () => {
const rte = uri('trkseg', {
gpx: 'gpxid',
trk: 'trkid',
trkseg: 'trksegid',
});
expect(rte).toBe('gpx/gpxid/2trk/trkid/trksegid');
const rte = uri('trkseg', id);
expect(rte).toBe(key);
});
test(', and versa', () => {
const rte = uri('trkseg', 'gpx/gpxid/2trk/trkid/trksegid');
expect(rte).toMatchObject({
gpx: 'gpxid',
trk: 'trkid',
trkseg: 'trksegid',
});
const rte = uri('trkseg', key);
expect(rte).toMatchObject(id);
});
});
describe('Checking trkpt ids', () => {
const id = {
gpx: 25n,
trk: 8,
trkseg: 0,
trkpt: 155,
};
const key = 'gpx/00000000000000025/2trk/000008/000000/000155';
test(', vice', () => {
const rte = uri('trkpt', {
gpx: 'gpxid',
trk: 'trkid',
trkseg: 'trksegid',
trkpt: 'trkptid',
});
expect(rte).toBe('gpx/gpxid/2trk/trkid/trksegid/trkptid');
const rte = uri('trkpt', id);
expect(rte).toBe(key);
});
test(', and versa', () => {
const rte = uri('trkpt', 'gpx/gpxid/2trk/trkid/trksegid/trkptid');
expect(rte).toMatchObject({
gpx: 'gpxid',
trk: 'trkid',
trkseg: 'trksegid',
trkpt: 'trkptid',
});
const rte = uri('trkpt', key);
expect(rte).toMatchObject(id);
});
});
+34 -10
View File
@@ -1,15 +1,39 @@
import { route } from 'docuri';
import { route } from './docuri';
const integerType = (n: number) => {
return {
encoder: (v: number) => v.toString().padStart(n, '0'),
decoder: parseInt,
};
};
const bigIntType = (n: number) => {
return {
encoder: (v: number) => v.toString().padStart(n, '0'),
decoder: BigInt,
};
};
const coding = {
gpx: bigIntType(17),
wpt: integerType(6),
rte: integerType(6),
rtept: integerType(6),
trk: integerType(6),
trkseg: integerType(6),
trkpt: integerType(6),
};
const routes = {
dbdef: route('dbdef'),
settings: route('settings'),
gpx: route('gpx/:gpx'),
wpt: route('gpx/:gpx/0wpt/:wpt'),
rte: route('gpx/:gpx/1rte/:rte'),
rtept: route('gpx/:gpx/1rte/:rte/:rtept'),
trk: route('gpx/:gpx/2trk/:trk'),
trkseg: route('gpx/:gpx/2trk/:trk/:trkseg'),
trkpt: route('gpx/:gpx/2trk/:trk/:trkseg/:trkpt'),
dbdef: route('dbdef', coding),
settings: route('settings', coding),
gpx: route('gpx/:gpx', coding),
wpt: route('gpx/:gpx/0wpt/:wpt', coding),
rte: route('gpx/:gpx/1rte/:rte', coding),
rtept: route('gpx/:gpx/1rte/:rte/:rtept', coding),
trk: route('gpx/:gpx/2trk/:trk', coding),
trkseg: route('gpx/:gpx/2trk/:trk/:trkseg', coding),
trkpt: route('gpx/:gpx/2trk/:trk/:trkseg/:trkpt', coding),
};
type RouteKey = keyof typeof routes;
+17 -17
View File
@@ -1,34 +1,34 @@
interface IdGpx {
gpx: string;
gpx: BigInt;
}
interface IdTrk {
gpx: string;
trk: string;
gpx: BigInt;
trk: number;
}
interface IdTrkseg {
gpx: string;
trk: string;
trkseg: string;
gpx: BigInt;
trk: number;
trkseg: number;
}
interface IdTrkpt {
gpx: string;
trk: string;
trkseg: string;
trkpt: string;
gpx: BigInt;
trk: number;
trkseg: number;
trkpt: number;
}
interface IdWpt {
gpx: string;
wpt: string;
gpx: BigInt;
wpt: number;
}
interface IdRte {
gpx: string;
rte: string;
gpx: BigInt;
rte: number;
}
interface IdRtept {
gpx: string;
rte: string;
rtept: string;
gpx: BigInt;
rte: number;
rtept: number;
}