mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-07 06:25:21 +02:00
Merge branch 'main' into feat/hawk_integration
This commit is contained in:
commit
3958e959f7
15 changed files with 23592 additions and 60 deletions
15
README.md
15
README.md
|
@ -83,6 +83,21 @@ yarn lint
|
|||
yarn test
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
You can configure application using configs in <code>/config</code> directory.
|
||||
|
||||
| Property | Role |
|
||||
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| <code>port</code> | to set port of application |
|
||||
| <code>database</code> | to name directory with data |
|
||||
| <code>rcFile</code> | to set destination of codexdocsrc config file |
|
||||
| <code>uploads</code> | to set destination of directory to save uploads |
|
||||
| <code>secret</code> | to set secret |
|
||||
| <code>favicon</code> | to set url or favicon path (favicon need to be in /public directory), like `/myFavicon.png`, to get favicon. Server uploads file by url and saves it to temporary directory. And you can get favicon by /favicon static route of application |
|
||||
|
||||
You can configure application using configs in <code>/config</code> directory.
|
||||
|
||||
### Authentication
|
||||
|
||||
To manage pages you need to authorize (available on `/auth`).
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
"database": ".db",
|
||||
"rcFile": "./.codexdocsrc",
|
||||
"uploads": "public/uploads",
|
||||
"secret": "iamasecretstring"
|
||||
"secret": "iamasecretstring",
|
||||
"favicon": ""
|
||||
}
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
"database": ".db",
|
||||
"rcFile": "./.codexdocsrc",
|
||||
"uploads": "/uploads",
|
||||
"secret": "iamasecretstring"
|
||||
"secret": "iamasecretstring",
|
||||
"favicon": ""
|
||||
}
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
"database": ".testdb",
|
||||
"rcFile": "./src/test/.codexdocsrc",
|
||||
"uploads": "public/uploads_test",
|
||||
"secret": "iamasecretstring"
|
||||
"secret": "iamasecretstring",
|
||||
"favicon": ""
|
||||
}
|
||||
|
|
23343
package-lock.json
generated
Normal file
23343
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,9 @@ import HttpException from './exceptions/httpException';
|
|||
import * as dotenv from 'dotenv';
|
||||
import config from 'config';
|
||||
import HawkCatcher from '@hawk.so/nodejs';
|
||||
import os from 'os';
|
||||
import appConfig from 'config';
|
||||
import { downloadFavicon, FaviconData } from './utils/downloadFavicon';
|
||||
|
||||
|
||||
dotenv.config();
|
||||
|
@ -19,6 +22,9 @@ if (process.env.HAWK_TOKEN_BACKEND) {
|
|||
HawkCatcher.init(process.env.HAWK_TOKEN_BACKEND);
|
||||
}
|
||||
|
||||
// Get url to upload favicon from config
|
||||
const favicon: string = appConfig.get('favicon');
|
||||
|
||||
app.locals.config = localConfig;
|
||||
// Set client error tracking token as app local.
|
||||
if (process.env.HAWK_TOKEN_CLIENT) {
|
||||
|
@ -30,12 +36,34 @@ app.set('views', path.join(__dirname, './', 'views'));
|
|||
app.set('view engine', 'twig');
|
||||
require('./utils/twig');
|
||||
|
||||
const downloadedFaviconFolder = os.tmpdir();
|
||||
|
||||
// Check if favicon is not empty
|
||||
if (favicon) {
|
||||
// Upload favicon by url, it's path on server is '/temp/favicon.{format}'
|
||||
downloadFavicon(favicon, downloadedFaviconFolder).then((res) => {
|
||||
app.locals.favicon = res;
|
||||
console.log('Favicon successfully uploaded');
|
||||
})
|
||||
.catch( (err) => {
|
||||
console.log(err);
|
||||
console.log('Favicon has not uploaded');
|
||||
});
|
||||
} else {
|
||||
console.log('Favicon is empty, using default path');
|
||||
app.locals.favicon = {
|
||||
destination: '/favicon.png',
|
||||
type: 'image/png',
|
||||
} as FaviconData;
|
||||
}
|
||||
|
||||
app.use(morgan('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, '../../public')));
|
||||
app.use('/uploads', express.static(config.get('uploads')));
|
||||
app.use('/favicon', express.static(downloadedFaviconFolder));
|
||||
|
||||
app.use('/', routes);
|
||||
|
||||
|
|
85
src/backend/utils/downloadFavicon.ts
Normal file
85
src/backend/utils/downloadFavicon.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
/**
|
||||
* Uploaded favicon data
|
||||
*/
|
||||
export interface FaviconData {
|
||||
// Uploaded favicon path
|
||||
destination: string;
|
||||
|
||||
// File type
|
||||
type: string;
|
||||
}
|
||||
|
||||
// Initiate controller for aborting request
|
||||
const controller = new AbortController();
|
||||
|
||||
/**
|
||||
* Check if string is url
|
||||
*
|
||||
* @param str - string to check
|
||||
*/
|
||||
function checkIsUrl(str: string): boolean {
|
||||
const re = new RegExp('https?://');
|
||||
|
||||
return re.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload favicon by url, or get it by path
|
||||
*
|
||||
* @param destination - url or path of favicon
|
||||
* @param faviconFolder - folder to save favicon
|
||||
* @returns { Promise<FaviconData> } - Promise with data about favicon
|
||||
*/
|
||||
export async function downloadFavicon(destination: string, faviconFolder: string): Promise<FaviconData> {
|
||||
// Check of destination is empty
|
||||
if (!destination) {
|
||||
throw Error('Favicon destination is empty');
|
||||
}
|
||||
|
||||
// Get file name by destination
|
||||
const filename = destination.substring(destination.lastIndexOf('/')+1);
|
||||
|
||||
// Get file format
|
||||
const format = filename.split('.')[1];
|
||||
|
||||
// Check if string is url
|
||||
if (!checkIsUrl(destination)) {
|
||||
return {
|
||||
destination: `/${filename}`,
|
||||
type: `image/${format}`,
|
||||
} as FaviconData;
|
||||
}
|
||||
|
||||
// Create timeout to abort request
|
||||
const timeoutId = setTimeout(() => {
|
||||
controller.abort();
|
||||
console.log('Favicon request has timed out.');
|
||||
}, 5000);
|
||||
|
||||
// Make get request to url
|
||||
const res = await fetch(destination, { signal: controller.signal });
|
||||
// Get buffer data from response
|
||||
const fileData = await res.buffer();
|
||||
|
||||
// Clear timeout, if data was got
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// Get file path in temporary directory
|
||||
const filePath = path.join(faviconFolder, `favicon.${format}`);
|
||||
|
||||
// Save file
|
||||
await fs.writeFile(filePath, fileData, (err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
destination: `/favicon/favicon.${format}`,
|
||||
type: `image/${format}`,
|
||||
} as FaviconData;
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
<meta property="og:title" content="{{ page.title | striptags }}" />
|
||||
<meta property="article:modified_time" content="{{ (page.body.time / 1000) | date("c") }}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<link rel="icon" type="{{ favicon.type }}" href="{{ favicon.destination }}">
|
||||
</head>
|
||||
<script>
|
||||
window.config = {
|
||||
|
@ -18,11 +18,11 @@
|
|||
<body>
|
||||
{% include "components/header.twig" with res.locals.isAuthorized %}
|
||||
<div class="docs">
|
||||
|
||||
|
||||
{% include "components/sidebar.twig" %}
|
||||
|
||||
|
||||
<div class="docs__content">
|
||||
|
||||
|
||||
<div class="docs__content-inner">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -13,14 +13,18 @@
|
|||
}
|
||||
</textarea>
|
||||
<header class="writing-header">
|
||||
<span class="writing-header__left">
|
||||
<span>
|
||||
New Page at the
|
||||
<div class="writing-header__inner-container">
|
||||
{% set currentPageId = 0 %}
|
||||
{% if page is not empty %}
|
||||
{% set currentPageId = page._id %}
|
||||
{% endif %}
|
||||
<select name="parent">
|
||||
<div class="select-wrapper">
|
||||
{% if parentsChildrenOrdered is not empty %}
|
||||
<label for="parent">Parent Page</label>
|
||||
{% else %}
|
||||
<label for="parent">New Page at the</label>
|
||||
{% endif %}
|
||||
<select id="parent" name="parent">
|
||||
<option value="0">Root</option>
|
||||
{% for _page in pagesAvailableGrouped %}
|
||||
{% if _page._id != currentPageId %}
|
||||
|
@ -34,24 +38,25 @@
|
|||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
{% if parentsChildrenOrdered is not empty %}
|
||||
<span>
|
||||
Put Above
|
||||
<select name="above">
|
||||
<div class="select-wrapper">
|
||||
<label for="above">Put Above</label>
|
||||
<select id="above" name="above">
|
||||
<option value="0">—</option>
|
||||
{% for _page in parentsChildrenOrdered %}
|
||||
<option value="{{ _page._id }}">{{ _page.title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
{% if page is not empty %}
|
||||
<p><input type="text" class="uri-input" name="uri-input" placeholder="URI (Optional)" value="{{ page.uri }}"></p>
|
||||
{% endif %}
|
||||
{% if page is not empty %}
|
||||
<div class="uri-input-wrapper">
|
||||
<label for="uri-input">Alias</label>
|
||||
<input type="text" id="uri-input" class="uri-input" name="uri-input" placeholder="URI (Optional)" value="{{ page.uri }}">
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
<div class="writing-editor">
|
||||
<div id="editorjs"></div>
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
<title>{{ config.title }}</title>
|
||||
<link rel="stylesheet" href="/dist/main.css" />
|
||||
<link rel="preload" href="{{ config.landingFrameSrc }}" as="document">
|
||||
<link rel="icon" type="image/png" href="/favicon.png?v=2">
|
||||
<link rel="icon" type="{{ favicon.type }}" href="{{ favicon.destination }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta property="og:title" content="{{ config.title }}" />
|
||||
<meta property="og:site_name" content="{{ config.title }}" />
|
||||
<meta name="description" property="og:description" content="{{ config.description }}">
|
||||
</head>
|
||||
<script>
|
||||
</script>
|
||||
<body class="landing-body">
|
||||
{% include "components/header.twig" %}
|
||||
<div class="landing-loader" id="frame-loader">
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
.writing-header {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 15px 0;
|
||||
|
@ -15,18 +14,32 @@
|
|||
margin: auto;
|
||||
}
|
||||
|
||||
&__left {
|
||||
&__inner-container {
|
||||
margin: auto 0;
|
||||
display: flex;
|
||||
color: var(--color-text-second);
|
||||
gap: 10px;
|
||||
|
||||
& span {
|
||||
margin-right: 10px;
|
||||
@media(--mobile) {
|
||||
flex-flow: column;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
max-width: 100px;
|
||||
vertical-align: bottom;
|
||||
& > * {
|
||||
flex: 0 1 33.3%;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
select {
|
||||
@apply --select;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply --input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,16 +51,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.uri-input {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(201, 201, 204, 0.48);
|
||||
box-shadow: inset 0 1px 2px 0 rgba(35, 44, 72, 0.06);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.writing-editor {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
--color-link-hover: #F3F6F8;
|
||||
--color-bg-light: #f8f7fa;
|
||||
--color-page-active: #ff1767;
|
||||
--color-input-primary: #F3F6F8;
|
||||
--color-input-border: #477CFF;
|
||||
|
||||
--color-button-primary: #3389FF;
|
||||
--color-button-primary-hover: #2E7AE6;
|
||||
|
@ -77,6 +79,49 @@
|
|||
}
|
||||
}
|
||||
|
||||
--select {
|
||||
position: relative;
|
||||
padding: 10px 30px 10px 10px;
|
||||
width: 100%;
|
||||
border: solid 1px transparent;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-input-primary);
|
||||
box-sizing: border-box;
|
||||
appearance: none;
|
||||
line-height: 18px;
|
||||
background-image: url("../svg/arrow-down.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 15px center;
|
||||
|
||||
&:focus {
|
||||
border: solid 1px var(--color-input-border);
|
||||
box-shadow: 0 0 0 3px rgba(18, 155, 255, 0.33);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
--input {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
line-height: 18px;
|
||||
border: solid 1px transparent;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-input-primary);
|
||||
box-sizing: border-box;
|
||||
|
||||
&:focus {
|
||||
border: solid 1px var(--color-input-border);
|
||||
box-shadow: 0 0 0 3px rgba(18, 155, 255, 0.33);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
--squircle {
|
||||
border-radius: 8px;
|
||||
|
||||
|
@ -95,4 +140,4 @@
|
|||
@custom-media --tablet all and (min-width: 980px) and (max-width: 1050px);
|
||||
@custom-media --mobile all and (max-width: 980px);
|
||||
@custom-media --retina all and (-webkit-min-device-pixel-ratio: 1.5);
|
||||
@custom-media --can-hover all and (hover:hover)
|
||||
@custom-media --can-hover all and (hover:hover);
|
||||
|
|
3
src/frontend/svg/arrow-down.svg
Normal file
3
src/frontend/svg/arrow-down.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="8" height="5" viewBox="0 0 8 5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.83053 0.193862C6.70562 0.0696944 6.53666 0 6.36053 0C6.18441 0 6.01544 0.0696944 5.89053 0.193862L3.4972 2.55386L1.1372 0.193862C1.01229 0.0696944 0.843323 0 0.667199 0C0.491075 0 0.322107 0.0696944 0.197199 0.193862C0.134713 0.255837 0.0851169 0.329571 0.0512711 0.410811C0.0174253 0.49205 0 0.579187 0 0.667195C0 0.755203 0.0174253 0.84234 0.0512711 0.92358C0.0851169 1.00482 0.134713 1.07855 0.197199 1.14053L3.02387 3.9672C3.08584 4.02968 3.15957 4.07928 3.24081 4.11312C3.32205 4.14697 3.40919 4.16439 3.4972 4.16439C3.58521 4.16439 3.67234 4.14697 3.75358 4.11312C3.83482 4.07928 3.90856 4.02968 3.97053 3.9672L6.83053 1.14053C6.89302 1.07855 6.94261 1.00482 6.97646 0.92358C7.0103 0.84234 7.02773 0.755203 7.02773 0.667195C7.02773 0.579187 7.0103 0.49205 6.97646 0.410811C6.94261 0.329571 6.89302 0.255837 6.83053 0.193862Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 958 B |
|
@ -40,7 +40,7 @@ module.exports = () => {
|
|||
options: {
|
||||
// you can specify a publicPath here
|
||||
// by default it use publicPath in webpackOptions.output
|
||||
publicPath: '../',
|
||||
// publicPath: '../',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
34
yarn.lock
34
yarn.lock
|
@ -1408,9 +1408,9 @@
|
|||
form-data "^3.0.0"
|
||||
|
||||
"@types/node@*":
|
||||
version "17.0.23"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da"
|
||||
integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==
|
||||
version "18.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
|
||||
integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
|
||||
|
||||
"@types/node@^16.4.1":
|
||||
version "16.11.26"
|
||||
|
@ -2278,7 +2278,7 @@ clone-deep@^4.0.1:
|
|||
clone-response@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
|
||||
integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
|
||||
integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
|
@ -2368,10 +2368,10 @@ component-emitter@^1.2.0:
|
|||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||
|
||||
compress-brotli@^1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.6.tgz#64bd6f21f4f3e9841dbac392f4c29218caf5e9d9"
|
||||
integrity sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==
|
||||
compress-brotli@^1.3.8:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db"
|
||||
integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==
|
||||
dependencies:
|
||||
"@types/json-buffer" "~3.0.0"
|
||||
json-buffer "~3.0.1"
|
||||
|
@ -3664,9 +3664,9 @@ gonzales-pe@^4.0.3:
|
|||
minimist "^1.2.5"
|
||||
|
||||
got@^11.8.2:
|
||||
version "11.8.3"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770"
|
||||
integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==
|
||||
version "11.8.5"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046"
|
||||
integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^4.0.0"
|
||||
"@szmarczak/http-timer" "^4.0.5"
|
||||
|
@ -4254,11 +4254,11 @@ jws@^3.2.2:
|
|||
safe-buffer "^5.0.1"
|
||||
|
||||
keyv@^4.0.0:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.2.2.tgz#4b6f602c0228ef4d8214c03c520bef469ed6b768"
|
||||
integrity sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ==
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2"
|
||||
integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==
|
||||
dependencies:
|
||||
compress-brotli "^1.3.6"
|
||||
compress-brotli "^1.3.8"
|
||||
json-buffer "3.0.1"
|
||||
|
||||
kind-of@^6.0.2:
|
||||
|
@ -4925,7 +4925,7 @@ on-headers@~1.0.2:
|
|||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
|
@ -6965,7 +6965,7 @@ wrap-ansi@^7.0.0:
|
|||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
write-file-atomic@^2.4.2:
|
||||
version "2.4.3"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue