5 Commits
Author SHA1 Message Date
ntr 413c1f4840 nice bundles markup 2022-08-03 00:05:06 +10:00
ntr 02aadcf2db unistore sucks i think 2022-08-01 18:29:23 +10:00
ntr baeaead650 client init 2022-08-01 00:05:55 +10:00
ntr 8700b15f39 bundle it up 2022-07-28 20:15:27 +10:00
ntr adcc19564f prisma pretty noice 2022-07-28 00:18:11 +10:00
47 changed files with 38486 additions and 826 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
node_modules
/build
/*.log
+19
View File
@@ -0,0 +1,19 @@
# mcrates
## CLI Commands
* `npm install`: Installs dependencies
* `npm run dev`: Run a development, HMR server
* `npm run serve`: Run a production-like server
* `npm run build`: Production-ready build
* `npm run lint`: Pass TypeScript files using ESLint
* `npm run test`: Run Jest and Enzyme with
[`enzyme-adapter-preact-pure`](https://github.com/preactjs/enzyme-adapter-preact-pure) for
your tests
For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md).
+37598
View File
File diff suppressed because it is too large. Load diff
+41
View File
@@ -0,0 +1,41 @@
{
"private": true,
"name": "mcrates-client",
"version": "0.1.0",
"license": "MIT",
"scripts": {
"build": "preact build",
"start": "preact watch",
"lint": "eslint src"
},
"eslintConfig": {
"parser": "@typescript-eslint/parser",
"extends": [
"preact",
"plugin:@typescript-eslint/recommended"
],
"ignorePatterns": [
"build/"
]
},
"dependencies": {
"preact": "^10.10.0",
"preact-render-to-string": "^5.2.1",
"preact-router": "^3.2.1",
"react-redux": "^8.0.2",
"redux": "^4.2.0",
"unistore": "^3.5.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.30.6",
"@typescript-eslint/parser": "^5.30.6",
"eslint": "^8.20.0",
"eslint-config-preact": "^1.3.0",
"eslint-plugin-jest": "^26.7.0",
"jest": "^28.1.3",
"less": "^4.1.3",
"less-loader": "^7.3.0",
"preact-cli": "^3.4.1",
"typescript": "^4.5.2"
}
}
+10
View File
@@ -0,0 +1,10 @@
import axios from 'axios';
const actions = store => ({
getBundles: async () => {
const { data: bundles } = await axios.get('http://localhost:41337/bundles');
return { bundles };
},
});
export default actions;
+30
View File
@@ -0,0 +1,30 @@
import { h } from 'preact';
import { Route, Router } from 'preact-router';
import { Provider } from 'unistore/preact'
import mcratesStore from './store';
import actions from './actions';
import Header from './components/header';
import Home from './routes/home';
import Profile from './routes/profile';
const store = mcratesStore();
console.log(Home);
const App = () => (
<Provider store={store}>
<div id="app">
<Header />
<Router>
<Route path="/" component={Home} />
<Route path="/profile/" component={Profile} user="me" />
<Route path="/profile/:user" component={Profile} />
</Router>
</div>
</Provider>
);
export default App;
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

+15
View File
@@ -0,0 +1,15 @@
import { h } from 'preact';
import { Link } from 'preact-router/match';
const Header = () => (
<header class={'header'}>
<h1>Mates Crates</h1>
<nav>
<Link activeClassName={'active'} href="/">Home</Link>
<Link activeClassName={'active'} href="/profile">Me</Link>
<Link activeClassName={'active'} href="/profile/john">John</Link>
</nav>
</header>
);
export default Header;
+4
View File
@@ -0,0 +1,4 @@
declare module "*.less" {
const mapping: Record<string, string>;
export default mapping;
}
+4
View File
@@ -0,0 +1,4 @@
import './style/index.less';
import App from './app';
export default App;
+21
View File
@@ -0,0 +1,21 @@
{
"name": "mcrates",
"short_name": "mcrates",
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#fff",
"theme_color": "#673ab8",
"icons": [
{
"src": "/assets/icons/android-chrome-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/assets/icons/android-chrome-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
]
}
+49
View File
@@ -0,0 +1,49 @@
import { h } from 'preact';
import { connect } from 'unistore/preact'
import actions from './../actions';
// import Bundle from './../components/bundle';
interface Props {
bundles: any[];
getBundles: () => void,
}
function Bundle(bundle) {
return (
<div class="bundle">
<h2>{bundle.username} - {bundle.seller}</h2>
<h3>{bundle.total} {bundle.currency}</h3>
{bundle.items.map(i => (
<div key={i.id}>
<a href={i.uri}>
<h4>{i.description}</h4>
</a>
<h5>{i.price} {i.currency}</h5>
</div>
))}
</div>
)
}
function Home({ bundles, getBundles }: Props) {
if (!bundles.length) {
getBundles();
return (
<div class={'home'}>
loading...
</div>
);
}
return (
<div class={'home'}>
{bundles.map(Bundle)}
</div>
);
}
export default connect('bundles', actions)(Home);
+35
View File
@@ -0,0 +1,35 @@
import { h } from 'preact';
import { useEffect, useState } from 'preact/hooks';
import style from './style.less';
interface Props {
user: string;
}
const Profile = ({ user }: Props) => {
const [time, setTime] = useState<number>(Date.now());
const [count, setCount] = useState<number>(0);
useEffect(() => {
let timer = setInterval(() => setTime(Date.now()), 1000);
return () => clearInterval(timer);
}, []);
return (
<div class={'profile'}>
<h1>Profile: {user}</h1>
<p>This is the user profile for a user named { user }.</p>
<div>Current time: {new Date(time).toLocaleString()}</div>
<p>
<button onClick={() => setCount((count) => count + 1)}>Click Me</button>
{' '}
Clicked {count} times.
</p>
</div>
);
};
export default Profile;
+6
View File
@@ -0,0 +1,6 @@
import createStore from 'unistore'
export default function mcratesStore() {
return createStore({ bundles: [] })
}
+90
View File
@@ -0,0 +1,90 @@
html, body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
background: #FAFAFA;
font-family: 'Helvetica Neue', arial, sans-serif;
font-weight: 400;
color: #444;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
}
#app {
height: 100%;
}
.home {
padding: 56px 20px;
min-height: 100%;
width: 100%;
display: flex;
.bundle {
flex: 1;
}
}
.profile {
padding: 56px 20px;
min-height: 100%;
width: 100%;
}
.header {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 56px;
padding: 0;
background: #673AB7;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
z-index: 50;
}
.header {
h1 {
float: left;
margin: 0;
padding: 0 15px;
font-size: 24px;
line-height: 56px;
font-weight: 400;
color: #FFF;
}
nav {
float: right;
font-size: 100%;
}
nav a {
display: inline-block;
height: 56px;
line-height: 56px;
padding: 0 15px;
min-width: 50px;
text-align: center;
background: rgba(255,255,255,0);
text-decoration: none;
color: #FFF;
will-change: background-color;
}
nav a:hover,
nav a:active {
background: rgba(0,0,0,0.2);
}
nav a.active {
background: rgba(0,0,0,0.4);
}
}
+64
View File
@@ -0,0 +1,64 @@
{
"compilerOptions": {
/* Basic Options */
"target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation: */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */
"jsxFragmentFactory": "Fragment", /* Specify the JSX fragment factory function to use when targeting react JSX emit with jsxFactory compiler option is specified, e.g. Fragment. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": false, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"esModuleInterop": true, /* */
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"react": ["./node_modules/preact/compat"],
"react-dom": ["./node_modules/preact/compat"]
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */
},
"include": ["src/**/*"]
}
@@ -1,8 +0,0 @@
# #!/bin/bash
# set -e
# psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
# CREATE USER mcrates;
# CREATE DATABASE mcrates;
# GRANT ALL PRIVILEGES ON DATABASE mcrates TO mcrates;
# EOSQL
-93
View File
@@ -1,93 +0,0 @@
import fs from 'fs/promises';
import _ from 'lodash';
import Promise from 'bluebird';
import discogsService from './src/discogs';
import pgService from './src/pg';
import { logAxiosError } from './src/utils';
async function start() {
const pg = pgService();
const discogs = discogsService(pg);
const usernames = [
'margh',
'jmcdermid1',
'vanderbabe',
];
async function fetchListings(w) {
return w.map(discogs.listings)
}
async function populateUserListings(user) {
console.log(user);
const wants = await discogs.wantlist(user);
console.log(user.discogs, 'has', wants.length, 'wants');
for (const want of wants) {
const listings = await discogs.listings(want);
const populated = listings.filter(l => l);
console.log(want.id, 'has', populated.length, '/', listings.length, 'listings');
}
// const listings = await Promise
// .all(releases.map(async want => {
// const listings = (await getListings(want)).filter(l => l);
// const insertions = await Promise.all(listings.map(async l => {
// const listing = await pg.listingUpsert(l, want);
// console.log(listing.id, listing.data.release.description, want.user, listing.seller);
// return listing;
// }));
// console.log(insertions.length, want.id);
// }));
// return listings;
}
return Promise.resolve(usernames)
.map(u => discogs.user(u))
.map(populateUserListings);
// return Promise.map(releases, async w => {
// try {
// const l = await discogs.listings(w.id)
// return l;
// } catch (e) {
// // console.error(e);
// return null;
// }
// })
// .then(listings => _
// .chain(listings)
// .filter(l => l)
// .flatten()
// // .map(l => ({
// // id: l.id,
// // description: l.release.description,
// // seller_id: l.seller.id,
// // seller_name: l.seller.username,
// // }))
// // .groupBy(l => l.seller_id)
// .value()
// );
}
function run() {
start()
.then(r => {
console.log(r);
setTimeout(run, 60 * 1000);
})
.catch(e => {
console.error(e);
if (e.message === 'rate limited') {
return setTimeout(run, 60 * 1000);
}
process.exit(1);
});
}
run();
-17
View File
@@ -1,17 +0,0 @@
module.exports = {
client: 'postgresql',
connection: {
host : '0.0.0.0',
port: 45432,
database: 'mcrates',
user: 'mcrates',
password: 'digdeep',
},
pool: {
min: 2,
max: 10
},
migrations: {
tableName: 'knex_migrations'
}
};
-17
View File
@@ -1,17 +0,0 @@
exports.up = async knex => {
await knex.schema.createTable('users', table => {
table.uuid('id')
.primary()
.defaultTo(knex.raw('gen_random_uuid()'));
table.string('discogs')
.unique();
table.jsonb('data')
.notNullable();
table.timestamps(true, true);
});
};
exports.down = async () => true;
-27
View File
@@ -1,27 +0,0 @@
exports.up = async knex => {
await knex.schema.createTable('wants', table => {
table.uuid('id')
.primary()
.defaultTo(knex.raw('gen_random_uuid()'));
table.integer('release')
.notNullable()
.index();
table.uuid('user')
table.foreign('user')
.references('id')
.inTable('users')
.onDelete('CASCADE');
table.jsonb('data')
.notNullable();
// user can only want a release once
table.unique(['user', 'release']);
table.timestamps(true, true);
});
};
exports.down = async () => true;
-23
View File
@@ -1,23 +0,0 @@
exports.up = async knex => {
await knex.schema.createTable('listings', table => {
table.integer('id')
.primary();
table.uuid('want')
table.foreign('want')
.references('id')
.inTable('wants')
.onDelete('CASCADE');
table.integer('seller')
.notNullable()
.index();
table.jsonb('data')
.notNullable();
table.timestamps(true, true);
});
};
exports.down = async () => true;
@@ -1,13 +0,0 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"discogs" TEXT,
"data" JSONB NOT NULL,
"creaedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_discogs_key" ON "User"("discogs");
-3
View File
@@ -1,3 +0,0 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
-20
View File
@@ -1,20 +0,0 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
discogs String? @unique
data Json
creaedAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
+14
View File
@@ -0,0 +1,14 @@
// global type aliases and whatnot
type UUID = string;
type DiscogsName = string;
type DiscogsListing = number;
type DiscogsSeller = number;
type BundleCandidate = {
seller: DiscogsSeller,
count: number,
listings: DiscogsListing[],
}
+74
View File
@@ -0,0 +1,74 @@
import fs from 'fs/promises';
import _ from 'lodash';
import Bunyan from 'bunyan';
import { PrismaClient } from '@prisma/client'
import Promise from "bluebird";
import discogsService from './src/discogs';
import pgService from './src/pg';
import { logAxiosError } from './src/utils';
import createLog from './src/log';
import createApi from './src/api';
async function start(log) {
const prisma = new PrismaClient();
const pg = pgService(log, prisma);
const discogs = discogsService(log, pg);
const api = createApi(log, pg);
const usernames = [
'vanderbabe',
'margh',
'jmcdermid1',
];
async function populateUserListings(user) {
log.info(user);
const wants = await discogs.wantlist(user);
// const latest = await discogs.wantlistLatest(user);
// if (wants.length !== latest.length)
log.info({ username: user.discogs, wantCount: wants.length });
return Promise.mapSeries(wants, async want => {
const listings = await discogs.listings(want);
const populated = listings.filter(l => l);
log.info({ id: want.id, release: want.release, populated: populated.length, total: listings.length });
return populated;
});
}
// await Promise.mapSeries(usernames, async u => {
// const user = await discogs.user(u)
// return populateUserListings(user);
// });
await api.listen();
// return getBundles();
}
async function run() {
const log = createLog();
process.on('SIGTERM', () => {
log.debug('SIGTERM received, exiting');
process.exit(0);
});
try {
const r = await start(log)
// log.info('done syncing', { totalListings: r.flat().length });
log.info(r);
setTimeout(run, 60 * 1000);
} catch (e) {
if (e.message === 'rate limited') {
log.info('rate limited, trying again in 60s');
return setTimeout(run, 60 * 1000);
}
log.error(e);
process.exit(1);
}
}
run();
+79 -468
View File
@@ -13,12 +13,13 @@
"axios": "^0.27.2", "axios": "^0.27.2",
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"bunyan": "^1.8.15", "bunyan": "^1.8.15",
"cors": "^2.8.5",
"express": "^4.18.1", "express": "^4.18.1",
"knex": "^2.2.0", "lodash": "^4.17.21"
"lodash": "^4.17.21",
"pg": "^8.7.3"
}, },
"devDependencies": { "devDependencies": {
"@types/bluebird": "^3.5.36",
"@types/lodash": "^4.14.182",
"node-dev": "^7.4.3", "node-dev": "^7.4.3",
"prisma": "^4.1.0", "prisma": "^4.1.0",
"ts-node-dev": "^2.0.0", "ts-node-dev": "^2.0.0",
@@ -118,6 +119,18 @@
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"dev": true "dev": true
}, },
"node_modules/@types/bluebird": {
"version": "3.5.36",
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz",
"integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==",
"dev": true
},
"node_modules/@types/lodash": {
"version": "4.14.182",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
"dev": true
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.0.6", "version": "18.0.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
@@ -279,14 +292,6 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true "dev": true
}, },
"node_modules/buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
"engines": {
"node": ">=4"
}
},
"node_modules/bunyan": { "node_modules/bunyan": {
"version": "1.8.15", "version": "1.8.15",
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz",
@@ -351,11 +356,6 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/colorette": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
},
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -367,14 +367,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/commander": {
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz",
"integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==",
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -413,6 +405,18 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
}, },
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/create-require": { "node_modules/create-require": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@@ -511,27 +515,11 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-html": { "node_modules/escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
}, },
"node_modules/esm": {
"version": "3.2.25",
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
"engines": {
"node": ">=6"
}
},
"node_modules/etag": { "node_modules/etag": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -709,15 +697,11 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"dev": true,
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/getopts": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
"integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="
},
"node_modules/glob": { "node_modules/glob": {
"version": "7.2.3", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -819,14 +803,6 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"node_modules/interpret": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -851,6 +827,7 @@
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true,
"dependencies": { "dependencies": {
"has": "^1.0.3" "has": "^1.0.3"
}, },
@@ -921,77 +898,6 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true "dev": true
}, },
"node_modules/knex": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/knex/-/knex-2.2.0.tgz",
"integrity": "sha512-yhm1Qe9Ok0TeXBq3nNHqZYJPrQ4Iw2tq9k/HxjrZ/EWec2ifOjJlkNHr26v8cQrWtk5iG3iwfUazTIWy+VKG5g==",
"dependencies": {
"colorette": "2.0.19",
"commander": "^9.1.0",
"debug": "4.3.4",
"escalade": "^3.1.1",
"esm": "^3.2.25",
"get-package-type": "^0.1.0",
"getopts": "2.3.0",
"interpret": "^2.2.0",
"lodash": "^4.17.21",
"pg-connection-string": "2.5.0",
"rechoir": "^0.8.0",
"resolve-from": "^5.0.0",
"tarn": "^3.0.2",
"tildify": "2.0.0"
},
"bin": {
"knex": "bin/cli.js"
},
"engines": {
"node": ">=12"
},
"peerDependenciesMeta": {
"better-sqlite3": {
"optional": true
},
"mysql": {
"optional": true
},
"mysql2": {
"optional": true
},
"pg": {
"optional": true
},
"pg-native": {
"optional": true
},
"sqlite3": {
"optional": true
},
"tedious": {
"optional": true
}
}
},
"node_modules/knex/node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/knex/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -1232,6 +1138,14 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.12.2", "version": "1.12.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
@@ -1260,11 +1174,6 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -1285,87 +1194,14 @@
"node_modules/path-parse": { "node_modules/path-parse": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
}, },
"node_modules/pg": {
"version": "8.7.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz",
"integrity": "sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==",
"dependencies": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.1",
"pg-protocol": "^1.5.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"pg-native": ">=2.0.0"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-connection-string": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
"integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz",
"integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -1378,41 +1214,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prisma": { "node_modules/prisma": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz", "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz",
@@ -1490,21 +1291,11 @@
"node": ">=8.10.0" "node": ">=8.10.0"
} }
}, },
"node_modules/rechoir": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
"integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
"dependencies": {
"resolve": "^1.20.0"
},
"engines": {
"node": ">= 10.13.0"
}
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"dependencies": { "dependencies": {
"is-core-module": "^2.9.0", "is-core-module": "^2.9.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
@@ -1517,14 +1308,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"engines": {
"node": ">=8"
}
},
"node_modules/rimraf": { "node_modules/rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -1667,14 +1450,6 @@
"source-map": "^0.6.0" "source-map": "^0.6.0"
} }
}, },
"node_modules/split2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
"integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/statuses": { "node_modules/statuses": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -1705,6 +1480,7 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@@ -1712,22 +1488,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/tarn": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
"integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/tildify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz",
"integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==",
"engines": {
"node": ">=8"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -1935,6 +1695,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true,
"engines": { "engines": {
"node": ">=0.4" "node": ">=0.4"
} }
@@ -2030,6 +1791,18 @@
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"dev": true "dev": true
}, },
"@types/bluebird": {
"version": "3.5.36",
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz",
"integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==",
"dev": true
},
"@types/lodash": {
"version": "4.14.182",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
"integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
"dev": true
},
"@types/node": { "@types/node": {
"version": "18.0.6", "version": "18.0.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
@@ -2166,11 +1939,6 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true "dev": true
}, },
"buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
},
"bunyan": { "bunyan": {
"version": "1.8.15", "version": "1.8.15",
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz",
@@ -2212,11 +1980,6 @@
"readdirp": "~3.6.0" "readdirp": "~3.6.0"
} }
}, },
"colorette": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
},
"combined-stream": { "combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -2225,11 +1988,6 @@
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
} }
}, },
"commander": {
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz",
"integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw=="
},
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2259,6 +2017,15 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
}, },
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"create-require": { "create-require": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@@ -2334,21 +2101,11 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
}, },
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
},
"escape-html": { "escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
}, },
"esm": {
"version": "3.2.25",
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA=="
},
"etag": { "etag": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -2480,12 +2237,8 @@
"get-package-type": { "get-package-type": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
}, "dev": true
"getopts": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
"integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="
}, },
"glob": { "glob": {
"version": "7.2.3", "version": "7.2.3",
@@ -2564,11 +2317,6 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"interpret": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
},
"ipaddr.js": { "ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -2587,6 +2335,7 @@
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true,
"requires": { "requires": {
"has": "^1.0.3" "has": "^1.0.3"
} }
@@ -2633,42 +2382,6 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true "dev": true
}, },
"knex": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/knex/-/knex-2.2.0.tgz",
"integrity": "sha512-yhm1Qe9Ok0TeXBq3nNHqZYJPrQ4Iw2tq9k/HxjrZ/EWec2ifOjJlkNHr26v8cQrWtk5iG3iwfUazTIWy+VKG5g==",
"requires": {
"colorette": "2.0.19",
"commander": "^9.1.0",
"debug": "4.3.4",
"escalade": "^3.1.1",
"esm": "^3.2.25",
"get-package-type": "^0.1.0",
"getopts": "2.3.0",
"interpret": "^2.2.0",
"lodash": "^4.17.21",
"pg-connection-string": "2.5.0",
"rechoir": "^0.8.0",
"resolve-from": "^5.0.0",
"tarn": "^3.0.2",
"tildify": "2.0.0"
},
"dependencies": {
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -2851,6 +2564,11 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true "dev": true
}, },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"object-inspect": { "object-inspect": {
"version": "1.12.2", "version": "1.12.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
@@ -2873,11 +2591,6 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
},
"parseurl": { "parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -2892,97 +2605,20 @@
"path-parse": { "path-parse": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
}, },
"path-to-regexp": { "path-to-regexp": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
}, },
"pg": {
"version": "8.7.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz",
"integrity": "sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==",
"requires": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.1",
"pg-protocol": "^1.5.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
}
},
"pg-connection-string": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
"integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
},
"pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
},
"pg-pool": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz",
"integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==",
"requires": {}
},
"pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
},
"pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"requires": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
}
},
"pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"requires": {
"split2": "^4.1.0"
}
},
"picomatch": { "picomatch": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true "dev": true
}, },
"postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
},
"postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
},
"postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
},
"postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"requires": {
"xtend": "^4.0.0"
}
},
"prisma": { "prisma": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz", "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.1.0.tgz",
@@ -3034,29 +2670,17 @@
"picomatch": "^2.2.1" "picomatch": "^2.2.1"
} }
}, },
"rechoir": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
"integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
"requires": {
"resolve": "^1.20.0"
}
},
"resolve": { "resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"requires": { "requires": {
"is-core-module": "^2.9.0", "is-core-module": "^2.9.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0" "supports-preserve-symlinks-flag": "^1.0.0"
} }
}, },
"resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -3166,11 +2790,6 @@
"source-map": "^0.6.0" "source-map": "^0.6.0"
} }
}, },
"split2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
"integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ=="
},
"statuses": { "statuses": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -3191,17 +2810,8 @@
"supports-preserve-symlinks-flag": { "supports-preserve-symlinks-flag": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
}, "dev": true
"tarn": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
"integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ=="
},
"tildify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz",
"integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw=="
}, },
"to-regex-range": { "to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
@@ -3334,7 +2944,8 @@
"xtend": { "xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
}, },
"yallist": { "yallist": {
"version": "4.0.0", "version": "4.0.0",
+5 -4
View File
@@ -7,11 +7,13 @@
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"migrate": "knex migrate:latest", "migrate": "knex migrate:latest",
"migrate:make": "prisma migrate dev --name", "migrate:make": "prisma migrate dev --name",
"start": "ts-node-dev --respawn --transpile-only index.ts" "start": "ts-node-dev --respawn --transpile-only index.ts | bunyan -o short"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@types/bluebird": "^3.5.36",
"@types/lodash": "^4.14.182",
"node-dev": "^7.4.3", "node-dev": "^7.4.3",
"prisma": "^4.1.0", "prisma": "^4.1.0",
"ts-node-dev": "^2.0.0", "ts-node-dev": "^2.0.0",
@@ -22,9 +24,8 @@
"axios": "^0.27.2", "axios": "^0.27.2",
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"bunyan": "^1.8.15", "bunyan": "^1.8.15",
"cors": "^2.8.5",
"express": "^4.18.1", "express": "^4.18.1",
"knex": "^2.2.0", "lodash": "^4.17.21"
"lodash": "^4.17.21",
"pg": "^8.7.3"
} }
} }
+51
View File
@@ -0,0 +1,51 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["interactiveTransactions"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Listing {
id Int @id
seller Int
data Json
wantId String @db.Uuid
want Want @relation(fields: [wantId], references: [id], onDelete: Cascade, onUpdate: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([seller])
}
model User {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
discogs String? @unique
data Json
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
wants Want[]
}
model Want {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
release Int?
data Json
userId String @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
listings Listing[]
@@unique([userId, release])
@@index([release])
}
+84
View File
@@ -0,0 +1,84 @@
import express from 'express';
import http from 'http';
import cors from 'cors';
import { groupBy } from 'lodash';
import type Log from 'bunyan';
import { Pg } from './pg';
import type { Listing, User, Prisma } from '@prisma/client';
export default function createApi(log: Log, pg: Pg) {
const PORT = 41337;
const app = express();
const server = http.createServer(app);
interface ListingData extends Listing {
data: any,
want: { user: User },
};
async function getBundles() {
const bundles = await pg.bundleCandidates();
return bundles.map((b: ListingData[]) => {
const total = b.reduce((acc, l) => acc + l.data.original_price.value, 0);
const items = b.map(l => ({
id: l.data.id,
description: l.data.release.description,
price: l.data.original_price.value,
currency: l.data.original_price.curr_abbr,
condition: l.data.condition,
uri: l.data.uri,
user: l.want.user.discogs,
}));
const shipping = b[0]?.data.seller.shipping;
console.log(b.map(l => l.data));
const bundle = {
username: b[0]?.data.seller.username,
seller: b[0]?.data.seller.id,
shipsFrom: b[0]?.data.ships_from,
total,
currency: b[0]?.data.original_price.curr_abbr,
shipping: '...',
items,
mentionsAustralia: shipping.toLowerCase().includes('australia'),
mentionsWorldwide: shipping.toLowerCase().includes('world'),
}
log.info(bundle);
return bundle;
});
}
function listen() {
return new Promise((resolve, reject) => {
server.on('error', err => {
reject(err);
})
server.listen(PORT, () => {
log.info('listening', { PORT });
});
});
}
app.use(cors());
app.get('/bundles', async (req, res, next) => {
const bundles = await getBundles();
// log.info(bundles);
res.json(bundles);
})
return {
app,
listen,
};
}
+28 -12
View File
@@ -2,11 +2,15 @@ import fs from 'fs/promises';
import axios from 'axios'; import axios from 'axios';
import cheerio from 'cheerio'; import cheerio from 'cheerio';
import Bluebird from 'bluebird';
import type Log from 'bunyan';
import type { PrismaClient, Want, User, Listing } from '@prisma/client';
import { logAxiosError } from './utils'; import { logAxiosError } from './utils';
import { Pg } from './pg'; import { Pg } from './pg';
export default function discogs(pg: Pg) { export default function discogs(log: Log, pg: Pg) {
const TOKEN = 'wRTXuYZGOPsIQgKRGKCAkUkidGtvpSfQgPWkYIpD'; const TOKEN = 'wRTXuYZGOPsIQgKRGKCAkUkidGtvpSfQgPWkYIpD';
const apiBase = 'https://api.discogs.com' const apiBase = 'https://api.discogs.com'
const discogsBase = 'https://www.discogs.com' const discogsBase = 'https://www.discogs.com'
@@ -14,20 +18,23 @@ export default function discogs(pg: Pg) {
const api = axios.create({ const api = axios.create({
headers: { headers: {
'Authorization': TOKEN, 'Authorization': TOKEN,
// discogs only returns the first page without a desktop u/a
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0',
}, },
}); });
function handleApiError(e) { async function handleApiError(e) {
logAxiosError(e); logAxiosError(e);
if (e.response.status === 429) { if (e.response.status === 429) {
throw new Error('rate limited'); throw new Error('rate limited');
} }
return null;
} }
async function user(discogs: string) { async function user(discogs: string) {
const user = await pg.userGetName(discogs); const user = await pg.userFindName(discogs);
if (user) return user; if (user) return user;
console.log('fetching user', discogs); log.debug('fetching user', discogs);
const res = await api.get(`${apiBase}/users/${discogs}`); const res = await api.get(`${apiBase}/users/${discogs}`);
return pg.userUpsert(res.data); return pg.userUpsert(res.data);
} }
@@ -35,8 +42,13 @@ export default function discogs(pg: Pg) {
async function wantlist(user: User): Promise<Want[]> { async function wantlist(user: User): Promise<Want[]> {
const stored = await pg.userWants(user); const stored = await pg.userWants(user);
if (stored.length) return stored; if (stored.length) return stored;
const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${user.data.username}/wants`); return wantlistLatest(user);
return Promise.all(wants.map(r => pg.wantUpsert(r, user))) }
async function wantlistLatest(user: User): Promise<Want[]> {
const { username } = user.data as { username: string };
const { data: { wants, pagination }} = await api.get(`${apiBase}/users/${username}/wants`);
return Bluebird.mapSeries(wants, async r => pg.wantUpsert(r, user));
} }
async function inventory(user: string) { async function inventory(user: string) {
@@ -44,8 +56,8 @@ export default function discogs(pg: Pg) {
return res.data.listings; return res.data.listings;
} }
async function listings(want: Want): Promise<Listing[]> { async function listings(want: Want) {
const res = await api.get(`https://www.discogs.com/sell/release/${want.release}`); const res = await api.get(`https://www.discogs.com/sell/release/${want.release}?limit=250`);
const $ = cheerio.load(res.data); const $ = cheerio.load(res.data);
const ids = $('.item_description_title[href]') const ids = $('.item_description_title[href]')
.map(function getListingId() { .map(function getListingId() {
@@ -53,25 +65,29 @@ export default function discogs(pg: Pg) {
return id; return id;
}) })
.get(); .get();
return Promise.all(ids.map(id => listing(id, want)));
const unlisted = await pg.listingRmUnlisted(want, ids);
if (unlisted) log.info({ wantId: want.id, unlisted: unlisted }, 'missing listings removed');
return Bluebird.mapSeries(ids, async id => listing(id, want));
} }
async function listing(id, want) { async function listing(id, want) {
const listing = await pg.listingGet(id); const listing = await pg.listingFind(id);
if (listing) return listing; if (listing) return listing;
try { try {
log.debug('fetching listing', id);
const res = await api.get(`${apiBase}/marketplace/listings/${id}`); const res = await api.get(`${apiBase}/marketplace/listings/${id}`);
return pg.listingUpsert(res.data, want); return pg.listingUpsert(res.data, want);
} catch (e) { } catch (e) {
handleApiError(e); return handleApiError(e);
return null;
} }
} }
return { return {
user, user,
wantlist, wantlist,
wantlistLatest,
inventory, inventory,
listings, listings,
listing, listing,
+10
View File
@@ -0,0 +1,10 @@
import bunyan from 'bunyan';
export default function createLog() {
const log = bunyan.createLogger({
name: 'mcrates',
stream: process.stdout
});
return log;
}
+148
View File
@@ -0,0 +1,148 @@
import type Log from 'bunyan';
import type { PrismaClient, Want, User, Listing, Prisma } from '@prisma/client';
import * as Bluebird from 'bluebird';
export interface Pg {
userFindId: (id: UUID) => Promise<User>,
userFindName: (name: DiscogsName) => Promise<User>,
userUpsert: (user: User) => Promise<User>,
userWants: (user: User) => Promise<Want[]>,
wantUpsert: (want: Want, user: User) => Promise<Want>,
listingUpsert: (listing: Listing, want: Want) => Promise<Listing>,
listingFind: (id: DiscogsListing) => Promise<Listing>,
listingRmUnlisted: (want: Want, ids: DiscogsListing[]) => Promise<number>,
bundleCandidates: () => Promise<Listing[][]>,
}
export default function createPg(log: Log, prisma: PrismaClient): Pg {
async function userFindId(id) {
return prisma.user.findUnique({ where: { id }});
}
async function userFindName(name: DiscogsName) {
return prisma.user.findUnique({ where: { discogs: name }});
}
async function userWants(user: User) {
return prisma.want.findMany({ where: { userId: user.id }});
}
async function userUpsert(user) {
const data = {
discogs: user.username,
data: user as Prisma.JsonObject,
};
return prisma.user.upsert({
where: {
discogs: user.username,
},
update: data,
create: data,
});
}
async function wantUpsert(dcWant, user) {
const values: { userId: UUID, release: DiscogsListing, data: any } = {
data: dcWant,
release: dcWant.id,
userId: user.id,
};
return prisma.want.upsert({
// get these from the generated types
// ./node_modules/.prisma/client/index.d.ts
where: { userId_release: {
userId: values.userId,
release: values.release || null,
}},
update: values,
create: values,
});
}
async function listingRmUnlisted(want: Want, ids: DiscogsListing[]) {
const { count } = await prisma.listing.deleteMany({
where: {
wantId: want.id,
id: {
notIn: ids,
}
}
});
return count;
}
async function listingUpsert(listing, want) {
const values = {
id: listing.id,
wantId: want.id,
seller: listing.seller.id,
data: listing,
};
return prisma.listing.upsert({
where: {
id: listing.id
},
update: values,
create: values,
});
}
async function listingFind(id: DiscogsListing) {
return prisma.listing.findUnique({
where: {
id,
},
include: {
want: {
include: { user: true },
}
}
});
}
async function bundleCandidates(): Promise<Listing[][]> {
const candidates = await prisma.$queryRaw`
select
seller, count(distinct("wantId")), array_agg(id) as listings
from
"Listing"
group by
seller
order by
count desc
limit 5;
` as BundleCandidate[];
return Promise.all(candidates.map(async c => prisma.listing.findMany({
where: {
id: { in: c.listings }
},
include: {
want: {
include: { user: true },
}
},
distinct: ['wantId'],
})));
}
return {
userFindId,
userFindName,
userUpsert,
userWants,
wantUpsert,
listingUpsert,
listingFind,
listingRmUnlisted,
bundleCandidates,
};
}
File renamed without changes.
+4 -2
View File
@@ -27,12 +27,14 @@
/* Modules */ /* Modules */
"module": "commonjs", /* Specify what module code is generated. */ "module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */ // "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
"types": ["./index.d.ts"], /* Specify type package names to be included without being referenced in a source file. */ "types": [
"./index.d.ts"
], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "resolveJsonModule": true, /* Enable importing .json files. */ // "resolveJsonModule": true, /* Enable importing .json files. */
-21
View File
@@ -1,21 +0,0 @@
type UUID = string;
type User = {
id: UUID,
discogs: string,
data: any,
}
type Listing = {
id: number,
want: UUID,
seller: number,
data: any,
}
type Want = {
id: UUID,
release: number,
user: UUID,
data: any,
}
-98
View File
@@ -1,98 +0,0 @@
import Knex from 'knex';
import config from '../knexfile';
export interface Pg {
userGetId: (id: UUID) => Promise<User>,
userGetName: (name: string) => Promise<User>,
userUpsert: (user: User) => Promise<User>,
userWants: (user: User) => Promise<Want[]>,
wantUpsert: (want: Want, user: User) => Promise<Want>,
listingUpsert: (listing: Listing, want: Want) => Promise<Listing>,
listingGet: (id: number) => Promise<Listing>,
}
export default function createPg(): Pg {
const pg = Knex(config);
async function userGetId(id): Promise<User> {
return pg('users')
.where({ id })
.first();
}
async function userGetName(name): Promise<User> {
return pg('users')
.where({ discogs: name })
.first();
}
async function userWants(user: User): Promise<Want[]> {
return pg('wants')
.where({ user: user.id });
}
async function userUpsert(user): Promise<User> {
const [row] = await pg('users')
.insert({
discogs: user.username,
data: user,
})
.onConflict('id')
.merge()
.onConflict('discogs')
.merge()
.returning('*');
return row;
}
async function wantUpsert(want, user): Promise<Want> {
const [row] = await pg('wants')
.insert({
data: want,
release: want.id,
user: user.id,
})
.onConflict('id')
.merge()
.onConflict(['user', 'release'])
.merge()
.returning('*');
return row;
}
async function listingUpsert(listing, want): Promise<Listing> {
const [row] = await pg('listings')
.insert({
id: listing.id,
want: want.id,
seller: listing.seller.id,
data: listing,
})
.onConflict('id')
.merge()
.returning('*');
return row;
}
async function listingGet(id): Promise<Listing> {
return pg('listings')
.where({ id })
.first();
}
return {
userGetId,
userGetName,
userUpsert,
userWants,
wantUpsert,
listingUpsert,
listingGet,
};
}