all repos — litestore @ fb229f76930b254274e5d28ba03316872cca494f

A minimalist nosql document store.

t push origin masterMerge branch 'skellock-whitespacery'
h3rald h3rald@h3rald.com
Mon, 30 Sep 2019 13:43:44 +0200
commit

fb229f76930b254274e5d28ba03316872cca494f

parent

79001faf5b7061114fd14dfebbb4449bf77baa78

M LICENSELICENSE

@@ -1,6 +1,6 @@

The MIT License (MIT) -Copyright (c) 2015-2018 Fabio Cevasco +Copyright (c) 2015-2019 Fabio Cevasco Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal

@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -
M litestore.nimblelitestore.nimble

@@ -52,7 +52,7 @@ shell compile, windows_x64, ls_file

task linux_x64_build, "Build LiteStore for Linux (x64)": shell compile, linux_x64, ls_file - + task macosx_x64_build, "Build LiteStore for Mac OS X (x64)": shell compile, macosx_x64, ls_file

@@ -72,8 +72,9 @@ echo "\n\n\n LINUX - x64:\n\n"

linux_x64_buildTask() shell zip, "$1 $2 $3 $4" % [filename_for("linux", "x64"), ls, doc, db] shell "rm", ls + echo "\n\n\n MAC OS X - x64:\n\n" macosx_x64_buildTask() shell zip, "$1 $2 $3 $4" % [filename_for("macosx", "x64"), ls, doc, db] - shell "rm", ls + shell "rm", ls echo "\n\n\n ALL DONE!"
M src/admin/js/app.jssrc/admin/js/app.js

@@ -1,11 +1,11 @@

(function(){ 'use strict'; var app = window.LS || (window.LS = {}); - + app.flash = m.prop(); app.system = {}; m.route.mode = "hash"; - + app.init = function(info){ app.system = info; app.system.v = app.system.version.match(/(v.+)$/)[1];

@@ -24,5 +24,5 @@ "/search/:q/:page/:limit": app.search

}); }; Info.get().then(app.init); - + }());
M src/admin/js/components/doclist.jssrc/admin/js/components/doclist.js

@@ -2,12 +2,12 @@ (function(){

'use strict'; var app = window.LS || (window.LS = {}); var u = app.utils; - + app.doclist = {}; - + // Subcomponent app.doclist.panel = { - + /** * @typedef {Object} DoclistPanelConfig * @prop {string} id

@@ -27,10 +27,10 @@ m("p", args.tags.map(function(tag){

return u.taglink({name: tag, key: u.guid()}); })) ]); - return m(".row.search-result", m(".col-md-12", [u.panel(obj)])); + return m(".row.search-result", m(".col-md-12", [u.panel(obj)])); } }; - + /** * @param {Function} ctrl * @param {Object} args

@@ -44,7 +44,7 @@ var results = m(".row", [m(".col-md-12", args.items.map(function(item){

item.key = u.guid(); return m.component(app.doclist.panel, item); }))]); - + return m("section", [ m(".row", [args.title]), m(".row", [args.subtitle]),

@@ -53,5 +53,5 @@ results,

m(".row.text-center", [u.paginator(args.querydata)]) ]); }; - + }());
M src/admin/js/components/document.jssrc/admin/js/components/document.js

@@ -9,7 +9,7 @@ app.document.vm.init = function() {

var vm = this; vm.id = m.prop(m.route.param("id")); vm.action = m.route.param("action"); - vm.readOnly = true; + vm.readOnly = true; vm.contentType = m.prop(""); vm.updatedTags = m.prop(""); vm.content = "";

@@ -21,7 +21,7 @@ vm.ext = vm.id().match(/\.([a-z0-9]+)$/i)[1];

} catch(e) { vm.ext = ""; } - + // Retrieve single document & update relevant variables vm.getDoc = function(cb){ vm.doc = Doc.get(vm.id());

@@ -37,7 +37,7 @@ }

} }, vm.flashError); }; - + // Reset some properties based on action switch (vm.action) { case 'create':

@@ -52,7 +52,7 @@ case 'view':

vm.getDoc(); break; } - + // View document in editor vm.viewDocument = function(){ if (vm.ext === "md" && vm.id().match(new RegExp("^admin\/md\/"))) {

@@ -62,14 +62,14 @@ } else {

m.route("/document/view/"+vm.id()); } }; - + // Set current document editable vm.edit = function(){ vm.editor.setReadOnly(false); vm.action = "edit"; vm.flash(""); }; - + // Save document vm.save = function(){ var doc = {};

@@ -95,7 +95,7 @@ } else {

put(); } }; - + // Delete Document vm.delete = function(){ Doc.delete(vm.id()).then(function(){

@@ -107,7 +107,7 @@ m.route("/info");

}); }, vm.flashError); }; - + // Cancel editing vm.cancel = function(){ if (vm.action === "create"){

@@ -116,7 +116,7 @@ } else {

vm.viewDocument(); } }; - + // Patch document (update tags) vm.patch = function(){ var sysTags = vm.tags.filter(function(t){return /^\$/.test(t)});

@@ -129,7 +129,7 @@ vm.viewDocument();

}); }, vm.flashError); }; - + // File uploader callbacks. var onSuccess = function(data){ vm.id(data.id);

@@ -139,13 +139,13 @@ app.system = info;

vm.viewDocument(); }); }; - + var onFailure = function(data) { vm.flashError(data); }; var modalId = u.guid(); vm.uploader = u.uploader({docid: vm.id() || "", onSuccess: onSuccess, onFailure: onFailure, id: modalId}); - + // Populate tools based on current action vm.tools = function(){ if (app.system.read_only) {

@@ -174,7 +174,7 @@ }

return tools; }; }; - + // Module main view app.document.main = function(){ var vm = app.document.vm;

@@ -196,8 +196,8 @@ action: vm.patch,

actionText: "Update", content: m("div", [ m("input", { - type: "text", - class:"form-control", + type: "text", + class:"form-control", onchange: m.withAttr("value", vm.updatedTags), value: vm.updatedTags(), placeholder: "Enter comma-separated tags..."

@@ -235,7 +235,7 @@ } else {

panelContent = m.component(app.editor, vm); } var title = m("span",[titleLeft, titleRight]); - + return m("div", [ vm.uploader, u.modal(deleteDialogCfg),

@@ -244,6 +244,6 @@ m(".row", [u.toolbar({links: vm.tools()})]),

m(".row", [u.panel({title: title, content:panelContent})]) ]); }; - + u.layout(app.document); -}());+}());
M src/admin/js/components/editor.jssrc/admin/js/components/editor.js

@@ -1,5 +1,5 @@

(function(){ - 'use strict'; + 'use strict'; var app = window.LS || (window.LS = {}); app.editor = {};

@@ -61,5 +61,5 @@ args.content = JSON.stringify(args.content);

} return m(".editor.panel.panal-default", {config: app.editor.config(args)}, args.content); }; - -}());+ +}());
M src/admin/js/components/guide.jssrc/admin/js/components/guide.js

@@ -16,11 +16,11 @@ vm.links = app.system.read_only ? m.prop([]) : m.prop([{action: vm.edit, title: "Edit", icon: "edit"}]);

}; app.guide.main = function(){ return m("article.row", [ - u.toolbar({links: app.guide.vm.links()}), + u.toolbar({links: app.guide.vm.links()}), m.trust(app.guide.vm.content()) ]); }; u.layout(app.guide); -}());+}());
M src/admin/js/components/htmldoc.jssrc/admin/js/components/htmldoc.js

@@ -18,7 +18,7 @@ vm.links = m.prop([{action: vm.view, title: "View Source", icon: "code"}]);

}; app.htmldoc.main = function(){ return m("article.row", [ - u.toolbar({links: app.htmldoc.vm.links()}), + u.toolbar({links: app.htmldoc.vm.links()}), m.trust(app.htmldoc.vm.content()) ]); };
M src/admin/js/components/info.jssrc/admin/js/components/info.js

@@ -15,8 +15,8 @@ } else {

return m("li", [m("span", title+": "), m("strong", content)]); } }; - var readonly = info.read_only ? m("span.label.label-success", "Yes") : m("span.label.label-danger", "No"); - var mirror = info.mount ? m("span.label.label-success", "Yes") : m("span.label.label-danger", "No"); + var readonly = info.read_only ? m("span.label.label-success", "Yes") : m("span.label.label-danger", "No"); + var mirror = info.mount ? m("span.label.label-success", "Yes") : m("span.label.label-danger", "No"); var infolist = m(".col-sm-6", [m("ul.list-unstyled", [ li("Version", info.version), li("Datastore Version", info.datastore_version),
M src/admin/js/components/navbar.jssrc/admin/js/components/navbar.js

@@ -25,7 +25,7 @@ {path: "/guide/api_docs", title: caret+"docs (LiteStore Documents)"},

{path: "/guide/api_tags", title: caret+"tags (LiteStore Tags)"}, {path: "/guide/credits", title: "Credits"} ]; - vm.taglinks = function(info){ + vm.taglinks = function(info){ return info.tags.map(function(tag){ var key = Object.keys(tag)[0]; return {path: "/tags/"+key, title: key+" ("+tag[key]+")"};

@@ -35,19 +35,19 @@ return vm;

}, view: function(ctrl){ var links = [ - m("li", {class: ctrl.activelink("info")}, [m("a", {href: "/info", config: m.route}, + m("li", {class: ctrl.activelink("info")}, [m("a", {href: "/info", config: m.route}, [m("i.fa.fa-info-circle"), " Info"])]), u.dropdown({title: "Guide", icon:"fa-book", links: ctrl.guidelinks, active: ctrl.activelink("guide")}), u.dropdown({title: "Tags", icon:"fa-tags", links: ctrl.taglinks(app.system), active: ctrl.activelink("tags")})]; if (!app.system.read_only) { - links.push(m("li", - {class: ctrl.activelink("new")}, [m("a", {href: "/document/create/", config: m.route}, + links.push(m("li", + {class: ctrl.activelink("new")}, [m("a", {href: "/document/create/", config: m.route}, [m("i.fa.fa-plus-circle"), " New"])])); } return m("ul.nav.navbar-nav", links); } }; - + app.navheader = { view: function(ctrl, args) { return m(".navbar-header", [

@@ -61,7 +61,7 @@ m("a.navbar-brand", {href: "/", config:m.route}, "LiteStore Admin")

]); } }; - + app.searchbox = { controller: function() { var vm = {};

@@ -86,25 +86,25 @@ view: function(ctrl, args) {

return m("form.navbar-form.navbar-right[role='search']", [ m(".input-group", [ m("input.form-control", { - type:"text", + type:"text", placeholder:"Search...", onchange: m.withAttr("value", ctrl.query), config: ctrl.keySearch, value: ctrl.query() }), - m("span.input-group-btn", + m("span.input-group-btn", m("button.btn.btn-default", { type: "button", onclick: ctrl.search - }, + }, [m("i.fa.fa-search")])) ]) ] ); } }; - + app.navbar = { view: function(ctrl, args) { return m("nav.navbar.navbar-inverse.navbar-fixed-top", [

@@ -114,8 +114,8 @@ m("#nav-collapse.collapse.navbar-collapse", [

m.component(app.navlinks), m.component(app.searchbox) ]) - ]) + ]) ]); } }; -}());+}());
M src/admin/js/components/search.jssrc/admin/js/components/search.js

@@ -20,7 +20,7 @@ Doc.search(vm.query, vm.offset, vm.limit).then(function(result){

vm.result(result); vm.total = result.total; vm.execTime = (result.execution_time*1000).toFixed(0); - }, vm.flashError); + }, vm.flashError); }; app.search.main = function(){ var vm = app.search.vm;
M src/admin/js/components/tags.jssrc/admin/js/components/tags.js

@@ -19,7 +19,7 @@ vm.docs = Doc.getByTag(vm.id, vm.offset, vm.limit).then(function(docs){

vm.total = docs.total; vm.execTime = (docs["execution_time"]*1000).toFixed(0); return docs; - }, vm.flashError); + }, vm.flashError); }; app.tags.main = function(){ var vm = app.tags.vm;

@@ -29,7 +29,7 @@ obj.querydata = vm;

obj.title = m("h2", ["Tag: ", m("em", docs.tags)]); obj.subtitle = m("p", [m("strong",docs.total), " results, ("+vm.execTime+" ms)"]); obj.items = docs.results; - obj.items.forEach(function(item){ + obj.items.forEach(function(item){ item.content = m("ul", [ m("li", [m("strong", "Created: "), u.date(item.created)]), m("li", [m("strong", "Modified: "), u.date(item.modified)]),
M src/admin/js/components/uploader.jssrc/admin/js/components/uploader.js

@@ -1,5 +1,5 @@

(function(){ - 'use strict'; + 'use strict'; var app = window.LS || (window.LS = {}); var u = app.utils;

@@ -10,7 +10,7 @@ return function(element, isInitialized, context){

$(element).change(function(event){ obj.file(element.files[0]); if (obj.reader.readyState != 1) { - obj.reader.readAsDataURL(obj.file()); + obj.reader.readAsDataURL(obj.file()); } }); };

@@ -21,7 +21,7 @@ var vm = this;

vm.docid = m.prop(args.docid); vm.file = m.prop(); - vm.id = args.id; + vm.id = args.id; vm.btnId = "#upload-"+vm.id+"-btn"; vm.modalId = "#upload-"+vm.id+"-modal"; vm.reader = new FileReader();

@@ -46,14 +46,14 @@ doc.data = window.atob(doc.data);

} return Doc.put(doc, vm.file().type).then(args.onSuccess, args.onFailure); }; - + vm.cancel = function(){ $("input:file").val(''); }; - + return vm; }; - + app.uploader.view = function(ctrl, args){ var config = { title: "Upload Document",

@@ -78,7 +78,7 @@ m("p.help-block", "Select a file to upload as document.")

]), m(".checkbox", [ m("label", [ - m("input", {type: "checkbox", value: ctrl.isText(), onchange: m.withAttr("value", ctrl.isText)}), + m("input", {type: "checkbox", value: ctrl.isText(), onchange: m.withAttr("value", ctrl.isText)}), "Text File" ]), m("p.help-block", "Select if the file to upload contains textual content.")

@@ -87,5 +87,5 @@ ])

}; return u.modal(config); }; - -}());+ +}());
M src/admin/js/components/widgets.jssrc/admin/js/components/widgets.js

@@ -1,7 +1,7 @@

(function(){ 'use strict'; var app = window.LS || (window.LS = {}); - + app.widgets = {}; /* PANEL */

@@ -11,7 +11,7 @@ var title = "";

