@miso.ai/doggoganger 0.9.0-beta.10 → 0.9.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +1 -0
- package/bin/data.js +15 -0
- package/dist/umd/doggoganger-browser.min.js +1 -0
- package/package.json +11 -2
- package/rollup.config.dev.mjs +3 -0
- package/rollup.config.mjs +3 -0
- package/rollup.util.mjs +42 -0
- package/src/api/ask.js +36 -80
- package/src/api/index.js +10 -17
- package/src/api/recommendation.js +18 -6
- package/src/api/search.js +12 -5
- package/src/browser.js +5 -0
- package/src/data/answers.js +30 -0
- package/src/data/fields.js +16 -1
- package/src/data/index.js +1 -0
- package/src/data/lorem.js +1 -7
- package/src/data/markdown/index.js +49 -21
- package/src/data/markdown/languages.js +101 -0
- package/src/data/utils.js +13 -0
- package/src/data/words.js +182 -0
- package/src/index.js +3 -2
- package/src/route/ask.js +37 -0
- package/src/route/index.js +20 -0
- package/src/route/recommendation.js +22 -0
- package/src/route/search.js +15 -0
- package/src/api/handlers.js +0 -11
- /package/{src/data → data}/words.yaml +0 -0
- /package/src/{api → route}/interactions.js +0 -0
- /package/src/{api → route}/utils.js +0 -0
|
@@ -23,6 +23,7 @@ jobs:
|
|
|
23
23
|
- run: git config --global user.name "${{ github.actor }}"
|
|
24
24
|
- run: git config --global user.email "github-action-${{ github.actor }}@users.noreply.github.com"
|
|
25
25
|
- run: npm run version ${{ steps.version.outputs.version }}
|
|
26
|
+
- run: npm run build
|
|
26
27
|
- run: npm publish
|
|
27
28
|
if: "!github.event.release.prerelease"
|
|
28
29
|
env:
|
package/bin/data.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import { dirname, resolve } from 'path';
|
|
3
|
+
import { writeFileSync, readFileSync } from 'fs';
|
|
4
|
+
import yaml from 'js-yaml';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
const WORDS_YAML = resolve(__dirname, '../data/words.yaml');
|
|
9
|
+
const WORDS_JS = resolve(__dirname, '../src/data/words.js');
|
|
10
|
+
|
|
11
|
+
const words = yaml.load(readFileSync(WORDS_YAML, 'utf8'));
|
|
12
|
+
writeFileSync(WORDS_JS, `
|
|
13
|
+
// genereated by bin/data.js
|
|
14
|
+
export default ${JSON.stringify(words, null, 2)};
|
|
15
|
+
`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).doggoganger=e()}(this,(function(){"use strict";function t(t,e){return null!=e&&e>t?t+Math.floor(Math.random()*(e-t)):t}function e(){return Math.random().toString(36).substring(2,10)}function n(t){return`https://picsum.photos/seed/${Math.floor(1e3*Math.random())}/${Array.isArray(t)?t.length>1?`${t[0]}/${t[1]}`:""+t[0]:""+t}`}function r(t,e){return void 0!==e?Math.ceil(t*e):t}var o=["lorem","ipsum","dolor","sit","amet","consectetur","adipiscing","elit","curabitur","vel","hendrerit","libero","eleifend","blandit","nunc","ornare","odio","ut","orci","gravida","imperdiet","nullam","purus","lacinia","a","pretium","quis","congue","praesent","sagittis","laoreet","auctor","mauris","non","velit","eros","dictum","proin","accumsan","sapien","nec","massa","volutpat","venenatis","sed","eu","molestie","lacus","quisque","porttitor","ligula","dui","mollis","tempus","at","magna","vestibulum","turpis","ac","diam","tincidunt","id","condimentum","enim","sodales","in","hac","habitasse","platea","dictumst","aenean","neque","fusce","augue","leo","eget","semper","mattis","tortor","scelerisque","nulla","interdum","tellus","malesuada","rhoncus","porta","sem","aliquet","et","nam","suspendisse","potenti","vivamus","luctus","fringilla","erat","donec","justo","vehicula","ultricies","varius","ante","primis","faucibus","ultrices","posuere","cubilia","curae","etiam","cursus","aliquam","quam","dapibus","nisl","feugiat","egestas","class","aptent","taciti","sociosqu","ad","litora","torquent","per","conubia","nostra","inceptos","himenaeos","phasellus","nibh","pulvinar","vitae","urna","iaculis","lobortis","nisi","viverra","arcu","morbi","pellentesque","metus","commodo","ut","facilisis","felis","tristique","ullamcorper","placerat","aenean","convallis","sollicitudin","integer","rutrum","duis","est","etiam","bibendum","donec","pharetra","vulputate","maecenas","mi","fermentum","consequat","suscipit","aliquam","habitant","senectus","netus","fames","quisque","euismod","curabitur","lectus","elementum","tempor","risus","cras"];function s({decorates:e=[],output:n="string",size:r,min:s,max:i,...u}={}){let c=function(e=[5,10]){const n="number"==typeof e?e:t(...e);return function*(t){let e=0;for(let r of t){if(e++>=n)break;yield r}}}(r||[s,i])(function*({words:t=o,fixedStarts:e=0}={}){const n=t.length;for(let r=0;;r++)yield t[e>r?r:Math.floor(Math.random()*n)]}(u));for(const t of e)c=a(t)(c);return a(n)(c)}const i={string:function({separator:t=" "}={}){return e=>[...e].join(t)},array:function(){return t=>[...t]},title:function({}={}){return function*(t){for(let e of t)yield u(e)}},description:function({wordsPerSentence:t={avg:24,std:5,min:1}}={}){return function*(e){let n,r=0;for(let o of e)n&&(yield n),n=o,0===r&&(n=u(n),r=c(t)),0==--r&&(n+=".");n&&(n.endsWith(".")||(n+="."),yield n)}},multiline:function({wordsPerLine:t={avg:10,std:3,min:1}}={}){return e=>{let n=c(t),r="";for(let o of e)r&&(0==n--?(r+="\n",n=c(t)):r+=" "),r+=o;return r}}};function a(t){return"string"==typeof t?i[t]():t}function u(t){return t[0].toUpperCase()+t.substring(1)}function c(t){if("number"==typeof t)return Math.round(e);let{avg:e,std:n,min:r,max:o}=t;void 0===n&&(n=e/4);let s=(l()+l()+l())*n+e;return void 0!==r&&(s=Math.max(r,s)),void 0!==o&&(s=Math.min(o,s)),Math.round(s)}function l(){return 2*Math.random()-1}const f="// module\nimport { a, b } from './module';\nimport * as module from './module';\nimport module from './module';\nexport default module;\nexport const a = 0;\nexport * from './module';\nexport * as module from './module';\n\n// variables\nlet a = 10;\nconst b = 20;\n\n// function declaration\nfunction sum(x, y) {\n return x + y;\n}\n\n// generator function\nfunction* iterator() {\n yield 0;\n yield 1;\n}\n\n// arrow function\nconst multiply = (x, y) => x * y;\n\n// class\nclass Person {\n constructor(name, age) {\n this.name = name;\n this.age = age;\n }\n\n greet() {\n console.log('Hello, my name is Miso.');\n }\n}\n\n// primitive\nconst str = 'Hello, world!';\nconst num = 10.99;\n\n// object\nconst person = new Person('John', 30);\nperson.greet();\n\n// object literal\nconst object = {\n name: 'John',\n [x]: 10,\n ...props,\n};\n\n// array literal\nconst arr = [1, 2, 3, 4, 5, ...props];\n\n// regexp literal\nconst regexp = /\\w+/g;\n\n// operators\nconst sum = a + b;\nconst product = a * b;\nconst negation = -a;\nconst max = a > b ? a : b;\n\n// flow control\nlet i = 9;\nfor (const n of arr) {\n if (n > i) {\n console.log(n);\n }\n i++;\n}\n\n// async/await\n(async () => {\n const result = await asyncFunction();\n})();\n\n// try/catch\ntry {\n} catch (e) {\n}\n\n// destructuring\nconst { name, age, ...rest } = person;\nconst [ x, y, ...rest ] = arr;\n\n// template literals\nconsole.log(`The sum of ${a} and ${b} is ${sum(a, b)}.`);";var d=Object.freeze({__proto__:null,javascript:function(){return f},js:function(){return f}});function m({features:e,blocks:r=[8,12],sampling:o=1}={}){let i=[];return[i,e]=function(t=[]){const e=new Set,n=[];for(const r of t)if(r.startsWith("lang-")){let t=r.slice(5);"javascript"===t&&(t="js"),e.add(t)}else n.push(r);return[[...e],n]}(e),function(t,e){const n=[];for(const r of t)1>e&&Math.random()>=e||n.push(r());return n}([()=>p({features:e}),()=>g({features:e}),...i.map((t=>()=>h({lang:t,features:e}))),...i.length?[]:[()=>h({features:e})],()=>g({features:e}),()=>function({columns:e=[2,4],rows:n=[2,8]}){e=t(...e),n=t(...n);const r=[...$({size:1},e-1),{size:[3,8]}],o=s({size:e,output:"array"}),i=r.map((()=>"---")),a=[o,i];for(let t=0;n-1>t;t++)a.push(r.map((({size:t})=>s({size:t}))));return a.map(x).join("\n")}({features:e}),()=>function({url:t,imageSize:e=[400,250],...r}={}){return t=t||n(e),`![${function({size:t=[1,3],content:e}={}){return e||s({size:t})}(r)}](${t})`}(),()=>g({features:e}),()=>"*-_".charAt(t(0,2)).repeat(t(3,6)),()=>p({features:e}),()=>g({features:e}),()=>w({features:e}),()=>g({features:e})],o).join("\n\n")}function p({level:e=[1,6],size:n=[1,8],content:r}){const o=r||s({size:n});return`${"#".repeat(t(...e))} ${o}`}function h({lang:e,content:n,size:r,fenceChar:o="`"}){return"random"===o&&(o="`~".charAt(t(0,1))),n=n||function({lang:t,size:e=[10,30]}){return t&&d[t]?d[t]():s({output:"multiline",size:e})}({lang:e,size:r}),`${o.repeat(3)}${e||""}\n${n}\n${o.repeat(3)}`}function g({size:t=[20,50]}){return s({size:t,decorates:["description",z()]})}const _=["ordered","bullet","task"];function w({features:e,type:n="random",count:r=[1,8],size:o=[5,15]}){r="number"==typeof r?r:t(...r);const s="random"===n?_[Math.floor(3*Math.random())]:n,i=[];for(;r>0;){const a=t(1,r);let u=g({features:e,size:o});a>1&&(u+="\n"+w({features:e,type:n,count:a-1,size:o})),i.push(M(s,u)),r-=a}return i.join("\n")}const y={"code-span":()=>["`","`"],"emphasis-1":()=>$(b(1),2),"emphasis-2":()=>$(b(2),2),"emphasis-3":()=>$(b(3),2),strikethrough:()=>["~","~"],link:({url:t="https://miso.ai"}={})=>["[",`](${t})`]};function b(e=[1,3]){return e="number"==typeof e?e:t(...e),"_*".charAt(t(0,1)).repeat(e)}const v=Object.keys(y),q=new Set(v);function z({features:e=v,size:n=[1,3],rest:r=[0,8]}={}){e=e.filter((t=>q.has(t)));const o=function(t){for(let e=t.length-1;e>0;e--){const n=Math.floor(Math.random()*(e+1));[t[e],t[n]]=[t[n],t[e]]}return t}([...e]);let s=o.length-1;const i=()=>"number"==typeof r?r:t(...r);return function*(r){let a,u,c=i();for(const l of r)if(u&&(yield u),0===c)if(a)u=`${l}${a}`,a=void 0,c=i();else{const[r,i]=y[0>s?e[t(0,e.length-1)]:o[s--]]();u=`${r}${l}`,a=i,c="number"==typeof n?n:t(...n)}else u=l,c--;u&&(yield void 0!==a?`${u}${a}`:u)}}function M(t,e){const[n,r]=e.split("\n",2),o=`${function(t,e="random"){switch(t){case"ordered":return"1.";case"bullet":return"-";case"task":return`- [${("random"===e?.5>Math.random():e)?"x":" "}]`;default:throw Error("unknown list item type: "+t)}}(t)} ${n}`;return r?o+"\n"+function(t,e){return e.split("\n").map((e=>" ".repeat(t)+e)).join("\n")}("ordered"===t?3:2,r):o}function x(t){return`| ${t.join(" | ")} |`}function $(t,e){const n=[];for(let r=0;e>r;r++)n.push("function"==typeof t?t():t);return n}function j({size:t=300}={}){return n(t)}function S({size:t=[1,3]}={}){return s({size:t,decorates:["title"],output:"array"})}function k({size:t=[1,4]}={}){return s({size:t,output:"array"})}function O({size:t=[2,6]}={}){return s({size:t,decorates:["title"]})}function A({size:t=[10,20]}={}){return s({size:t,decorates:["description"]})}function T(){return Math.floor(1e4*Math.random())/100}function*C({rows:t,...e}={}){for(let n=0;t>n;n++)yield F({...e,index:n})}function F({}={}){const n=e(),r=function(e,n){const r=t(...n),o=[];for(let t=0;r>t;t++)o.push(e());return o}(T,[1,2]);return r.sort(),{product_id:n,authors:S(),categories:[],tags:k(),title:O(),description:A(),cover_image:j(),url:"/products/"+n,sale_price:r[0],original_price:r[r.length-1],rating:Math.floor(500*Math.random())/100+1,availability:Math.random()>.3?"IN_STOCK":"OUT_OF_STOCK"}}function*D({rows:t,...e}={}){for(let n=0;t>n;n++)yield L({...e,index:n})}function L({}={}){const t=e();return{product_id:t,authors:S(),categories:[],tags:k(),title:O({size:[4,10]}),snippet:A({size:[20,40]}),cover_image:j(),url:"/products/"+t}}function P({question:t,parent_question_id:e,timestamp:n=Date.now()},{answerFormat:o="markdown",answerSampling:i,answerLanguages:a=[]}={}){const u="10000000-1000-4000-8000-100000000000".replace(/[018]/g,(t=>(t^16*Math.random()>>t/4).toString(16))),c=function(t){const e=new Date(t).toISOString();return e.endsWith("Z")?e.slice(0,-1):e}(n),l=void 0!==i?Math.max(0,Math.min(1,i)):void 0,f=function({format:t,sampling:e,features:n}){return"markdown"===t?m({sampling:e,features:n}):s({min:r(50,e),max:r(50,e),decorates:["description"]})}({format:o,sampling:l,features:a.length?a.map((t=>"lang-"+t)):void 0});return{question:t,question_id:u,...e&&{parent_question_id:e},datetime:c,answer:f,related_resources:[...D({rows:W(6,8,l)})],sources:[...D({rows:W(4,6,l)})]}}function W(e,n,o){return t(r(e,o),r(n,o))}const H=[{name:"fetch",duration:1.5,text:"Checking the question and fetching results... "},{name:"verify",duration:1.5,text:"Verifying results... "},{name:"generate",duration:1.5,text:"Generating answer... "}];class I{constructor(t={}){this._options=t,this._answers=new Map}questions(t,e={}){return new J(t,{...this._options,...e})}}class J{constructor({question:t,parent_question_id:e},{answerFormat:n,answerSampling:r,answerLanguages:o,...s}={}){this._options=Object.freeze(s);const i=this.timestamp=Date.now();this._data=P({question:t,parent_question_id:e,timestamp:i},{answerFormat:n,answerSampling:r,answerLanguages:o})}get question_id(){return this._data.question_id}get(){const t=(Date.now()-this.timestamp)*(this._options.speedRate||1)/1e3,[e,n,r]=this._answer(t),o=this._sources(t,r),s=this._related_resources(t,r),{question_id:i,question:a,datetime:u,parent_question_id:c}=this._data;return{answer:n,answer_stage:e,datetime:u,finished:r,parent_question_id:c,question:a,question_id:i,related_resources:s,sources:o}}_answer(t){for(const e of H)if(0>(t-=e.duration))return[e.name,e.text,!1];const{answer:e}=this._data,n=Math.floor(100*t),r=n>=e.length;return["result",r?e:e.slice(0,n),r]}_sources(t,e){const{sources:n}=this._data;if(e)return n;const{length:r}=n;return n.slice(0,Math.floor(r*t/3))}_related_resources(t,e){const{related_resources:n}=this._data;if(e)return n;const{length:r}=n;return n.slice(0,Math.floor(r*t/3))}}class K{constructor(t){this._options=t}search({rows:t=5}){return{products:[...C({rows:t})]}}}class U{constructor(t){this._options=t}user_to_products({rows:t=5}){return{products:[...C({rows:t})]}}product_to_products({rows:t=5}){return{products:[...C({rows:t})]}}}class E{constructor(t){this.ask=new I(t),this.search=new K(t),this.recommendation=new U(t)}}return doggoganger={buildApi:(...t)=>new E(...t)}}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miso.ai/doggoganger",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.12",
|
|
4
4
|
"description": "A dummy miso endpoint for demo and testing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
+
"build:data": "node ./bin/data.js",
|
|
14
|
+
"build:js": "rollup -c",
|
|
15
|
+
"build": "npm run build:data && npm run build:js",
|
|
16
|
+
"start:js": "rollup -w -c rollup.config.dev.mjs",
|
|
13
17
|
"start": "nodemon src/index.js",
|
|
14
18
|
"version": "node ./bin/version.js"
|
|
15
19
|
},
|
|
@@ -33,7 +37,12 @@
|
|
|
33
37
|
"koa-body": "^6.0.1",
|
|
34
38
|
"koa-static": "^5.0.0",
|
|
35
39
|
"nodemon": "^2.0.15",
|
|
36
|
-
"uuid": "^8.3.2",
|
|
37
40
|
"yargs": "^17.5.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@rollup/plugin-terser": "^0.4.3",
|
|
44
|
+
"rollup": "^3.23.0",
|
|
45
|
+
"rollup-plugin-livereload": "^2.0.5",
|
|
46
|
+
"rollup-plugin-serve": "^2.0.2"
|
|
38
47
|
}
|
|
39
48
|
}
|
package/rollup.util.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import terser from '@rollup/plugin-terser';
|
|
2
|
+
import serve from 'rollup-plugin-serve';
|
|
3
|
+
import livereload from 'rollup-plugin-livereload';
|
|
4
|
+
|
|
5
|
+
export function config(env) {
|
|
6
|
+
const prod = env === 'prod';
|
|
7
|
+
let plugins;
|
|
8
|
+
if (prod) {
|
|
9
|
+
plugins = [
|
|
10
|
+
terser({
|
|
11
|
+
compress: {
|
|
12
|
+
pure_getters: true,
|
|
13
|
+
unsafe: true,
|
|
14
|
+
unsafe_comps: true,
|
|
15
|
+
warnings: false
|
|
16
|
+
},
|
|
17
|
+
}),
|
|
18
|
+
];
|
|
19
|
+
} else {
|
|
20
|
+
plugins = [
|
|
21
|
+
serve({
|
|
22
|
+
port: 10098,
|
|
23
|
+
}),
|
|
24
|
+
livereload({
|
|
25
|
+
delay: 500,
|
|
26
|
+
watch: 'dist',
|
|
27
|
+
}),
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
input: `src/browser.js`,
|
|
32
|
+
output: {
|
|
33
|
+
file: prod ? `dist/umd/doggoganger-browser.min.js` : `dist/umd/doggoganger-browser.js`,
|
|
34
|
+
format: 'umd',
|
|
35
|
+
name: 'doggoganger',
|
|
36
|
+
exports: 'default',
|
|
37
|
+
indent: !prod,
|
|
38
|
+
},
|
|
39
|
+
watch: !prod,
|
|
40
|
+
plugins,
|
|
41
|
+
};
|
|
42
|
+
}
|
package/src/api/ask.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { v4 as uuid } from 'uuid';
|
|
3
|
-
import { lorem, md, articles, utils } from '../data/index.js';
|
|
4
|
-
import { parseBodyIfNecessary } from './utils.js';
|
|
5
|
-
|
|
6
|
-
const { randomInt } = utils;
|
|
1
|
+
import { answer } from '../data/index.js';
|
|
7
2
|
|
|
8
3
|
const CPS = 100;
|
|
9
4
|
const ITEMS_LOADING_TIME = 3;
|
|
@@ -25,63 +20,45 @@ const STAGES = [
|
|
|
25
20
|
},
|
|
26
21
|
];
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
const str = new Date(timestamp).toISOString();
|
|
30
|
-
return str.endsWith('Z') ? str.slice(0, -1) : str;
|
|
31
|
-
}
|
|
23
|
+
export default class Ask {
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return md.markdown({});
|
|
37
|
-
case 'plaintext':
|
|
38
|
-
default:
|
|
39
|
-
return lorem.lorem({
|
|
40
|
-
min: 50,
|
|
41
|
-
max: 100,
|
|
42
|
-
decorates: ['description'],
|
|
43
|
-
});
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
this._options = options;
|
|
27
|
+
this._answers = new Map();
|
|
44
28
|
}
|
|
45
|
-
}
|
|
46
29
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
30
|
+
questions(payload, options = {}) {
|
|
31
|
+
return new Answer(payload, { ...this._options, ...options });
|
|
32
|
+
}
|
|
50
33
|
|
|
51
|
-
|
|
52
|
-
this.question_id = uuid();
|
|
53
|
-
this.question = question;
|
|
54
|
-
this.previous_answer_id = previous_question_id;
|
|
55
|
-
this.timestamp = Date.now();
|
|
56
|
-
this.datetime = formatDatetime(this.timestamp);
|
|
34
|
+
}
|
|
57
35
|
|
|
58
|
-
|
|
59
|
-
this.relatedResources = [...articles({ rows: randomInt(6, 8) })];
|
|
60
|
-
this.sources = [...articles({ rows: randomInt(4, 6) })];
|
|
36
|
+
class Answer {
|
|
61
37
|
|
|
62
|
-
|
|
63
|
-
|
|
38
|
+
constructor({ question, parent_question_id }, { answerFormat, answerSampling, answerLanguages, ...options } = {}) {
|
|
39
|
+
this._options = Object.freeze(options);
|
|
40
|
+
const timestamp = this.timestamp = Date.now();
|
|
41
|
+
this._data = answer({ question, parent_question_id, timestamp }, { answerFormat, answerSampling, answerLanguages });
|
|
64
42
|
}
|
|
65
43
|
|
|
66
|
-
get
|
|
67
|
-
return this.question_id;
|
|
44
|
+
get question_id() {
|
|
45
|
+
return this._data.question_id;
|
|
68
46
|
}
|
|
69
47
|
|
|
70
48
|
get() {
|
|
71
49
|
const now = Date.now();
|
|
72
|
-
const elapsed = (now - this.timestamp) / 1000;
|
|
50
|
+
const elapsed = (now - this.timestamp) * (this._options.speedRate || 1) / 1000;
|
|
73
51
|
const [answer_stage, answer, finished] = this._answer(elapsed);
|
|
74
|
-
const sources = this._sources(elapsed);
|
|
75
|
-
const related_resources = this.
|
|
76
|
-
const { question_id, question, datetime,
|
|
52
|
+
const sources = this._sources(elapsed, finished);
|
|
53
|
+
const related_resources = this._related_resources(elapsed, finished);
|
|
54
|
+
const { question_id, question, datetime, parent_question_id } = this._data;
|
|
77
55
|
|
|
78
56
|
return {
|
|
79
|
-
affiliation: undefined,
|
|
80
57
|
answer,
|
|
81
58
|
answer_stage,
|
|
82
59
|
datetime,
|
|
83
60
|
finished,
|
|
84
|
-
|
|
61
|
+
parent_question_id,
|
|
85
62
|
question,
|
|
86
63
|
question_id,
|
|
87
64
|
related_resources,
|
|
@@ -96,52 +73,31 @@ class Answer {
|
|
|
96
73
|
return [stage.name, stage.text, false];
|
|
97
74
|
}
|
|
98
75
|
}
|
|
76
|
+
const { answer } = this._data;
|
|
99
77
|
const length = Math.floor(elapsed * CPS);
|
|
100
|
-
const finished = length >=
|
|
101
|
-
const text = finished ?
|
|
78
|
+
const finished = length >= answer.length;
|
|
79
|
+
const text = finished ? answer : answer.slice(0, length);
|
|
102
80
|
return ['result', text, finished];
|
|
103
81
|
}
|
|
104
82
|
|
|
105
|
-
_sources(elapsed) {
|
|
106
|
-
const { sources } = this;
|
|
83
|
+
_sources(elapsed, finished) {
|
|
84
|
+
const { sources } = this._data;
|
|
85
|
+
if (finished) {
|
|
86
|
+
return sources;
|
|
87
|
+
}
|
|
107
88
|
const { length } = sources;
|
|
108
89
|
const loaded = Math.floor(length * elapsed / ITEMS_LOADING_TIME);
|
|
109
90
|
return sources.slice(0, loaded);
|
|
110
91
|
}
|
|
111
92
|
|
|
112
|
-
|
|
113
|
-
const {
|
|
114
|
-
|
|
93
|
+
_related_resources(elapsed, finished) {
|
|
94
|
+
const { related_resources } = this._data;
|
|
95
|
+
if (finished) {
|
|
96
|
+
return related_resources;
|
|
97
|
+
}
|
|
98
|
+
const { length } = related_resources;
|
|
115
99
|
const loaded = Math.floor(length * elapsed / ITEMS_LOADING_TIME);
|
|
116
|
-
return
|
|
100
|
+
return related_resources.slice(0, loaded);
|
|
117
101
|
}
|
|
118
102
|
|
|
119
103
|
}
|
|
120
|
-
|
|
121
|
-
export default function({ answerFormat }) {
|
|
122
|
-
const options = Object.freeze({ answerFormat });
|
|
123
|
-
const router = new Router();
|
|
124
|
-
|
|
125
|
-
router.post('/questions', (ctx) => {
|
|
126
|
-
const { question, previous_answer_id } = parseBodyIfNecessary(ctx.request.body);
|
|
127
|
-
const answerFormat = ctx.get('x-answer-format') || options.answerFormat;
|
|
128
|
-
const answer = new Answer(question, previous_answer_id, { answerFormat });
|
|
129
|
-
const data = {
|
|
130
|
-
question_id: answer.id,
|
|
131
|
-
};
|
|
132
|
-
ctx.body = JSON.stringify({ data });
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
router.get('/questions/:id/answer', (ctx) => {
|
|
136
|
-
const { id } = ctx.params;
|
|
137
|
-
const answer = answers.get(id);
|
|
138
|
-
if (!answer) {
|
|
139
|
-
ctx.status = 404;
|
|
140
|
-
} else {
|
|
141
|
-
const data = answer.get();
|
|
142
|
-
ctx.body = JSON.stringify({ data });
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
return router;
|
|
147
|
-
}
|
package/src/api/index.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import search from './search.js';
|
|
5
|
-
import interactions from './interactions.js';
|
|
1
|
+
import Ask from './ask.js';
|
|
2
|
+
import Search from './search.js';
|
|
3
|
+
import Recommendation from './recommendation.js';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
router.use(path, middleware.routes(), middleware.allowedMethods());
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default function(options) {
|
|
12
|
-
const router = new Router();
|
|
5
|
+
export default class Api {
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.ask = new Ask(options);
|
|
9
|
+
this.search = new Search(options);
|
|
10
|
+
this.recommendation = new Recommendation(options);
|
|
11
|
+
}
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
};
|
|
13
|
+
}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { products } from './handlers.js';
|
|
1
|
+
import { products } from '../data/index.js';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
export default class Recommendation {
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this._options = options;
|
|
7
|
+
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
user_to_products({ rows = 5 }) {
|
|
10
|
+
return {
|
|
11
|
+
products: [...products({ rows })],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
product_to_products({ rows = 5 }) {
|
|
16
|
+
return {
|
|
17
|
+
products: [...products({ rows })],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
}
|
package/src/api/search.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { products } from './handlers.js';
|
|
1
|
+
import { products } from '../data/index.js';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
export default class Search {
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this._options = options;
|
|
7
|
+
}
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
search({ rows = 5 }) {
|
|
10
|
+
return {
|
|
11
|
+
products: [...products({ rows })],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
}
|
package/src/browser.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { randomInt, formatDatetime, sample, uuid } from './utils.js';
|
|
2
|
+
import * as fields from './fields.js';
|
|
3
|
+
import { articles } from './articles.js';
|
|
4
|
+
|
|
5
|
+
export function answer({ question, parent_question_id, timestamp = Date.now() }, { answerFormat = 'markdown', answerSampling, answerLanguages = [] } = {}) {
|
|
6
|
+
|
|
7
|
+
const question_id = uuid();
|
|
8
|
+
const datetime = formatDatetime(timestamp);
|
|
9
|
+
|
|
10
|
+
const sampling = answerSampling !== undefined ? Math.max(0, Math.min(1, answerSampling)) : undefined;
|
|
11
|
+
const features = answerLanguages.length ? answerLanguages.map(language => `lang-${language}`) : undefined;
|
|
12
|
+
|
|
13
|
+
const answer = fields.answer({ format: answerFormat, sampling, features });
|
|
14
|
+
const related_resources = [...articles({ rows: sampleRandomInt(6, 8, sampling) })];
|
|
15
|
+
const sources = [...articles({ rows: sampleRandomInt(4, 6, sampling) })];
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
question,
|
|
19
|
+
question_id,
|
|
20
|
+
...(parent_question_id && { parent_question_id }),
|
|
21
|
+
datetime,
|
|
22
|
+
answer,
|
|
23
|
+
related_resources,
|
|
24
|
+
sources,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sampleRandomInt(min, max, sampling) {
|
|
29
|
+
return randomInt(sample(min, sampling), sample(max, sampling));
|
|
30
|
+
}
|
package/src/data/fields.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as lorem from './lorem.js';
|
|
2
|
-
import { imageUrl } from './utils.js';
|
|
2
|
+
import { imageUrl, sample } from './utils.js';
|
|
3
|
+
import * as md from './markdown/index.js';
|
|
3
4
|
|
|
4
5
|
export function image({ size = 300 } = {}) {
|
|
5
6
|
return imageUrl(size);
|
|
@@ -47,3 +48,17 @@ export function price() {
|
|
|
47
48
|
export function rating() {
|
|
48
49
|
return Math.floor(Math.random() * 500) / 100 + 1;
|
|
49
50
|
}
|
|
51
|
+
|
|
52
|
+
export function answer({ format, sampling, features }) {
|
|
53
|
+
switch (format) {
|
|
54
|
+
case 'markdown':
|
|
55
|
+
return md.markdown({ sampling, features });
|
|
56
|
+
case 'plaintext':
|
|
57
|
+
default:
|
|
58
|
+
return lorem.lorem({
|
|
59
|
+
min: sample(50, sampling),
|
|
60
|
+
max: sample(50, sampling),
|
|
61
|
+
decorates: ['description'],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/data/index.js
CHANGED
package/src/data/lorem.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
import { dirname, resolve } from 'path';
|
|
4
|
-
import yaml from 'js-yaml';
|
|
5
1
|
import { randomInt } from './utils.js';
|
|
6
|
-
|
|
7
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const DEFAULT_WORDS = yaml.load(readFileSync(resolve(__dirname, './words.yaml'), 'utf8'));
|
|
2
|
+
import DEFAULT_WORDS from './words.js';
|
|
9
3
|
|
|
10
4
|
export function lorem({ decorates = [], output = 'string', size, min, max, ...options } = {}) {
|
|
11
5
|
let iterator = limit(size || [min, max])(base(options));
|
|
@@ -1,24 +1,55 @@
|
|
|
1
1
|
import { randomInt, imageUrl, shuffle } from '../utils.js';
|
|
2
2
|
import * as lorem from '../lorem.js';
|
|
3
|
+
import * as languages from './languages.js';
|
|
3
4
|
|
|
4
5
|
// TODO: wild mode that generates edge cases
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
function extractLangFeatures(features = []) {
|
|
8
|
+
const languages = new Set();
|
|
9
|
+
const rest = [];
|
|
10
|
+
for (const feature of features) {
|
|
11
|
+
if (feature.startsWith('lang-')) {
|
|
12
|
+
let lang = feature.slice(5);
|
|
13
|
+
if (lang === 'javascript') {
|
|
14
|
+
lang = 'js';
|
|
15
|
+
}
|
|
16
|
+
languages.add(lang);
|
|
17
|
+
} else {
|
|
18
|
+
rest.push(feature);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return [[...languages], rest];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function markdown({ features, blocks = [8, 12], sampling = 1 } = {}) {
|
|
25
|
+
let languages = [];
|
|
26
|
+
[languages, features] = extractLangFeatures(features);
|
|
7
27
|
// TODO: block features
|
|
8
|
-
return [
|
|
9
|
-
atxHeading({ features }),
|
|
10
|
-
paragraph({ features }),
|
|
11
|
-
fencedCodeBlock({ features }),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
return sample([
|
|
29
|
+
() => atxHeading({ features }),
|
|
30
|
+
() => paragraph({ features }),
|
|
31
|
+
...languages.map(lang => () => fencedCodeBlock({ lang, features })),
|
|
32
|
+
...(languages.length ? [] : [() => fencedCodeBlock({ features })]),
|
|
33
|
+
() => paragraph({ features }),
|
|
34
|
+
() => table({ features }),
|
|
35
|
+
() => image(),
|
|
36
|
+
() => paragraph({ features }),
|
|
37
|
+
() => hr(),
|
|
38
|
+
() => atxHeading({ features }),
|
|
39
|
+
() => paragraph({ features }),
|
|
40
|
+
() => list({ features }),
|
|
41
|
+
() => paragraph({ features }),
|
|
42
|
+
], sampling).join('\n\n');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function sample(fns, sampling) {
|
|
46
|
+
const arr = [];
|
|
47
|
+
for (const fn of fns) {
|
|
48
|
+
if (sampling >= 1 || Math.random() < sampling) {
|
|
49
|
+
arr.push(fn());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return arr;
|
|
22
53
|
}
|
|
23
54
|
|
|
24
55
|
// leaf blocks //
|
|
@@ -57,7 +88,8 @@ export function fencedCodeBlock({ lang, content, size, fenceChar = '`' }) {
|
|
|
57
88
|
}
|
|
58
89
|
|
|
59
90
|
export function paragraph({ features, size = [20, 50] }) {
|
|
60
|
-
|
|
91
|
+
// force all inline features
|
|
92
|
+
return lorem.lorem({ size, decorates: ['description', decorate()] });
|
|
61
93
|
}
|
|
62
94
|
|
|
63
95
|
export function table({ features, columns = [2, 4], rows = [2, 8] }) {
|
|
@@ -214,11 +246,7 @@ function listItemPrefix(type, checked = 'random') {
|
|
|
214
246
|
}
|
|
215
247
|
|
|
216
248
|
function codeContent({ lang, size = [10, 30] }) {
|
|
217
|
-
|
|
218
|
-
switch (lang) {
|
|
219
|
-
default:
|
|
220
|
-
return lorem.lorem({ output: 'multiline', size });
|
|
221
|
-
}
|
|
249
|
+
return lang && languages[lang] ? languages[lang]() : lorem.lorem({ output: 'multiline', size });
|
|
222
250
|
}
|
|
223
251
|
|
|
224
252
|
function tableRow(cells) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export function javascript() {
|
|
2
|
+
return JAVASCRIPT;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function js() {
|
|
6
|
+
return JAVASCRIPT;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const JAVASCRIPT = `
|
|
10
|
+
// module
|
|
11
|
+
import { a, b } from './module';
|
|
12
|
+
import * as module from './module';
|
|
13
|
+
import module from './module';
|
|
14
|
+
export default module;
|
|
15
|
+
export const a = 0;
|
|
16
|
+
export * from './module';
|
|
17
|
+
export * as module from './module';
|
|
18
|
+
|
|
19
|
+
// variables
|
|
20
|
+
let a = 10;
|
|
21
|
+
const b = 20;
|
|
22
|
+
|
|
23
|
+
// function declaration
|
|
24
|
+
function sum(x, y) {
|
|
25
|
+
return x + y;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// generator function
|
|
29
|
+
function* iterator() {
|
|
30
|
+
yield 0;
|
|
31
|
+
yield 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// arrow function
|
|
35
|
+
const multiply = (x, y) => x * y;
|
|
36
|
+
|
|
37
|
+
// class
|
|
38
|
+
class Person {
|
|
39
|
+
constructor(name, age) {
|
|
40
|
+
this.name = name;
|
|
41
|
+
this.age = age;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
greet() {
|
|
45
|
+
console.log('Hello, my name is Miso.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// primitive
|
|
50
|
+
const str = 'Hello, world!';
|
|
51
|
+
const num = 10.99;
|
|
52
|
+
|
|
53
|
+
// object
|
|
54
|
+
const person = new Person('John', 30);
|
|
55
|
+
person.greet();
|
|
56
|
+
|
|
57
|
+
// object literal
|
|
58
|
+
const object = {
|
|
59
|
+
name: 'John',
|
|
60
|
+
[x]: 10,
|
|
61
|
+
...props,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// array literal
|
|
65
|
+
const arr = [1, 2, 3, 4, 5, ...props];
|
|
66
|
+
|
|
67
|
+
// regexp literal
|
|
68
|
+
const regexp = /\\w+/g;
|
|
69
|
+
|
|
70
|
+
// operators
|
|
71
|
+
const sum = a + b;
|
|
72
|
+
const product = a * b;
|
|
73
|
+
const negation = -a;
|
|
74
|
+
const max = a > b ? a : b;
|
|
75
|
+
|
|
76
|
+
// flow control
|
|
77
|
+
let i = 9;
|
|
78
|
+
for (const n of arr) {
|
|
79
|
+
if (n > i) {
|
|
80
|
+
console.log(n);
|
|
81
|
+
}
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// async/await
|
|
86
|
+
(async () => {
|
|
87
|
+
const result = await asyncFunction();
|
|
88
|
+
})();
|
|
89
|
+
|
|
90
|
+
// try/catch
|
|
91
|
+
try {
|
|
92
|
+
} catch (e) {
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// destructuring
|
|
96
|
+
const { name, age, ...rest } = person;
|
|
97
|
+
const [ x, y, ...rest ] = arr;
|
|
98
|
+
|
|
99
|
+
// template literals
|
|
100
|
+
${'console.log(`The sum of ${a} and ${b} is ${sum(a, b)}.`);'}
|
|
101
|
+
`.trim();
|
package/src/data/utils.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export function uuid() {
|
|
2
|
+
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, a => (a ^ Math.random() * 16 >> a / 4).toString(16));
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
export function randomInt(min, max) {
|
|
2
6
|
return max == null || (max <= min) ? min : (min + Math.floor(Math.random() * (max - min)));
|
|
3
7
|
}
|
|
@@ -29,3 +33,12 @@ export function imageUrl(size) {
|
|
|
29
33
|
const sizePath = Array.isArray(size) ? size.length > 1 ? `${size[0]}/${size[1]}` : `${size[0]}` : `${size}`;
|
|
30
34
|
return `https://picsum.photos/seed/${seed}/${sizePath}`;
|
|
31
35
|
}
|
|
36
|
+
|
|
37
|
+
export function formatDatetime(timestamp) {
|
|
38
|
+
const str = new Date(timestamp).toISOString();
|
|
39
|
+
return str.endsWith('Z') ? str.slice(0, -1) : str;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function sample(size, sampling) {
|
|
43
|
+
return sampling !== undefined ? Math.ceil(size * sampling) : size;
|
|
44
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
|
|
2
|
+
// genereated by bin/data.js
|
|
3
|
+
export default [
|
|
4
|
+
"lorem",
|
|
5
|
+
"ipsum",
|
|
6
|
+
"dolor",
|
|
7
|
+
"sit",
|
|
8
|
+
"amet",
|
|
9
|
+
"consectetur",
|
|
10
|
+
"adipiscing",
|
|
11
|
+
"elit",
|
|
12
|
+
"curabitur",
|
|
13
|
+
"vel",
|
|
14
|
+
"hendrerit",
|
|
15
|
+
"libero",
|
|
16
|
+
"eleifend",
|
|
17
|
+
"blandit",
|
|
18
|
+
"nunc",
|
|
19
|
+
"ornare",
|
|
20
|
+
"odio",
|
|
21
|
+
"ut",
|
|
22
|
+
"orci",
|
|
23
|
+
"gravida",
|
|
24
|
+
"imperdiet",
|
|
25
|
+
"nullam",
|
|
26
|
+
"purus",
|
|
27
|
+
"lacinia",
|
|
28
|
+
"a",
|
|
29
|
+
"pretium",
|
|
30
|
+
"quis",
|
|
31
|
+
"congue",
|
|
32
|
+
"praesent",
|
|
33
|
+
"sagittis",
|
|
34
|
+
"laoreet",
|
|
35
|
+
"auctor",
|
|
36
|
+
"mauris",
|
|
37
|
+
"non",
|
|
38
|
+
"velit",
|
|
39
|
+
"eros",
|
|
40
|
+
"dictum",
|
|
41
|
+
"proin",
|
|
42
|
+
"accumsan",
|
|
43
|
+
"sapien",
|
|
44
|
+
"nec",
|
|
45
|
+
"massa",
|
|
46
|
+
"volutpat",
|
|
47
|
+
"venenatis",
|
|
48
|
+
"sed",
|
|
49
|
+
"eu",
|
|
50
|
+
"molestie",
|
|
51
|
+
"lacus",
|
|
52
|
+
"quisque",
|
|
53
|
+
"porttitor",
|
|
54
|
+
"ligula",
|
|
55
|
+
"dui",
|
|
56
|
+
"mollis",
|
|
57
|
+
"tempus",
|
|
58
|
+
"at",
|
|
59
|
+
"magna",
|
|
60
|
+
"vestibulum",
|
|
61
|
+
"turpis",
|
|
62
|
+
"ac",
|
|
63
|
+
"diam",
|
|
64
|
+
"tincidunt",
|
|
65
|
+
"id",
|
|
66
|
+
"condimentum",
|
|
67
|
+
"enim",
|
|
68
|
+
"sodales",
|
|
69
|
+
"in",
|
|
70
|
+
"hac",
|
|
71
|
+
"habitasse",
|
|
72
|
+
"platea",
|
|
73
|
+
"dictumst",
|
|
74
|
+
"aenean",
|
|
75
|
+
"neque",
|
|
76
|
+
"fusce",
|
|
77
|
+
"augue",
|
|
78
|
+
"leo",
|
|
79
|
+
"eget",
|
|
80
|
+
"semper",
|
|
81
|
+
"mattis",
|
|
82
|
+
"tortor",
|
|
83
|
+
"scelerisque",
|
|
84
|
+
"nulla",
|
|
85
|
+
"interdum",
|
|
86
|
+
"tellus",
|
|
87
|
+
"malesuada",
|
|
88
|
+
"rhoncus",
|
|
89
|
+
"porta",
|
|
90
|
+
"sem",
|
|
91
|
+
"aliquet",
|
|
92
|
+
"et",
|
|
93
|
+
"nam",
|
|
94
|
+
"suspendisse",
|
|
95
|
+
"potenti",
|
|
96
|
+
"vivamus",
|
|
97
|
+
"luctus",
|
|
98
|
+
"fringilla",
|
|
99
|
+
"erat",
|
|
100
|
+
"donec",
|
|
101
|
+
"justo",
|
|
102
|
+
"vehicula",
|
|
103
|
+
"ultricies",
|
|
104
|
+
"varius",
|
|
105
|
+
"ante",
|
|
106
|
+
"primis",
|
|
107
|
+
"faucibus",
|
|
108
|
+
"ultrices",
|
|
109
|
+
"posuere",
|
|
110
|
+
"cubilia",
|
|
111
|
+
"curae",
|
|
112
|
+
"etiam",
|
|
113
|
+
"cursus",
|
|
114
|
+
"aliquam",
|
|
115
|
+
"quam",
|
|
116
|
+
"dapibus",
|
|
117
|
+
"nisl",
|
|
118
|
+
"feugiat",
|
|
119
|
+
"egestas",
|
|
120
|
+
"class",
|
|
121
|
+
"aptent",
|
|
122
|
+
"taciti",
|
|
123
|
+
"sociosqu",
|
|
124
|
+
"ad",
|
|
125
|
+
"litora",
|
|
126
|
+
"torquent",
|
|
127
|
+
"per",
|
|
128
|
+
"conubia",
|
|
129
|
+
"nostra",
|
|
130
|
+
"inceptos",
|
|
131
|
+
"himenaeos",
|
|
132
|
+
"phasellus",
|
|
133
|
+
"nibh",
|
|
134
|
+
"pulvinar",
|
|
135
|
+
"vitae",
|
|
136
|
+
"urna",
|
|
137
|
+
"iaculis",
|
|
138
|
+
"lobortis",
|
|
139
|
+
"nisi",
|
|
140
|
+
"viverra",
|
|
141
|
+
"arcu",
|
|
142
|
+
"morbi",
|
|
143
|
+
"pellentesque",
|
|
144
|
+
"metus",
|
|
145
|
+
"commodo",
|
|
146
|
+
"ut",
|
|
147
|
+
"facilisis",
|
|
148
|
+
"felis",
|
|
149
|
+
"tristique",
|
|
150
|
+
"ullamcorper",
|
|
151
|
+
"placerat",
|
|
152
|
+
"aenean",
|
|
153
|
+
"convallis",
|
|
154
|
+
"sollicitudin",
|
|
155
|
+
"integer",
|
|
156
|
+
"rutrum",
|
|
157
|
+
"duis",
|
|
158
|
+
"est",
|
|
159
|
+
"etiam",
|
|
160
|
+
"bibendum",
|
|
161
|
+
"donec",
|
|
162
|
+
"pharetra",
|
|
163
|
+
"vulputate",
|
|
164
|
+
"maecenas",
|
|
165
|
+
"mi",
|
|
166
|
+
"fermentum",
|
|
167
|
+
"consequat",
|
|
168
|
+
"suscipit",
|
|
169
|
+
"aliquam",
|
|
170
|
+
"habitant",
|
|
171
|
+
"senectus",
|
|
172
|
+
"netus",
|
|
173
|
+
"fames",
|
|
174
|
+
"quisque",
|
|
175
|
+
"euismod",
|
|
176
|
+
"curabitur",
|
|
177
|
+
"lectus",
|
|
178
|
+
"elementum",
|
|
179
|
+
"tempor",
|
|
180
|
+
"risus",
|
|
181
|
+
"cras"
|
|
182
|
+
];
|
package/src/index.js
CHANGED
|
@@ -3,13 +3,14 @@ import Router from '@koa/router';
|
|
|
3
3
|
import cors from '@koa/cors';
|
|
4
4
|
import serveStatic from 'koa-static';
|
|
5
5
|
import { koaBody } from 'koa-body';
|
|
6
|
-
import
|
|
6
|
+
import _route from './route/index.js';
|
|
7
|
+
import Api from './api/index.js';
|
|
7
8
|
import { exclusion } from './utils.js';
|
|
8
9
|
|
|
9
10
|
export default function doggoganger({ port = 9901, serve = false, ...options } = {}) {
|
|
10
11
|
const app = new Koa();
|
|
11
12
|
const router = new Router();
|
|
12
|
-
const api =
|
|
13
|
+
const api = _route(new Api(options));
|
|
13
14
|
|
|
14
15
|
router.use('/api', api.routes(), api.allowedMethods());
|
|
15
16
|
|
package/src/route/ask.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
function getOptionsFromCtx(ctx) {
|
|
5
|
+
const speedRate = Number(ctx.get('x-speed-rate')) || undefined;
|
|
6
|
+
const answerFormat = ctx.get('x-answer-format') || undefined;
|
|
7
|
+
const answerSampling = Number(ctx.get('x-answer-sampling')) || undefined;
|
|
8
|
+
const answerLanguagesStr = ctx.get('x-answer-languages') || undefined;
|
|
9
|
+
const answerLanguages = answerLanguagesStr ? answerLanguagesStr.split(',') : undefined;
|
|
10
|
+
return { answerFormat, answerSampling, answerLanguages, speedRate };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function(api) {
|
|
14
|
+
const answers = new Map();
|
|
15
|
+
const router = new Router();
|
|
16
|
+
|
|
17
|
+
router.post('/questions', (ctx) => {
|
|
18
|
+
const { question, parent_question_id } = parseBodyIfNecessary(ctx.request.body);
|
|
19
|
+
const answer = api.ask.questions({ question, parent_question_id }, getOptionsFromCtx(ctx));
|
|
20
|
+
const { question_id } = answer;
|
|
21
|
+
answers.set(question_id, answer);
|
|
22
|
+
ctx.body = JSON.stringify({ data: { question_id } });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
router.get('/questions/:id/answer', (ctx) => {
|
|
26
|
+
const { id } = ctx.params;
|
|
27
|
+
const answer = answers.get(id);
|
|
28
|
+
if (!answer) {
|
|
29
|
+
ctx.status = 404;
|
|
30
|
+
} else {
|
|
31
|
+
const data = answer.get();
|
|
32
|
+
ctx.body = JSON.stringify({ data });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return router;
|
|
37
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import ask from './ask.js';
|
|
3
|
+
import recommendation from './recommendation.js';
|
|
4
|
+
import search from './search.js';
|
|
5
|
+
import interactions from './interactions.js';
|
|
6
|
+
|
|
7
|
+
function use(router, path, middleware) {
|
|
8
|
+
router.use(path, middleware.routes(), middleware.allowedMethods());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function(api) {
|
|
12
|
+
const router = new Router();
|
|
13
|
+
|
|
14
|
+
use(router, '/ask', ask(api));
|
|
15
|
+
use(router, '/recommendation', recommendation(api));
|
|
16
|
+
use(router, '/search', search(api));
|
|
17
|
+
use(router, '/interactions', interactions);
|
|
18
|
+
|
|
19
|
+
return router;
|
|
20
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export default function(api) {
|
|
5
|
+
const router = new Router();
|
|
6
|
+
|
|
7
|
+
router.post('/user_to_products', (ctx) => {
|
|
8
|
+
const { rows = 5 } = parseBodyIfNecessary(ctx.request.body);
|
|
9
|
+
ctx.body = {
|
|
10
|
+
data: api.recommendation.user_to_products({ rows }),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
router.post('/product_to_products', (ctx) => {
|
|
15
|
+
const { rows = 5 } = parseBodyIfNecessary(ctx.request.body);
|
|
16
|
+
ctx.body = {
|
|
17
|
+
data: api.recommendation.product_to_products({ rows }),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return router;
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export default function(api) {
|
|
5
|
+
const router = new Router();
|
|
6
|
+
|
|
7
|
+
router.post('/search', (ctx) => {
|
|
8
|
+
const { rows = 5 } = parseBodyIfNecessary(ctx.request.body);
|
|
9
|
+
ctx.body = {
|
|
10
|
+
data: api.search.search({ rows }),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return router;
|
|
15
|
+
}
|
package/src/api/handlers.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { products as _products } from '../data/index.js';
|
|
2
|
-
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
-
|
|
4
|
-
export function products(ctx) {
|
|
5
|
-
const { rows = 5 } = parseBodyIfNecessary(ctx.request.body);
|
|
6
|
-
ctx.body = {
|
|
7
|
-
data: {
|
|
8
|
-
products: [..._products({ rows })],
|
|
9
|
-
},
|
|
10
|
-
};
|
|
11
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|