mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 21:19:13 +00:00
Refactor feed errors and error presentation (#459)
* Add support for setting a global feed timeout * Track failures, and don't show the first non-serious error * Tidy up error reporting * Make a prettier error pane * Use the prettier event pane * Show failed feed attempts in the widget * Ensure we catch connection resets * Add styling file * Ensure we only track one result per fetch * changelog * Refactor to use better status codes * Make feed results cheaper * splice
This commit is contained in:
parent
5f84cda39c
commit
63ab457b45
1
changelog.d/459.feature
Normal file
1
changelog.d/459.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Added new config option `feeds.pollTimeoutSeconds` to explictly set how long to wait for a feed response.
|
@ -87,6 +87,7 @@ feeds:
|
|||||||
#
|
#
|
||||||
enabled: false
|
enabled: false
|
||||||
pollIntervalSeconds: 600
|
pollIntervalSeconds: 600
|
||||||
|
pollTimeoutSeconds: 10
|
||||||
provisioning:
|
provisioning:
|
||||||
# (Optional) Provisioning API for integration managers
|
# (Optional) Provisioning API for integration managers
|
||||||
#
|
#
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"figma-js": "^1.14.0",
|
"figma-js": "^1.14.0",
|
||||||
|
"http-status-codes": "^2.2.0",
|
||||||
"ioredis": "^4.28.0",
|
"ioredis": "^4.28.0",
|
||||||
"jira-client": "^8.0.0",
|
"jira-client": "^8.0.0",
|
||||||
"markdown-it": "^12.3.2",
|
"markdown-it": "^12.3.2",
|
||||||
@ -85,7 +86,7 @@
|
|||||||
"@types/micromatch": "^4.0.1",
|
"@types/micromatch": "^4.0.1",
|
||||||
"@types/mime": "^2.0.3",
|
"@types/mime": "^2.0.3",
|
||||||
"@types/mocha": "^9.0.0",
|
"@types/mocha": "^9.0.0",
|
||||||
"@types/node": "^14",
|
"@types/node": "^16",
|
||||||
"@types/node-emoji": "^1.8.1",
|
"@types/node-emoji": "^1.8.1",
|
||||||
"@types/uuid": "^8.3.3",
|
"@types/uuid": "^8.3.3",
|
||||||
"@types/xml2js": "^0.4.11",
|
"@types/xml2js": "^0.4.11",
|
||||||
@ -103,6 +104,7 @@
|
|||||||
"sass": "^1.51.0",
|
"sass": "^1.51.0",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
"typescript": "^4.5.2",
|
"typescript": "^4.5.2",
|
||||||
"vite": "^2.9.13"
|
"vite": "^2.9.13",
|
||||||
|
"vite-svg-loader": "^3.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -613,6 +613,11 @@ export class Bridge {
|
|||||||
(data) => connManager.getConnectionsForFeedUrl(data.feed.url),
|
(data) => connManager.getConnectionsForFeedUrl(data.feed.url),
|
||||||
(c, data) => c.handleFeedEntry(data),
|
(c, data) => c.handleFeedEntry(data),
|
||||||
);
|
);
|
||||||
|
this.bindHandlerToQueue<FeedEntry, FeedConnection>(
|
||||||
|
"feed.success",
|
||||||
|
(data) => connManager.getConnectionsForFeedUrl(data.feed.url),
|
||||||
|
c => c.handleFeedSuccess(),
|
||||||
|
);
|
||||||
this.bindHandlerToQueue<FeedError, FeedConnection>(
|
this.bindHandlerToQueue<FeedError, FeedConnection>(
|
||||||
"feed.error",
|
"feed.error",
|
||||||
(data) => connManager.getConnectionsForFeedUrl(data.url),
|
(data) => connManager.getConnectionsForFeedUrl(data.url),
|
||||||
|
@ -232,15 +232,20 @@ export class BridgeConfigGitLab {
|
|||||||
export interface BridgeConfigFeedsYAML {
|
export interface BridgeConfigFeedsYAML {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
pollIntervalSeconds: number;
|
pollIntervalSeconds: number;
|
||||||
|
pollTimeoutSeconds?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BridgeConfigFeeds {
|
export class BridgeConfigFeeds {
|
||||||
public enabled: boolean;
|
public enabled: boolean;
|
||||||
public pollIntervalSeconds: number;
|
public pollIntervalSeconds: number;
|
||||||
|
public pollTimeoutSeconds: number;
|
||||||
|
|
||||||
constructor(yaml: BridgeConfigFeedsYAML) {
|
constructor(yaml: BridgeConfigFeedsYAML) {
|
||||||
this.enabled = yaml.enabled;
|
this.enabled = yaml.enabled;
|
||||||
this.pollIntervalSeconds = yaml.pollIntervalSeconds;
|
this.pollIntervalSeconds = yaml.pollIntervalSeconds;
|
||||||
|
assert.strictEqual(typeof this.pollIntervalSeconds, "number");
|
||||||
|
this.pollTimeoutSeconds = yaml.pollTimeoutSeconds ?? 10;
|
||||||
|
assert.strictEqual(typeof this.pollTimeoutSeconds, "number");
|
||||||
}
|
}
|
||||||
|
|
||||||
@hideKey()
|
@hideKey()
|
||||||
|
@ -111,6 +111,7 @@ export const DefaultConfig = new BridgeConfig({
|
|||||||
feeds: {
|
feeds: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
pollIntervalSeconds: 600,
|
pollIntervalSeconds: 600,
|
||||||
|
pollTimeoutSeconds: 10,
|
||||||
},
|
},
|
||||||
provisioning: {
|
provisioning: {
|
||||||
secret: "!secretToken"
|
secret: "!secretToken"
|
||||||
|
@ -10,16 +10,33 @@ import axios from "axios";
|
|||||||
import markdown from "markdown-it";
|
import markdown from "markdown-it";
|
||||||
import { Connection, ProvisionConnectionOpts } from "./IConnection";
|
import { Connection, ProvisionConnectionOpts } from "./IConnection";
|
||||||
import { GetConnectionsResponseItem } from "../provisioning/api";
|
import { GetConnectionsResponseItem } from "../provisioning/api";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
const log = new LogWrapper("FeedConnection");
|
const log = new LogWrapper("FeedConnection");
|
||||||
const md = new markdown();
|
const md = new markdown();
|
||||||
|
|
||||||
|
export interface LastResultOk {
|
||||||
|
timestamp: number;
|
||||||
|
ok: true;
|
||||||
|
}
|
||||||
|
export interface LastResultFail {
|
||||||
|
timestamp: number;
|
||||||
|
ok: false;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface FeedConnectionState extends IConnectionState {
|
export interface FeedConnectionState extends IConnectionState {
|
||||||
url: string;
|
url: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FeedResponseItem = GetConnectionsResponseItem<FeedConnectionState, object>;
|
export interface FeedConnectionSecrets {
|
||||||
|
lastResults: Array<LastResultOk|LastResultFail>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FeedResponseItem = GetConnectionsResponseItem<FeedConnectionState, FeedConnectionSecrets>;
|
||||||
|
|
||||||
|
const MAX_LAST_RESULT_ITEMS = 5;
|
||||||
|
|
||||||
@Connection
|
@Connection
|
||||||
export class FeedConnection extends BaseConnection implements IConnection {
|
export class FeedConnection extends BaseConnection implements IConnection {
|
||||||
@ -37,14 +54,23 @@ export class FeedConnection extends BaseConnection implements IConnection {
|
|||||||
static async validateUrl(url: string): Promise<void> {
|
static async validateUrl(url: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
new URL(url);
|
new URL(url);
|
||||||
const res = await axios.head(url).catch(_ => axios.get(url));
|
} catch (ex) {
|
||||||
const contentType = res.headers['content-type'];
|
throw new ApiError("Feed URL doesn't appear valid", ErrCode.BadValue);
|
||||||
// we're deliberately liberal here, since different things pop up in the wild
|
}
|
||||||
if (!contentType.match(/xml/)) {
|
let res;
|
||||||
throw new Error(`${contentType} doesn't look like an RSS/Atom feed`);
|
try {
|
||||||
}
|
res = await axios.head(url).catch(() => axios.get(url));
|
||||||
} catch (err) {
|
} catch (ex) {
|
||||||
throw new Error(`${url} doesn't look like a valid feed URL: ${err}`);
|
throw new ApiError(`Could not read from URL: ${ex.message}`, ErrCode.BadValue);
|
||||||
|
}
|
||||||
|
const contentType = res.headers['content-type'];
|
||||||
|
// we're deliberately liberal here, since different things pop up in the wild
|
||||||
|
if (!contentType.match(/xml/)) {
|
||||||
|
throw new ApiError(
|
||||||
|
`Feed responded with a content type of "${contentType}", which doesn't look like an RSS/Atom feed`,
|
||||||
|
ErrCode.BadValue,
|
||||||
|
StatusCodes.UNSUPPORTED_MEDIA_TYPE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,11 +83,7 @@ export class FeedConnection extends BaseConnection implements IConnection {
|
|||||||
if (typeof url !== 'string') {
|
if (typeof url !== 'string') {
|
||||||
throw new ApiError('No URL specified', ErrCode.BadValue);
|
throw new ApiError('No URL specified', ErrCode.BadValue);
|
||||||
}
|
}
|
||||||
try {
|
await FeedConnection.validateUrl(url);
|
||||||
await FeedConnection.validateUrl(url);
|
|
||||||
} catch (err: any) {
|
|
||||||
throw new ApiError(err.toString(), ErrCode.BadValue);
|
|
||||||
}
|
|
||||||
if (typeof data.label !== 'undefined' && typeof data.label !== 'string') {
|
if (typeof data.label !== 'undefined' && typeof data.label !== 'string') {
|
||||||
throw new ApiError('Label must be a string', ErrCode.BadValue);
|
throw new ApiError('Label must be a string', ErrCode.BadValue);
|
||||||
}
|
}
|
||||||
@ -95,10 +117,14 @@ export class FeedConnection extends BaseConnection implements IConnection {
|
|||||||
url: this.feedUrl,
|
url: this.feedUrl,
|
||||||
label: this.state.label,
|
label: this.state.label,
|
||||||
},
|
},
|
||||||
|
secrets: {
|
||||||
|
lastResults: this.lastResults,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private hasError = false;
|
private hasError = false;
|
||||||
|
private readonly lastResults = new Array<LastResultOk|LastResultFail>();
|
||||||
|
|
||||||
public get feedUrl(): string {
|
public get feedUrl(): string {
|
||||||
return this.state.url;
|
return this.state.url;
|
||||||
@ -121,7 +147,6 @@ export class FeedConnection extends BaseConnection implements IConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async handleFeedEntry(entry: FeedEntry): Promise<void> {
|
public async handleFeedEntry(entry: FeedEntry): Promise<void> {
|
||||||
this.hasError = false;
|
|
||||||
|
|
||||||
let entryDetails;
|
let entryDetails;
|
||||||
if (entry.title && entry.link) {
|
if (entry.title && entry.link) {
|
||||||
@ -143,7 +168,27 @@ export class FeedConnection extends BaseConnection implements IConnection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFeedSuccess() {
|
||||||
|
this.hasError = false;
|
||||||
|
this.lastResults.unshift({
|
||||||
|
ok: true,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
});
|
||||||
|
this.lastResults.splice(MAX_LAST_RESULT_ITEMS-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
public async handleFeedError(error: FeedError): Promise<void> {
|
public async handleFeedError(error: FeedError): Promise<void> {
|
||||||
|
this.lastResults.unshift({
|
||||||
|
ok: false,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
this.lastResults.splice(MAX_LAST_RESULT_ITEMS-1, 1);
|
||||||
|
const wasLastResultSuccessful = this.lastResults[0]?.ok !== false;
|
||||||
|
if (wasLastResultSuccessful && error.shouldErrorBeSilent) {
|
||||||
|
// To avoid short term failures bubbling up, if the error is serious, we still bubble.
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.hasError) {
|
if (!this.hasError) {
|
||||||
await this.as.botIntent.sendEvent(this.roomId, {
|
await this.as.botIntent.sendEvent(this.roomId, {
|
||||||
msgtype: 'm.notice',
|
msgtype: 'm.notice',
|
||||||
|
@ -14,6 +14,7 @@ import { AdminRoom } from "../AdminRoom";
|
|||||||
import { GitLabRepoConnection } from "./GitlabRepo";
|
import { GitLabRepoConnection } from "./GitlabRepo";
|
||||||
import { IConnectionState, ProvisionConnectionOpts } from "./IConnection";
|
import { IConnectionState, ProvisionConnectionOpts } from "./IConnection";
|
||||||
import LogWrapper from "../LogWrapper";
|
import LogWrapper from "../LogWrapper";
|
||||||
|
import { ApiError } from "matrix-appservice-bridge";
|
||||||
const md = new markdown();
|
const md = new markdown();
|
||||||
const log = new LogWrapper("SetupConnection");
|
const log = new LogWrapper("SetupConnection");
|
||||||
|
|
||||||
@ -182,7 +183,11 @@ export class SetupConnection extends CommandConnection {
|
|||||||
await FeedConnection.validateUrl(url);
|
await FeedConnection.validateUrl(url);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
log.debug(`Feed URL '${url}' failed validation: ${err}`);
|
log.debug(`Feed URL '${url}' failed validation: ${err}`);
|
||||||
throw new CommandError("Invalid URL", `${url} doesn't look like a valid feed URL`);
|
if (err instanceof ApiError) {
|
||||||
|
throw new CommandError("Invalid URL", err.error);
|
||||||
|
} else {
|
||||||
|
throw new CommandError("Invalid URL", `${url} doesn't look like a valid feed URL`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await FeedConnection.provisionConnection(this.roomId, userId, { url, label }, this.provisionOpts);
|
await FeedConnection.provisionConnection(this.roomId, userId, { url, label }, this.provisionOpts);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ErrorObject } from "ajv";
|
import { ErrorObject } from "ajv";
|
||||||
import { NextFunction, Response, Request } from "express";
|
import { NextFunction, Response, Request } from "express";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { IApiError } from "matrix-appservice-bridge";
|
import { IApiError } from "matrix-appservice-bridge";
|
||||||
import LogWrapper from "../LogWrapper";
|
import LogWrapper from "../LogWrapper";
|
||||||
|
|
||||||
@ -53,26 +54,26 @@ export enum ErrCode {
|
|||||||
MethodNotAllowed = "HS_METHOD_NOT_ALLOWED"
|
MethodNotAllowed = "HS_METHOD_NOT_ALLOWED"
|
||||||
}
|
}
|
||||||
|
|
||||||
const ErrCodeToStatusCode: Record<ErrCode, number> = {
|
const ErrCodeToStatusCode: Record<ErrCode, StatusCodes> = {
|
||||||
HS_UNKNOWN: 500,
|
HS_UNKNOWN: StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
HS_NOTFOUND: 404,
|
HS_NOTFOUND: StatusCodes.NOT_FOUND,
|
||||||
HS_UNSUPPORTED_OPERATION: 400,
|
HS_UNSUPPORTED_OPERATION: StatusCodes.BAD_REQUEST,
|
||||||
HS_FORBIDDEN_USER: 403,
|
HS_FORBIDDEN_USER: StatusCodes.FORBIDDEN,
|
||||||
HS_FORBIDDEN_BOT: 403,
|
HS_FORBIDDEN_BOT: StatusCodes.FORBIDDEN,
|
||||||
HS_NOT_IN_ROOM: 403,
|
HS_NOT_IN_ROOM: StatusCodes.FORBIDDEN,
|
||||||
HS_BAD_VALUE: 400,
|
HS_BAD_VALUE: StatusCodes.BAD_REQUEST,
|
||||||
HS_BAD_TOKEN: 401,
|
HS_BAD_TOKEN: StatusCodes.UNAUTHORIZED,
|
||||||
HS_DISABLED_FEATURE: 500,
|
HS_DISABLED_FEATURE: StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
HS_ADDITIONAL_ACTION_REQUIRED: 400,
|
HS_ADDITIONAL_ACTION_REQUIRED: StatusCodes.BAD_REQUEST,
|
||||||
HS_CONFLICTING_CONNECTION: 409,
|
HS_CONFLICTING_CONNECTION: StatusCodes.CONFLICT,
|
||||||
HS_METHOD_NOT_ALLOWED: 405,
|
HS_METHOD_NOT_ALLOWED: StatusCodes.METHOD_NOT_ALLOWED,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ApiError extends Error implements IApiError {
|
export class ApiError extends Error implements IApiError {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly error: string,
|
public readonly error: string,
|
||||||
public readonly errcode = ErrCode.Unknown,
|
public readonly errcode = ErrCode.Unknown,
|
||||||
public readonly statusCode = -1,
|
public readonly statusCode: number|StatusCodes = -1,
|
||||||
public readonly additionalContent: Record<string, unknown> = {},
|
public readonly additionalContent: Record<string, unknown> = {},
|
||||||
) {
|
) {
|
||||||
super(`API error ${errcode}: ${error}`);
|
super(`API error ${errcode}: ${error}`);
|
||||||
|
@ -9,6 +9,8 @@ import Ajv from "ajv";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Parser from "rss-parser";
|
import Parser from "rss-parser";
|
||||||
import Metrics from "../Metrics";
|
import Metrics from "../Metrics";
|
||||||
|
import UserAgent from "../UserAgent";
|
||||||
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
const log = new LogWrapper("FeedReader");
|
const log = new LogWrapper("FeedReader");
|
||||||
|
|
||||||
@ -16,9 +18,28 @@ export class FeedError extends Error {
|
|||||||
constructor(
|
constructor(
|
||||||
public url: string,
|
public url: string,
|
||||||
public cause: Error,
|
public cause: Error,
|
||||||
|
public readonly fetchKey: string,
|
||||||
) {
|
) {
|
||||||
super(`Error fetching feed ${url}: ${cause.message}`);
|
super(`Error fetching feed ${url}: ${cause.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get shouldErrorBeSilent() {
|
||||||
|
if (axios.isAxiosError(this.cause) && this.cause.response?.status) {
|
||||||
|
if (this.cause.response.status % 500 < 100) {
|
||||||
|
// 5XX error, retry these as it might be a server screwup.
|
||||||
|
return true;
|
||||||
|
} else if (this.cause.response.status % 400 < 100) {
|
||||||
|
// 4XX error, actually report these because the server is explicity stating we can't read the resource.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (axios.isAxiosError(this.cause) && this.cause.code === 'ECONNRESET') {
|
||||||
|
// Fuzzy match this, because it's usually a tempoary error.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Err on the side of safety and report the rest
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FeedEntry {
|
export interface FeedEntry {
|
||||||
@ -28,6 +49,10 @@ export interface FeedEntry {
|
|||||||
},
|
},
|
||||||
title: string|null,
|
title: string|null,
|
||||||
link: string|null,
|
link: string|null,
|
||||||
|
/**
|
||||||
|
* Unique key to identify the specific fetch across entries.
|
||||||
|
*/
|
||||||
|
fetchKey: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AccountData {
|
interface AccountData {
|
||||||
@ -65,10 +90,10 @@ export class FeedReader {
|
|||||||
static readonly seenEntriesEventType = "uk.half-shot.matrix-hookshot.feed.reader.seenEntries";
|
static readonly seenEntriesEventType = "uk.half-shot.matrix-hookshot.feed.reader.seenEntries";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private config: BridgeConfigFeeds,
|
private readonly config: BridgeConfigFeeds,
|
||||||
private connectionManager: ConnectionManager,
|
private readonly connectionManager: ConnectionManager,
|
||||||
private queue: MessageQueue,
|
private readonly queue: MessageQueue,
|
||||||
private matrixClient: MatrixClient,
|
private readonly matrixClient: MatrixClient,
|
||||||
) {
|
) {
|
||||||
this.connections = this.connectionManager.getAllConnectionsOfType(FeedConnection);
|
this.connections = this.connectionManager.getAllConnectionsOfType(FeedConnection);
|
||||||
this.calculateFeedUrls();
|
this.calculateFeedUrls();
|
||||||
@ -145,8 +170,15 @@ export class FeedReader {
|
|||||||
const fetchingStarted = Date.now();
|
const fetchingStarted = Date.now();
|
||||||
|
|
||||||
for (const url of this.observedFeedUrls.values()) {
|
for (const url of this.observedFeedUrls.values()) {
|
||||||
|
const fetchKey = randomUUID();
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(url.toString());
|
const res = await axios.get(url.toString(), {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': UserAgent,
|
||||||
|
},
|
||||||
|
// We don't want to wait forever for the feed.
|
||||||
|
timeout: this.config.pollTimeoutSeconds * 1000,
|
||||||
|
});
|
||||||
const feed = await (new Parser()).parseString(res.data);
|
const feed = await (new Parser()).parseString(res.data);
|
||||||
let initialSync = false;
|
let initialSync = false;
|
||||||
let seenGuids = this.seenEntries.get(url);
|
let seenGuids = this.seenEntries.get(url);
|
||||||
@ -182,6 +214,7 @@ export class FeedReader {
|
|||||||
},
|
},
|
||||||
title: item.title ? stripHtml(item.title) : null,
|
title: item.title ? stripHtml(item.title) : null,
|
||||||
link: item.link || null,
|
link: item.link || null,
|
||||||
|
fetchKey
|
||||||
};
|
};
|
||||||
|
|
||||||
log.debug('New entry:', entry);
|
log.debug('New entry:', entry);
|
||||||
@ -200,10 +233,12 @@ export class FeedReader {
|
|||||||
const newSeenItems = Array.from(new Set([ ...newGuids, ...seenGuids ]).values()).slice(0, maxGuids);
|
const newSeenItems = Array.from(new Set([ ...newGuids, ...seenGuids ]).values()).slice(0, maxGuids);
|
||||||
this.seenEntries.set(url, newSeenItems);
|
this.seenEntries.set(url, newSeenItems);
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
this.queue.push<undefined>({ eventName: 'feed.success', sender: 'FeedReader', data: undefined});
|
||||||
const error = new FeedError(url.toString(), err);
|
} catch (err: unknown) {
|
||||||
log.error(error.message);
|
const error = err instanceof Error ? err : new Error(`Unknown error ${err}`);
|
||||||
this.queue.push<FeedError>({ eventName: 'feed.error', sender: 'FeedReader', data: error });
|
const feedError = new FeedError(url.toString(), error, fetchKey);
|
||||||
|
log.error("Unable to read feed:", feedError.message);
|
||||||
|
this.queue.push<FeedError>({ eventName: 'feed.error', sender: 'FeedReader', data: feedError});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (seenEntriesChanged) await this.saveSeenEntries();
|
if (seenEntriesChanged) await this.saveSeenEntries();
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import preact from '@preact/preset-vite'
|
import preact from '@preact/preset-vite'
|
||||||
|
import svgLoader from 'vite-svg-loader'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [preact()],
|
plugins: [preact(), svgLoader({ defaultImport: 'url'})],
|
||||||
root: 'web',
|
root: 'web',
|
||||||
base: '',
|
base: '',
|
||||||
build: {
|
build: {
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.error-pane {
|
|
||||||
max-width: 480px;
|
|
||||||
}
|
|
4
web/components/elements/ErrorPane.module.scss
Normal file
4
web/components/elements/ErrorPane.module.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.errorPane {
|
||||||
|
max-width: 480px;
|
||||||
|
color: #FF4B55;
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { h, FunctionComponent } from "preact";
|
import { h, FunctionComponent } from "preact";
|
||||||
import "./ErrorPane.css";
|
import ErrorBadge from "../../icons/warning-badge.svg";
|
||||||
|
import style from "./ErrorPane.module.scss";
|
||||||
|
|
||||||
export const ErrorPane: FunctionComponent<{header?: string}> = ({ children, header }) => {
|
export const ErrorPane: FunctionComponent<{header?: string}> = ({ children, header }) => {
|
||||||
return <div class="card error error-pane">
|
return <div class={`card error ${style.errorPane}`}>
|
||||||
<h3>{ header || "Error occured during widget load" }</h3>
|
<p><strong><img src={ErrorBadge} /> { header || "Error occured during widget load" }</strong>: {children}</p>
|
||||||
<p>{children}</p>
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
4
web/components/roomConfig/FeedConnection.module.scss
Normal file
4
web/components/roomConfig/FeedConnection.module.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.resultListItem {
|
||||||
|
list-style: none;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
@ -4,9 +4,28 @@ import { BridgeConfig } from "../../BridgeAPI";
|
|||||||
import { FeedConnectionState, FeedResponseItem } from "../../../src/Connections/FeedConnection";
|
import { FeedConnectionState, FeedResponseItem } from "../../../src/Connections/FeedConnection";
|
||||||
import { ConnectionConfigurationProps, RoomConfig } from "./RoomConfig";
|
import { ConnectionConfigurationProps, RoomConfig } from "./RoomConfig";
|
||||||
import { Button, ButtonSet, InputField } from "../elements";
|
import { Button, ButtonSet, InputField } from "../elements";
|
||||||
|
import styles from "./FeedConnection.module.scss";
|
||||||
|
|
||||||
import FeedsIcon from "../../icons/feeds.png";
|
import FeedsIcon from "../../icons/feeds.png";
|
||||||
|
|
||||||
|
const FeedRecentResults: FunctionComponent<{item: FeedResponseItem}> = ({ item }) => {
|
||||||
|
if (!item.secrets) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <>
|
||||||
|
<h3>Recent feed results</h3>
|
||||||
|
{!item.secrets.lastResults.length && <span>There have been no recent updates for this feed.</span>}
|
||||||
|
<ul>
|
||||||
|
{item.secrets.lastResults.map(item => <li styles={styles.resultListItem} key={item.timestamp}>
|
||||||
|
{new Date(item.timestamp).toLocaleString()}:
|
||||||
|
{item.ok && `✅ Successful fetch`}
|
||||||
|
{!item.ok && `⚠️ ${item.error}`}
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ServiceConfig, FeedResponseItem, FeedConnectionState>> = ({existingConnection, onSave, onRemove}) => {
|
const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ServiceConfig, FeedResponseItem, FeedConnectionState>> = ({existingConnection, onSave, onRemove}) => {
|
||||||
const urlRef = createRef<HTMLInputElement>();
|
const urlRef = createRef<HTMLInputElement>();
|
||||||
const labelRef = createRef<HTMLInputElement>();
|
const labelRef = createRef<HTMLInputElement>();
|
||||||
@ -27,15 +46,20 @@ const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<Se
|
|||||||
}, [canEdit, onSave, urlRef, labelRef, existingConnection]);
|
}, [canEdit, onSave, urlRef, labelRef, existingConnection]);
|
||||||
|
|
||||||
return <form onSubmit={handleSave}>
|
return <form onSubmit={handleSave}>
|
||||||
<InputField visible={!existingConnection} label="URL" noPadding={true}>
|
{ existingConnection && <FeedRecentResults item={existingConnection} />}
|
||||||
<input ref={urlRef} disabled={!canEdit} placeholder="Feed URL" type="text" value={existingConnection?.config.url} />
|
|
||||||
<input ref={labelRef} disabled={!canEdit} placeholder="Label (optional)" type="text" value={existingConnection?.config.label} />
|
|
||||||
</InputField>
|
|
||||||
|
|
||||||
|
<InputField visible={!existingConnection} label="URL" noPadding={true}>
|
||||||
|
<input ref={urlRef} disabled={!canEdit} type="text" value={existingConnection?.config.url} />
|
||||||
|
</InputField>
|
||||||
|
<InputField visible={!existingConnection} label="Label" noPadding={true}>
|
||||||
|
<input ref={labelRef} disabled={!canEdit} type="text" value={existingConnection?.config.label} />
|
||||||
|
</InputField>
|
||||||
|
|
||||||
<ButtonSet>
|
<ButtonSet>
|
||||||
{ canEdit && <Button type="submit">{ existingConnection ? "Save" : "Subscribe" }</Button>}
|
{ canEdit && <Button type="submit">{ existingConnection ? "Save" : "Subscribe" }</Button>}
|
||||||
{ canEdit && existingConnection && <Button intent="remove" onClick={onRemove}>Unsubscribe</Button>}
|
{ canEdit && existingConnection && <Button intent="remove" onClick={onRemove}>Unsubscribe</Button>}
|
||||||
</ButtonSet>
|
</ButtonSet>
|
||||||
|
|
||||||
</form>;
|
</form>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { h, FunctionComponent } from "preact";
|
import { h, FunctionComponent } from "preact";
|
||||||
import { useCallback, useEffect, useReducer, useState } from "preact/hooks"
|
import { useCallback, useEffect, useReducer, useState } from "preact/hooks"
|
||||||
import { BridgeAPI } from "../../BridgeAPI";
|
import { BridgeAPI, BridgeAPIError } from "../../BridgeAPI";
|
||||||
import { ErrorPane, ListItem } from "../elements";
|
import { ErrorPane, ListItem } from "../elements";
|
||||||
import style from "./RoomConfig.module.scss";
|
import style from "./RoomConfig.module.scss";
|
||||||
import { GetConnectionsResponseItem } from "../../../src/provisioning/api";
|
import { GetConnectionsResponseItem } from "../../../src/provisioning/api";
|
||||||
@ -33,7 +33,7 @@ interface IRoomConfigProps<SConfig, ConnectionType extends GetConnectionsRespons
|
|||||||
export const RoomConfig = function<SConfig, ConnectionType extends GetConnectionsResponseItem, ConnectionState>(props: IRoomConfigProps<SConfig, ConnectionType, ConnectionState>) {
|
export const RoomConfig = function<SConfig, ConnectionType extends GetConnectionsResponseItem, ConnectionState>(props: IRoomConfigProps<SConfig, ConnectionType, ConnectionState>) {
|
||||||
const { api, roomId, type, headerImg, text, listItemName, connectionEventType } = props;
|
const { api, roomId, type, headerImg, text, listItemName, connectionEventType } = props;
|
||||||
const ConnectionConfigComponent = props.connectionConfigComponent;
|
const ConnectionConfigComponent = props.connectionConfigComponent;
|
||||||
const [ error, setError ] = useState<null|string>(null);
|
const [ error, setError ] = useState<null|{header?: string, message: string}>(null);
|
||||||
const [ connections, setConnections ] = useState<ConnectionType[]|null>(null);
|
const [ connections, setConnections ] = useState<ConnectionType[]|null>(null);
|
||||||
const [ serviceConfig, setServiceConfig ] = useState<SConfig|null>(null);
|
const [ serviceConfig, setServiceConfig ] = useState<SConfig|null>(null);
|
||||||
const [ canEditRoom, setCanEditRoom ] = useState<boolean>(false);
|
const [ canEditRoom, setCanEditRoom ] = useState<boolean>(false);
|
||||||
@ -44,18 +44,28 @@ export const RoomConfig = function<SConfig, ConnectionType extends GetConnection
|
|||||||
api.getConnectionsForService<ConnectionType>(roomId, type).then(res => {
|
api.getConnectionsForService<ConnectionType>(roomId, type).then(res => {
|
||||||
setCanEditRoom(res.canEdit);
|
setCanEditRoom(res.canEdit);
|
||||||
setConnections(res.connections);
|
setConnections(res.connections);
|
||||||
|
setError(null);
|
||||||
}).catch(ex => {
|
}).catch(ex => {
|
||||||
console.warn("Failed to fetch existing connections", ex);
|
console.warn("Failed to fetch existing connections", ex);
|
||||||
setError("Failed to fetch existing connections");
|
setError({
|
||||||
|
header: "Failed to fetch existing connections",
|
||||||
|
message: ex instanceof BridgeAPIError ? ex.message : "Unknown error"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}, [api, roomId, type, newConnectionKey]);
|
}, [api, roomId, type, newConnectionKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.getServiceConfig<SConfig>(type)
|
api.getServiceConfig<SConfig>(type)
|
||||||
.then(setServiceConfig)
|
.then(setServiceConfig)
|
||||||
|
.then(() => {
|
||||||
|
setError(null);
|
||||||
|
})
|
||||||
.catch(ex => {
|
.catch(ex => {
|
||||||
console.warn("Failed to fetch service config", ex);
|
console.warn("Failed to fetch service config", ex);
|
||||||
setError("Failed to fetch service config");
|
setError({
|
||||||
|
header: "Failed to fetch service config",
|
||||||
|
message: ex instanceof BridgeAPIError ? ex.message : "Unknown error"
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}, [api, type]);
|
}, [api, type]);
|
||||||
|
|
||||||
@ -63,15 +73,19 @@ export const RoomConfig = function<SConfig, ConnectionType extends GetConnection
|
|||||||
api.createConnection(roomId, connectionEventType, config).then(() => {
|
api.createConnection(roomId, connectionEventType, config).then(() => {
|
||||||
// Force reload
|
// Force reload
|
||||||
incrementConnectionKey(undefined);
|
incrementConnectionKey(undefined);
|
||||||
|
setError(null);
|
||||||
}).catch(ex => {
|
}).catch(ex => {
|
||||||
console.warn("Failed to create connection", ex);
|
console.warn("Failed to create connection", ex);
|
||||||
setError("Failed to create connection");
|
setError({
|
||||||
|
header: "Failed to create connection",
|
||||||
|
message: ex instanceof BridgeAPIError ? ex.message : "Unknown error"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}, [api, roomId, connectionEventType]);
|
}, [api, roomId, connectionEventType]);
|
||||||
|
|
||||||
return <main>
|
return <main>
|
||||||
{
|
{
|
||||||
error && <ErrorPane header="Error">{error}</ErrorPane>
|
error && <ErrorPane header={error.header || "Error"}>{error.message}</ErrorPane>
|
||||||
}
|
}
|
||||||
<header className={style.header}>
|
<header className={style.header}>
|
||||||
<img src={headerImg} />
|
<img src={headerImg} />
|
||||||
@ -97,17 +111,25 @@ export const RoomConfig = function<SConfig, ConnectionType extends GetConnection
|
|||||||
api.updateConnection(roomId, c.id, config).then(() => {
|
api.updateConnection(roomId, c.id, config).then(() => {
|
||||||
// Force reload
|
// Force reload
|
||||||
incrementConnectionKey(undefined);
|
incrementConnectionKey(undefined);
|
||||||
|
setError(null);
|
||||||
}).catch(ex => {
|
}).catch(ex => {
|
||||||
console.warn("Failed to create connection", ex);
|
console.warn("Failed to create connection", ex);
|
||||||
setError("Failed to create connection");
|
setError({
|
||||||
|
header: "Failed to create connection",
|
||||||
|
message: ex instanceof BridgeAPIError ? ex.message : "Unknown error"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onRemove={() => {
|
onRemove={() => {
|
||||||
api.removeConnection(roomId, c.id).then(() => {
|
api.removeConnection(roomId, c.id).then(() => {
|
||||||
setConnections(conn => conn.filter(conn => c.id !== conn.id));
|
setConnections(conn => conn.filter(conn => c.id !== conn.id));
|
||||||
|
setError(null);
|
||||||
}).catch(ex => {
|
}).catch(ex => {
|
||||||
console.warn("Failed to remove connection", ex);
|
console.warn("Failed to remove connection", ex);
|
||||||
setError("Failed to remove connection");
|
setError({
|
||||||
|
header: "Failed to remove connection",
|
||||||
|
message: ex instanceof BridgeAPIError ? ex.message : "Unknown error"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
5
web/icons/warning-badge.svg
Normal file
5
web/icons/warning-badge.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="8" cy="8" r="8" fill="#FF4B55"/>
|
||||||
|
<rect x="7" y="3" width="2" height="6" rx="1" fill="white"/>
|
||||||
|
<rect x="7" y="11" width="2" height="2" rx="1" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 271 B |
204
yarn.lock
204
yarn.lock
@ -330,6 +330,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.6.tgz#8f194828193e8fa79166f34a4b4e52f3e769a314"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.6.tgz#8f194828193e8fa79166f34a4b4e52f3e769a314"
|
||||||
integrity sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==
|
integrity sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==
|
||||||
|
|
||||||
|
"@babel/parser@^7.16.4":
|
||||||
|
version "7.18.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.13.tgz#5b2dd21cae4a2c5145f1fbd8ca103f9313d3b7e4"
|
||||||
|
integrity sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==
|
||||||
|
|
||||||
"@babel/parser@^7.16.7", "@babel/parser@^7.17.3", "@babel/parser@^7.17.8":
|
"@babel/parser@^7.16.7", "@babel/parser@^7.17.3", "@babel/parser@^7.17.8":
|
||||||
version "7.17.8"
|
version "7.17.8"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240"
|
||||||
@ -1077,6 +1082,11 @@
|
|||||||
domhandler "^4.2.0"
|
domhandler "^4.2.0"
|
||||||
selderee "^0.6.0"
|
selderee "^0.6.0"
|
||||||
|
|
||||||
|
"@trysound/sax@0.2.0":
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
||||||
|
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
||||||
|
|
||||||
"@tsconfig/node10@^1.0.7":
|
"@tsconfig/node10@^1.0.7":
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
|
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
|
||||||
@ -1250,10 +1260,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.31.tgz#a5bb84ecfa27eec5e1c802c6bbf8139bdb163a5d"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.31.tgz#a5bb84ecfa27eec5e1c802c6bbf8139bdb163a5d"
|
||||||
integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==
|
integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==
|
||||||
|
|
||||||
"@types/node@^14":
|
"@types/node@^16":
|
||||||
version "14.18.12"
|
version "16.11.56"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.12.tgz#0d4557fd3b94497d793efd4e7d92df2f83b4ef24"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.56.tgz#dcbb617669481e158e0f1c6204d1c768cd675901"
|
||||||
integrity sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==
|
integrity sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==
|
||||||
|
|
||||||
"@types/qs@*":
|
"@types/qs@*":
|
||||||
version "6.9.7"
|
version "6.9.7"
|
||||||
@ -1439,6 +1449,64 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
|
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
|
||||||
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
|
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
|
||||||
|
|
||||||
|
"@vue/compiler-core@3.2.37":
|
||||||
|
version "3.2.37"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a"
|
||||||
|
integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.16.4"
|
||||||
|
"@vue/shared" "3.2.37"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
"@vue/compiler-dom@3.2.37":
|
||||||
|
version "3.2.37"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5"
|
||||||
|
integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-core" "3.2.37"
|
||||||
|
"@vue/shared" "3.2.37"
|
||||||
|
|
||||||
|
"@vue/compiler-sfc@^3.2.20":
|
||||||
|
version "3.2.37"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4"
|
||||||
|
integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.16.4"
|
||||||
|
"@vue/compiler-core" "3.2.37"
|
||||||
|
"@vue/compiler-dom" "3.2.37"
|
||||||
|
"@vue/compiler-ssr" "3.2.37"
|
||||||
|
"@vue/reactivity-transform" "3.2.37"
|
||||||
|
"@vue/shared" "3.2.37"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
magic-string "^0.25.7"
|
||||||
|
postcss "^8.1.10"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
"@vue/compiler-ssr@3.2.37":
|
||||||
|
version "3.2.37"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff"
|
||||||
|
integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.2.37"
|
||||||
|
"@vue/shared" "3.2.37"
|
||||||
|
|
||||||
|
"@vue/reactivity-transform@3.2.37":
|
||||||
|
version "3.2.37"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca"
|
||||||
|
integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.16.4"
|
||||||
|
"@vue/compiler-core" "3.2.37"
|
||||||
|
"@vue/shared" "3.2.37"
|
||||||
|
estree-walker "^2.0.2"
|
||||||
|
magic-string "^0.25.7"
|
||||||
|
|
||||||
|
"@vue/shared@3.2.37":
|
||||||
|
version "3.2.37"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702"
|
||||||
|
integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==
|
||||||
|
|
||||||
abbrev@1:
|
abbrev@1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||||
@ -1777,6 +1845,11 @@ body-parser@^1.19.0:
|
|||||||
raw-body "2.4.3"
|
raw-body "2.4.3"
|
||||||
type-is "~1.6.18"
|
type-is "~1.6.18"
|
||||||
|
|
||||||
|
boolbase@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||||
|
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
@ -2032,6 +2105,11 @@ commander@^2.19.0:
|
|||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
|
commander@^7.2.0:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||||
|
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
@ -2108,6 +2186,37 @@ cross-spawn@^7.0.2:
|
|||||||
shebang-command "^2.0.0"
|
shebang-command "^2.0.0"
|
||||||
which "^2.0.1"
|
which "^2.0.1"
|
||||||
|
|
||||||
|
css-select@^4.1.3:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
|
||||||
|
integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
|
||||||
|
dependencies:
|
||||||
|
boolbase "^1.0.0"
|
||||||
|
css-what "^6.0.1"
|
||||||
|
domhandler "^4.3.1"
|
||||||
|
domutils "^2.8.0"
|
||||||
|
nth-check "^2.0.1"
|
||||||
|
|
||||||
|
css-tree@^1.1.2, css-tree@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
|
||||||
|
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
|
||||||
|
dependencies:
|
||||||
|
mdn-data "2.0.14"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
css-what@^6.0.1:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
|
||||||
|
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
|
||||||
|
|
||||||
|
csso@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
|
||||||
|
integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
|
||||||
|
dependencies:
|
||||||
|
css-tree "^1.1.2"
|
||||||
|
|
||||||
dashdash@^1.12.0:
|
dashdash@^1.12.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||||
@ -2257,14 +2366,14 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
|
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
|
||||||
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
||||||
|
|
||||||
domhandler@^4.0.0, domhandler@^4.2.0:
|
domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
|
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
|
||||||
integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
|
integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
|
|
||||||
domutils@^2.5.2:
|
domutils@^2.5.2, domutils@^2.8.0:
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
||||||
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
||||||
@ -2695,7 +2804,7 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
|
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
|
||||||
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
|
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
|
||||||
|
|
||||||
estree-walker@^2.0.1:
|
estree-walker@^2.0.1, estree-walker@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||||
@ -3265,6 +3374,11 @@ http-signature@~1.3.1:
|
|||||||
jsprim "^2.0.2"
|
jsprim "^2.0.2"
|
||||||
sshpk "^1.14.1"
|
sshpk "^1.14.1"
|
||||||
|
|
||||||
|
http-status-codes@^2.2.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.2.0.tgz#bb2efe63d941dfc2be18e15f703da525169622be"
|
||||||
|
integrity sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||||
@ -3853,6 +3967,13 @@ lru-cache@^7.10.1:
|
|||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4"
|
||||||
integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==
|
integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==
|
||||||
|
|
||||||
|
magic-string@^0.25.7:
|
||||||
|
version "0.25.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
|
||||||
|
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
|
||||||
|
dependencies:
|
||||||
|
sourcemap-codec "^1.4.8"
|
||||||
|
|
||||||
make-error@^1.1.1:
|
make-error@^1.1.1:
|
||||||
version "1.3.6"
|
version "1.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||||
@ -3935,6 +4056,11 @@ matrix-widget-api@^1.0.0:
|
|||||||
"@types/events" "^3.0.0"
|
"@types/events" "^3.0.0"
|
||||||
events "^3.2.0"
|
events "^3.2.0"
|
||||||
|
|
||||||
|
mdn-data@2.0.14:
|
||||||
|
version "2.0.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||||
|
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||||
|
|
||||||
mdurl@^1.0.1:
|
mdurl@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
||||||
@ -4115,7 +4241,7 @@ nanoid@3.1.20:
|
|||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
||||||
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
|
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
|
||||||
|
|
||||||
nanoid@^3.3.3:
|
nanoid@^3.1.30, nanoid@^3.3.3, nanoid@^3.3.4:
|
||||||
version "3.3.4"
|
version "3.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||||
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||||
@ -4197,6 +4323,13 @@ normalize-path@^3.0.0, normalize-path@~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"
|
||||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||||
|
|
||||||
|
nth-check@^2.0.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
|
||||||
|
integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
|
||||||
|
dependencies:
|
||||||
|
boolbase "^1.0.0"
|
||||||
|
|
||||||
oauth-sign@~0.9.0:
|
oauth-sign@~0.9.0:
|
||||||
version "0.9.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||||
@ -4436,7 +4569,25 @@ pify@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||||
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
||||||
|
|
||||||
postcss@^8.3.11, postcss@^8.4.13:
|
postcss@^8.1.10:
|
||||||
|
version "8.4.16"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
|
||||||
|
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.3.4"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
|
postcss@^8.3.11:
|
||||||
|
version "8.4.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95"
|
||||||
|
integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.1.30"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
source-map-js "^1.0.1"
|
||||||
|
|
||||||
|
postcss@^8.4.13:
|
||||||
version "8.4.13"
|
version "8.4.13"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575"
|
||||||
integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
|
integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
|
||||||
@ -4971,7 +5122,7 @@ slash@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||||
|
|
||||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
|
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||||
@ -4989,11 +5140,16 @@ source-map@^0.5.0:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||||
|
|
||||||
source-map@^0.6.0:
|
source-map@^0.6.0, source-map@^0.6.1:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
|
||||||
|
sourcemap-codec@^1.4.8:
|
||||||
|
version "1.4.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||||
|
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||||
|
|
||||||
sprintf-js@1.1.2:
|
sprintf-js@1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
||||||
@ -5029,6 +5185,11 @@ sshpk@^1.7.0:
|
|||||||
safer-buffer "^2.0.2"
|
safer-buffer "^2.0.2"
|
||||||
tweetnacl "~0.14.0"
|
tweetnacl "~0.14.0"
|
||||||
|
|
||||||
|
stable@^0.1.8:
|
||||||
|
version "0.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
||||||
|
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
|
||||||
|
|
||||||
stack-trace@0.0.x:
|
stack-trace@0.0.x:
|
||||||
version "0.0.10"
|
version "0.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
|
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
|
||||||
@ -5177,6 +5338,19 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
|
svgo@^2.7.0:
|
||||||
|
version "2.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
|
||||||
|
integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
|
||||||
|
dependencies:
|
||||||
|
"@trysound/sax" "0.2.0"
|
||||||
|
commander "^7.2.0"
|
||||||
|
css-select "^4.1.3"
|
||||||
|
css-tree "^1.1.3"
|
||||||
|
csso "^4.2.0"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
stable "^0.1.8"
|
||||||
|
|
||||||
tdigest@^0.1.1:
|
tdigest@^0.1.1:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.1.tgz#2e3cb2c39ea449e55d1e6cd91117accca4588021"
|
resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.1.tgz#2e3cb2c39ea449e55d1e6cd91117accca4588021"
|
||||||
@ -5390,6 +5564,14 @@ verror@1.10.0:
|
|||||||
core-util-is "1.0.2"
|
core-util-is "1.0.2"
|
||||||
extsprintf "^1.2.0"
|
extsprintf "^1.2.0"
|
||||||
|
|
||||||
|
vite-svg-loader@^3.4.0:
|
||||||
|
version "3.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite-svg-loader/-/vite-svg-loader-3.4.0.tgz#4638827fe86b85ecfcea1ad61dd972c351d5befd"
|
||||||
|
integrity sha512-xD3yb1FX+f4l9/TmsYIqyki8ncpcVsZ2gEJFh/wLuNNqt55C8OJ+JlcMWOA/Z9gRA+ylV/TA1wmJLxzZkCRqlA==
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-sfc" "^3.2.20"
|
||||||
|
svgo "^2.7.0"
|
||||||
|
|
||||||
vite@^2.9.13:
|
vite@^2.9.13:
|
||||||
version "2.9.13"
|
version "2.9.13"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.13.tgz#859cb5d4c316c0d8c6ec9866045c0f7858ca6abc"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.13.tgz#859cb5d4c316c0d8c6ec9866045c0f7858ca6abc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user