var footer = ""; if (args.title){ title = m(".panel-heading", [ - m("h2.panel-title", [args.title]) + m("h2.panel-title", [args.title]) ]); } if (args.footer){

@@ -26,7 +26,7 @@ footer

]); } }; - + /* PAGINATOR */ app.widgets.paginator = { view: function(ctrl, args){

@@ -42,7 +42,7 @@ }

var first = (n === 0); var last = (n == max_page); var offset = args.limit * n; - sign = sign || n+1; + sign = sign || n+1; return m("li", {class: klass}, [m("a", { href: args.baseurl +(n+1), // assuming 10 elements per page //+"/"+obj.limit,

@@ -51,7 +51,7 @@ }, [m.trust(sign)]

)] ); }; - + var pages = []; var prev; var next;

@@ -78,7 +78,7 @@ pages.push(next);

return m("nav", [m("ul.pagination", pages)]); } }; - + /* DROPDOWN */ app.widgets.dropdown = { view: function(ctrl, args){

@@ -90,9 +90,9 @@ }

return m(el, [ m("a.dropdown-toggle[href='#'][data-toggle='dropdown'][role='button'][aria-expanded='false']", [icon, m("span", " "+args.title+" "), m("span.caret")]), - m("ul.dropdown-menu[role='menu']", + m("ul.dropdown-menu[role='menu']", args.links.map(function(e){ - return m("li", + return m("li", [m("a", {href: e.path, config: m.route}, m.trust(e.title))]);})) ]); }

@@ -102,18 +102,18 @@ /* DROPDOWN */

app.widgets.taglink = { view: function(ctrl, args) { var color = /^\$/.test(args.name) ? "warning" : "primary"; - return m("span.tag-label.label.label-"+color, + return m("span.tag-label.label.label-"+color, [m("i.fa.fa-tag"), " ", m("a", {href: "/tags/"+args.name, config:m.route}, args.name)]); } }; - + /* DOCLINK */ app.widgets.doclink = { view: function(ctrl, args) { return m("a", {href: "/document/view/"+args.id, config: m.route}, id); } }; - + /* TAGBUTTON */ app.widgets.tagbutton = { view: function(ctrl, args) {

@@ -121,20 +121,20 @@ return m("a", {href: "/tags/"+args.name, config:m.route},

[m("i.fa.fa-tag"), " "+args.name+" ", m("span.badge", args.n)]); } }; - + /* TOOLBAR */ app.widgets.toolbar = { view: function(ctrl, args){ - return m("nav.toolbar.btn-group[role='group'][aria-label='...'].pull-right", + return m("nav.toolbar.btn-group[role='group'][aria-label='...'].pull-right", args.links.map(function(l){ - return m("a.btn.btn-default", - {onclick:l.action, config: l.config}, + return m("a.btn.btn-default", + {onclick:l.action, config: l.config}, [m("i.fa.fa-"+l.icon), " "+l.title]); - }) + }) ); } }; - + /* MODAL */ app.widgets.modal = { view: function(ctrl, args){

@@ -144,13 +144,13 @@ }

if (!args.dismissText) { args.dismissText = "Close"; } - return m(".modal.fade", + return m(".modal.fade", {id: args.id, tabindex: "-1", role: "dialog"}, [ m(".modal-dialog", [ m(".modal-content", [ m(".modal-header", [ - m("button", {type: "button", class: "close", "data-dismiss": "modal"}, + m("button", {type: "button", class: "close", "data-dismiss": "modal"}, [m.trust("×")]), m("h4.modal-title", args.title) ]),

@@ -166,4 +166,4 @@ );

} }; -}());+}());
M src/admin/js/models.jssrc/admin/js/models.js

@@ -7,44 +7,44 @@ var u = app.utils;

app.host = ""; //'http://localhost:9500'; var host = location.origin === app.host ? "" : app.host; - + Page.get = function(id) { var content = m.prop(""); return m.request({ - method: "GET", + method: "GET", url: host+"/docs/admin/md/"+id+".md", deserialize: function(value) { return value; } - }).then(function(content){ + }).then(function(content){ return u.markdown(content); }); }; - + Info.get = function(){ var content = m.prop(""); return m.request({ - method: "GET", + method: "GET", url: host+"/info" }).then(content); }; - + Doc.getByTag = function(tag, offset, limit) { offset = offset || 0; limit = limit || 10; var docs = m.prop(""); return m.request({ - method: "GET", + method: "GET", url: host+"/docs?contents=false&tags="+tag+"&limit="+limit+"&offset="+offset }).then(docs); }; - + Doc.search = function(search, offset, limit){ offset = offset || 0; limit = limit || 10; var docs = m.prop(""); return m.request({ - method: "GET", + method: "GET", url: host+"/docs?contents=false&search="+search+"&limit="+limit+"&offset="+offset, }).then(docs); };

@@ -52,23 +52,23 @@

Doc.get = function(id) { var doc = m.prop(""); return m.request({ - method: "GET", + method: "GET", url: host+"/docs/"+id+"?raw=true" }).then(doc); }; - + Doc.delete = function(id){ return m.request({ - method: "DELETE", + method: "DELETE", url: host+"/docs/"+id }); }; - + Doc.put = function(doc, contentType){ xhrcfg = u.setContentType(doc, contentType); console.log("Doc.put - Saving Document:", doc); return m.request({ - method: "PUT", + method: "PUT", url: host+"/docs/"+doc.id, data: doc.data, serialize: function(data){

@@ -77,7 +77,7 @@ },

config: xhrcfg }); }; - + Doc.upload = function(doc) { console.log("Doc.put - Uploading Document:", doc); return m.request({

@@ -89,7 +89,7 @@ return data;

} }); }; - + Doc.patch = function(id, updatedTags){ return Doc.get(id).then(function(doc){ var tags = doc.tags;

@@ -121,4 +121,4 @@ data: ops

}); }); }; -}());+}());
M src/admin/js/utils.jssrc/admin/js/utils.js

@@ -2,7 +2,7 @@ (function(){

'use strict'; var app = window.LS || (window.LS = {}); var u = app.utils = {}; - + // http://byronsalau.com/blog/how-to-create-a-guid-uuid-in-javascript/ u.guid = function(){ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {

@@ -10,7 +10,7 @@ var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);

return v.toString(16); }); }; - + u.fixHeadings = function(html, maxheading){ var $content = $(html); var n = maxheading;

@@ -26,7 +26,7 @@ }

return u.fixHeadings($content, maxheading); } }; - + u.setContentType = function(doc, contentType){ var type = ""; var subtype = "";

@@ -80,7 +80,7 @@ var html = marked(s, {renderer: hs}).replace(/\{\{\$version\}\}/g, app.system.v);

var $html = $('<div>').append($(html).clone()); return u.fixHeadings($html, 2); }; - + /** * mod object: * @param {Object} mod

@@ -129,7 +129,7 @@ u.date = function(date) {

return (date) ? new Date(Date.parse(date)).toUTCString() : "n/a"; }; - + u.showModal = function(sel){ return function(){ $(sel).modal();

@@ -165,7 +165,7 @@ */

u.panel = function(obj){ return m.component(app.widgets.panel, obj); }; - + /** * @typedef {Object} PaginatorConfig * @prop {string} baseurl

@@ -204,7 +204,7 @@ */

