Working data and basic demo pages
This commit is contained in:
parent
e2d2599a95
commit
9983720bd1
@ -21,6 +21,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
"node-cache": "^5.1.2",
|
||||||
"postgres": "^3.4.4",
|
"postgres": "^3.4.4",
|
||||||
"ssh2": "^1.15.0"
|
"ssh2": "^1.15.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3>{post.name}</h3>
|
<a href="/posts/{post.id}">{post.name}</a>
|
||||||
<!-- <p>{post.author.name}</p> -->
|
<!-- <p>{post.author.name}</p> -->
|
||||||
<p>{post.content}</p>
|
<p>{post.content}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,35 +1,6 @@
|
|||||||
// @ts-nocheck
|
import { sql } from '$lib/db.server';
|
||||||
import { env } from '$lib/env';
|
|
||||||
import postgres from 'postgres';
|
|
||||||
import ssh2 from 'ssh2';
|
|
||||||
|
|
||||||
export const handle = async ({event, resolve}) => {
|
export const handle = async ({event, resolve}) => {
|
||||||
const sql = postgres(
|
|
||||||
// 'postgres://doki8902:ChangeItN8w@pgsql3.mif:5432/studentu',
|
|
||||||
{
|
|
||||||
host: env.PG_HOST,
|
|
||||||
port: parseInt(env.PG_PORT),
|
|
||||||
database: env.PG_DATABASE,
|
|
||||||
username: env.PG_USERNAME,
|
|
||||||
password: env.PG_PASSWORD,
|
|
||||||
socket: ({ host: [host], port: [port] }) => new Promise((resolve, reject) => {
|
|
||||||
const ssh = new ssh2.Client();
|
|
||||||
ssh
|
|
||||||
.on('error', reject)
|
|
||||||
.on('ready', () =>
|
|
||||||
ssh.forwardOut('127.0.0.1', 12345, host, port,
|
|
||||||
(err, socket) => err ? reject(err) : resolve(socket)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.connect({
|
|
||||||
host: env.SSH_HOST,
|
|
||||||
port: parseInt(env.SSH_PORT),
|
|
||||||
username: env.SSH_USERNAME,
|
|
||||||
password: env.SSH_PASSWORD
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// https://github.com/porsager/postgres/issues/762
|
// https://github.com/porsager/postgres/issues/762
|
||||||
|
|
||||||
|
|||||||
5
src/lib/cache.server.js
Normal file
5
src/lib/cache.server.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import NodeCache from "node-cache";
|
||||||
|
|
||||||
|
export const createCache = () => new NodeCache({
|
||||||
|
stdTTL: 100
|
||||||
|
});
|
||||||
32
src/lib/db.server.js
Normal file
32
src/lib/db.server.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { env } from '$lib/env';
|
||||||
|
import postgres from 'postgres';
|
||||||
|
import ssh2 from 'ssh2';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export const sql = postgres(
|
||||||
|
// 'postgres://doki8902:ChangeItN8w@pgsql3.mif:5432/studentu',
|
||||||
|
{
|
||||||
|
host: env.PG_HOST,
|
||||||
|
port: parseInt(env.PG_PORT),
|
||||||
|
database: env.PG_DATABASE,
|
||||||
|
username: env.PG_USERNAME,
|
||||||
|
password: env.PG_PASSWORD,
|
||||||
|
// @ts-ignore
|
||||||
|
socket: ({ host: [host], port: [port] }) => new Promise((resolve, reject) => {
|
||||||
|
const ssh = new ssh2.Client();
|
||||||
|
ssh
|
||||||
|
.on('error', reject)
|
||||||
|
.on('ready', () =>
|
||||||
|
ssh.forwardOut('127.0.0.1', 12345, host, port,
|
||||||
|
(err, socket) => err ? reject(err) : resolve(socket)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.connect({
|
||||||
|
host: env.SSH_HOST,
|
||||||
|
port: parseInt(env.SSH_PORT),
|
||||||
|
username: env.SSH_USERNAME,
|
||||||
|
password: env.SSH_PASSWORD
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* @param {import('postgres').Sql} sql
|
|
||||||
* @param {number | undefined} category
|
|
||||||
* @param {number} limit
|
|
||||||
* @param {number} offset
|
|
||||||
* @returns {Promise<import('$types/base').Post[]>}
|
|
||||||
*/
|
|
||||||
export async function getPosts(sql, category = undefined, limit = 10, offset = 0) {
|
|
||||||
let query;
|
|
||||||
|
|
||||||
if (category === undefined) {
|
|
||||||
query = sql`
|
|
||||||
SELECT name, latest_content
|
|
||||||
FROM doki8902.message_post
|
|
||||||
FETCH FIRST ${limit} ROWS ONLY
|
|
||||||
OFFSET ${offset};`;
|
|
||||||
} else {
|
|
||||||
query = sql`
|
|
||||||
SELECT name, latest_content
|
|
||||||
FROM doki8902.message_post
|
|
||||||
WHERE category_id = ${category}
|
|
||||||
FETCH FIRST ${limit} ROWS ONLY
|
|
||||||
OFFSET ${offset};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let posts = await query;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import('$types/base').Post[]}
|
|
||||||
*/
|
|
||||||
let result = [];
|
|
||||||
|
|
||||||
posts.forEach(row => {
|
|
||||||
result.push({
|
|
||||||
name: row['name'],
|
|
||||||
content: row['latest_content']
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
// export function runQuery(
|
|
||||||
// /** @type {import('postgres').Sql} */ sql,
|
|
||||||
// /** @type {string} */ query,
|
|
||||||
// /** @type {any[]} */ args = []) {
|
|
||||||
// sql`${query}`
|
|
||||||
// }
|
|
||||||
@ -5,4 +5,5 @@ dotenv.config();
|
|||||||
/**
|
/**
|
||||||
* @type {import('$types/env').Env}
|
* @type {import('$types/env').Env}
|
||||||
*/
|
*/
|
||||||
|
// @ts-ignore
|
||||||
export const env = process.env;
|
export const env = process.env;
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
// place files you want to import through the `$lib` alias in this folder.
|
|
||||||
75
src/lib/server/db/category.js
Normal file
75
src/lib/server/db/category.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { createCache } from '$lib/cache.server';
|
||||||
|
import { cacheUpdater, cachedMethod } from './root';
|
||||||
|
|
||||||
|
const cache = createCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {import('$types/base').Result<T>} Result<T>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('$types/base').Category} Category
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Result<Category>} categories
|
||||||
|
* @returns {Result<Category>}
|
||||||
|
*/
|
||||||
|
const updateCategoryCache = cacheUpdater(cache);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {number[]} user_ids
|
||||||
|
* @returns {Promise<Result<Category>>}
|
||||||
|
*/
|
||||||
|
export const getCategoriesCached = cachedMethod(cache, getCategories);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {number[]} category_ids
|
||||||
|
* @returns {Promise<Result<Category>>}
|
||||||
|
*/
|
||||||
|
export async function getCategories(sql, category_ids) {
|
||||||
|
if (category_ids.length == 0) return {};
|
||||||
|
|
||||||
|
const query = sql`
|
||||||
|
SELECT id, name
|
||||||
|
FROM doki8902.post_category
|
||||||
|
WHERE id IN ${ sql(category_ids) };`;
|
||||||
|
|
||||||
|
let categories = await query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Result<Category>}
|
||||||
|
*/
|
||||||
|
let result = {};
|
||||||
|
|
||||||
|
categories.forEach(row => {
|
||||||
|
result[row['id']] = {
|
||||||
|
id: row['id'],
|
||||||
|
name: row['name']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return updateCategoryCache(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {number} category_id
|
||||||
|
* @returns {Promise<Category | import('$types/error').Error>}
|
||||||
|
*/
|
||||||
|
export async function getCategoryCached(sql, category_id) {
|
||||||
|
const categories = await getCategoriesCached(sql, [category_id]);
|
||||||
|
|
||||||
|
if (Object.keys(categories).length == 0) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
msg: `Could not find Category of ID ${category_id}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories[category_id];
|
||||||
|
}
|
||||||
123
src/lib/server/db/post.js
Normal file
123
src/lib/server/db/post.js
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { getCategoriesCached, getCategoryCached } from './category';
|
||||||
|
import { getUser, getUsersCached } from './user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('$types/base').Post} Post
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {import('$types/base').Category | undefined} category
|
||||||
|
* @param {number} limit
|
||||||
|
* @param {number} offset
|
||||||
|
* @returns {Promise<Post[]>}
|
||||||
|
*/
|
||||||
|
export async function getPosts(sql, category = undefined, limit = 10, offset = 0) {
|
||||||
|
let filter;
|
||||||
|
|
||||||
|
if (category === undefined) {
|
||||||
|
filter = sql``;
|
||||||
|
} else {
|
||||||
|
filter = sql`WHERE category_id = ${ category.id }`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = sql`
|
||||||
|
SELECT id, author_id, name, category_id, latest_content, created_date, likes, dislikes
|
||||||
|
FROM doki8902.message_post
|
||||||
|
${ filter }
|
||||||
|
FETCH FIRST ${ limit } ROWS ONLY
|
||||||
|
OFFSET ${ offset };`;
|
||||||
|
|
||||||
|
const posts = await query;
|
||||||
|
|
||||||
|
const users = await getUsersCached(sql, posts.map(row => {
|
||||||
|
return row['author_id'];
|
||||||
|
}));
|
||||||
|
|
||||||
|
const categories = await getCategoriesCached(sql, posts.map(row => {
|
||||||
|
return row['category_id'];
|
||||||
|
}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Post[]}
|
||||||
|
*/
|
||||||
|
return posts.map(row => {
|
||||||
|
return {
|
||||||
|
id: row['id'],
|
||||||
|
author: users[row['author_id']] || null,
|
||||||
|
name: row['name'],
|
||||||
|
category: categories[row['category_id']],
|
||||||
|
content: row['latest_content'],
|
||||||
|
post_date: row['created_date'],
|
||||||
|
rating: {
|
||||||
|
likes: BigInt(row['likes']),
|
||||||
|
dislikes: BigInt(row['dislikes']),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {number} post_id
|
||||||
|
* @returns {Promise<Post | import('$types/error').Error>}
|
||||||
|
*/
|
||||||
|
export async function getPost(sql, post_id) {
|
||||||
|
const query = sql`
|
||||||
|
SELECT id, author_id, name, category_id, latest_content, created_date, likes, dislikes
|
||||||
|
FROM doki8902.message_post
|
||||||
|
WHERE id = ${ post_id };`;
|
||||||
|
|
||||||
|
const post = (await query).at(0);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
msg: `Could not find Post of ID ${ post_id }`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const user_guess = await getUser(sql, post['author_id']);
|
||||||
|
/**
|
||||||
|
* @type {import('$types/base').User | null}
|
||||||
|
*/
|
||||||
|
const author = function () {
|
||||||
|
if (Object.hasOwn(user_guess, 'error')) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return /** @type {import('$types/base').User} */ (user_guess);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
const category_guess = await getCategoryCached(sql, post['category_id']);
|
||||||
|
if (Object.hasOwn(category_guess, 'error')) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
msg: `Post of ID ${ post_id } has an invalid Category ID ${ post['category_id'] }`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('$types/base').Category}
|
||||||
|
*/
|
||||||
|
const category = function () {
|
||||||
|
return /** @type {import('$types/base').Category} */ (category_guess);
|
||||||
|
}();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Post}
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
id: post['id'],
|
||||||
|
author: author,
|
||||||
|
name: post['name'],
|
||||||
|
category: category,
|
||||||
|
content: post['latest_content'],
|
||||||
|
post_date: post['created_date'],
|
||||||
|
rating: {
|
||||||
|
likes: BigInt(post['likes']),
|
||||||
|
dislikes: BigInt(post['dislikes']),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
46
src/lib/server/db/root.js
Normal file
46
src/lib/server/db/root.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {import('node-cache')} cache
|
||||||
|
* @returns {function({[id: number]: T})}
|
||||||
|
*/
|
||||||
|
export const cacheUpdater = (cache) => {
|
||||||
|
return function updateUserCache(data) {
|
||||||
|
Object.keys(data).forEach(id => {
|
||||||
|
cache.set(parseInt(id), data[parseInt(id)]);
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {import('node-cache')} cache
|
||||||
|
* @param {function(import('postgres').Sql, number[]): Promise<{[id: number]: T}>} method
|
||||||
|
* @returns {function(import('postgres').Sql, number[]): Promise<{[id: number]: T}>}
|
||||||
|
*/
|
||||||
|
export const cachedMethod = (cache, method) => {
|
||||||
|
return async function(sql, ids) {
|
||||||
|
/**
|
||||||
|
* @type {{[id: number]: T}}
|
||||||
|
*/
|
||||||
|
let results = {};
|
||||||
|
/**
|
||||||
|
* @type {number[]}
|
||||||
|
*/
|
||||||
|
let missing = [];
|
||||||
|
|
||||||
|
ids.forEach(id => {
|
||||||
|
if (id === null || id === undefined)
|
||||||
|
return;
|
||||||
|
let user = cache.get(id);
|
||||||
|
if (user)
|
||||||
|
results[id] = user;
|
||||||
|
else
|
||||||
|
missing.push(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const remaining = await method(sql, missing);
|
||||||
|
|
||||||
|
return Object.assign({}, results, remaining);
|
||||||
|
};
|
||||||
|
}
|
||||||
75
src/lib/server/db/user.js
Normal file
75
src/lib/server/db/user.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { createCache } from '$lib/cache.server';
|
||||||
|
import { cacheUpdater, cachedMethod } from './root';
|
||||||
|
|
||||||
|
const cache = createCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {import('$types/base').Result<T>} Result<T>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('$types/base').User} User
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Result<User>} users
|
||||||
|
* @returns {Result<User>}
|
||||||
|
*/
|
||||||
|
const updateUserCache = cacheUpdater(cache);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {number[]} user_ids
|
||||||
|
* @returns {Promise<Result<User>>}
|
||||||
|
*/
|
||||||
|
export const getUsersCached = cachedMethod(cache, getUsers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {number[]} user_ids
|
||||||
|
* @returns {Promise<Result<User>>}
|
||||||
|
*/
|
||||||
|
export async function getUsers(sql, user_ids) {
|
||||||
|
if (user_ids.length == 0) return {};
|
||||||
|
|
||||||
|
const query = sql`
|
||||||
|
SELECT id, username, join_time
|
||||||
|
FROM doki8902.user
|
||||||
|
WHERE id IN ${ sql(user_ids) };`;
|
||||||
|
|
||||||
|
let users = await query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Result<User>}
|
||||||
|
*/
|
||||||
|
let result = {};
|
||||||
|
|
||||||
|
users.forEach(row => {
|
||||||
|
result[row['id']] = {
|
||||||
|
id: row['id'],
|
||||||
|
name: row['username'],
|
||||||
|
join_date: row['join_time']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return updateUserCache(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('postgres').Sql} sql
|
||||||
|
* @param {number} user_id
|
||||||
|
* @returns {Promise<User | import('$types/error').Error>}
|
||||||
|
*/
|
||||||
|
export async function getUser(sql, user_id) {
|
||||||
|
const users = await getUsers(sql, [user_id]);
|
||||||
|
|
||||||
|
if (Object.keys(users).length == 0) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
msg: `Could not find user of ID ${user_id}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return users[user_id];
|
||||||
|
}
|
||||||
@ -1,12 +1,10 @@
|
|||||||
import { getPosts } from '$lib/db/post';
|
import { getPosts } from '$lib/server/db/post';
|
||||||
import { env } from '$lib/env';
|
|
||||||
|
|
||||||
/** @type {import('./$types').PageServerLoad} */
|
/** @type {import('./$types').PageServerLoad} */
|
||||||
export async function load({ locals }) {
|
export async function load({ locals }) {
|
||||||
let result = await getPosts(locals.sql);
|
let result = await getPosts(locals.sql);
|
||||||
|
|
||||||
// console.log();
|
|
||||||
|
|
||||||
console.log(result);
|
console.log(result);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
11
src/routes/(app)/posts/[id]/+page.server.js
Normal file
11
src/routes/(app)/posts/[id]/+page.server.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { getPost } from "$lib/server/db/post";
|
||||||
|
|
||||||
|
/** @type {import("@sveltejs/kit").ServerLoad} */
|
||||||
|
export async function load({ params, locals }) {
|
||||||
|
/** @type {import("$types/base").Post | import("$types/error").Error} */
|
||||||
|
const post = await getPost(locals.sql, Number(params.id));
|
||||||
|
|
||||||
|
return {
|
||||||
|
post: post
|
||||||
|
};
|
||||||
|
}
|
||||||
12
src/routes/(app)/posts/[id]/+page.svelte
Normal file
12
src/routes/(app)/posts/[id]/+page.svelte
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* @type {{post: import("$types/base").Post}}
|
||||||
|
*/
|
||||||
|
export let data;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>{data.post.name}</h1>
|
||||||
|
<a href="#">{data.post.author?.name}</a>
|
||||||
|
<p>{data.post.content}</p>
|
||||||
0
src/stores.js
Normal file
0
src/stores.js
Normal file
@ -1,21 +1,27 @@
|
|||||||
|
export type Result<T> = {[id: number]: T};
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
name: string
|
id: number,
|
||||||
|
name: string,
|
||||||
|
join_date: Date
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Rating = {
|
export type Rating = {
|
||||||
likes: number,
|
likes: BigInt,
|
||||||
dislikes: number
|
dislikes: BigInt
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Category = {
|
export type Category = {
|
||||||
|
id: number,
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Post = {
|
export type Post = {
|
||||||
// author: User,
|
id: number,
|
||||||
|
author: User | null,
|
||||||
name: string,
|
name: string,
|
||||||
// category: Category,
|
category: Category,
|
||||||
content: string,
|
content: string,
|
||||||
// post_date: Date,
|
post_date: Date,
|
||||||
// rating: Rating
|
rating: Rating
|
||||||
};
|
};
|
||||||
|
|||||||
4
src/types/error.ts
Normal file
4
src/types/error.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export type Error = {
|
||||||
|
error: boolean,
|
||||||
|
msg: string
|
||||||
|
};
|
||||||
12
yarn.lock
12
yarn.lock
@ -419,6 +419,11 @@ chokidar@^3.4.1:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
clone@2.x:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
|
||||||
|
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
|
||||||
|
|
||||||
code-red@^1.0.3:
|
code-red@^1.0.3:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35"
|
resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35"
|
||||||
@ -744,6 +749,13 @@ nanoid@^3.3.7:
|
|||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
||||||
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||||
|
|
||||||
|
node-cache@^5.1.2:
|
||||||
|
version "5.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d"
|
||||||
|
integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==
|
||||||
|
dependencies:
|
||||||
|
clone "2.x"
|
||||||
|
|
||||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user