@miso.ai/doggoganger 0.9.0-beta.2

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.
@@ -0,0 +1,33 @@
1
+ name: NPM publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish-npm:
9
+ environment: production
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Get version
13
+ id: version
14
+ run: |
15
+ VERSION=${{ github.event.release.tag_name }}
16
+ echo "::set-output name=version::$(echo $VERSION | sed -e 's/v//gI')"
17
+ - uses: actions/checkout@v2
18
+ - uses: actions/setup-node@v2
19
+ with:
20
+ node-version: 16
21
+ registry-url: https://registry.npmjs.org/
22
+ - run: npm install
23
+ - run: git config --global user.name "${{ github.actor }}"
24
+ - run: git config --global user.email "github-action-${{ github.actor }}@users.noreply.github.com"
25
+ - run: npm run version ${{ steps.version.outputs.version }}
26
+ - run: npm publish
27
+ if: "!github.event.release.prerelease"
28
+ env:
29
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
30
+ - run: npm publish --tag beta
31
+ if: "github.event.release.prerelease"
32
+ env:
33
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Miso Technologies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # doggoganger
2
+ Mock API endpoint service for demo and testing
package/bin/version.js ADDED
@@ -0,0 +1,46 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { dirname, join } from 'path';
3
+ import { writeFileSync, readFileSync, existsSync } from 'fs';
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+
7
+ const VERSION_REGEXP = /^\d+\.\d+\.\d+(?:-beta\.\d+)?$/;
8
+ const version = process.argv[2];
9
+
10
+ if (!version) {
11
+ console.log(`Usage: npm run version [version]`);
12
+ process.exit();
13
+ }
14
+ if (!VERSION_REGEXP.test(version)) {
15
+ console.error(`Illegal version: ${version}`);
16
+ process.exit(1);
17
+ }
18
+
19
+ const rootDir = join(__dirname, '..');
20
+ //const VERSION_FILE_NAME = 'src/version.js';
21
+ const PACKAGE_FILE_NAME = 'package.json';
22
+
23
+ /*
24
+ function writeVersionFile(path, version) {
25
+ const filePath = join(rootDir, path, VERSION_FILE_NAME);
26
+ if (existsSync(filePath)) {
27
+ writeFileSync(filePath, `export default '${version}';`);
28
+ }
29
+ }
30
+ */
31
+
32
+ function readPackageFileSync(path) {
33
+ const file = join(path, PACKAGE_FILE_NAME);
34
+ return existsSync(file) ? JSON.parse(readFileSync(file)) : undefined;
35
+ }
36
+
37
+ function writePackageFileSync(path, data) {
38
+ const file = join(path, PACKAGE_FILE_NAME);
39
+ if (!existsSync(file)) {
40
+ mkdirSync(dirname(file), { recursive: true });
41
+ }
42
+ writeFileSync(join(path, PACKAGE_FILE_NAME), JSON.stringify(data, null, 2));
43
+ }
44
+
45
+ const { name, version: _version, ...project } = readPackageFileSync(rootDir);
46
+ writePackageFileSync(rootDir, { name, version, ...project });
package/cli/nodemon.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, resolve, join } from 'path';
4
+ import nodemon from 'nodemon';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ nodemon({
9
+ script: resolve(__dirname, 'server.js'),
10
+ watch: join(__dirname, '../src'),
11
+ ext: 'js json yaml',
12
+ verbose: true,
13
+ });
14
+
15
+ nodemon.on('log', ({ colour }) => {
16
+ console.log(colour);
17
+ });
18
+
19
+ nodemon.on('quit', () => {
20
+ process.exit();
21
+ });
package/cli/server.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import doggoganger from '../src/index.js';
3
+
4
+ doggoganger();
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@miso.ai/doggoganger",
3
+ "version": "0.9.0-beta.2",
4
+ "description": "A dummy miso endpoint for demo and testing",
5
+ "type": "module",
6
+ "bin": {
7
+ "doggomon": "cli/nodemon.js",
8
+ "doggoganger": "cli/server.js"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "scripts": {
14
+ "start": "nodemon src/index.js",
15
+ "version": "node ./bin/version.js"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/MisoAI/miso-client-js-sdk.git"
20
+ },
21
+ "homepage": "https://misoai.github.io/miso-client-js-sdk/",
22
+ "license": "MIT",
23
+ "contributors": [
24
+ "simonpai <simon.pai@askmiso.com>"
25
+ ],
26
+ "bugs": {
27
+ "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
28
+ },
29
+ "dependencies": {
30
+ "@koa/cors": "^4.0.0",
31
+ "@koa/router": "^12.0.0",
32
+ "js-yaml": "^4.1.0",
33
+ "koa": "^2.14.1",
34
+ "koa-body": "^6.0.1",
35
+ "uuid": "^8.3.2"
36
+ },
37
+ "devDependencies": {
38
+ "nodemon": "^2.0.15"
39
+ }
40
+ }
@@ -0,0 +1,117 @@
1
+ import Router from '@koa/router';
2
+ import { v4 as uuid } from 'uuid';
3
+ import { lorem, articles, utils } from '../data/index.js';
4
+
5
+ const router = new Router();
6
+
7
+ const WPS = 20;
8
+ const ITEMS_LOADING_TIME = 3;
9
+ const STAGES = [
10
+ {
11
+ duration: 1.5,
12
+ text: `Fetching results...`,
13
+ },
14
+ {
15
+ duration: 1.5,
16
+ text: `Generating answer...`,
17
+ }
18
+ ];
19
+
20
+ function formatDatetime(timestamp) {
21
+ const str = new Date(timestamp).toISOString();
22
+ return str.endsWith('Z') ? str.slice(0, -1) : str;
23
+ }
24
+
25
+ const answers = new Map();
26
+
27
+ class Answer {
28
+
29
+ constructor(question, previous_question_id) {
30
+ this.question_id = uuid();
31
+ this.question = question;
32
+ this.previous_answer_id = previous_question_id;
33
+ this.timestamp = Date.now();
34
+ this.datetime = formatDatetime(this.timestamp);
35
+
36
+ this.answer = lorem.lorem({
37
+ min: 50,
38
+ max: 100,
39
+ decorates: ['description'],
40
+ output: 'array',
41
+ });
42
+ this.relatedResources = [...articles({ rows: utils.randomInt(6, 8) })];
43
+ this.sources = [...articles({ rows: utils.randomInt(4, 6) })];
44
+
45
+ this.previous_question_id = previous_question_id && answers.get(previous_question_id) || undefined;
46
+ answers.set(this.question_id, this);
47
+ }
48
+
49
+ get() {
50
+ const now = Date.now();
51
+ const elapsed = (now - this.timestamp) / 1000;
52
+ const [answer, finished] = this._answer(elapsed);
53
+ const sources = this._sources(elapsed);
54
+ const related_resources = this._relatedResources(elapsed);
55
+ const { question_id, question, datetime, previous_question_id } = this;
56
+
57
+ return {
58
+ affiliation: undefined,
59
+ answer,
60
+ datetime,
61
+ finished,
62
+ previous_question_id,
63
+ question,
64
+ question_id,
65
+ related_resources,
66
+ sources,
67
+ };
68
+ }
69
+
70
+ _answer(elapsed) {
71
+ for (const stage of STAGES) {
72
+ elapsed -= stage.duration;
73
+ if (elapsed < 0) {
74
+ return [stage.text, false];
75
+ }
76
+ }
77
+ const wordCount = Math.floor(elapsed * WPS);
78
+ const finished = wordCount >= this.answer.length;
79
+ const text = (finished ? this.answer : this.answer.slice(0, wordCount)).join(' ');
80
+ return [text, finished];
81
+ }
82
+
83
+ _sources(elapsed) {
84
+ const { sources } = this;
85
+ const { length } = sources;
86
+ const loaded = Math.floor(length * elapsed / ITEMS_LOADING_TIME);
87
+ return sources.slice(0, loaded);
88
+ }
89
+
90
+ _relatedResources(elapsed) {
91
+ const { relatedResources } = this;
92
+ const { length } = relatedResources;
93
+ const loaded = Math.floor(length * elapsed / ITEMS_LOADING_TIME);
94
+ return relatedResources.slice(0, loaded);
95
+ }
96
+
97
+ }
98
+
99
+ router.post('/questions', (ctx) => {
100
+ const { q: question, previous_answer_id } = JSON.parse(ctx.request.body);
101
+ const answer = new Answer(question, previous_answer_id);
102
+ const data = answer.get();
103
+ ctx.body = JSON.stringify({ data });
104
+ });
105
+
106
+ router.get('/questions/:id/answer', (ctx) => {
107
+ const { id } = ctx.params;
108
+ const answer = answers.get(id);
109
+ if (!answer) {
110
+ ctx.status = 404;
111
+ } else {
112
+ const data = answer.get();
113
+ ctx.body = JSON.stringify({ data });
114
+ }
115
+ });
116
+
117
+ export default router;
@@ -0,0 +1,10 @@
1
+ import { products as _products } from '../data/index.js';
2
+
3
+ export function products(ctx) {
4
+ const { rows = 5 } = JSON.parse(ctx.request.body);
5
+ ctx.body = {
6
+ data: {
7
+ products: [..._products({ rows })],
8
+ },
9
+ };
10
+ }
@@ -0,0 +1,18 @@
1
+ import Router from '@koa/router';
2
+ import answers from './answers.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
+ const router = new Router();
12
+
13
+ use(router, '/answers', answers);
14
+ use(router, '/recommendation', recommendation);
15
+ use(router, '/search', search);
16
+ use(router, '/interactions', interactions);
17
+
18
+ export default router;
@@ -0,0 +1,9 @@
1
+ import Router from '@koa/router';
2
+
3
+ const router = new Router();
4
+
5
+ router.post('/', (ctx) => {
6
+ ctx.body = { took: 1, errors: false, data: [] };
7
+ });
8
+
9
+ export default router;
@@ -0,0 +1,9 @@
1
+ import Router from '@koa/router';
2
+ import { products } from './handlers.js';
3
+
4
+ const router = new Router();
5
+
6
+ router.post('/user_to_products', products);
7
+ router.post('/product_to_products', products);
8
+
9
+ export default router;
@@ -0,0 +1,8 @@
1
+ import Router from '@koa/router';
2
+ import { products } from './handlers.js';
3
+
4
+ const router = new Router();
5
+
6
+ router.post('/search', products);
7
+
8
+ export default router;
@@ -0,0 +1,47 @@
1
+ import * as u from './utils.js';
2
+ import * as lorem from './lorem.js';
3
+
4
+ export function *articles({ rows, ...options } = {}) {
5
+ for (let i = 0; i < rows; i++) {
6
+ yield article({ ...options, index: i });
7
+ }
8
+ }
9
+
10
+ function article({} = {}) {
11
+ const id = u.id();
12
+ const prices = u.repeat(u.price, 1, 2);
13
+ prices.sort();
14
+ const seed = Math.floor(Math.random() * 1000);
15
+
16
+ return {
17
+ product_id: id,
18
+ authors: lorem.lorem({
19
+ min: 1,
20
+ max: 3,
21
+ fixedStarts: 0,
22
+ decorates: ['title'],
23
+ output: 'array',
24
+ }),
25
+ categories: [],
26
+ tags: lorem.lorem({
27
+ min: 1,
28
+ max: 4,
29
+ fixedStarts: 0,
30
+ output: 'array',
31
+ }),
32
+ title: lorem.lorem({
33
+ min: 4,
34
+ max: 10,
35
+ fixedStarts: 0,
36
+ decorates: ['title'],
37
+ }),
38
+ description: lorem.lorem({
39
+ min: 20,
40
+ max: 40,
41
+ decorates: ['description'],
42
+ }),
43
+ //html,
44
+ cover_image: `https://picsum.photos/seed/${seed}/300`,
45
+ url: `/products/${id}`,
46
+ };
47
+ }
@@ -0,0 +1,4 @@
1
+ export * as lorem from './lorem.js';
2
+ export * from './products.js';
3
+ export * from './articles.js';
4
+ export * as utils from './utils.js';
@@ -0,0 +1,112 @@
1
+ import { readFileSync } from 'fs';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, resolve } from 'path';
4
+ import yaml from 'js-yaml';
5
+ 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'));
9
+
10
+ export function lorem({ decorates = [], min, max, n, output = 'string', ...options } = {}) {
11
+ let iterator = limit({ min, max, n })(base(options));
12
+ for (const decorate of decorates) {
13
+ iterator = lookup(decorate)(iterator);
14
+ }
15
+ return lookup(output)(iterator);
16
+ }
17
+
18
+ const FNS = {
19
+ string,
20
+ array,
21
+ title,
22
+ description,
23
+ }
24
+
25
+ function lookup(fn) {
26
+ return typeof fn === 'string' ? FNS[fn]() : fn();
27
+ }
28
+
29
+ // base //
30
+ export function *base({ words = DEFAULT_WORDS, fixedStarts = 2 } = {}) {
31
+ const wordsLength = words.length;
32
+ for (let i = 0; ; i++) {
33
+ yield words[i < fixedStarts ? i : Math.floor(Math.random() * wordsLength)];
34
+ }
35
+ }
36
+
37
+ // output //
38
+ export function string({ separator = ' ' } = {}) {
39
+ return iterator => [...iterator].join(separator);
40
+ }
41
+
42
+ export function array() {
43
+ return iterator => [...iterator];
44
+ }
45
+
46
+ // decorators //
47
+ export function limit({ n, min = 5, max = 10 }) {
48
+ n = n || randomInt(min, max);
49
+ return function *(iterator) {
50
+ let i = 0;
51
+ for (let word of iterator) {
52
+ if (i++ >= n) {
53
+ break;
54
+ }
55
+ yield word;
56
+ }
57
+ };
58
+ }
59
+
60
+ export function title({} = {}) {
61
+ return function *(iterator) {
62
+ for (let word of iterator) {
63
+ yield capitalize(word);
64
+ }
65
+ };
66
+ }
67
+
68
+ export function description({
69
+ wordsPerSentenceAvg = 24,
70
+ wordsPerSentenceStd = 5,
71
+ } = {}) {
72
+ return function *(iterator) {
73
+ let word;
74
+ let slen = 0;
75
+ for (let _word of iterator) {
76
+ if (word) {
77
+ yield word;
78
+ }
79
+ word = _word;
80
+ if (slen === 0) {
81
+ word = capitalize(word);
82
+ slen = Math.max(1, gaussMS(wordsPerSentenceAvg, wordsPerSentenceStd));
83
+ }
84
+ if (--slen === 0) {
85
+ word += '.';
86
+ }
87
+ }
88
+ if (word) {
89
+ if (!word.endsWith('.')) {
90
+ word += '.';
91
+ }
92
+ yield word;
93
+ }
94
+ };
95
+ }
96
+
97
+ // helpers //
98
+ function capitalize(word) {
99
+ return word[0].toUpperCase() + word.substring(1);
100
+ }
101
+
102
+ function gaussMS(mean, std) {
103
+ return Math.round(gaussRandom() * std + mean);
104
+ }
105
+
106
+ function gaussRandom() {
107
+ return uniformRandom() + uniformRandom() + uniformRandom();
108
+ }
109
+
110
+ function uniformRandom() {
111
+ return Math.random() * 2 - 1;
112
+ }
@@ -0,0 +1,51 @@
1
+ import * as u from './utils.js';
2
+ import * as lorem from './lorem.js';
3
+
4
+ export function *products({ rows, ...options } = {}) {
5
+ for (let i = 0; i < rows; i++) {
6
+ yield product({ ...options, index: i });
7
+ }
8
+ }
9
+
10
+ function product({} = {}) {
11
+ const id = u.id();
12
+ const prices = u.repeat(u.price, 1, 2);
13
+ prices.sort();
14
+ const seed = Math.floor(Math.random() * 1000);
15
+
16
+ return {
17
+ product_id: id,
18
+ authors: lorem.lorem({
19
+ min: 1,
20
+ max: 3,
21
+ fixedStarts: 0,
22
+ decorates: ['title'],
23
+ output: 'array',
24
+ }),
25
+ categories: [],
26
+ tags: lorem.lorem({
27
+ min: 1,
28
+ max: 4,
29
+ fixedStarts: 0,
30
+ output: 'array',
31
+ }),
32
+ title: lorem.lorem({
33
+ min: 2,
34
+ max: 6,
35
+ fixedStarts: 0,
36
+ decorates: ['title'],
37
+ }),
38
+ description: lorem.lorem({
39
+ min: 10,
40
+ max: 20,
41
+ decorates: ['description'],
42
+ }),
43
+ //html,
44
+ cover_image: `https://picsum.photos/seed/${seed}/300`,
45
+ url: `/products/${id}`,
46
+ sale_price: prices[0],
47
+ original_price: prices[prices.length - 1],
48
+ rating: u.rating(),
49
+ availability: u.availability(),
50
+ };
51
+ }
@@ -0,0 +1,28 @@
1
+ export function randomInt(min, max) {
2
+ return max > min ? min + Math.floor(Math.random() * (max - min)) : min;
3
+ }
4
+
5
+ export function repeat(fn, min = 1, max = 2) {
6
+ const n = randomInt(min, max);
7
+ const result = [];
8
+ for (let i = 0; i < n; i++) {
9
+ result.push(fn());
10
+ }
11
+ return result;
12
+ }
13
+
14
+ export function id() {
15
+ return Math.random().toString(36).substring(2, 10);
16
+ }
17
+
18
+ export function availability() {
19
+ return Math.random() > 0.3 ? 'IN_STOCK' : 'OUT_OF_STOCK';
20
+ }
21
+
22
+ export function price() {
23
+ return Math.floor(Math.random() * 10000) / 100;
24
+ }
25
+
26
+ export function rating() {
27
+ return Math.floor(Math.random() * 500) / 100 + 1;
28
+ }
@@ -0,0 +1,178 @@
1
+ - lorem
2
+ - ipsum
3
+ - dolor
4
+ - sit
5
+ - amet
6
+ - consectetur
7
+ - adipiscing
8
+ - elit
9
+ - curabitur
10
+ - vel
11
+ - hendrerit
12
+ - libero
13
+ - eleifend
14
+ - blandit
15
+ - nunc
16
+ - ornare
17
+ - odio
18
+ - ut
19
+ - orci
20
+ - gravida
21
+ - imperdiet
22
+ - nullam
23
+ - purus
24
+ - lacinia
25
+ - a
26
+ - pretium
27
+ - quis
28
+ - congue
29
+ - praesent
30
+ - sagittis
31
+ - laoreet
32
+ - auctor
33
+ - mauris
34
+ - non
35
+ - velit
36
+ - eros
37
+ - dictum
38
+ - proin
39
+ - accumsan
40
+ - sapien
41
+ - nec
42
+ - massa
43
+ - volutpat
44
+ - venenatis
45
+ - sed
46
+ - eu
47
+ - molestie
48
+ - lacus
49
+ - quisque
50
+ - porttitor
51
+ - ligula
52
+ - dui
53
+ - mollis
54
+ - tempus
55
+ - at
56
+ - magna
57
+ - vestibulum
58
+ - turpis
59
+ - ac
60
+ - diam
61
+ - tincidunt
62
+ - id
63
+ - condimentum
64
+ - enim
65
+ - sodales
66
+ - in
67
+ - hac
68
+ - habitasse
69
+ - platea
70
+ - dictumst
71
+ - aenean
72
+ - neque
73
+ - fusce
74
+ - augue
75
+ - leo
76
+ - eget
77
+ - semper
78
+ - mattis
79
+ - tortor
80
+ - scelerisque
81
+ - nulla
82
+ - interdum
83
+ - tellus
84
+ - malesuada
85
+ - rhoncus
86
+ - porta
87
+ - sem
88
+ - aliquet
89
+ - et
90
+ - nam
91
+ - suspendisse
92
+ - potenti
93
+ - vivamus
94
+ - luctus
95
+ - fringilla
96
+ - erat
97
+ - donec
98
+ - justo
99
+ - vehicula
100
+ - ultricies
101
+ - varius
102
+ - ante
103
+ - primis
104
+ - faucibus
105
+ - ultrices
106
+ - posuere
107
+ - cubilia
108
+ - curae
109
+ - etiam
110
+ - cursus
111
+ - aliquam
112
+ - quam
113
+ - dapibus
114
+ - nisl
115
+ - feugiat
116
+ - egestas
117
+ - class
118
+ - aptent
119
+ - taciti
120
+ - sociosqu
121
+ - ad
122
+ - litora
123
+ - torquent
124
+ - per
125
+ - conubia
126
+ - nostra
127
+ - inceptos
128
+ - himenaeos
129
+ - phasellus
130
+ - nibh
131
+ - pulvinar
132
+ - vitae
133
+ - urna
134
+ - iaculis
135
+ - lobortis
136
+ - nisi
137
+ - viverra
138
+ - arcu
139
+ - morbi
140
+ - pellentesque
141
+ - metus
142
+ - commodo
143
+ - ut
144
+ - facilisis
145
+ - felis
146
+ - tristique
147
+ - ullamcorper
148
+ - placerat
149
+ - aenean
150
+ - convallis
151
+ - sollicitudin
152
+ - integer
153
+ - rutrum
154
+ - duis
155
+ - est
156
+ - etiam
157
+ - bibendum
158
+ - donec
159
+ - pharetra
160
+ - vulputate
161
+ - maecenas
162
+ - mi
163
+ - fermentum
164
+ - consequat
165
+ - suscipit
166
+ - aliquam
167
+ - habitant
168
+ - senectus
169
+ - netus
170
+ - fames
171
+ - quisque
172
+ - euismod
173
+ - curabitur
174
+ - lectus
175
+ - elementum
176
+ - tempor
177
+ - risus
178
+ - cras
package/src/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import Koa from 'koa';
2
+ import Router from '@koa/router';
3
+ import cors from '@koa/cors';
4
+ import { koaBody } from 'koa-body';
5
+ import api from './api/index.js';
6
+
7
+ export default function doggoganger() {
8
+ const app = new Koa();
9
+ const router = new Router();
10
+
11
+ router.use('/api', api.routes(), api.allowedMethods());
12
+
13
+ app
14
+ .use(cors())
15
+ .use(koaBody())
16
+ .use(router.routes())
17
+ .use(router.allowedMethods())
18
+ .listen(9901);
19
+ }