{"_id":"pintura","_rev":"68-82b143da7d161970b7b3f2ef323372d2","name":"pintura","description":"JSGI-based RESTful JSON/JavaScript server","dist-tags":{"latest":"0.3.10"},"versions":{"0.2.6":{"name":"pintura","version":"0.2.6","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","commonjs-utils":"http://github.com/kriszyp/commonjs-utils/zipball/v0.2.2","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"git://github.com/kriszyp/tunguska.git"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"tunguska":">0.2.4","rql":">0.2.2","websocket-server":">=1.4.01","perstore":">0.2.4","promised-io":">0.2.3","commonjs-utils":">0.2.2","multi-node":">0.2.3","jsgi-node":">0.2.5"},"devDependencies":{"patr":">0.2.6"},"_npmUser":{"name":"dojofoundation","email":"kzyp@dojofoundation.org"},"_id":"pintura@0.2.6","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.104","_nodeVersion":"v0.6.0","_defaultsLoaded":true,"dist":{"shasum":"dc9e844c8b7a9ace0f09cbeeb0de91c996b3e304","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.2.6.tgz","integrity":"sha512-OVzrgLzmvANm2MfqJK45QSVDdw5yBRv1a03CU0IQTcOcXQmMxo+m62U6boLhHGOgN/ohpebLqtX4yxlGRB7Isw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF+FTnuWpu+1UxDTNnFc8ptPWEEYe9Sjvt7GKH35UpaDAiEA7adEI3whpLuipg+tQwU5PHhA+MDW910FOVWJfyv/OaU="}]}},"0.3.1":{"name":"pintura","version":"0.3.1","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"git://github.com/kriszyp/tunguska.git"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"tunguska":">=0.3.0","rql":">=0.3.1","websocket-server":">=1.4.01","perstore":">=0.3.0","promised-io":">=0.3.0","formidable":">=1.0.0","templify":">=0.9.0","jsgi-node":">=0.2.5"},"devDependencies":{"patr":">=0.2.6"},"_npmUser":{"name":"dojofoundation","email":"kzyp@dojofoundation.org"},"_id":"pintura@0.3.1","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.104","_nodeVersion":"v0.6.0","_defaultsLoaded":true,"dist":{"shasum":"a8d6250caf5c39a01920bacb3e6570254330d283","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.1.tgz","integrity":"sha512-PU7v/zhrJ/QnHT+ocgZc0dtc0Cnw4idWqGNishUWw1yCiz2nt/S473i6PffFNiF3JErrS2wbPgEBN/VDmP3dzg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCF+YGyLTXl37U2byTBQbzz9Hpy7tO1kGy2WqlEMPJMVQIhAKYWKboL7cPlcjTqOz0Vv5GkIqAU+lJpDAsi2A1IicFP"}]}},"0.3.2":{"name":"pintura","version":"0.3.2","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"http://github.com/persvr/pintura"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"tunguska":">=0.3.0","rql":">=0.3.1","websocket-server":">=1.4.01","perstore":">=0.3.0","promised-io":">=0.3.0","formidable":">=1.0.0","templify":">=0.9.0","jsgi-node":">=0.2.5"},"devDependencies":{"patr":">=0.2.6"},"bugs":{"url":"https://github.com/persvr/pintura/issues"},"_id":"pintura@0.3.2","dist":{"shasum":"027718717854e03e5b4b4506c8c9df1424dbb2e0","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.2.tgz","integrity":"sha512-Wf0KoYB/uhrNkho266aX7JHrEmyG6bdqC36VY75esYQ9jZPMGA0wPe8VKQe5gLAww4VgVKgdAp4YqEaZ/ivwDQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjUQL7tH1CW9qC4SDLHJ8vaFWY79Ii0h1jRijlftp3MQIgDLZeejpGWBM7BfrgUnQvlhXpe2hlbVGkPDXiQcoVbyA="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"}},"0.3.3":{"name":"pintura","version":"0.3.3","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"http://github.com/persvr/pintura"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"tunguska":">=0.3.0","rql":">=0.3.1","websocket-server":">=1.4.01","perstore":">=0.3.0","promised-io":">=0.3.0","formidable":">=1.0.0","templify":">=0.9.0","jsgi-node":">=0.2.5"},"devDependencies":{"patr":">=0.2.6"},"bugs":{"url":"https://github.com/persvr/pintura/issues"},"_id":"pintura@0.3.3","dist":{"shasum":"93c00ae83e0f90cb06b7119d0e71a6c880d3883a","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.3.tgz","integrity":"sha512-zW6rgIl71EyZR4ONIX3DLrjKwjgN20WO/Sb6Hn5gGjnPGv54L+wwGRfTOuFikhGqhEp/6M36TPntjuLsKtFpWA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDrKRWyQ6/vadJUYCjt08525GodSayeXk2qjsRo9wP66AIgIEYFp+fdE7XV5J8XGF979/1R/RMOQVTGfuboUCvt8Ps="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"}},"0.3.4":{"name":"pintura","version":"0.3.4","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"http://github.com/persvr/pintura"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"tunguska":">=0.3.0","rql":">=0.3.1","websocket-server":">=1.4.01","perstore":">=0.3.0","promised-io":">=0.3.0","formidable":">=1.0.0","templify":">=0.9.0","jsgi-node":">=0.2.5"},"devDependencies":{"patr":">=0.2.6"},"bugs":{"url":"https://github.com/persvr/pintura/issues"},"_id":"pintura@0.3.4","dist":{"shasum":"3d1c7a62f74b7bd297e73bca9f93758c34a17666","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.4.tgz","integrity":"sha512-7wSFOBLhD7wxQUFc+fPXxTPxszdbSpI8nofx6DhvgmLhIR8+SfPTndzVOcE1p2fC+dgHgNTZihZBRvA19wwZ+g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID1pNWX1dqngV/YEVCBUPv1Nfmk6c4uiS5bodVNi/yv8AiBKW2ZZxS3/cI3soX5Y4uLKWggD91yFr/E1LBpeaViRqA=="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"}},"0.3.5":{"name":"pintura","version":"0.3.5","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"http://github.com/persvr/pintura"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"tunguska":">=0.3.0","rql":">=0.3.1","websocket-server":">=1.4.01","perstore":">=0.3.0","promised-io":">=0.3.0","formidable":">=1.0.0","templify":">=0.9.0","jsgi-node":">=0.2.5"},"devDependencies":{"patr":">=0.2.6"},"bugs":{"url":"https://github.com/persvr/pintura/issues"},"_id":"pintura@0.3.5","dist":{"shasum":"c8797d5484302b8f2ee20c51468c46b9e05fbbf3","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.5.tgz","integrity":"sha512-QSb1J5yyblvI54uzVaKkA2h3F1EaOeGUYmCV/2e1cQ9M0x/bNzPKYxPPdbM7K6cZnVz3h1o0bPgtZCYoBXLQUw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID8xYSBZXilrV/DLIjKSuOOHsCQSF1BqTSX9A+qkU/G1AiEAuWhWqYIZ08UgfnqfiJ0xOiCd8SzJyfz/fC8o1nlZqc0="}]},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"}},"0.3.6":{"name":"pintura","version":"0.3.6","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"http://github.com/persvr/pintura"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"tunguska":">=0.3.0","rql":">=0.3.1","websocket-server":">=1.4.01","perstore":">=0.3.0","promised-io":">=0.3.0","formidable":">=1.0.0","templify":">=0.9.0","jsgi-node":">=0.2.5"},"devDependencies":{"patr":">=0.2.6"},"bugs":{"url":"https://github.com/persvr/pintura/issues"},"homepage":"https://github.com/persvr/pintura","_id":"pintura@0.3.6","dist":{"shasum":"5399ca6e18977d9e00ddc31d8dad455f59197562","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.6.tgz","integrity":"sha512-5/cauq9JZS4I9YD6o78583k78+2nJl5e0p3LMVGSsWzoxvxlcpdEXXoA8R/4ocNfnvLpL+OPSzDaIXcyXPWa2Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCdAXU8nRW3v/PYnrVVsODFW4Wn/Y/9qWoXx4s9grA2OQIhAOl2Il85F9bR09Bmw+fX63QpSPrdg3/pfsJr0P7loQaj"}]},"_from":".","_npmVersion":"1.3.17","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"}},"0.3.8":{"name":"pintura","version":"0.3.8","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"http://github.com/persvr/pintura"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"formidable":">=1.0.0","jsgi-node":">=0.3.2","perstore":">=0.3.0","promised-io":">=0.3.0","rql":">=0.3.1","templify":">=0.9.0","tunguska":">=0.3.0","ws":"^0.4.32"},"devDependencies":{"patr":">=0.2.6"},"bugs":{"url":"https://github.com/persvr/pintura/issues"},"homepage":"https://github.com/persvr/pintura","_id":"pintura@0.3.8","dist":{"shasum":"a79d555a4b0e9feb4ed2237f0d7a0d1a42a797dc","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.8.tgz","integrity":"sha512-c+r1SEwXTES4P8DGo3mebkyGeJ23bxSjF3hP/dZqHFmE1Y9jgbDX0VP01u4POV9bN0DSW7EgQc3Li8Ye0PFxYQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCfF2/HZnG6DQ4aqTZCUSIZ76q9PmQW/kDdo28gFexVGQIhAN0YFMqcDrJiyDTnvp4SH4XiCANM36sIksIhdsI596Bn"}]},"_from":".","_npmVersion":"1.3.17","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"}},"0.3.9":{"name":"pintura","version":"0.3.9","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","templify":"http://github.com/dmachi/templify/zipball/master","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"http://github.com/persvr/pintura"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"formidable":">=1.0.0","jsgi-node":">=0.3.3","perstore":">=0.3.0","promised-io":">=0.3.0","rql":">=0.3.1","templify":">=0.9.0","tunguska":">=0.3.0","ws":"^0.4.32"},"devDependencies":{"patr":">=0.2.6"},"bugs":{"url":"https://github.com/persvr/pintura/issues"},"homepage":"https://github.com/persvr/pintura","_id":"pintura@0.3.9","dist":{"shasum":"1df552c44f820965898481b96785539c3487e87d","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.9.tgz","integrity":"sha512-14saY2boIJouTb4GzdxQqonzVtslkq5PhzeuILcOcl6/6K5cEVMsU/IU1A6AxsRh6vW0mNJ6JYf9LQLhyzPKEg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDy4z0denBTRwV1Luo6h2VK1ZJkXiFsMaXDyfX3/h3kIwIgdm3vAtZw+9DJGAnbt0GcVjPBoCKOp90EYLcZ6tZh7Xk="}]},"_from":".","_npmVersion":"1.3.17","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"}},"0.3.10":{"name":"pintura","version":"0.3.10","author":{"name":"Kris Zyp"},"email":"kriszyp@gmail.com","description":"JSGI-based RESTful JSON/JavaScript server","contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"keywords":["rest","database","web","json","persevere"],"mappings":{"perstore":"http://github.com/kriszyp/perstore/zipball/v0.2.4","promised-io":"jar:http://github.com/kriszyp/promised-io/zipball/v0.2.3!/","patr":"jar:http://github.com/kriszyp/patr/zipball/v0.2.6!/","tunguska":"http://github.com/kriszyp/tunguska/zipball/v0.2.4","jsgi-node":"http://github.com/kriszyp/jsgi-node/zipball/v0.2.5","node-formidable":"http://github.com/felixge/node-formidable/zipball/v0.9.8"},"overlay":{"narwhal":{"mappings":{"uri":"url","jsgi/multipart":"./engines/rhino/lib/jsgi/multipart.js"}},"node":{"mappings":{"jsgi/multipart":"./engines/node/lib/jsgi/multipart.js"}}},"licenses":[{"type":"AFLv2.1","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"},{"type":"BSD","url":"http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"}],"repository":{"type":"git","url":"git+ssh://git@github.com/persvr/pintura.git"},"directories":{"lib":"."},"maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"dependencies":{"formidable":">=1.0.0","jsgi-node":">=0.3.3","perstore":">=0.3.0","promised-io":">=0.3.0","rql":">=0.3.1","tunguska":">=0.3.0","ws":"^0.4.32"},"devDependencies":{"patr":">=0.2.6"},"gitHead":"c6ba59a946a8137d5748adfd74fccdd6a3195489","bugs":{"url":"https://github.com/persvr/pintura/issues"},"homepage":"https://github.com/persvr/pintura#readme","_id":"pintura@0.3.10","scripts":{},"_shasum":"8ff9e562a25b77879302cc13309aa20cf0de6bd6","_from":".","_npmVersion":"3.5.3","_nodeVersion":"4.2.4","_npmUser":{"name":"dojofoundation","email":"kriszyp@gmail.com"},"dist":{"shasum":"8ff9e562a25b77879302cc13309aa20cf0de6bd6","tarball":"https://registry.npmjs.org/pintura/-/pintura-0.3.10.tgz","integrity":"sha512-GJstaMgvlKL68VRjTXeRYJw2BBYl4W2V4dUyjnjbRA2H4L/1kPAfwCoOt3sNoK+yCQnZwfG3MpmOHfhnmR5ewg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmV8VFTnSlINOc/R32xNhTVtRkxT3BKv9koZk9N1RImgIgWyJv/tncVs+CICy8JCddB5kzIdXY9ApYnx9c0sV2/vM="}]}}},"readme":"[Pintura](http://www.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=pintura&sll=40.554798,-111.881839&sspn=0.009211,0.016351&ie=UTF8&hq=&hnear=Pintura,+Washington,+Utah&ll=37.31666,-113.171539&spn=0.308538,0.523224&t=p&z=11)\r\nis a cross-platform server side JavaScript based REST architecture web framework \r\nusing standards based HTTP client/server interaction with a focus on JSON formatted data. \r\nPintura gives you out of the box RESTful HTTP/JSON interface to data, you can simply\r\ncreate data models and Pintura automatically provides an HTTP interface. Pintura consists of reusable \r\n[CommonJS](http://wiki.commonjs.org/) modules and \r\n[JSGI](http://wiki.commonjs.org/wiki/JSGI/Level0/A/Draft2) middleware such that it can be used on any \r\nJSGI compliant JavaScript platform, but is tested on NodeJS and RingoJS. Pintura \r\nforms the core of the [Persevere](http://www.persvr.org/) 2.0 framework which is \r\ndesigned for rich Internet applications that rely heavily on Ajax-driven data \r\ncommunication from the browser. To learn more about features, capabilities, and philosophy of\r\nPintura see the [introduction to Pintura](http://www.sitepen.com/blog/2010/01/22/introducing-pintura/).\r\nThe [getting started with Pintura](http://www.sitepen.com/blog/2010/01/25/getting-started-with-pintura/) \r\narticle provides a great starting point for using Pintura to build Persevere applications.\r\nPintura is primarily composed of JSGI middleware components, and these components\r\nare [described here](http://www.sitepen.com/blog/2010/03/04/pintura-jsgi-modules/).\r\n\r\nSetup Pintura\r\n=================\r\n\r\nPintura can be installed with NPM via:\r\n\r\n\tnpm install pintura\r\n\r\nHowever, one of the easiest way to get started with Pintura is start with the \r\n[Persevere example app](http://github.com/persvr/persevere-example-wiki),\r\nwhich can be installed with:\r\n\r\n\tnpm install persevere-example-wiki\r\n\r\nPintura can be installed in RingoJS likewise:\r\n\r\n\tringo-admin install persvr/pintura\r\n\r\nSee the [Persevere installation instructions for more information](http://persvr.org/Page/Installation).\r\n\r\nYou can then use \"app\" property from require(\"pintura/pintura\") as a JSGI application. \r\nWith [jsgi-node](http://github.com/persvr/jsgi-node) you can start Pintura:\r\n\r\n    require(\"jsgi-node\").start(require(\"pintura/pintura\").app); \r\n    \r\nPintura requires a local.json file to be present in the current working directory.\r\nAn example of this file can be found [here](https://github.com/persvr/persevere-example-wiki/blob/master/local.json).\r\nAt a minimum Pintura also requires an implementation of the require(\"pintura/pintura\").getDataModel\r\nfunction to provide the data model that drives the HTTP REST interface.\r\n\r\nYou can see a more in-depth example of serving static files in combination with Pintura\r\nin the Persevere example app [startup file](http://github.com/persvr/persevere-example-wiki/blob/master/lib/index.js).\r\n \r\nUsing Pintura\r\n===========\r\n\r\nPersevere applications are generally built upon models, which act as classes for data.\r\n[Perstore](http://github.com/persvr/perstore) is the persistence library that is used \r\nby Persevere for defining models. Models defined with Perstore are automatically\r\nmade accessible through HTTP by Pintura. The goal of Pintura is that your application\r\nwill primarily consist of model design and Pintura will handle the most of the mechanics\r\nof exposing that model through an HTTP/REST API. A simple example of a model is:\r\n\r\n    var Model = require(\"perstore/model\").Model,\r\n    \tstore = require(\"perstore/mongodb\").MongoDB(\"Product\");\r\n    Product = Model(store, {\r\n        properties: {\r\n            name: String\r\n            // we can define other properties, all \r\n        },\r\n        // we can define handlers\r\n        put: function(object, directives){\r\n            object.updated = new Date();\r\n            store.put(object, directives);\r\n        }\r\n    }); \r\n\r\nWe can then expose this data model through Pintura's HTTP REST interface by implementing \r\nthe getDataModel function on the pintura module. This function is called for each HTTP\r\nrequest:\r\n\r\n\trequire(\"pintura/pintura\").getDataModel = function(request){\r\n\t\treturn {\r\n\t\t\tProduct: Product\r\n\t\t};\r\n\t};\r\n\r\nOur data model will then be available at the path of /Product/ such that we can make\r\nHTTP requests like GET /Product/2.\r\n\r\nHTTP/REST Basics\r\n----------------\r\n\r\nWith Persevere we can automatically interact with this data model through standard\r\nHTTP requests:\r\n\r\n* GET /{model}/{id} - Gets the object with the given id from the model store.\r\n* PUT /{model}/{id} - Updates or creates object with the given id in the model store.\r\n* DELETE /{model}/{id} - Deletes the object with the given id from the model store.\r\n* POST /{model}/ - Creates or incrementally updates an object in model store.\r\n\r\nPintura converts HTTP requests to method calls on the model. When an HTTP request\r\nis received it is converted to a call to the model of the form:\r\n\r\n    {model}.{httpMethod}({id or body},metadata);\r\n\r\nFor example if a request is received:\r\n\r\n    GET /Product/33 \r\n\r\nThis will result in a call to the model like:\r\n\r\n    Product.get(\"33\", metadata); \r\n\r\nFor the model above, there is no \"get\" method defined, so the default \"get\" handler\r\nwould be used, which delegates to the store to get the object by id. If we made a\r\nrequest like:\r\n\r\n    PUT /Product/33 \r\n\r\nThis would call the \"put\" handler defined in the model above.\r\nOne can also query stores through HTTP. Requests of the form /{model}/?{query}\r\nare passed through to the model by calls to the \"query\" method on the model.\r\nPerstore provides \r\n[query parsing capabilities](http://github.com/persvr/perstore) and stores implement \r\nquery execution (dependent on the capabilities of the DB), based on [RQL](http://github.com/persvr/rql).\r\nAn example of a query:\r\n\r\n    GET /Product/?type=shoe&price=lt=20&sort(-rating)\r\n\r\nSecurity\r\n========\r\n\r\nPersevere's facet-based security model forms the foundation of access control and\r\nis [described in this article](http://www.sitepen.com/blog/2010/03/08/object-capability-model-and-facets-in-perstorepintura/).\r\nFacets are used to define the different levels of access for models. Pintura's security\r\nconfiguration object can then be configured to define how users are authenticated\r\nand which facets or access levels each user is given. The security configuration object\r\nis available at require(\"pintura/pintura\").config.security. The primary functions\r\nthat can be overriden or used are:\r\n\r\n* authenticate(username, password) -  The authenticate method\r\nallows you to define a custom authentication method and defaults to authenticating\r\nagainst the auto-generated User model. Should return a user object.\r\n* createUser(username, password) - This should create a new user for with the given\r\ncredentials.\r\n* getUsername(user) - Should return the name of the given user.\r\n* {g|s}etUserClass - Retrieve or set the user class used to find users\r\n* {g|s}etAuthClass - Retrieve or set the authentication class used to find authentication tokens\r\n* encryptPassword(username, password) - By default applies SHA1 hashing to the password.\r\n* getUserModel()/getAuthenticationFacet() - Allows you to define your own user model\r\nand facet for access to the user model (for unauthenticated users).\r\n\r\nFor example, we could choose to store passwords in plaintext by changing the\r\nencryptPassword method to a return the password unchanged:\r\n\r\n\trequire(\"pintura/pintura\").config.security.encryptPassword = function(username, password){\r\n\t\treturn password;\r\n\t};\r\n\r\n## Access After Authentication\r\n\r\nOnce authentication is established, we could then use the user's authentication state to restrict or allow access to different\r\nparts of the application data model. For example, we could check to see if a user is\r\nlogged to determine if we should provide access to the \"Secret\" data: \r\n\r\n\tvar publicModel = {\r\n\t\tProduct: Product\r\n\t};\r\n\tvar authorizedModel = {\r\n\t\tProduct: Product,\r\n\t\tSecret: SecretModel\r\n\t};\r\n\trequire(\"pintura/pintura\").getDataModel = function(request){\r\n\t\tvar user = request.remoteUser;\r\n\t\tif(user){\r\n\t\t\treturn authorizedModel;\r\n\t\t}\r\n\t\treturn publicModel;\r\n\t};\r\n\r\nWe could also potentially have a data model that is readonly for some users and \r\neditable for others. In the example above, we could specify that the Product table\r\nis readonly for users that are not logged in:\r\n\r\n\tvar Restrictive = require(\"perstore/facet\").Restrictive;\r\n\tvar publicModel = {\r\n\t\t// the Product table is restricted to readonly for public access \r\n\t\tProduct: Restrictive(Product)\r\n\t};\r\n\tvar authorizedModel = {\r\n\t\t// the Product table is unrestricted here for authorized users \r\n\t\tProduct: Product,\r\n\t\tSecret: SecretModel\r\n\t};\r\n\t// assign the data model based on authentication as above\r\n\t\r\nError Handling\r\n===========\r\n\r\nPintura includes middleware for catching errors and converting them to appropriate\r\nHTTP error status codes. This makes it easy for application code to throw an error\r\nand have it automatically propagate to an appropriate HTTP failure response.\r\nThe following uncaught errors (until the error middleware catches them)\r\nare translated:\r\n\r\n* URIError - 400\r\n* TypeError - 403\r\n* require(\"perstore/errors\").NotFoundError - 404\r\n* require(\"perstore/errors\").PreconditionFailed - 412\r\n* require(\"perstore/errors\").AccessError - if user is authenticated 403, if not 401\r\n* require(\"perstore/errors\").MethodNotAllowedError - 405\r\n* RangeError - 416\r\n* Other errors - 500 or if the error object has a \"status\" property, that will be used\r\n\r\nFor example, if we had a model definition, we could throw a TypeError if the date\r\nproperty on PUT requests is not a valid date. This will result in a 403 response from the server\r\nto the client for PUT requests that violate this condition:\r\n\r\n\tPurchase = Model(store, {\r\n\t    // we can define handlers\r\n\t    put: function(object, directives){\r\n\t    \tif(isNaN(new Date(object.date).getTime())){\r\n\t    \t\tthrow new TypeError(\"Invalid date\");\r\n\t    \t}\r\n\t    \t...\r\n\t    }\r\n\t}); \r\n\r\nContent Negotiation\r\n===============\r\n\r\nOne of the core concepts of the REST architecture is content negotiation which permits\r\nmultiple views or representations of resources or objects. Providing content negotiation\r\nis a key functionality that Pintura provides. Pintura utilizes a set of media type handlers\r\nto find the best representation for serializing (or deserializing) data. Pintura comes\r\nwith several media type handlers (found in pintura/media/) including JSON, JavaScript,\r\nmultipart form-data, HTML, Atom, and others.\r\n\r\nTo request a JSON view of data, include an Accept header in your HTTP request:\r\n\r\n    Accept: application/json\r\n\r\nAccept headers can include multiple options and quality values. By default application/javascript\r\nis considered the highest quality represention by Pintura (it is basically the same as JSON\r\nbut also can include date literals and special numeric types like NaN and Infinite).\r\n\r\n## media (folder)\r\n\r\nThis folder contains modules that implement various media types. These media\r\ntypes can deserialize raw content to objects and serialize objects to raw content. These\r\nmedia types are registered by pintura module. Below are the media type modules,\r\ntheir name and default quality. The quality is a number between 0 - 1 that determines\r\nit's preference. The content type is selected by choosing the media with the highest product of the \r\nrequested quality setting (from the Accept header) and the server's quality setting (defined by the media module).\r\n\r\n* media/javascript - application/javascript, q=0.9: This represents utilizing JavaScript constructs\r\nlike native Date objects, NaN, Infinite, etc. to extend JSON\r\n* media/json - application/json, q=0.8: JSON representation of objects\r\n* media/plain - text/plain, q=0.1\r\n* media/uri-list - text/uri-list, q=0.05: Represents arrays as a plain text list, one item per line\r\n* media/url-encoded - application/x-www-form-urlencoded, q=0.1: This is default encoding\r\nof forms in web pages, and is useful for decoding form data sent from form submissions\r\n* media/csv - text/csv, q=0.2: Comma seperated values representation \r\n* media/atom+xml - application/atom+xml, q=0.5: Atom feed representation of data\r\n* media/html - text/html, q=0.1: A simple default representation of data as HTML. If \r\nyou are planning on rendering objects as HTML, you will probably want to register your\r\nown HTML media type handler. This handler only serializes.\r\n* media/multipart-form-data - multipart/form-data, q=0.2: Deserializes form data\r\nsubmitted with multipart/form-data as the content type. This media type is important\r\nfor handling forms with file uploads\r\n* media/message/json - message/json, q=0.75: This is a representation of messages\r\nin JSON format. This can be used for triggering a series of actions in a single request.\r\nThis is described in more detail above in the Bulk Updates section.\r\n\r\n## media (module)\r\n\r\nThis module is responsible for handling content negotiation, determining the appropriate\r\nmedia deserialization or renderer for a given content type or requested content type, by\r\nchoosing the media type with the highest calculated quality setting for the negotiation.\r\n\r\nCreating new media types is a common way to extend Pintura with additional formats.\r\nThis module provides a constructor for creating new media handlers that will be registered\r\nfor the content negotiation process. To create a new media type handler, use the Media constructor.\r\nThis constructor takes an object argument with four properties:\r\n\r\n* mediaType - The name of the media type.\r\n* getQuality(object) - A function that returns a numeric value indicating the quality of the media type (generally a number from 0 - 1).\r\n* serialize(object, parameters, request, response) - A function that is called to serialize the data (JavaScript objects or arrays) to string output for the response.\r\n* deserialize(inputStream, request) - A function that is called to deserialize the request input data to JavaScript objects or arrays.\r\n\r\nMake sure you include your media module in your top level app module so it is executed.\r\n\r\nPaging/Range Requests\r\n-------------------\r\n\r\nPintura can handle requests for \"pages\" of data, providing query results with start and ending\r\nindex limits, through Range headers. To request items 10 through 19 of a query,\r\ninclude a Range header:\r\n\r\n    GET /Product/?type=shoe\r\n    Range: items=10-19\r\n\r\nThe server will return a Content-Range header indicating the range returned and total\r\ncount of the results:\r\n\r\n\tHTTP/1.1 206 Partial Content\r\n\tContent-Range: items 10-19/122\r\n\r\nBulk Updates and Comet\r\n================\r\n\r\nPintura utilizes the message/* category of media types for indicating a set of requests \r\nor messages. Normally each HTTP request triggers one action in the store in its own\r\ntransaction, but a \r\nrequest with a content type of message/sub-type (usually message/json or message/javascript)\r\nwill be treated as a set of requests\r\nthat are all processed within one transaction. This allows you to do several updates\r\nwith one HTTP request. For request with a set of messages, the body should be an\r\narray of objects, where each object can have the following properties (only \"method\" is required):\r\n\r\n* to - The id/path of the target object of the action. This is resolved relative to the path of the request URI.\r\n* method - The method to execute, can be \"get\", \"put\", \"post\", \"subscribe\", or any other method on the target store.\r\n* body - The body of the request; the main payload of data.\r\n* queryString - query string\r\n* id - A message id, any subsequent message with the same id should be ignored (allows for idempotent messages) \r\n* metadata - Any metadata needed for the request\r\n\r\nFor example, updating two objects could be done:\r\n\r\n    POST /Product/\r\n    Content-Type: message/json \r\n    Accept: message/json\r\n    \r\n    [\r\n      {to:\"2\", method:\"put\", body:{name:\"updated 2\"}, id: 1},\r\n      {to:\"3\", method:\"put\", body:{name:\"updated 3\"}, id: 2}\r\n    ]\r\n    \r\nThe message/* media type can also be used in Accept headers to indicate that a response\r\nwith a set of messages should be returned. This should be used for bulk updates. A\r\nresponse will be an array of objects where each object may have the following properties:\r\n\r\n* from - The id/path of the object that was acted on \r\n* body - The body of the response\r\n* id - The id of the message that this message is in response to\r\n* type - The type of the action that was executed\r\n\r\nAn example response (for the requests above):\r\n\r\n    Content-Type: message/json\r\n    \r\n    [\r\n      {\"from\":\"2\", \"body\":{\"name\":\"updated 2\"}, \"id\": 1},\r\n      {\"from\":\"3\", \"body\":{\"name\":\"updated 3\"}, \"id\": 2}\r\n    ]\r\n\r\nReal-Time/Comet\r\n-------------\r\n\r\nThe message/* media type can also be useful for real-time notification of events, AKA\r\ncomet. Stores and models that support notifications can return observable objects, typically\r\nthrough the \"subscribe\" method, to indicate that multiple events may be emitted that\r\ncan later be delivered to the client. When message requests are observable instead of\r\ndirect value, responses will not be sent to the client until there is a message ready to be sent.\r\nFor example, to subscribe to all events that take place on /User/john:\r\n\r\n    POST /User/\r\n    Content-Type: message/json\r\n    Client-Id: 251ab4ac9312f\r\n    Accept: message/json\r\n    \r\n    [\r\n      {to:\"john\", method:\"subscribe\"}\r\n    ]\r\n\r\nThe response to the client will be delayed until an event/message for /User/john occurs.\r\n\r\nFor maximum browser compatibility, typically long-polling is used for comet applications.\r\nHowever, there is always a time gap between responses and the next request from the\r\nbrowser. Consequently for continuous gap-free subscriptions, it can be highly useful\r\nto emulate a continuous connection or queue for messages. This can be done by \r\nincluding a Client-Id header. Clients can generate a random id, and repeated connect\r\nusing the same client id. Between requests, any events (from subscriptions) will be\r\npushed into a queue for the given client id until the next request.\r\n\r\nThe Client-Id header can be included in standard requests as well, allowing other operations\r\nto add event sources and subscriptions to the current client queue. \r\n\r\nSome browsers support XHR streaming and do not require long-polling repeated reconnections.\r\nIf you wish to use streaming, include a Streaming header:\r\n\r\n    Streaming: true\r\n    \r\nThe response will continue indefinitely, sending new messages as they occur.  \r\n\r\nCross domain support\r\n-------------------\r\n\r\nPintura includes support for cross-domain requests from the browser/JavaScript through\r\nJSONP, window.name, or CORS. To make a request with JSONP, you can do add a callback\r\nparameter\r\n\r\n    /Product/33?callback=my_callback\r\n\r\nSessions\r\n========\r\n\r\nPintura provides session management through session middleware. This middleware\r\nadds a getSession(createIfNecessary, expires) method to the request object. There is\r\nalso a statically accessible exported function for accessing sessions:\r\n\r\n    require(\"pintura/jsgi/session\").getCurrentSession(createIfNecessary, expires)\r\n\r\nThe session object is a persistent object and therefore the save() method that must \r\nbe called if any changes are made to the session object (that need to be persisted to \r\nfuture requests).\r\n\r\nIt is worth noting that one of the goals of REST applications is to minimize server\r\nmanagement of application state, so session use should be generally be avoided or\r\nkept to a minimum when possible.\r\n\r\n# JSON-RPC\r\n\r\nPintura supports JSON-RPC to call methods on objects. One can call a method on a\r\npersisted object by using the URL for the object, and JSON-RPC encoded request entity\r\nthat describes the method invocation to make. For example:\r\n\r\n    POST /Product/33\r\n    Content-Type: application/json\r\n    Accept: application/json\r\n    \r\n    {\r\n      method:\"addNote\",\r\n      params:[\"cool product\"],\r\n      id:\"call1\"\r\n    }\r\n\r\nPintura will then lookup the object with the id of \"/Product/33\" and call object.addNote(\"cool product\").\r\nThe return value or thrown error from the call will be returned in a JSON-RPC response. \r\n\r\n\r\n# JSGI\r\n\r\nPintura is composed of a set of JSGI middleware components. JSGI is designed for \r\nasynchronous web applications, and is well-suited for NodeJS's asynchronous \r\narchitecture. JSGI applications are functions that can be called with a request object as an argument,\r\nand return a response object, or a promise for a response. JSGI middleware generally\r\nrefers to a function that takes a JSGI application as an argument and returns a new\r\nJSGI application with adding functionality. For example, we could be create a very simple\r\nJSGI application:\r\n\r\n\tapp = function(request){\r\n\t\treturn {\r\n\t\t\tstatus: 200,\r\n\t\t\theaders: {},\r\n\t\t\tbody: ['{\"some\":\"json\"}']\r\n\t\t};\r\n\t}\r\n\r\nNow we could apply middleware to this application to add functionality. For example,\r\nwe could add JsonP support (for cross-domain requests) with the xsite middleware:\r\n\r\n\tnewApp = require(\"pintura/jsgi/xsite\").JsonP(app);\r\n\r\nThe top-level pintura module, by default, applies a set of 16 middleware components to create a JSGI\r\napplication providing a robust web framework. An introduction to the Pintura middleware\r\ncan be found [here](http://www.sitepen.com/blog/2010/03/04/pintura-jsgi-modules/).\r\nBy default you don't need to directly\r\nmanipulate these JSGI modules unless you want to customize or alter the middleware\r\nstack. The pintura module provides all these middleware components with a default working \r\nsetup that be immediately used without any knowledge of the middleware components\r\ndescribed below. However, understanding the middleware modules can be important\r\nin understanding the full capabilities of Pintura.\r\n\r\nThe middleware modules in Pintura \r\nare found in the \"jsgi\" folder. Most of these modules directly a function that can be\r\nused as the middleware function, and typically take configuration information as the\r\nfirst parameter and the next application as the second. Below are the syntax and description of these modules:\r\n\r\n## auth\r\n\r\n\tapp = require('pintura/jsgi/auth')(security, nextApp);\r\n\r\nThe auth module handles HTTP authorization, performing the HTTP request side of user \r\nauthentication and calling the security module to perform the authentication and determine the authorization of\r\nthe user. This module will set the \"remoteUser\" property on the request and the \"currentUser\"\r\nproperty on the promise context if a user is authenticated. This module returns\r\na middleware function that takes a security object as the first argument, and the\r\nnext app as the second argument.  \r\n\r\n## rest-store\r\n\r\n\tapp = require('pintura/jsgi/rest-store')(config);\r\n\r\nThis module delegates the HTTP REST requests to the appropriate data model. This\r\ncomponent will call the method on the model corresponding the request method name \r\n(converted to lowercase), so a PUT request will result in a model.put() call. The model\r\nis determined by the path of the request before the first slash. The first argument provided\r\nto the call will be the path for the requests without a body, and the body for requests\r\nwith a body. The second argument is an object with the headers and the path as the \"id\"\r\nproperty.\r\n\r\nThis component will alternately call the query() method if the request is a GET with a \r\nquery string. It will also handle the Range header, converting it to an appropriate limit()\r\nparameter in the query string.\r\n\r\n## media\r\n\r\n\tapp = require('pintura/jsgi/media').Serialize(mediaSelector, nextApp);\r\n\tapp = require('pintura/jsgi/media').Deserialize(mediaSelector, nextApp);\r\n\r\nThis component processes the HTTP content negotiation headers, calling the pintura/media\r\nmodule to perform content negotiation. This handles the request body deserialization\r\nand response body serialization. The upstream middleware/apps can expect the request.body\r\nvalue to be a deserialized object (for example, JSON would be parsed), and can return\r\nan object, array, or other value in the response.body and this middleware component\r\nwill serialize it based on the client's preferred media type (defined in the Accept header).\r\nThe content negotiation is described in more detail in the Content Negotiation section.\r\n\r\nBy default the mediaSelector should come from require(\"./media\").Media.optimumMedia.\r\n\r\n## csrf\r\n\r\n\tapp = require('pintura/jsgi/csrf')(customHeader, nextApp);\r\n\r\nThis module provides CSRF protection to safeguard against malicious attempts to change\r\ndata from other websites. This protection means that requests must prove that they\r\nare from your (same-origin) page and are therefore authorized requests. XHR requests\r\ncan be validated by including a custom header (defaults to header named \"x-requested-with\", with any header value) to prove that the request\r\nwas generated through XHR. Non-XHR requests (such as form-based requests) can prove\r\ntheir same-origin validation by including the cookie value from the \"pintura-session\" in\r\na \"pintura-session\" query parameter.\r\n\r\nIf a request is not provably same-origin, the request object will include a \"crossSiteForgeable\"\r\nproperty value of true to indicate that it should be regarded with suspicion.\r\n\r\nThe customHeader argument can be the name of an alternate custom header to test for.\r\n\r\n## xsite\r\n\r\n\tapp = require('pintura/jsgi/xsite')(nextApp);\r\n\tapp = require('pintura/jsgi/xsite').JsonP(nextApp);\r\n\t\r\nThis module provides support for cross-domain requests, that is, it enables web pages\r\nthat originate from other servers to request data from your server. This module\r\nsupports three different forms of cross-domain support (each of these is a property:\r\n\r\n* JsonP - With JsonP the response is wrapped in a callback function so that the requesting\r\npage can provide a function to be called with the response data. The response will normally\r\nuse the Javascript media type.\r\n* CrossSiteXhr - Modern browsers support cross-domain XHR requests if the server\r\nprovides the proper authorization headers. This middleware component provides these\r\nheaders to enable these XHR requests.\r\n* WindowName - This technique is similar to JsonP, but allows the requesting page\r\nto embed the response in a frame to insulate it from arbitrary code execution, and\r\nis described [here](http://www.sitepen.com/blog/2008/07/22/windowname-transport/).\r\n* CrossSite - This combines support for all three of the mechanisms above. This is the export\r\nof the module.\r\n\r\n## http-params\r\n\r\n\tapp = require('pintura/jsgi/http-params')(nextApp);\r\n\r\nThis module provides a means for emulating HTTP headers and methods using query\r\nparameters. This is usually used in conjunction with the xsite middleware to enable \r\nfurther functionality for cross-domain requests. The following query parameters\r\ncan be included:\r\n\r\n* path?http-&lt;header-name>=&lt;header-value> - The middleware will translate this to having \r\na header of the with the specified header name and value. For example, we could emulate\r\na Accept: application/json with path?http-Accept=application/json\r\n* path?http-method=&lt;method-name> - This will be translated to a request with the \r\ngiven HTTP method\r\n* path?http-content=&lt;content> - This will be translated to having the parameter value\r\nas the request body. For example, we could emulate a POST with content: path?http-method=POST&http-content=%7B%22some%22%3A%22json%22%7D\r\n\r\n## compress\r\n\r\n\tapp = require('pintura/jsgi/compress')(nextApp);\r\n\r\nThis module provides gzipping of content. Gzipping can significantly reduce the size\r\nof responses and improve performance. This module requires the installation of the\r\n\"compress\" package (npm install compress).\r\n\r\n## cascade\r\n\r\n\tapp = require('pintura/jsgi/cascade')(apps);\r\n\r\nThis module provides the ability to progressively try several JSGI applications until\r\none provides a successful response. For example:\r\n\r\n\tapp = require('pintura/jsgi/cascade')([\r\n\t\tapp1, app2, app3\r\n\t]);\r\n\r\nIn this scenario, the request would be first handled by app1. If the response was successful (not a 404),\r\nthen the response would go to the client. If the response was a 404, then cascade will\r\ndelegate to app2 to handle the request, and so on.\r\n\r\n## static\r\n\r\n\tapp = require('pintura/jsgi/static')({\r\n\t\turls: urls,\r\n\t\troot: root,\r\n\t\tdirectoryListing\r\n\t});\r\n\r\nThis module provides request handling for static files. The static handler will only match\r\npaths that begin with one of the strings provided in the \"urls\" array. It will look in the\r\ndirectory specified by the \"root\" parameter for a file matching the path. The\r\ndirectoryListing parameter is a boolean indicating whether or not to show directory\r\nlistings.\r\n\r\n## error\r\n\r\n\tapp = require('pintura/jsgi/error')(nextApp);\r\n\r\nThis module provides error handling, catching JavaScript errors and converting them\r\nto corresponding HTTP error responses. These responses are documented above in\r\nthe Error Handling section.\r\n\r\n## session\r\n\r\n\tapp = require('pintura/jsgi/session').Session({\r\n\t\tmodel: model\r\n\t}, nextApp);\r\n\r\nThe session component adds support for HTTP sessions that persist across HTTP requests.\r\nSessions are often used for maintaining user authorization and other application state\r\ninformation. If the optional \"model\" parameter is provided, the provided model will be\r\nused to store the sessions for the application. The Sessions section above for more\r\ninformation on accessing the current session.\r\n\r\n## context\r\n\r\n\tapp = require('pintura/jsgi/context')(vars, nextApp);\r\n\r\nOne of the challenges with working asynchronous code is that there can be times when\r\nyou wish for some contextual state information to be preserved across multiple\r\nasynchronous actions, without having to actually pass the state to each function in\r\nthe asynchronous chain. A common examples of such contextual state would be tracking\r\nthe current transaction, or the currently logged in user. Such state information could be \r\nstored in a singleton (a module property or a global variable), but with asynchronous\r\nactions being interleaved, this is unsuitable for tracking state across asynchronous continuations\r\nof an action. \r\n\r\nThe promised-io package's promise module provides a facility for tracking state across\r\nasynchronous operations. The promise module tracks the \"context\" global variable,\r\nand whatever value that was in the variable at the time a promise was created\r\nwill be restored when that promise is fulfilled (or rejected). The pintura/jsgi/context module adds a context object to the JSGI request object\r\nthat is tied to the promise module.\r\n\r\n## head\r\n\r\n\tapp = require('pintura/jsgi/head')(nextApp);\r\n\r\nThis is a very simple middleware module that adds automatic support for HTTP HEAD\r\nrequests. This component will convert HEAD requests to a GET request for downstream\r\napplications and then strip the response body from the response.\r\n\r\n## cache\r\n\r\n\tapp = require('pintura/jsgi/cache').FetchCache(cache, nextApp);\r\n\tapp = require('pintura/jsgi/cache').UpdateCache(cache, nextApp);\r\n \r\nProvides caching of downstream responses JSGI applications.\r\n\r\n## redirect\r\n\r\n\tapp = require('pintura/jsgi/redirect')(path);\r\n\t\r\nSends a redirect response to the provided path.\r\n\r\n## pintura-headers\r\n\r\n\tapp = require('pintura/jsgi/pintura-headers')(serverName, nextApp);\r\n\r\nThis middleware adds a Server header and a username header to the HTTP responses.\r\n\r\n# Top-Level Modules\r\n\r\nBelow are the top-level modules that are available in Pintura:\r\n\r\n## pintura\r\n\r\nThis module provides the default stack of Pintura middleware and an interface for \r\nconfiguring it. It registers the default set of media types. This module is the main interface\r\nfor implementing access to the data model, and provides several important properties:\r\n\r\n### app\r\n\r\nThis is a JSGI application composed of the full stack of middleware that will expose\r\nyour data models through RESTful requests. You can use this if you want to resuse the\r\nstack and add more middleware components.\r\n\r\n### config\r\n\r\nThis takes the Pintura configuration object. Properties on this object can be overriden\r\nto customize the behavior. The most important property on this object is the getDataModel,\r\nwhich has been described above as the means for defining which data models are exposed\r\nthrough the HTTP interface.\r\n\r\n### addConnection(connection)\r\n\r\nThis function adds a connection to another server for the purposes of clustering. The\r\nconnection object should conform to the WebSocket API, and provides a communication\r\nchannel for the data to be shared. \r\n\r\n## start-node\r\n\r\nThis module provides a convenience function for easily starting Pintura on NodeJS \r\nusing the jsgi-node delegator with WebSocket support.\r\n\r\n### start(app)\r\n\r\nThis will start the given app, and route WebSocket requests through the app as well. For example:\r\n\r\n\trequire(\"pintura/start-node\").start(require(\"pintura/pintura\").app);\r\n\t\r\n## security\r\n\r\nThis module is responsible for implementing authentication and authorization. Typically\r\nyou would modify the pintura module's config.security object to customize the security.\r\nHowever, the module also allows you to create new security objects.\r\n\r\n### DefaultSecurity()\r\n\r\nThis is a constructor that creates a new security object.\r\n\r\n\r\n### Homepage:\r\n\r\n* [http://persvr.org/](http://persvr.org/)\r\n\r\n### Source & Download:\r\n\r\n* [http://github.com/persvr/pintura/](http://github.com/persvr/pintura)\r\n\r\n### Mailing list:\r\n\r\n* [http://groups.google.com/group/persevere-framework](http://groups.google.com/group/persevere-framework)\r\n\r\n### IRC:\r\n\r\n* [\\#persevere on irc.freenode.net](http://webchat.freenode.net/?channels=persevere)\r\n\r\nPintura is part of the Persevere project, and therefore is licensed under the\r\nAFL or BSD license. The Persevere project is administered under the Dojo foundation,\r\nand all contributions require a Dojo CLA.","maintainers":[{"name":"dojofoundation","email":"kzyp@dojofoundation.org"}],"time":{"modified":"2022-06-24T00:16:29.337Z","created":"2011-11-09T22:47:43.552Z","0.2.6":"2011-11-09T22:47:44.422Z","0.3.0":"2011-11-16T17:18:06.311Z","0.3.1":"2011-11-17T04:18:59.276Z","0.3.2":"2013-10-12T20:46:15.633Z","0.3.3":"2013-10-29T16:21:24.639Z","0.3.4":"2013-10-29T16:47:57.403Z","0.3.5":"2013-10-29T22:15:42.481Z","0.3.6":"2014-08-28T19:51:47.232Z","0.3.7":"2014-09-29T19:24:21.571Z","0.3.8":"2014-09-29T19:31:30.494Z","0.3.9":"2014-09-29T20:00:40.623Z","0.3.10":"2016-01-20T22:00:07.880Z"},"author":{"name":"Kris Zyp"},"repository":{"type":"git","url":"git+ssh://git@github.com/persvr/pintura.git"},"users":{"hellboy81":true},"keywords":["rest","database","web","json","persevere"],"contributors":[{"name":"Vladimir Dronnikov","email":"dronnikov@gmail.com"}],"bugs":{"url":"https://github.com/persvr/pintura/issues"},"readmeFilename":"README.md","homepage":"https://github.com/persvr/pintura#readme"}