u.taglink = function(obj){ return m.component(app.widgets.taglink, obj); }; - + /** * Creates a DocLink component. * @param {Object} obj
M src/admin/md/admin_app.mdsrc/admin/md/admin_app.md

@@ -64,4 +64,4 @@ * It is possible to upload local files instead of creating them by hand.

* Preview is available for images and HTML documents * Source code highlighting is available for Javascript, CSS, HTML, JSON and Markdown files. -![Document](images/app_document.png)+![Document](images/app_document.png)
M src/admin/md/api_docs.mdsrc/admin/md/api_docs.md

@@ -551,4 +551,4 @@ Content-Length: 0

Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Origin: * Server: LiteStore/1.0.3 -```+```
M src/admin/md/api_tags.mdsrc/admin/md/api_tags.md

@@ -104,4 +104,4 @@ access-control-allow-headers: Content-Type

Content-Length: 34 {"id":"$type:text","documents":32} -```+```
M src/admin/md/architecture.mdsrc/admin/md/architecture.md

@@ -48,4 +48,4 @@ The [tags](class:kwd) table is used to store the associations between tags and documents. Tags can be added by users or add automatically by the system when a document is imported into the data store.

##### searchdata Table -This table is used as full-text index for searchable documents.+This table is used as full-text index for searchable documents.
M src/admin/md/credits.mdsrc/admin/md/credits.md

@@ -7,4 +7,4 @@ * Andreas Rumpf and all the contributors to the [Nim Programming Language](http://nim-lang.org/), used to develop LiteStore.

* Leo Horie, for creating the [Mithril Javascript Framework](https://lhorie.github.io/mithril/), used to develop the LiteStore Administration App. * The creators and contributors to the [Bootstrap](http://getbootstrap.com/) CSS and Javascript framework, used by the LiteStore Administration App. * The creators and contributors to the [Ace Editor](http://ace.c9.io/), used by the LiteStore Administration App. -* Cristopher Jeffrey and all the contributors to the [Marked Javascript Library](https://github.com/chjj/marked) used by the LiteStore Administration App.+* Cristopher Jeffrey and all the contributors to the [Marked Javascript Library](https://github.com/chjj/marked) used by the LiteStore Administration App.
M src/admin/md/data_model.mdsrc/admin/md/data_model.md

@@ -20,4 +20,4 @@ All system tags are prefixed by a [$](class:kwd) characters, and are used to identify the following document metadata. More specifically:

* **$dir:*directory*** &ndash; System tags starting with [$dir:](class:kwd) identify the name of a directory whose contents were imported into a data store. All files within the specified directory will be tagged with a [$dir:](class:kwd) system tag. Example: If a directory called **admin** is imported, imported files will be tagged with [$dir:admin](class:kwd). * **$type:*type*** &ndash; System tags starting with [$type:](class:kwd) identify the type of a document (i.e. the first portion of its content type). Example: Documents whose content type is **text/plain** will be tagged with [$type:text](class:kwd). -* **$subtype:*subtype*** &ndash; System tags starting with [$subtype:](class:kwd) identify the subtype of a document (i.e. the second portion of its content type). Example: Documents whose content type is **text/plain** will be tagged with [$subtype:plain](class:kwd).+* **$subtype:*subtype*** &ndash; System tags starting with [$subtype:](class:kwd) identify the subtype of a document (i.e. the second portion of its content type). Example: Documents whose content type is **text/plain** will be tagged with [$subtype:plain](class:kwd).
M src/admin/md/nim-api.mdsrc/admin/md/nim-api.md

@@ -113,6 +113,3 @@

runForever() ``` - - -
M src/admin/md/use-cases.mdsrc/admin/md/use-cases.md

@@ -42,4 +42,4 @@ Your app could then be served on any desktop system able to run LiteStore (e.g. OSX, Windows, Linux, ...even on a [Raspberry Pi](https://www.raspberrypi.org)).

#### Static Site Backend -LiteStore can be configured to run in read-only mode, so that only GET, HEAD, or OPTIONS request are accepted by the server. This makes it ideal as a backend for static web site generated with something like [nanoc](http://nanoc.ws) or [Jekyll](http://jekyllrb.com).+LiteStore can be configured to run in read-only mode, so that only GET, HEAD, or OPTIONS request are accepted by the server. This makes it ideal as a backend for static web site generated with something like [nanoc](http://nanoc.ws) or [Jekyll](http://jekyllrb.com).
M src/admin/styles/elements.lesssrc/admin/styles/elements.less

@@ -148,4 +148,4 @@

.search-result { margin: 5px 0; word-break: break-word; -}+}
M src/admin/styles/fa-core.lesssrc/admin/styles/fa-core.less

@@ -8,5 +8,4 @@ font-size: inherit; // can't have font-size inherit on line above, so need to override

text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - }
M src/admin/styles/fa-variables.lesssrc/admin/styles/fa-variables.less

@@ -705,4 +705,3 @@ @fa-var-yen: "\f157";

@fa-var-youtube: "\f167"; @fa-var-youtube-play: "\f16a"; @fa-var-youtube-square: "\f166"; -
M src/litestore.nimsrc/litestore.nim

@@ -1,12 +1,12 @@

-import - strutils, +import + strutils, os, uri, httpcore import litestorepkg/lib/types, litestorepkg/lib/logger, - litestorepkg/lib/utils, + litestorepkg/lib/utils, litestorepkg/lib/core, litestorepkg/lib/cli, litestorepkg/lib/server

@@ -28,7 +28,7 @@ let body = LS.execution.body

let ctype = LS.execution.ctype let uri = LS.execution.uri let operation = LS.execution.operation - var req:LSRequest + var req:LSRequest case operation.toUpperAscii: of "GET": req.reqMethod = HttpGet

@@ -89,7 +89,7 @@ if LS.operation == opVacuum:

setup(false) vacuum LS.file else: - # Open Datastore + # Open Datastore setup(true) case LS.operation:

@@ -118,13 +118,13 @@ for pair in pairs:

let data = pair.split("=") result[data[0]] = data[1] - proc query*(table: StringTableRef): string = + proc query*(table: StringTableRef): string = var params = newSeq[string](0) for key, value in pairs(table): params.add("$1=$2" % @[key, value]) return params.join("&") - proc newLSRequest(meth: HttpMethod, resource, id, body = "", params = newStringTable(), headers = newHttpHeaders()): LSRequest = + proc newLSRequest(meth: HttpMethod, resource, id, body = "", params = newStringTable(), headers = newHttpHeaders()): LSRequest = result.reqMethod = meth result.body = body result.headers = headers
M src/litestorepkg/examples/jester_integration.nimsrc/litestorepkg/examples/jester_integration.nim

@@ -5,29 +5,29 @@

routes: # Just a simple, unrelated Jester route - get "/": + get "/": resp "Hello, World!" - + # Remapping LiteStore routes on Jester get re"^\/litestore\/(docs|info)\/?(.*)": let r = get(request.matches[0], request.matches[1], newStringTable(toSeq(request.params.pairs)), request.headers) - resp(r.code, r.content, r.headers["Content-Type"]) + resp(r.code, r.content, r.headers["Content-Type"]) post re"^\/litestore\/docs\/?(.*)": let r = post("docs", request.matches[0], request.body, request.headers) - resp(r.code, r.content, r.headers["Content-Type"]) + resp(r.code, r.content, r.headers["Content-Type"]) put re"^\/litestore\/docs\/?(.*)": let r = put("docs", request.matches[0], request.body, request.headers) - resp(r.code, r.content, r.headers["Content-Type"]) + resp(r.code, r.content, r.headers["Content-Type"]) patch re"^\/litestore\/docs\/?(.*)": let r = patch("docs", request.matches[0], request.body, request.headers) - resp(r.code, r.content, r.headers["Content-Type"]) + resp(r.code, r.content, r.headers["Content-Type"]) delete re"^\/litestore\/docs\/?(.*)": let r = delete("docs", request.matches[0], request.headers) - resp(r.code, r.content) + resp(r.code, r.content) head re"^\/litestore\/docs\/?(.*)": let r = head("docs", request.matches[0], request.headers)
M src/litestorepkg/lib/api_v1.nimsrc/litestorepkg/lib/api_v1.nim

@@ -1,4 +1,4 @@

-import +import asynchttpserver, strutils, cgi,

@@ -7,7 +7,7 @@ pegs,

json, os, times -import +import types, contenttypes, core,

@@ -187,10 +187,10 @@ content["datastore_version"] = %version

content["size"] = %($((LS.file.getFileSize().float/(1024*1024)).formatFloat(ffDecimal, 2)) & " MB") content["read_only"] = %LS.readonly content["log_level"] = %LS.loglevel - if LS.directory.len == 0: + if LS.directory.len == 0: content["directory"] = newJNull() - else: - content["directory"] = %LS.directory + else: + content["directory"] = %LS.directory content["mount"] = %LS.mount content["total_documents"] = %total_documents content["total_tags"] = %total_tags

@@ -360,7 +360,7 @@ else:

discard # never happens really. -proc post(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc post(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id == "": var ct = "text/plain" if req.headers.hasKey("Content-Type"):

@@ -369,7 +369,7 @@ return LS.postDocument(req.body.strip, ct)

else: return resError(Http400, "Bad request: document ID cannot be specified in POST requests.") -proc put(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc put(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": var ct = "text/plain" if req.headers.hasKey("Content-Type"):

@@ -378,13 +378,13 @@ return LS.putDocument(id, req.body.strip, ct)

else: return resError(Http400, "Bad request: document ID must be specified in PUT requests.") -proc delete(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc delete(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.deleteDocument(id) else: return resError(Http400, "Bad request: document ID must be specified in DELETE requests.") -proc patch(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc patch(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.patchDocument(id, req.body) else:

@@ -414,24 +414,24 @@ return resError(Http500, "Unable to read file '$1'." % path)

else: return resError(Http404, "File '$1' not found." % path) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) -proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = +proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = var reqMethod = $req.reqMethod if req.headers.hasKey("X-HTTP-Method-Override"): reqMethod = req.headers["X-HTTP-Method-Override"] case reqMethod.toUpperAscii: of "POST": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, post) of "PUT": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, put) of "DELETE": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, delete) of "HEAD": return validate(req, LS, resource, id, head)

@@ -441,7 +441,7 @@ of "GET":

return validate(req, LS, resource, id, get) of "PATCH": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, patch) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod)
M src/litestorepkg/lib/api_v2.nimsrc/litestorepkg/lib/api_v2.nim

@@ -1,4 +1,4 @@

-import +import asynchttpserver, strutils, cgi,

@@ -7,7 +7,7 @@ pegs,

json, os, times -import +import types, contenttypes, core,

@@ -65,7 +65,7 @@ var fragments = querystring.split('&')

for f in fragments: f.parseQueryOption(options) -proc validate*(req: LSRequest, LS: LiteStore, resource: string, id: string, cb: proc(req: LSRequest, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = +proc validate*(req: LSRequest, LS: LiteStore, resource: string, id: string, cb: proc(req: LSRequest, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = if req.reqMethod == HttpPost or req.reqMethod == HttpPut or req.reqMethod == HttpPatch: var ct = "" let body = req.body.strip

@@ -206,10 +206,10 @@ content["datastore_version"] = %version

content["size"] = %($((LS.file.getFileSize().float/(1024*1024)).formatFloat(ffDecimal, 2)) & " MB") content["read_only"] = %LS.readonly content["log_level"] = %LS.loglevel - if LS.directory.len == 0: + if LS.directory.len == 0: content["directory"] = newJNull() - else: - content["directory"] = %LS.directory + else: + content["directory"] = %LS.directory content["mount"] = %LS.mount content["total_documents"] = %total_documents content["total_tags"] = %total_tags

@@ -220,7 +220,7 @@ result.code = Http200

proc postDocument*(LS: LiteStore, body: string, ct: string, folder=""): LSResponse = if not folder.isFolder: - return resError(Http400, "Invalid folder specified when creating document: $1" % folder) + return resError(Http400, "Invalid folder specified when creating document: $1" % folder) try: var doc = LS.store.createDocument(folder, body, ct) if doc != "":

@@ -401,13 +401,13 @@ else:

discard # never happens really. -proc post*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc post*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = var ct = "text/plain" if req.headers.hasKey("Content-Type"): ct = req.headers["Content-Type"] return LS.postDocument(req.body.strip, ct, id) -proc put*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc put*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": var ct = "text/plain" if req.headers.hasKey("Content-Type"):

@@ -416,13 +416,13 @@ return LS.putDocument(id, req.body.strip, ct)

else: return resError(Http400, "Bad request: document ID must be specified in PUT requests.") -proc delete*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc delete*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.deleteDocument(id) else: return resError(Http400, "Bad request: document ID must be specified in DELETE requests.") -proc patch*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc patch*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.patchDocument(id, req.body) else:

@@ -452,24 +452,24 @@ return resError(Http500, "Unable to read file '$1'." % path)

else: return resError(Http404, "File '$1' not found." % path) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) -proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = +proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = var reqMethod = $req.reqMethod if req.headers.hasKey("X-HTTP-Method-Override"): reqMethod = req.headers["X-HTTP-Method-Override"] case reqMethod.toUpperAscii: of "POST": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, post) of "PUT": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, put) of "DELETE": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, delete) of "HEAD": return validate(req, LS, resource, id, head)

@@ -479,7 +479,7 @@ of "GET":

return validate(req, LS, resource, id, get) of "PATCH": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, patch) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod)
M src/litestorepkg/lib/api_v3.nimsrc/litestorepkg/lib/api_v3.nim

@@ -1,4 +1,4 @@

-import +import asynchttpserver, strutils, sequtils,

@@ -8,7 +8,7 @@ pegs,

json, os, times -import +import types, contenttypes, core,

@@ -50,7 +50,7 @@ else:

clauses.add("$1 ASC" % field) return clauses.join(", ") -proc selectClause*(str: string, options: var QueryOptions) = +proc selectClause*(str: string, options: var QueryOptions) = let tokens = """ path <- '$' (objItem / objField)+ ident <- [a-zA-Z0-9_]+

@@ -60,10 +60,10 @@ objItem <- objField objIndex

""" let fields = peg(""" fields <- ^{field} (\s* ',' \s* {field})*$ - field <- path \s+ ('as' / 'AS') \s+ ident + field <- path \s+ ('as' / 'AS') \s+ ident """ & tokens) let field = peg(""" - field <- ^{path} \s+ ('as' / 'AS') \s+ {ident}$ + field <- ^{path} \s+ ('as' / 'AS') \s+ {ident}$ """ & tokens) var fieldMatches = newSeq[string](10) if str.strip.match(fields, fieldMatches):

@@ -73,7 +73,7 @@ var rawTuple = newSeq[string](2)

if m.match(field, rawTuple): options.jsonSelect.add((path: rawTuple[0], alias: rawTuple[1])) -proc filterClauses*(str: string, options: var QueryOptions) = +proc filterClauses*(str: string, options: var QueryOptions) = let tokens = """ operator <- 'not eq' / 'eq' / 'gte' / 'gt' / 'lte' / 'lt' / 'contains' value <- string / number / 'null' / 'true' / 'false'

@@ -180,7 +180,7 @@ var fragments = querystring.split('&')

for f in fragments: f.parseQueryOption(options) -proc validate*(req: LSRequest, LS: LiteStore, resource: string, id: string, cb: proc(req: LSRequest, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = +proc validate*(req: LSRequest, LS: LiteStore, resource: string, id: string, cb: proc(req: LSRequest, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = if req.reqMethod == HttpPost or req.reqMethod == HttpPut or req.reqMethod == HttpPatch: var ct = "" let body = req.body.strip

@@ -251,11 +251,11 @@ d.elems[index] = value

case op: of "remove": d.elems.del(index) - of "add": + of "add": d.elems.insert(value, index) - of "replace": + of "replace": d.elems[index] = value - of "test": + of "test": if d.elems[index] != value: return false else:

@@ -273,14 +273,14 @@ if d.hasKey(key):

d.delete(key) else: raise newException(EInvalidRequest, "key '$1' not found in path '$2'" % [key, path]) - of "add": + of "add": d[key] = value - of "replace": + of "replace": if d.hasKey(key): d[key] = value else: raise newException(EInvalidRequest, "key '$1' not found in path '$2'" % [key, path]) - of "test": + of "test": if dorig.hasKey(key): if dorig[key] != value: return false

@@ -397,10 +397,10 @@ content["datastore_version"] = %version

content["size"] = %($((LS.file.getFileSize().float/(1024*1024)).formatFloat(ffDecimal, 2)) & " MB") content["read_only"] = %LS.readonly content["log_level"] = %LS.loglevel - if LS.directory.len == 0: + if LS.directory.len == 0: content["directory"] = newJNull() - else: - content["directory"] = %LS.directory + else: + content["directory"] = %LS.directory content["mount"] = %LS.mount content["total_documents"] = %total_documents content["total_tags"] = %total_tags

@@ -411,7 +411,7 @@ result.code = Http200

proc postDocument*(LS: LiteStore, body: string, ct: string, folder=""): LSResponse = if not folder.isFolder: - return resError(Http400, "Invalid folder specified when creating document: $1" % folder) + return resError(Http400, "Invalid folder specified when creating document: $1" % folder) try: var doc = LS.store.createDocument(folder, body, ct) if doc != "":

@@ -610,13 +610,13 @@ else:

discard # never happens really. -proc post*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc post*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = var ct = "text/plain" if req.headers.hasKey("Content-Type"): ct = req.headers["Content-Type"] return LS.postDocument(req.body.strip, ct, id) -proc put*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc put*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": var ct = "text/plain" if req.headers.hasKey("Content-Type"):

@@ -625,13 +625,13 @@ return LS.putDocument(id, req.body.strip, ct)

else: return resError(Http400, "Bad request: document ID must be specified in PUT requests.") -proc delete*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc delete*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.deleteDocument(id) else: return resError(Http400, "Bad request: document ID must be specified in DELETE requests.") -proc patch*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc patch*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.patchDocument(id, req.body) else:

@@ -661,24 +661,24 @@ return resError(Http500, "Unable to read file '$1'." % path)

else: return resError(Http404, "File '$1' not found." % path) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) -proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = +proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = var reqMethod = $req.reqMethod if req.headers.hasKey("X-HTTP-Method-Override"): reqMethod = req.headers["X-HTTP-Method-Override"] case reqMethod.toUpperAscii: of "POST": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, post) of "PUT": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, put) of "DELETE": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, delete) of "HEAD": return validate(req, LS, resource, id, head)

@@ -688,7 +688,7 @@ of "GET":

return validate(req, LS, resource, id, get) of "PATCH": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, patch) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod)
M src/litestorepkg/lib/api_v4.nimsrc/litestorepkg/lib/api_v4.nim

@@ -1,4 +1,4 @@

-import +import asynchttpserver, strutils, sequtils,

@@ -8,7 +8,7 @@ pegs,

json, os, times -import +import types, contenttypes, core,

@@ -50,7 +50,7 @@ else:

clauses.add("$1 ASC" % field) return clauses.join(", ") -proc selectClause*(str: string, options: var QueryOptions) = +proc selectClause*(str: string, options: var QueryOptions) = let tokens = """ path <- '$' (objItem / objField)+ ident <- [a-zA-Z0-9_]+

@@ -60,10 +60,10 @@ objItem <- objField objIndex

""" let fields = peg(""" fields <- ^{field} (\s* ',' \s* {field})*$ - field <- path \s+ ('as' / 'AS') \s+ ident + field <- path \s+ ('as' / 'AS') \s+ ident """ & tokens) let field = peg(""" - field <- ^{path} \s+ ('as' / 'AS') \s+ {ident}$ + field <- ^{path} \s+ ('as' / 'AS') \s+ {ident}$ """ & tokens) var fieldMatches = newSeq[string](10) if str.strip.match(fields, fieldMatches):

@@ -73,7 +73,7 @@ var rawTuple = newSeq[string](2)

if m.match(field, rawTuple): options.jsonSelect.add((path: rawTuple[0], alias: rawTuple[1])) -proc filterClauses*(str: string, options: var QueryOptions) = +proc filterClauses*(str: string, options: var QueryOptions) = let tokens = """ operator <- 'not eq' / 'eq' / 'gte' / 'gt' / 'lte' / 'lt' / 'contains' value <- string / number / 'null' / 'true' / 'false'

@@ -204,7 +204,7 @@ var fragments = querystring.split('&')

for f in fragments: f.parseQueryOption(options) -proc validate*(req: LSRequest, LS: LiteStore, resource: string, id: string, cb: proc(req: LSRequest, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = +proc validate*(req: LSRequest, LS: LiteStore, resource: string, id: string, cb: proc(req: LSRequest, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = if req.reqMethod == HttpPost or req.reqMethod == HttpPut or req.reqMethod == HttpPatch: var ct = "" let body = req.body.strip

@@ -274,11 +274,11 @@ d.elems[index] = value

case op: of "remove": d.elems.del(index) - of "add": + of "add": d.elems.insert(value, index) - of "replace": + of "replace": d.elems[index] = value - of "test": + of "test": if d.elems[index] != value: return false else:

@@ -296,14 +296,14 @@ if d.hasKey(key):

d.delete(key) else: raise newException(EInvalidRequest, "key '$1' not found in path '$2'" % [key, path]) - of "add": + of "add": d[key] = value - of "replace": + of "replace": if d.hasKey(key): d[key] = value else: raise newException(EInvalidRequest, "key '$1' not found in path '$2'" % [key, path]) - of "test": + of "test": if dorig.hasKey(key): if dorig[key] != value: return false

@@ -459,10 +459,10 @@ content["datastore_version"] = %version

content["size"] = %($((LS.file.getFileSize().float/(1024*1024)).formatFloat(ffDecimal, 2)) & " MB") content["read_only"] = %LS.readonly content["log_level"] = %LS.loglevel - if LS.directory.len == 0: + if LS.directory.len == 0: content["directory"] = newJNull() - else: - content["directory"] = %LS.directory + else: + content["directory"] = %LS.directory content["mount"] = %LS.mount content["total_documents"] = %total_documents content["total_tags"] = %total_tags

@@ -474,7 +474,7 @@ result.code = Http200

proc postDocument*(LS: LiteStore, body: string, ct: string, folder="", req: LSRequest): LSResponse = if not folder.isFolder: - return resError(Http400, "Invalid folder specified when creating document: $1" % folder) + return resError(Http400, "Invalid folder specified when creating document: $1" % folder) try: var doc = LS.store.createDocument(folder, body, ct) echo doc

@@ -691,7 +691,7 @@ of "tags":

var options = newQueryOptions() try: parseQueryOptions(req.url.query, options); - if id != "": + if id != "": return LS.getTag(id, options, req) else: return LS.getTags(options, req)

@@ -704,13 +704,13 @@ return LS.getInfo(req)

else: discard # never happens really. -proc post*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc post*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = var ct = "text/plain" if req.headers.hasKey("Content-Type"): ct = req.headers["Content-Type"] return LS.postDocument(req.body.strip, ct, id, req) -proc put*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc put*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": var ct = "text/plain" if req.headers.hasKey("Content-Type"):

@@ -719,13 +719,13 @@ return LS.putDocument(id, req.body.strip, ct, req)

else: return resError(Http400, "Bad request: document ID must be specified in PUT requests.") -proc delete*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc delete*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.deleteDocument(id, req) else: return resError(Http400, "Bad request: document ID must be specified in DELETE requests.") -proc patch*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = +proc patch*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": return LS.patchDocument(id, req.body, req) else:

@@ -756,24 +756,24 @@ return resError(Http500, "Unable to read file '$1'." % path)

else: return resError(Http404, "File '$1' not found." % path) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) -proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = +proc route*(req: LSRequest, LS: LiteStore, resource = "docs", id = ""): LSResponse = var reqMethod = $req.reqMethod if req.headers.hasKey("X-HTTP-Method-Override"): reqMethod = req.headers["X-HTTP-Method-Override"] case reqMethod.toUpperAscii: of "POST": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, post) of "PUT": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, put) of "DELETE": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, delete) of "HEAD": return validate(req, LS, resource, id, head)

@@ -783,7 +783,7 @@ of "GET":

return validate(req, LS, resource, id, get) of "PATCH": if LS.readonly: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod) return validate(req, LS, resource, id, patch) else: - return resError(Http405, "Method not allowed: $1" % $req.reqMethod) + return resError(Http405, "Method not allowed: $1" % $req.reqMethod)
M src/litestorepkg/lib/cli.nimsrc/litestorepkg/lib/cli.nim

@@ -9,7 +9,7 @@ utils

const favicon = "../../admin/favicon.ico".slurp -var +var operation = opRun directory:string = "" readonly = false

@@ -20,10 +20,10 @@ exFile:string = ""

exBody:string = "" exType:string = "" exUri:string = "" - + let usage* = appname & " v" & pkgVersion & " - Lightweight REST Document Store" & """ - + (c) 2015-2018 Fabio Cevasco Usage:
M src/litestorepkg/lib/config.nimsrc/litestorepkg/lib/config.nim

@@ -1,6 +1,6 @@

const pkgName* = "litestore" - pkgVersion* = "1.5.1" + pkgVersion* = "1.5.1" pkgAuthor* = "Fabio Cevasco" pkgDescription* = "Self-contained, lightweight, RESTful document store." pkgLicense* = "MIT"

@@ -10,4 +10,3 @@ var

file* = "data.db" address* = "127.0.0.1" port* = 9500 -
M src/litestorepkg/lib/contenttypes.jsonsrc/litestorepkg/lib/contenttypes.json

@@ -1,2468 +1,2468 @@

-[ - { +[ + { ".123":"application\/vnd.lotus-1-2-3" }, - { + { ".3dml":"text\/vnd.in3d.3dml" }, - { + { ".3g2":"video\/3gpp2" }, - { + { ".3gp":"video\/3gpp" }, - { + { ".a":"application\/octet-stream" }, - { + { ".aab":"application\/x-authorware-bin" }, - { + { ".aac":"audio\/x-aac" }, - { + { ".aam":"application\/x-authorware-map" }, - { + { ".aas":"application\/x-authorware-seg" }, - { + { ".abw":"application\/x-abiword" }, - { + { ".acc":"application\/vnd.americandynamics.acc" }, - { + { ".ace":"application\/x-ace-compressed" }, - { + { ".acu":"application\/vnd.acucobol" }, - { + { ".acutc":"application\/vnd.acucorp" }, - { + { ".adp":"audio\/adpcm" }, - { + { ".aep":"application\/vnd.audiograph" }, - { + { ".afm":"application\/x-font-type1" }, - { + { ".afp":"application\/vnd.ibm.modcap" }, - { + { ".ai":"application\/postscript" }, - { + { ".aif":"audio\/x-aiff" }, - { + { ".aifc":"audio\/x-aiff" }, - { + { ".aiff":"audio\/x-aiff" }, - { + { ".air":"application\/vnd.adobe.air-application-installer-package+zip" }, - { + { ".ami":"application\/vnd.amiga.ami" }, - { + { ".apk":"application\/vnd.android.package-archive" }, - { + { ".application":"application\/x-ms-application" }, - { + { ".apr":"application\/vnd.lotus-approach" }, - { + { ".asc":"application\/pgp-signature" }, - { + { ".asf":"video\/x-ms-asf" }, - { + { ".asm":"text\/x-asm" }, - { + { ".aso":"application\/vnd.accpac.simply.aso" }, - { + { ".asx":"video\/x-ms-asf" }, - { + { ".atc":"application\/vnd.acucorp" }, - { + { ".atom":"application\/atom+xml" }, - { + { ".atomcat":"application\/atomcat+xml" }, - { + { ".atomsvc":"application\/atomsvc+xml" }, - { + { ".atx":"application\/vnd.antix.game-component" }, - { + { ".au":"audio\/basic" }, - { + { ".avi":"video\/x-msvideo" }, - { + { ".aw":"application\/applixware" }, - { + { ".azf":"application\/vnd.airzip.filesecure.azf" }, - { + { ".azs":"application\/vnd.airzip.filesecure.azs" }, - { + { ".azw":"application\/vnd.amazon.ebook" }, - { + { ".bat":"application\/x-msdownload" }, - { + { ".bcpio":"application\/x-bcpio" }, - { + { ".bdf":"application\/x-font-bdf" }, - { + { ".bdm":"application\/vnd.syncml.dm+wbxml" }, - { + { ".bh2":"application\/vnd.fujitsu.oasysprs" }, - { + { ".bin":"application\/octet-stream" }, - { + { ".bmi":"application\/vnd.bmi" }, - { + { ".bmp":"image\/bmp" }, - { + { ".book":"application\/vnd.framemaker" }, - { + { ".box":"application\/vnd.previewsystems.box" }, - { + { ".boz":"application\/x-bzip2" }, - { + { ".bpk":"application\/octet-stream" }, - { + { ".btif":"image\/prs.btif" }, - { + { ".bz":"application\/x-bzip" }, - { + { ".bz2":"application\/x-bzip2" }, - { + { ".c":"text\/x-c" }, - { + { ".c4d":"application\/vnd.clonk.c4group" }, - { + { ".c4f":"application\/vnd.clonk.c4group" }, - { + { ".c4g":"application\/vnd.clonk.c4group" }, - { + { ".c4p":"application\/vnd.clonk.c4group" }, - { + { ".c4u":"application\/vnd.clonk.c4group" }, - { + { ".cab":"application\/vnd.ms-cab-compressed" }, - { + { ".car":"application\/vnd.curl.car" }, - { + { ".cat":"application\/vnd.ms-pki.seccat" }, - { + { ".cc":"text\/x-c" }, - { + { ".cct":"application\/x-director" }, - { + { ".ccxml":"application\/ccxml+xml" }, - { + { ".cdbcmsg":"application\/vnd.contact.cmsg" }, - { + { ".cdf":"application\/x-netcdf" }, - { + { ".cdkey":"application\/vnd.mediastation.cdkey" }, - { + { ".cdx":"chemical\/x-cdx" }, - { + { ".cdxml":"application\/vnd.chemdraw+xml" }, - { + { ".cdy":"application\/vnd.cinderella" }, - { + { ".cer":"application\/pkix-cert" }, - { + { ".cgm":"image\/cgm" }, - { + { ".chat":"application\/x-chat" }, - { + { ".chm":"application\/vnd.ms-htmlhelp" }, - { + { ".chrt":"application\/vnd.kde.kchart" }, - { + { ".cif":"chemical\/x-cif" }, - { + { ".cii":"application\/vnd.anser-web-certificate-issue-initiation" }, - { + { ".cil":"application\/vnd.ms-artgalry" }, - { + { ".cla":"application\/vnd.claymore" }, - { + { ".class":"application\/java-vm" }, - { + { ".clkk":"application\/vnd.crick.clicker.keyboard" }, - { + { ".clkp":"application\/vnd.crick.clicker.palette" }, - { + { ".clkt":"application\/vnd.crick.clicker.template" }, - { + { ".clkw":"application\/vnd.crick.clicker.wordbank" }, - { + { ".clkx":"application\/vnd.crick.clicker" }, - { + { ".clp":"application\/x-msclip" }, - { + { ".cmc":"application\/vnd.cosmocaller" }, - { + { ".cmdf":"chemical\/x-cmdf" }, - { + { ".cml":"chemical\/x-cml" }, - { + { ".cmp":"application\/vnd.yellowriver-custom-menu" }, - { + { ".cmx":"image\/x-cmx" }, - { + { ".cod":"application\/vnd.rim.cod" }, - { + { ".com":"application\/x-msdownload" }, - { + { ".conf":"text\/plain" }, - { + { ".cpio":"application\/x-cpio" }, - { + { ".cpp":"text\/x-c" }, - { + { ".cpt":"application\/mac-compactpro" }, - { + { ".crd":"application\/x-mscardfile" }, - { + { ".crl":"application\/pkix-crl" }, - { + { ".crt":"application\/x-x509-ca-cert" }, - { + { ".csh":"application\/x-csh" }, - { + { ".csml":"chemical\/x-csml" }, - { + { ".csp":"application\/vnd.commonspace" }, - { + { ".css":"text\/css" }, - { + { ".cst":"application\/x-director" }, - { + { ".csv":"text\/csv" }, - { + { ".cu":"application\/cu-seeme" }, - { + { ".curl":"text\/vnd.curl" }, - { + { ".cww":"application\/prs.cww" }, - { + { ".cxt":"application\/x-director" }, - { + { ".cxx":"text\/x-c" }, - { + { ".daf":"application\/vnd.mobius.daf" }, - { + { ".dataless":"application\/vnd.fdsn.seed" }, - { + { ".davmount":"application\/davmount+xml" }, - { + { ".dcr":"application\/x-director" }, - { + { ".dcurl":"text\/vnd.curl.dcurl" }, - { + { ".dd2":"application\/vnd.oma.dd2+xml" }, - { + { ".ddd":"application\/vnd.fujixerox.ddd" }, - { + { ".deb":"application\/x-debian-package" }, - { + { ".def":"text\/plain" }, - { + { ".deploy":"application\/octet-stream" }, - { + { ".der":"application\/x-x509-ca-cert" }, - { + { ".dfac":"application\/vnd.dreamfactory" }, - { + { ".dic":"text\/x-c" }, - { + { ".diff":"text\/plain" }, - { + { ".dir":"application\/x-director" }, - { + { ".dis":"application\/vnd.mobius.dis" }, - { + { ".dist":"application\/octet-stream" }, - { + { ".distz":"application\/octet-stream" }, - { + { ".djv":"image\/vnd.djvu" }, - { + { ".djvu":"image\/vnd.djvu" }, - { + { ".dll":"application\/x-msdownload" }, - { + { ".dmg":"application\/octet-stream" }, - { + { ".dms":"application\/octet-stream" }, - { + { ".dna":"application\/vnd.dna" }, - { + { ".doc":"application\/msword" }, - { + { ".docm":"application\/vnd.ms-word.document.macroenabled.12" }, - { + { ".docx":"application\/vnd.openxmlformats-officedocument.wordprocessingml.document" }, - { + { ".dot":"application\/msword" }, - { + { ".dotm":"application\/vnd.ms-word.template.macroenabled.12" }, - { + { ".dotx":"application\/vnd.openxmlformats-officedocument.wordprocessingml.template" }, - { + { ".dp":"application\/vnd.osgi.dp" }, - { + { ".dpg":"application\/vnd.dpgraph" }, - { + { ".dsc":"text\/prs.lines.tag" }, - { + { ".dtb":"application\/x-dtbook+xml" }, - { + { ".dtd":"application\/xml-dtd" }, - { + { ".dts":"audio\/vnd.dts" }, - { + { ".dtshd":"audio\/vnd.dts.hd" }, - { + { ".dump":"application\/octet-stream" }, - { + { ".dvi":"application\/x-dvi" }, - { + { ".dwf":"model\/vnd.dwf" }, - { + { ".dwg":"image\/vnd.dwg" }, - { + { ".dxf":"image\/vnd.dxf" }, - { + { ".dxp":"application\/vnd.spotfire.dxp" }, - { + { ".dxr":"application\/x-director" }, - { + { ".ecelp4800":"audio\/vnd.nuera.ecelp4800" }, - { + { ".ecelp7470":"audio\/vnd.nuera.ecelp7470" }, - { + { ".ecelp9600":"audio\/vnd.nuera.ecelp9600" }, - { + { ".ecma":"application\/ecmascript" }, - { + { ".edm":"application\/vnd.novadigm.edm" }, - { + { ".edx":"application\/vnd.novadigm.edx" }, - { + { ".efif":"application\/vnd.picsel" }, - { + { ".ei6":"application\/vnd.pg.osasli" }, - { + { ".elc":"application\/octet-stream" }, - { + { ".eml":"message\/rfc822" }, - { + { ".emma":"application\/emma+xml" }, - { + { ".eol":"audio\/vnd.digital-winds" }, - { + { ".eot":"application\/vnd.ms-fontobject" }, - { + { ".eps":"application\/postscript" }, - { + { ".epub":"application\/epub+zip" }, - { + { ".es3":"application\/vnd.eszigno3+xml" }, - { + { ".esf":"application\/vnd.epson.esf" }, - { + { ".et3":"application\/vnd.eszigno3+xml" }, - { + { ".etx":"text\/x-setext" }, - { + { ".exe":"application\/x-msdownload" }, - { + { ".ext":"application\/vnd.novadigm.ext" }, - { + { ".ez":"application\/andrew-inset" }, - { + { ".ez2":"application\/vnd.ezpix-album" }, - { + { ".ez3":"application\/vnd.ezpix-package" }, - { + { ".f":"text\/x-fortran" }, - { + { ".f4v":"video\/x-f4v" }, - { + { ".f77":"text\/x-fortran" }, - { + { ".f90":"text\/x-fortran" }, - { + { ".fbs":"image\/vnd.fastbidsheet" }, - { + { ".fdf":"application\/vnd.fdf" }, - { + { ".fe_launch":"application\/vnd.denovo.fcselayout-link" }, - { + { ".fg5":"application\/vnd.fujitsu.oasysgp" }, - { + { ".fgd":"application\/x-director" }, - { + { ".fh":"image\/x-freehand" }, - { + { ".fh4":"image\/x-freehand" }, - { + { ".fh5":"image\/x-freehand" }, - { + { ".fh7":"image\/x-freehand" }, - { + { ".fhc":"image\/x-freehand" }, - { + { ".fig":"application\/x-xfig" }, - { + { ".fli":"video\/x-fli" }, - { + { ".flo":"application\/vnd.micrografx.flo" }, - { + { ".flv":"video\/x-flv" }, - { + { ".flw":"application\/vnd.kde.kivio" }, - { + { ".flx":"text\/vnd.fmi.flexstor" }, - { + { ".fly":"text\/vnd.fly" }, - { + { ".fm":"application\/vnd.framemaker" }, - { + { ".fnc":"application\/vnd.frogans.fnc" }, - { + { ".for":"text\/x-fortran" }, - { + { ".fpx":"image\/vnd.fpx" }, - { + { ".frame":"application\/vnd.framemaker" }, - { + { ".fsc":"application\/vnd.fsc.weblaunch" }, - { + { ".fst":"image\/vnd.fst" }, - { + { ".ftc":"application\/vnd.fluxtime.clip" }, - { + { ".fti":"application\/vnd.anser-web-funds-transfer-initiation" }, - { + { ".fvt":"video\/vnd.fvt" }, - { + { ".fzs":"application\/vnd.fuzzysheet" }, - { + { ".g3":"image\/g3fax" }, - { + { ".gac":"application\/vnd.groove-account" }, - { + { ".gdl":"model\/vnd.gdl" }, - { + { ".geo":"application\/vnd.dynageo" }, - { + { ".gex":"application\/vnd.geometry-explorer" }, - { + { ".ggb":"application\/vnd.geogebra.file" }, - { + { ".ggt":"application\/vnd.geogebra.tool" }, - { + { ".ghf":"application\/vnd.groove-help" }, - { + { ".gif":"image\/gif" }, - { + { ".gim":"application\/vnd.groove-identity-message" }, - { + { ".gmx":"application\/vnd.gmx" }, - { + { ".gnumeric":"application\/x-gnumeric" }, - { + { ".gph":"application\/vnd.flographit" }, - { + { ".gqf":"application\/vnd.grafeq" }, - { + { ".gqs":"application\/vnd.grafeq" }, - { + { ".gram":"application\/srgs" }, - { + { ".gre":"application\/vnd.geometry-explorer" }, - { + { ".grv":"application\/vnd.groove-injector" }, - { + { ".grxml":"application\/srgs+xml" }, - { + { ".gsf":"application\/x-font-ghostscript" }, - { + { ".gtar":"application\/x-gtar" }, - { + { ".gtm":"application\/vnd.groove-tool-message" }, - { + { ".gtw":"model\/vnd.gtw" }, - { + { ".gv":"text\/vnd.graphviz" }, - { + { ".gz":"application\/x-gzip" }, - { + { ".h":"text\/x-c" }, - { + { ".h261":"video\/h261" }, - { + { ".h263":"video\/h263" }, - { + { ".h264":"video\/h264" }, - { + { ".hbci":"application\/vnd.hbci" }, - { + { ".hdf":"application\/x-hdf" }, - { + { ".hh":"text\/x-c" }, - { + { ".hlp":"application\/winhlp" }, - { + { ".hpgl":"application\/vnd.hp-hpgl" }, - { + { ".hpid":"application\/vnd.hp-hpid" }, - { + { ".hps":"application\/vnd.hp-hps" }, - { + { ".hqx":"application\/mac-binhex40" }, - { + { ".htke":"application\/vnd.kenameaapp" }, - { + { ".htm":"text\/html" }, - { + { ".html":"text\/html" }, - { + { ".hvd":"application\/vnd.yamaha.hv-dic" }, - { + { ".hvp":"application\/vnd.yamaha.hv-voice" }, - { + { ".hvs":"application\/vnd.yamaha.hv-script" }, - { + { ".icc":"application\/vnd.iccprofile" }, - { + { ".ice":"x-conference\/x-cooltalk" }, - { + { ".icm":"application\/vnd.iccprofile" }, - { + { ".ico":"image\/x-icon" }, - { + { ".ics":"text\/calendar" }, - { + { ".ief":"image\/ief" }, - { + { ".ifb":"text\/calendar" }, - { + { ".ifm":"application\/vnd.shana.informed.formdata" }, - { + { ".iges":"model\/iges" }, - { + { ".igl":"application\/vnd.igloader" }, - { + { ".igs":"model\/iges" }, - { + { ".igx":"application\/vnd.micrografx.igx" }, - { + { ".iif":"application\/vnd.shana.informed.interchange" }, - { + { ".imp":"application\/vnd.accpac.simply.imp" }, - { + { ".ims":"application\/vnd.ms-ims" }, - { + { ".in":"text\/plain" }, - { + { ".ipk":"application\/vnd.shana.informed.package" }, - { + { ".irm":"application\/vnd.ibm.rights-management" }, - { + { ".irp":"application\/vnd.irepository.package+xml" }, - { + { ".iso":"application\/octet-stream" }, - { + { ".itp":"application\/vnd.shana.informed.formtemplate" }, - { + { ".ivp":"application\/vnd.immervision-ivp" }, - { + { ".ivu":"application\/vnd.immervision-ivu" }, - { + { ".jad":"text\/vnd.sun.j2me.app-descriptor" }, - { + { ".jam":"application\/vnd.jam" }, - { + { ".jar":"application\/java-archive" }, - { + { ".java":"text\/x-java-source" }, - { + { ".jisp":"application\/vnd.jisp" }, - { + { ".jlt":"application\/vnd.hp-jlyt" }, - { + { ".jnlp":"application\/x-java-jnlp-file" }, - { + { ".joda":"application\/vnd.joost.joda-archive" }, - { + { ".jpe":"image\/jpeg" }, - { + { ".jpeg":"image\/jpeg" }, - { + { ".jpg":"image\/jpeg" }, - { + { ".jpgm":"video\/jpm" }, - { + { ".jpgv":"video\/jpeg" }, - { + { ".jpm":"video\/jpm" }, - { + { ".js":"application\/javascript" }, - { + { ".json":"application\/json" }, - { + { ".kar":"audio\/midi" }, - { + { ".karbon":"application\/vnd.kde.karbon" }, - { + { ".kfo":"application\/vnd.kde.kformula" }, - { + { ".kia":"application\/vnd.kidspiration" }, - { + { ".kil":"application\/x-killustrator" }, - { + { ".kml":"application\/vnd.google-earth.kml+xml" }, - { + { ".kmz":"application\/vnd.google-earth.kmz" }, - { + { ".kne":"application\/vnd.kinar" }, - { + { ".knp":"application\/vnd.kinar" }, - { + { ".kon":"application\/vnd.kde.kontour" }, - { + { ".kpr":"application\/vnd.kde.kpresenter" }, - { + { ".kpt":"application\/vnd.kde.kpresenter" }, - { + { ".ksh":"text\/plain" }, - { + { ".ksp":"application\/vnd.kde.kspread" }, - { + { ".ktr":"application\/vnd.kahootz" }, - { + { ".ktz":"application\/vnd.kahootz" }, - { + { ".kwd":"application\/vnd.kde.kword" }, - { + { ".kwt":"application\/vnd.kde.kword" }, - { + { ".latex":"application\/x-latex" }, - { + { ".lbd":"application\/vnd.llamagraphics.life-balance.desktop" }, - { + { ".lbe":"application\/vnd.llamagraphics.life-balance.exchange+xml" }, - { + { ".les":"application\/vnd.hhe.lesson-player" }, { ".less":"text/x-less" }, - { + { ".lha":"application\/octet-stream" }, - { + { ".link66":"application\/vnd.route66.link66+xml" }, - { + { ".list":"text\/plain" }, - { + { ".list3820":"application\/vnd.ibm.modcap" }, - { + { ".listafp":"application\/vnd.ibm.modcap" }, - { + { ".log":"text\/plain" }, - { + { ".lostxml":"application\/lost+xml" }, - { + { ".lrf":"application\/octet-stream" }, - { + { ".lrm":"application\/vnd.ms-lrm" }, - { + { ".ltf":"application\/vnd.frogans.ltf" }, - { + { ".lvp":"audio\/vnd.lucent.voice" }, - { + { ".lwp":"application\/vnd.lotus-wordpro" }, - { + { ".lzh":"application\/octet-stream" }, - { + { ".m13":"application\/x-msmediaview" }, - { + { ".m14":"application\/x-msmediaview" }, - { + { ".m1v":"video\/mpeg" }, - { + { ".m2a":"audio\/mpeg" }, - { + { ".m2v":"video\/mpeg" }, - { + { ".m3a":"audio\/mpeg" }, - { + { ".m3u":"audio\/x-mpegurl" }, - { + { ".m4u":"video\/vnd.mpegurl" }, - { + { ".m4v":"video\/x-m4v" }, - { + { ".ma":"application\/mathematica" }, - { + { ".mag":"application\/vnd.ecowin.chart" }, - { + { ".maker":"application\/vnd.framemaker" }, - { + { ".man":"text\/troff" }, - { + { ".markdown":"text\/x-markdown" }, - { + { ".mathml":"application\/mathml+xml" }, - { + { ".mb":"application\/mathematica" }, - { + { ".mbk":"application\/vnd.mobius.mbk" }, - { + { ".mbox":"application\/mbox" }, - { + { ".mc1":"application\/vnd.medcalcdata" }, - { + { ".mcd":"application\/vnd.mcd" }, - { + { ".mcurl":"text\/vnd.curl.mcurl" }, - { + { ".md":"text\/x-markdown" }, - { + { ".mdb":"application\/x-msaccess" }, - { + { ".mdi":"image\/vnd.ms-modi" }, - { + { ".me":"text\/troff" }, - { + { ".mesh":"model\/mesh" }, - { + { ".mfm":"application\/vnd.mfmp" }, - { + { ".mgz":"application\/vnd.proteus.magazine" }, - { + { ".mht":"message\/rfc822" }, - { + { ".mhtml":"message\/rfc822" }, - { + { ".mid":"audio\/midi" }, - { + { ".midi":"audio\/midi" }, - { + { ".mif":"application\/vnd.mif" }, - { + { ".mime":"message\/rfc822" }, - { + { ".mj2":"video\/mj2" }, - { + { ".mjp2":"video\/mj2" }, - { + { ".mlp":"application\/vnd.dolby.mlp" }, - { + { ".mmd":"application\/vnd.chipnuts.karaoke-mmd" }, - { + { ".mmf":"application\/vnd.smaf" }, - { + { ".mmr":"image\/vnd.fujixerox.edmics-mmr" }, - { + { ".mny":"application\/x-msmoney" }, - { + { ".mobi":"application\/x-mobipocket-ebook" }, - { + { ".mov":"video\/quicktime" }, - { + { ".movie":"video\/x-sgi-movie" }, - { + { ".mp2":"audio\/mpeg" }, - { + { ".mp2a":"audio\/mpeg" }, - { + { ".mp3":"audio\/mpeg" }, - { + { ".mp4":"video\/mp4" }, - { + { ".mp4a":"audio\/mp4" }, - { + { ".mp4s":"application\/mp4" }, - { + { ".mp4v":"video\/mp4" }, - { + { ".mpa":"video\/mpeg" }, - { + { ".mpc":"application\/vnd.mophun.certificate" }, - { + { ".mpe":"video\/mpeg" }, - { + { ".mpeg":"video\/mpeg" }, - { + { ".mpg":"video\/mpeg" }, - { + { ".mpg4":"video\/mp4" }, - { + { ".mpga":"audio\/mpeg" }, - { + { ".mpkg":"application\/vnd.apple.installer+xml" }, - { + { ".mpm":"application\/vnd.blueice.multipass" }, - { + { ".mpn":"application\/vnd.mophun.application" }, - { + { ".mpp":"application\/vnd.ms-project" }, - { + { ".mpt":"application\/vnd.ms-project" }, - { + { ".mpy":"application\/vnd.ibm.minipay" }, - { + { ".mqy":"application\/vnd.mobius.mqy" }, - { + { ".mrc":"application\/marc" }, - { + { ".ms":"text\/troff" }, - { + { ".mscml":"application\/mediaservercontrol+xml" }, - { + { ".mseed":"application\/vnd.fdsn.mseed" }, - { + { ".mseq":"application\/vnd.mseq" }, - { + { ".msf":"application\/vnd.epson.msf" }, - { + { ".msh":"model\/mesh" }, - { + { ".msi":"application\/x-msdownload" }, - { + { ".msl":"application\/vnd.mobius.msl" }, - { + { ".msty":"application\/vnd.muvee.style" }, - { + { ".mts":"model\/vnd.mts" }, - { + { ".mus":"application\/vnd.musician" }, - { + { ".musicxml":"application\/vnd.recordare.musicxml+xml" }, - { + { ".mvb":"application\/x-msmediaview" }, - { + { ".mwf":"application\/vnd.mfer" }, - { + { ".mxf":"application\/mxf" }, - { + { ".mxl":"application\/vnd.recordare.musicxml" }, - { + { ".mxml":"application\/xv+xml" }, - { + { ".mxs":"application\/vnd.triscape.mxs" }, - { + { ".mxu":"video\/vnd.mpegurl" }, - { + { ".n-gage":"application\/vnd.nokia.n-gage.symbian.install" }, - { + { ".nb":"application\/mathematica" }, - { + { ".nc":"application\/x-netcdf" }, - { + { ".ncx":"application\/x-dtbncx+xml" }, - { + { ".ngdat":"application\/vnd.nokia.n-gage.data" }, - { + { ".nlu":"application\/vnd.neurolanguage.nlu" }, - { + { ".nml":"application\/vnd.enliven" }, - { + { ".nnd":"application\/vnd.noblenet-directory" }, - { + { ".nns":"application\/vnd.noblenet-sealer" }, - { + { ".nnw":"application\/vnd.noblenet-web" }, - { + { ".npx":"image\/vnd.net-fpx" }, - { + { ".nsf":"application\/vnd.lotus-notes" }, - { + { ".nws":"message\/rfc822" }, - { + { ".o":"application\/octet-stream" }, - { + { ".oa2":"application\/vnd.fujitsu.oasys2" }, - { + { ".oa3":"application\/vnd.fujitsu.oasys3" }, - { + { ".oas":"application\/vnd.fujitsu.oasys" }, - { + { ".obd":"application\/x-msbinder" }, - { + { ".obj":"application\/octet-stream" }, - { + { ".oda":"application\/oda" }, - { + { ".odb":"application\/vnd.oasis.opendocument.database" }, - { + { ".odc":"application\/vnd.oasis.opendocument.chart" }, - { + { ".odf":"application\/vnd.oasis.opendocument.formula" }, - { + { ".odft":"application\/vnd.oasis.opendocument.formula-template" }, - { + { ".odg":"application\/vnd.oasis.opendocument.graphics" }, - { + { ".odi":"application\/vnd.oasis.opendocument.image" }, - { + { ".odp":"application\/vnd.oasis.opendocument.presentation" }, - { + { ".ods":"application\/vnd.oasis.opendocument.spreadsheet" }, - { + { ".odt":"application\/vnd.oasis.opendocument.text" }, - { + { ".oga":"audio\/ogg" }, - { + { ".ogg":"audio\/ogg" }, - { + { ".ogv":"video\/ogg" }, - { + { ".ogx":"application\/ogg" }, - { + { ".onepkg":"application\/onenote" }, - { + { ".onetmp":"application\/onenote" }, - { + { ".onetoc":"application\/onenote" }, - { + { ".onetoc2":"application\/onenote" }, - { + { ".opf":"application\/oebps-package+xml" }, - { + { ".oprc":"application\/vnd.palm" }, - { + { ".org":"application\/vnd.lotus-organizer" }, - { + { ".osf":"application\/vnd.yamaha.openscoreformat" }, - { + { ".osfpvg":"application\/vnd.yamaha.openscoreformat.osfpvg+xml" }, - { + { ".otc":"application\/vnd.oasis.opendocument.chart-template" }, - { + { ".otf":"application\/x-font-otf" }, - { + { ".otg":"application\/vnd.oasis.opendocument.graphics-template" }, - { + { ".oth":"application\/vnd.oasis.opendocument.text-web" }, - { + { ".oti":"application\/vnd.oasis.opendocument.image-template" }, - { + { ".otm":"application\/vnd.oasis.opendocument.text-master" }, - { + { ".otp":"application\/vnd.oasis.opendocument.presentation-template" }, - { + { ".ots":"application\/vnd.oasis.opendocument.spreadsheet-template" }, - { + { ".ott":"application\/vnd.oasis.opendocument.text-template" }, - { + { ".oxt":"application\/vnd.openofficeorg.extension" }, - { + { ".p":"text\/x-pascal" }, - { + { ".p10":"application\/pkcs10" }, - { + { ".p12":"application\/x-pkcs12" }, - { + { ".p7b":"application\/x-pkcs7-certificates" }, - { + { ".p7c":"application\/pkcs7-mime" }, - { + { ".p7m":"application\/pkcs7-mime" }, - { + { ".p7r":"application\/x-pkcs7-certreqresp" }, - { + { ".p7s":"application\/pkcs7-signature" }, - { + { ".pas":"text\/x-pascal" }, - { + { ".pbd":"application\/vnd.powerbuilder6" }, - { + { ".pbm":"image\/x-portable-bitmap" }, - { + { ".pcf":"application\/x-font-pcf" }, - { + { ".pcl":"application\/vnd.hp-pcl" }, - { + { ".pclxl":"application\/vnd.hp-pclxl" }, - { + { ".pct":"image\/x-pict" }, - { + { ".pcurl":"application\/vnd.curl.pcurl" }, - { + { ".pcx":"image\/x-pcx" }, - { + { ".pdb":"application\/vnd.palm" }, - { + { ".pdf":"application\/pdf" }, - { + { ".pfa":"application\/x-font-type1" }, - { + { ".pfb":"application\/x-font-type1" }, - { + { ".pfm":"application\/x-font-type1" }, - { + { ".pfr":"application\/font-tdpfr" }, - { + { ".pfx":"application\/x-pkcs12" }, - { + { ".pgm":"image\/x-portable-graymap" }, - { + { ".pgn":"application\/x-chess-pgn" }, - { + { ".pgp":"application\/pgp-encrypted" }, - { + { ".pic":"image\/x-pict" }, - { + { ".pkg":"application\/octet-stream" }, - { + { ".pki":"application\/pkixcmp" }, - { + { ".pkipath":"application\/pkix-pkipath" }, - { + { ".pl":"text\/plain" }, - { + { ".plb":"application\/vnd.3gpp.pic-bw-large" }, - { + { ".plc":"application\/vnd.mobius.plc" }, - { + { ".plf":"application\/vnd.pocketlearn" }, - { + { ".pls":"application\/pls+xml" }, - { + { ".pml":"application\/vnd.ctc-posml" }, - { + { ".png":"image\/png" }, - { + { ".pnm":"image\/x-portable-anymap" }, - { + { ".portpkg":"application\/vnd.macports.portpkg" }, - { + { ".pot":"application\/vnd.ms-powerpoint" }, - { + { ".potm":"application\/vnd.ms-powerpoint.template.macroenabled.12" }, - { + { ".potx":"application\/vnd.openxmlformats-officedocument.presentationml.template" }, - { + { ".ppa":"application\/vnd.ms-powerpoint" }, - { + { ".ppam":"application\/vnd.ms-powerpoint.addin.macroenabled.12" }, - { + { ".ppd":"application\/vnd.cups-ppd" }, - { + { ".ppm":"image\/x-portable-pixmap" }, - { + { ".pps":"application\/vnd.ms-powerpoint" }, - { + { ".ppsm":"application\/vnd.ms-powerpoint.slideshow.macroenabled.12" }, - { + { ".ppsx":"application\/vnd.openxmlformats-officedocument.presentationml.slideshow" }, - { + { ".ppt":"application\/vnd.ms-powerpoint" }, - { + { ".pptm":"application\/vnd.ms-powerpoint.presentation.macroenabled.12" }, - { + { ".pptx":"application\/vnd.openxmlformats-officedocument.presentationml.presentation" }, - { + { ".pqa":"application\/vnd.palm" }, - { + { ".prc":"application\/x-mobipocket-ebook" }, - { + { ".pre":"application\/vnd.lotus-freelance" }, - { + { ".prf":"application\/pics-rules" }, - { + { ".ps":"application\/postscript" }, - { + { ".psb":"application\/vnd.3gpp.pic-bw-small" }, - { + { ".psd":"image\/vnd.adobe.photoshop" }, - { + { ".psf":"application\/x-font-linux-psf" }, - { + { ".ptid":"application\/vnd.pvi.ptid1" }, - { + { ".pub":"application\/x-mspublisher" }, - { + { ".pvb":"application\/vnd.3gpp.pic-bw-var" }, - { + { ".pwn":"application\/vnd.3m.post-it-notes" }, - { + { ".pwz":"application\/vnd.ms-powerpoint" }, - { + { ".py":"text\/x-python" }, - { + { ".pya":"audio\/vnd.ms-playready.media.pya" }, - { + { ".pyc":"application\/x-python-code" }, - { + { ".pyo":"application\/x-python-code" }, - { + { ".pyv":"video\/vnd.ms-playready.media.pyv" }, - { + { ".qam":"application\/vnd.epson.quickanime" }, - { + { ".qbo":"application\/vnd.intu.qbo" }, - { + { ".qfx":"application\/vnd.intu.qfx" }, - { + { ".qps":"application\/vnd.publishare-delta-tree" }, - { + { ".qt":"video\/quicktime" }, - { + { ".qwd":"application\/vnd.quark.quarkxpress" }, - { + { ".qwt":"application\/vnd.quark.quarkxpress" }, - { + { ".qxb":"application\/vnd.quark.quarkxpress" }, - { + { ".qxd":"application\/vnd.quark.quarkxpress" }, - { + { ".qxl":"application\/vnd.quark.quarkxpress" }, - { + { ".qxt":"application\/vnd.quark.quarkxpress" }, - { + { ".ra":"audio\/x-pn-realaudio" }, - { + { ".ram":"audio\/x-pn-realaudio" }, - { + { ".rar":"application\/x-rar-compressed" }, - { + { ".ras":"image\/x-cmu-raster" }, - { + { ".rcprofile":"application\/vnd.ipunplugged.rcprofile" }, - { + { ".rdf":"application\/rdf+xml" }, - { + { ".rdz":"application\/vnd.data-vision.rdz" }, - { + { ".rep":"application\/vnd.businessobjects" }, - { + { ".res":"application\/x-dtbresource+xml" }, - { + { ".rgb":"image\/x-rgb" }, - { + { ".rif":"application\/reginfo+xml" }, - { + { ".rl":"application\/resource-lists+xml" }, - { + { ".rlc":"image\/vnd.fujixerox.edmics-rlc" }, - { + { ".rld":"application\/resource-lists-diff+xml" }, - { + { ".rm":"application\/vnd.rn-realmedia" }, - { + { ".rmi":"audio\/midi" }, - { + { ".rmp":"audio\/x-pn-realaudio-plugin" }, - { + { ".rms":"application\/vnd.jcp.javame.midlet-rms" }, - { + { ".rnc":"application\/relax-ng-compact-syntax" }, - { + { ".roff":"text\/troff" }, - { + { ".rpm":"application\/x-rpm" }, - { + { ".rpss":"application\/vnd.nokia.radio-presets" }, - { + { ".rpst":"application\/vnd.nokia.radio-preset" }, - { + { ".rq":"application\/sparql-query" }, - { + { ".rs":"application\/rls-services+xml" }, - { + { ".rsd":"application\/rsd+xml" }, - { + { ".rss":"application\/rss+xml" }, - { + { ".rtf":"application\/rtf" }, - { + { ".rtx":"text\/richtext" }, - { + { ".s":"text\/x-asm" }, - { + { ".saf":"application\/vnd.yamaha.smaf-audio" }, { ".sass":"text/x-sass" }, - { + { ".sbml":"application\/sbml+xml" }, - { + { ".sc":"application\/vnd.ibm.secure-container" }, - { + { ".scd":"application\/x-msschedule" }, - { + { ".scm":"application\/vnd.lotus-screencam" }, - { + { ".scq":"application\/scvp-cv-request" }, - { + { ".scs":"application\/scvp-cv-response" }, { ".scss":"text/x-scss" }, - { + { ".scurl":"text\/vnd.curl.scurl" }, - { + { ".sda":"application\/vnd.stardivision.draw" }, - { + { ".sdc":"application\/vnd.stardivision.calc" }, - { + { ".sdd":"application\/vnd.stardivision.impress" }, - { + { ".sdkd":"application\/vnd.solent.sdkm+xml" }, - { + { ".sdkm":"application\/vnd.solent.sdkm+xml" }, - { + { ".sdp":"application\/sdp" }, - { + { ".sdw":"application\/vnd.stardivision.writer" }, - { + { ".see":"application\/vnd.seemail" }, - { + { ".seed":"application\/vnd.fdsn.seed" }, - { + { ".sema":"application\/vnd.sema" }, - { + { ".semd":"application\/vnd.semd" }, - { + { ".semf":"application\/vnd.semf" }, - { + { ".ser":"application\/java-serialized-object" }, - { + { ".setpay":"application\/set-payment-initiation" }, - { + { ".setreg":"application\/set-registration-initiation" }, - { + { ".sfd-hdstx":"application\/vnd.hydrostatix.sof-data" }, - { + { ".sfs":"application\/vnd.spotfire.sfs" }, - { + { ".sgl":"application\/vnd.stardivision.writer-global" }, - { + { ".sgm":"text\/sgml" }, - { + { ".sgml":"text\/sgml" }, - { + { ".sh":"application\/x-sh" }, - { + { ".shar":"application\/x-shar" }, - { + { ".shf":"application\/shf+xml" }, - { + { ".si":"text\/vnd.wap.si" }, - { + { ".sic":"application\/vnd.wap.sic" }, - { + { ".sig":"application\/pgp-signature" }, - { + { ".silo":"model\/mesh" }, - { + { ".sis":"application\/vnd.symbian.install" }, - { + { ".sisx":"application\/vnd.symbian.install" }, - { + { ".sit":"application\/x-stuffit" }, - { + { ".sitx":"application\/x-stuffitx" }, - { + { ".skd":"application\/vnd.koan" }, - { + { ".skm":"application\/vnd.koan" }, - { + { ".skp":"application\/vnd.koan" }, - { + { ".skt":"application\/vnd.koan" }, - { + { ".sl":"text\/vnd.wap.sl" }, - { + { ".slc":"application\/vnd.wap.slc" }, - { + { ".sldm":"application\/vnd.ms-powerpoint.slide.macroenabled.12" }, - { + { ".sldx":"application\/vnd.openxmlformats-officedocument.presentationml.slide" }, - { + { ".slt":"application\/vnd.epson.salt" }, - { + { ".smf":"application\/vnd.stardivision.math" }, - { + { ".smi":"application\/smil+xml" }, - { + { ".smil":"application\/smil+xml" }, - { + { ".snd":"audio\/basic" }, - { + { ".snf":"application\/x-font-snf" }, - { + { ".so":"application\/octet-stream" }, - { + { ".spc":"application\/x-pkcs7-certificates" }, - { + { ".spf":"application\/vnd.yamaha.smaf-phrase" }, - { + { ".spl":"application\/x-futuresplash" }, - { + { ".spot":"text\/vnd.in3d.spot" }, - { + { ".spp":"application\/scvp-vp-response" }, - { + { ".spq":"application\/scvp-vp-request" }, - { + { ".spx":"audio\/ogg" }, - { + { ".src":"application\/x-wais-source" }, - { + { ".srx":"application\/sparql-results+xml" }, - { + { ".sse":"application\/vnd.kodak-descriptor" }, - { + { ".ssf":"application\/vnd.epson.ssf" }, - { + { ".ssml":"application\/ssml+xml" }, - { + { ".stc":"application\/vnd.sun.xml.calc.template" }, - { + { ".std":"application\/vnd.sun.xml.draw.template" }, - { + { ".stf":"application\/vnd.wt.stf" }, - { + { ".sti":"application\/vnd.sun.xml.impress.template" }, - { + { ".stk":"application\/hyperstudio" }, - { + { ".stl":"application\/vnd.ms-pki.stl" }, - { + { ".str":"application\/vnd.pg.format" }, - { + { ".stw":"application\/vnd.sun.xml.writer.template" }, - { + { ".sus":"application\/vnd.sus-calendar" }, - { + { ".susp":"application\/vnd.sus-calendar" }, - { + { ".sv4cpio":"application\/x-sv4cpio" }, - { + { ".sv4crc":"application\/x-sv4crc" }, - { + { ".svd":"application\/vnd.svd" }, - { + { ".svg":"image\/svg+xml" }, - { + { ".svgz":"image\/svg+xml" }, - { + { ".swa":"application\/x-director" }, - { + { ".swf":"application\/x-shockwave-flash" }, - { + { ".swi":"application\/vnd.arastra.swi" }, - { + { ".sxc":"application\/vnd.sun.xml.calc" }, - { + { ".sxd":"application\/vnd.sun.xml.draw" }, - { + { ".sxg":"application\/vnd.sun.xml.writer.global" }, - { + { ".sxi":"application\/vnd.sun.xml.impress" }, - { + { ".sxm":"application\/vnd.sun.xml.math" }, - { + { ".sxw":"application\/vnd.sun.xml.writer" }, - { + { ".t":"text\/troff" }, - { + { ".tao":"application\/vnd.tao.intent-module-archive" }, - { + { ".tar":"application\/x-tar" }, - { + { ".tcap":"application\/vnd.3gpp2.tcap" }, - { + { ".tcl":"application\/x-tcl" }, - { + { ".teacher":"application\/vnd.smart.teacher" }, - { + { ".tex":"application\/x-tex" }, - { + { ".texi":"application\/x-texinfo" }, - { + { ".texinfo":"application\/x-texinfo" }, - { + { ".text":"text\/plain" }, - { + { ".tfm":"application\/x-tex-tfm" }, - { + { ".tgz":"application\/x-gzip" }, - { + { ".tif":"image\/tiff" }, - { + { ".tiff":"image\/tiff" }, - { + { ".tmo":"application\/vnd.tmobile-livetv" }, - { + { ".torrent":"application\/x-bittorrent" }, - { + { ".tpl":"application\/vnd.groove-tool-template" }, - { + { ".tpt":"application\/vnd.trid.tpt" }, - { + { ".tr":"text\/troff" }, - { + { ".tra":"application\/vnd.trueapp" }, - { + { ".trm":"application\/x-msterminal" }, - { + { ".tsv":"text\/tab-separated-values" }, - { + { ".ttc":"application\/x-font-ttf" }, - { + { ".ttf":"application\/x-font-ttf" }, - { + { ".twd":"application\/vnd.simtech-mindmapper" }, - { + { ".twds":"application\/vnd.simtech-mindmapper" }, - { + { ".txd":"application\/vnd.genomatix.tuxedo" }, - { + { ".txf":"application\/vnd.mobius.txf" }, - { + { ".txt":"text\/plain" }, - { + { ".u32":"application\/x-authorware-bin" }, - { + { ".udeb":"application\/x-debian-package" }, - { + { ".ufd":"application\/vnd.ufdl" }, - { + { ".ufdl":"application\/vnd.ufdl" }, - { + { ".umj":"application\/vnd.umajin" }, - { + { ".unityweb":"application\/vnd.unity" }, - { + { ".uoml":"application\/vnd.uoml+xml" }, - { + { ".uri":"text\/uri-list" }, - { + { ".uris":"text\/uri-list" }, - { + { ".urls":"text\/uri-list" }, - { + { ".ustar":"application\/x-ustar" }, - { + { ".utz":"application\/vnd.uiq.theme" }, - { + { ".uu":"text\/x-uuencode" }, - { + { ".vcd":"application\/x-cdlink" }, - { + { ".vcf":"text\/x-vcard" }, - { + { ".vcg":"application\/vnd.groove-vcard" }, - { + { ".vcs":"text\/x-vcalendar" }, - { + { ".vcx":"application\/vnd.vcx" }, - { + { ".vis":"application\/vnd.visionary" }, - { + { ".viv":"video\/vnd.vivo" }, - { + { ".vor":"application\/vnd.stardivision.writer" }, - { + { ".vox":"application\/x-authorware-bin" }, - { + { ".vrml":"model\/vrml" }, - { + { ".vsd":"application\/vnd.visio" }, - { + { ".vsf":"application\/vnd.vsf" }, - { + { ".vss":"application\/vnd.visio" }, - { + { ".vst":"application\/vnd.visio" }, - { + { ".vsw":"application\/vnd.visio" }, - { + { ".vtu":"model\/vnd.vtu" }, - { + { ".vxml":"application\/voicexml+xml" }, - { + { ".w3d":"application\/x-director" }, - { + { ".wad":"application\/x-doom" }, - { + { ".wav":"audio\/x-wav" }, - { + { ".wax":"audio\/x-ms-wax" }, - { + { ".wbmp":"image\/vnd.wap.wbmp" }, - { + { ".wbs":"application\/vnd.criticaltools.wbs+xml" }, - { + { ".wbxml":"application\/vnd.wap.wbxml" }, - { + { ".wcm":"application\/vnd.ms-works" }, - { + { ".wdb":"application\/vnd.ms-works" }, - { + { ".wiz":"application\/msword" }, - { + { ".wks":"application\/vnd.ms-works" }, - { + { ".wm":"video\/x-ms-wm" }, - { + { ".wma":"audio\/x-ms-wma" }, - { + { ".wmd":"application\/x-ms-wmd" }, - { + { ".wmf":"application\/x-msmetafile" }, - { + { ".wml":"text\/vnd.wap.wml" }, - { + { ".wmlc":"application\/vnd.wap.wmlc" }, - { + { ".wmls":"text\/vnd.wap.wmlscript" }, - { + { ".wmlsc":"application\/vnd.wap.wmlscriptc" }, - { + { ".wmv":"video\/x-ms-wmv" }, - { + { ".wmx":"video\/x-ms-wmx" }, - { + { ".wmz":"application\/x-ms-wmz" }, - { + { ".wpd":"application\/vnd.wordperfect" }, - { + { ".wpl":"application\/vnd.ms-wpl" }, - { + { ".wps":"application\/vnd.ms-works" }, - { + { ".wqd":"application\/vnd.wqd" }, - { + { ".wri":"application\/x-mswrite" }, - { + { ".wrl":"model\/vrml" }, - { + { ".wsdl":"application\/wsdl+xml" }, - { + { ".wspolicy":"application\/wspolicy+xml" }, - { + { ".wtb":"application\/vnd.webturbo" }, - { + { ".wvx":"video\/x-ms-wvx" }, - { + { ".x32":"application\/x-authorware-bin" }, - { + { ".x3d":"application\/vnd.hzn-3d-crossword" }, - { + { ".xap":"application\/x-silverlight-app" }, - { + { ".xar":"application\/vnd.xara" }, - { + { ".xbap":"application\/x-ms-xbap" }, - { + { ".xbd":"application\/vnd.fujixerox.docuworks.binder" }, - { + { ".xbm":"image\/x-xbitmap" }, - { + { ".xdm":"application\/vnd.syncml.dm+xml" }, - { + { ".xdp":"application\/vnd.adobe.xdp+xml" }, - { + { ".xdw":"application\/vnd.fujixerox.docuworks" }, - { + { ".xenc":"application\/xenc+xml" }, - { + { ".xer":"application\/patch-ops-error+xml" }, - { + { ".xfdf":"application\/vnd.adobe.xfdf" }, - { + { ".xfdl":"application\/vnd.xfdl" }, - { + { ".xht":"application\/xhtml+xml" }, - { + { ".xhtml":"application\/xhtml+xml" }, - { + { ".xhvml":"application\/xv+xml" }, - { + { ".xif":"image\/vnd.xiff" }, - { + { ".xla":"application\/vnd.ms-excel" }, - { + { ".xlam":"application\/vnd.ms-excel.addin.macroenabled.12" }, - { + { ".xlb":"application\/vnd.ms-excel" }, - { + { ".xlc":"application\/vnd.ms-excel" }, - { + { ".xlm":"application\/vnd.ms-excel" }, - { + { ".xls":"application\/vnd.ms-excel" }, - { + { ".xlsb":"application\/vnd.ms-excel.sheet.binary.macroenabled.12" }, - { + { ".xlsm":"application\/vnd.ms-excel.sheet.macroenabled.12" }, - { + { ".xlsx":"application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, - { + { ".xlt":"application\/vnd.ms-excel" }, - { + { ".xltm":"application\/vnd.ms-excel.template.macroenabled.12" }, - { + { ".xltx":"application\/vnd.openxmlformats-officedocument.spreadsheetml.template" }, - { + { ".xlw":"application\/vnd.ms-excel" }, - { + { ".xml":"application\/xml" }, - { + { ".xo":"application\/vnd.olpc-sugar" }, - { + { ".xop":"application\/xop+xml" }, - { + { ".xpdl":"application\/xml" }, - { + { ".xpi":"application\/x-xpinstall" }, - { + { ".xpm":"image\/x-xpixmap" }, - { + { ".xpr":"application\/vnd.is-xpr" }, - { + { ".xps":"application\/vnd.ms-xpsdocument" }, - { + { ".xpw":"application\/vnd.intercon.formnet" }, - { + { ".xpx":"application\/vnd.intercon.formnet" }, - { + { ".xsl":"application\/xml" }, - { + { ".xslt":"application\/xslt+xml" }, - { + { ".xsm":"application\/vnd.syncml+xml" }, - { + { ".xspf":"application\/xspf+xml" }, - { + { ".xul":"application\/vnd.mozilla.xul+xml" }, - { + { ".xvm":"application\/xv+xml" }, - { + { ".xvml":"application\/xv+xml" }, - { + { ".xwd":"image\/x-xwindowdump" }, - { + { ".xyz":"chemical\/x-xyz" }, - { + { ".zaz":"application\/vnd.zzazz.deck+xml" }, - { + { ".zip":"application\/zip" }, - { + { ".zir":"application\/vnd.zul" }, - { + { ".zirz":"application\/vnd.zul" }, - { + { ".zmm":"application\/vnd.handheld-entertainment+xml" } ]
M src/litestorepkg/lib/contenttypes.nimsrc/litestorepkg/lib/contenttypes.nim

@@ -9,7 +9,7 @@ for item in json.items:

for pair in item.pairs: result[$pair.key] = $pair.val.getStr -var CONTENT_TYPES* {.threadvar.}: StringTableRef +var CONTENT_TYPES* {.threadvar.}: StringTableRef CONTENT_TYPES = loadContentTypes()
M src/litestorepkg/lib/core.nimsrc/litestorepkg/lib/core.nim

@@ -1,11 +1,11 @@

-import - x_sqlite3, +import + x_sqlite3, x_db_sqlite as db, - strutils, + strutils, os, oids, json, - pegs, + pegs, strtabs, strutils, base64,

@@ -27,13 +27,13 @@ db.exec SQL_CREATE_INDEX_DOCUMENTS_ID

db.exec SQL_CREATE_INDEX_TAGS_TAG_ID db.exec SQL_CREATE_INDEX_TAGS_DOCUMENT_ID -proc dropIndexes(db: DbConn) = +proc dropIndexes(db: DbConn) = db.exec SQL_DROP_INDEX_DOCUMENTS_DOCID db.exec SQL_DROP_INDEX_DOCUMENTS_ID db.exec SQL_DROP_INDEX_TAGS_TAG_ID db.exec SQL_DROP_INDEX_TAGS_DOCUMENT_ID -proc createDatastore*(file:string) = +proc createDatastore*(file:string) = if file.fileExists(): raise newException(EDatastoreExists, "Datastore '$1' already exists." % file) LOG.debug("Creating datastore '$1'", file)

@@ -48,7 +48,7 @@ LOG.debug("Creating indexes")

data.createIndexes() LOG.debug("Database created") -proc closeDatastore*(store: Datastore) = +proc closeDatastore*(store: Datastore) = try: db.close(store.db) except:

@@ -196,7 +196,7 @@ var data = rawdata

if contenttype == "application/json": # Validate JSON data try: - discard data.parseJson + discard data.parseJson except: raise newException(JsonParsingError, "Invalid JSON content - " & getCurrentExceptionMsg()) if id == "":

@@ -228,7 +228,7 @@ store.commit()

return $store.retrieveRawDocument(id) except: store.rollback() - eWarn() + eWarn() raise proc updateDocument*(store: Datastore, id: string, rawdata: string, contenttype = "text/plain", binary = -1, searchable = 1): string =

@@ -239,7 +239,7 @@ var data = rawdata

if contenttype == "application/json": # Validate JSON data try: - discard data.parseJson + discard data.parseJson except: raise newException(JsonParsingError, "Invalid JSON content - " & getCurrentExceptionMsg()) var searchable = searchable

@@ -384,7 +384,7 @@ raise newException(EDirectoryNotFound, "Directory '$1' not found." % dir)

for f in dir.walkDirRec(): if f.existsDir: continue - if f.splitFile.name.startsWith("."): + if f.splitFile.name.startsWith("."): # Ignore hidden files continue files.add(f)

@@ -397,7 +397,7 @@ store.begin()

LOG.info("Importing $1 files in $2 batches", files.len, nBatches) LOG.debug("Dropping column indexes...") store.db.dropIndexes() - for f in files: + for f in files: try: store.importFile(f, dir) cFiles.inc
M src/litestorepkg/lib/logger.nimsrc/litestorepkg/lib/logger.nim

@@ -1,4 +1,4 @@

-import +import strutils, times

@@ -18,19 +18,19 @@ stderr.writeLine(currentTime(true) & " " & kind & ": " & s)

else: echo currentTime(true), " ", kind, ": ", s -proc error*(logger: Logger, message: string, params: varargs[string, `$`]) = +proc error*(logger: Logger, message: string, params: varargs[string, `$`]) = if logger.level <= lvError: logger.msg(" ERROR", message, params) -proc warn*(logger: Logger, message: string, params: varargs[string, `$`]) = +proc warn*(logger: Logger, message: string, params: varargs[string, `$`]) = if logger.level <= lvWarn: logger.msg("WARNING", message, params) -proc info*(logger: Logger, message: string, params: varargs[string, `$`]) = +proc info*(logger: Logger, message: string, params: varargs[string, `$`]) = if logger.level <= lvInfo: logger.msg(" INFO", message, params) -proc debug*(logger: Logger, message: string, params: varargs[string, `$`]) = +proc debug*(logger: Logger, message: string, params: varargs[string, `$`]) = if logger.level <= lvDebug: logger.msg(" DEBUG", message, params)
M src/litestorepkg/lib/queries.nimsrc/litestorepkg/lib/queries.nim

@@ -20,21 +20,21 @@ SQL_CREATE_INDEX_DOCUMENTS_ID* = sql"CREATE INDEX IF NOT EXISTS documents_id ON documents(id)"

SQL_CREATE_INDEX_TAGS_DOCUMENT_ID* = sql"CREATE INDEX IF NOT EXISTS tags_document_id ON tags(document_id)" SQL_CREATE_INDEX_TAGS_TAG_ID* = sql"CREATE INDEX IF NOT EXISTS tags_tag_id ON tags(tag_id)" - SQL_DROP_INDEX_DOCUMENTS_DOCID* = sql"DROP INDEX IF EXISTS documents_docid" + SQL_DROP_INDEX_DOCUMENTS_DOCID* = sql"DROP INDEX IF EXISTS documents_docid" SQL_DROP_INDEX_DOCUMENTS_ID* = sql"DROP INDEX IF EXISTS documents_id" SQL_DROP_INDEX_TAGS_DOCUMENT_ID* = sql"DROP INDEX IF EXISTS tags_document_id" SQL_DROP_INDEX_TAGS_TAG_ID* = sql"DROP INDEX IF EXISTS tags_tag_id" - + SQL_REINDEX* = sql"REINDEX" SQL_OPTIMIZE* = sql"INSERT INTO searchdata(searchdata) VALUES('optimize')" SQL_REBUILD* = sql"INSERT INTO searchdata(searchdata) VALUES('rebuild')" - + SQL_VACUUM* = sql"VACUUM" const SQL_CREATE_SEARCHDATA_TABLE* = sql""" CREATE VIRTUAL TABLE searchdata USING fts4( id TEXT UNIQUE NOT NULL, -data TEXT, +data TEXT, tokenize=porter) """

@@ -102,7 +102,7 @@ """

const SQL_DELETE_DOCUMENT* = sql""" DELETE FROM documents -WHERE id = ? +WHERE id = ? """ const SQL_INSERT_TAG* = sql"""

@@ -161,32 +161,32 @@ tag_id = ?

""" const SQL_SELECT_TAGS_WITH_TOTALS* = sql""" -SELECT DISTINCT tag_id, COUNT(document_id) +SELECT DISTINCT tag_id, COUNT(document_id) FROM tags GROUP BY tag_id ORDER BY tag_id ASC """ const SQL_COUNT_TAGS* = sql""" -SELECT COUNT(DISTINCT tag_id) FROM tags +SELECT COUNT(DISTINCT tag_id) FROM tags """ const SQL_COUNT_DOCUMENTS* = sql""" -SELECT COUNT(docid) FROM documents +SELECT COUNT(docid) FROM documents """ const SQL_DELETE_DOCUMENTS_BY_TAG* = sql""" DELETE FROM documents -WHERE documents.id IN +WHERE documents.id IN (SELECT document_id FROM tags WHERE tag_id = ?) """ const SQL_DELETE_SEARCHDATA_BY_TAG* = sql""" DELETE FROM searchdata -WHERE id IN +WHERE id IN (SELECT document_id FROM tags WHERE tag_id = ?) """ const SQL_DELETE_TAGS_BY_TAG* = sql""" DELETE FROM tags -WHERE document_id IN +WHERE document_id IN (SELECT document_id FROM tags WHERE tag_id = ?) """
M src/litestorepkg/lib/server.nimsrc/litestorepkg/lib/server.nim

@@ -1,20 +1,20 @@

-import +import asynchttpserver, - asyncdispatch, - times, - strutils, - pegs, + asyncdispatch, + times, + strutils, + pegs, logger, cgi -import - types, - utils, +import + types, + utils, api_v1, api_v2, api_v3, api_v4 -export +export api_v4 proc getReqInfo(req: LSRequest): string =

@@ -30,7 +30,7 @@ echo ""

LOG.info("Exiting...") quit() -proc processApiUrl(req: LSRequest, LS: LiteStore, info: ResourceInfo): LSResponse = +proc processApiUrl(req: LSRequest, LS: LiteStore, info: ResourceInfo): LSResponse = if info.version == "v4": if info.resource.match(peg"^docs / info / tags$"): return api_v4.route(req, LS, info.resource, info.id)

@@ -61,7 +61,7 @@ else:

return resError(Http400, "Bad Request - Not serving any directory." % info.version) else: return resError(Http404, "Resource Not Found: $1" % info.resource) - elif info.version == "v1": + elif info.version == "v1": if info.resource.match(peg"^docs / info$"): return api_v1.route(req, LS, info.resource, info.id) elif info.resource.match(peg"^dir$"):

@@ -85,7 +85,7 @@ var matches = @["", "", ""]

template route(req: LSRequest, peg: Peg, op: untyped): untyped = if req.url.path.find(peg, matches) != -1: op - try: + try: var info: ResourceInfo req.route peg"^\/?$": info.version = "v4"

@@ -129,4 +129,3 @@ echo(LS.appname & " v" & LS.appversion & " started on " & LS.address & ":" & $LS.port & ".")

if LS.mount: echo("Mirroring datastore changes to: " & LS.directory) asyncCheck server.serve(LS.port.Port, handleHttpRequest, LS.address) -
M src/litestorepkg/lib/types.nimsrc/litestorepkg/lib/types.nim

@@ -1,11 +1,11 @@

-import - x_db_sqlite, - asynchttpserver, +import + x_db_sqlite, + asynchttpserver, pegs import config -type +type EDatastoreExists* = object of Exception EDatastoreDoesNotExist* = object of Exception EDatastoreUnavailable* = object of Exception

@@ -14,7 +14,6 @@ EDirectoryNotFound* = object of Exception

EFileNotFound* = object of Exception EFileExists* = object of Exception EInvalidRequest* = object of Exception - uarray* [T] = array[0..0, T] ExecutionData* = object operation*: string file*: string

@@ -30,10 +29,10 @@ tables*: seq[string]

jsonFilter*: string jsonSelect*: seq[tuple[path: string, alias: string]] select*: seq[string] - single*:bool - limit*: int - offset*: int - orderby*: string + single*:bool + limit*: int + offset*: int + orderby*: string tags*: string like*: string createdAfter*: string

@@ -47,10 +46,10 @@ tag*: string

startswith*: bool endswith*: bool negated*: bool - Operation* = enum - opRun, - opImport, - opExport, + Operation* = enum + opRun, + opImport, + opExport, opDelete, opVacuum, opOptimize,

@@ -88,7 +87,7 @@ id: string,

version: string ] -var +var PEG_TAG* {.threadvar.}: Peg PEG_USER_TAG* {.threadvar.}: Peg PEG_DEFAULT_URL* {.threadvar.}: Peg

@@ -113,6 +112,6 @@ "Server": LS.appname & "/" & LS.appversion

} proc newQueryOptions*(): QueryOptions = - return QueryOptions(select: @["documents.id AS id", "documents.data AS data", "content_type", "binary", "searchable", "created", "modified"], - single: false, limit: 0, offset: 0, orderby: "", tags: "", search: "", folder: "", like: "", + return QueryOptions(select: @["documents.id AS id", "documents.data AS data", "content_type", "binary", "searchable", "created", "modified"], + single: false, limit: 0, offset: 0, orderby: "", tags: "", search: "", folder: "", like: "", createdAfter: "", createdBefore: "", modifiedAfter: "", modifiedBefore: "", jsonFilter: "", jsonSelect: newSeq[tuple[path: string, alias: string]](), tables: newSeq[string]())
M src/litestorepkg/lib/utils.nimsrc/litestorepkg/lib/utils.nim

@@ -1,20 +1,20 @@

-import +import x_sqlite3, - x_db_sqlite, + x_db_sqlite, json, - strutils, - pegs, + strutils, + pegs, asynchttpserver, - math, + math, sequtils -import - types, - queries, - contenttypes, +import + types, + queries, + contenttypes, logger -proc setOrigin*(LS: LiteStore, req: LSRequest, headers: var HttpHeaders) = +proc setOrigin*(LS: LiteStore, req: LSRequest, headers: var HttpHeaders) = var host = "" var port = "" var protocol = "http"

@@ -54,7 +54,7 @@ for tag in tags.split(','):

if not tag.match(PEG_TAG): raise newException(EInvalidTag, "Invalid Tag '$1'" % tag) result = result & "AND " & doc_id_col & " IN (" & select_tagged & tag & "') " - + proc prepareSelectDocumentsQuery*(options: var QueryOptions): string = var tables = options.tables result = "SELECT "

@@ -66,11 +66,11 @@ options.tags = options.tags.split(",").concat(@["$subtype:json"]).join(",")

if options.search.len > 0: if options.select[0] != "COUNT(docid)": let rank = "rank(matchinfo(searchdata, 'pcxnal'), 1.20, 0.75, 5.0, 0.5) AS rank" - let snippet = "snippet(searchdata, \"<strong>\", \"</strong>\", \"<strong>&hellip;</strong>\", -1, 30) as highlight" + let snippet = "snippet(searchdata, \"<strong>\", \"</strong>\", \"<strong>&hellip;</strong>\", -1, 30) as highlight" options.select.add(snippet) options.select.add("ranktable.rank AS rank") options.orderby = "rank DESC" - # Create inner select + # Create inner select var innerSelect = "SELECT docid, " & rank & " FROM searchdata WHERE searchdata MATCH '" & options.search.replace("'", "''") & "' " if options.tags.len > 0: innerSelect = innerSelect & options.tags.selectDocumentsByTags()

@@ -118,14 +118,14 @@ result = result & "AND " & options.jsonFilter

if options.search.len > 0: result = result & "AND searchdata MATCH '" & options.search.replace("'", "''") & "' " if options.orderby.len > 0 and options.select[0] != "COUNT(docid)": - result = result & "ORDER BY " & options.orderby & " " - if options.limit > 0 and options.search.len == 0: + result = result & "ORDER BY " & options.orderby & " " + if options.limit > 0 and options.search.len == 0: # If searching, do not add limit to the outer select, it's already in the nested select (ranktable) result = result & "LIMIT " & $options.limit & " " if options.offset > 0: result = result & "OFFSET " & $options.offset & " " LOG.debug(result.replace("$", "$$")) - + proc prepareSelectTagsQuery*(options: QueryOptions): string = var group = true if options.select.len > 0 and options.select[0] == "COUNT(tag_id)":

@@ -139,9 +139,9 @@ if options.single:

result = result & "WHERE tag_id = ?" elif options.like.len > 0: if options.like[options.like.len-1] == '*': - result = result & "WHERE tag_id BETWEEN ? AND ? " + result = result & "WHERE tag_id BETWEEN ? AND ? " else: - result = result & "WHERE tag_id LIKE ? " + result = result & "WHERE tag_id LIKE ? " if group: result = result & "GROUP BY tag_id " if options.limit > 0:

@@ -239,7 +239,7 @@ tags.add "$format:text"

for tag in tags: store.db.exec(SQL_INSERT_TAG, tag, docid) -proc destroyDocumentSystemTags*(store: Datastore, docid: string) = +proc destroyDocumentSystemTags*(store: Datastore, docid: string) = discard store.db.execAffectedRows(SQL_DELETE_DOCUMENT_SYSTEM_TAGS, docid) proc fail*(code: int, msg: string) =

@@ -251,7 +251,7 @@ var h = newHttpHeaders(TAB_HEADERS)

h["Content-Type"] = ct return h -proc ctJsonHeader*(): HttpHeaders = +proc ctJsonHeader*(): HttpHeaders = return ctHeader("application/json") proc resError*(code: HttpCode, message: string, trace = ""): LSResponse =

@@ -273,7 +273,7 @@ var e = getCurrentException()

LOG.warn(e.msg) LOG.debug(getStackTrace(e)) -proc validate*(req: Request, LS: LiteStore, resource: string, id: string, cb: proc(req: Request, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = +proc validate*(req: Request, LS: LiteStore, resource: string, id: string, cb: proc(req: Request, LS: LiteStore, resource: string, id: string):LSResponse): LSResponse = if req.reqMethod == HttpPost or req.reqMethod == HttpPut or req.reqMethod == HttpPatch: var ct = "" let body = req.body.strip

@@ -306,7 +306,7 @@ # Setting the default values and ignoring argument based inputs so the extra

# arguments can be the column weights instead. if nVal < 2: pCtx.result_error("wrong number of arguments to function okapi_bm25_kb(), expected k1 parameter", -1) - if nVal < 3: + if nVal < 3: pCtx.result_error("wrong number of arguments to function okapi_bm25_kb(), expected b parameter", -1); let K1 = value_double(apVal[1]) # 1.2 let B = value_double(apVal[2]) # 0.75

@@ -323,8 +323,8 @@ let N_OFFSET = X_OFFSET + 3*termCount*colCount

let A_OFFSET = N_OFFSET + 1 let L_OFFSET = A_OFFSET + colCount let totalDocs = matchinfo[N_OFFSET].float - var avgLength:float = 0.0 - var docLength:float = 0.0 + var avgLength:float = 0.0 + var docLength:float = 0.0 for col in 0..colCount-1: avgLength = avgLength + matchinfo[A_OFFSET + col].float docLength = docLength + matchinfo[L_OFFSET + col].float
M test/http_api.nimtest/http_api.nim

@@ -133,7 +133,7 @@ var ops = %*[

{"op": "remove", "path": "/data/name/first"}, {"op": "add", "path": "/data/test", "value": 111}, {"op": "replace", "path": "/data/friends/0", "value": {"id": 11, "name": "Tom Paris"}} - ] + ] var rpatch = jpatch("docs/" & ids[0], ops) var data = rpatch.body.parseJson["data"] check(data["name"] == %*{"last": "Walters"})

@@ -143,7 +143,7 @@ ops = %*[

{"op": "add", "path": "/data/not_added", "value": "!!!"}, {"op": "test", "path": "/data/test", "value": 222}, {"op": "replace", "path": "/data/test", "value": "!!!"} - ] + ] rpatch = jpatch("docs/" & ids[0], ops) data = rpatch.body.parseJson["data"] check(data["test"] == %111)

@@ -152,7 +152,7 @@ ops = %*[

{"op": "replace", "path": "/data/test", "value": 222}, {"op": "test", "path": "/data/test", "value": 222}, {"op": "add", "path": "/data/not_added", "value": "!!!"} - ] + ] rpatch = jpatch("docs/" & ids[0], ops) data = rpatch.body.parseJson["data"] check(data["test"] == %111)