Re-enabled general eslint rules in TS config
refs https://github.com/TryGhost/DevOps/issues/50 - when creating a TS config in our `eslint-plugin-ghost` dependency, I only extended the recommended config, which left out a lot of stylistic things we used to enforce in JS - this fixes that by bumping the dependency to a version which extends those shared configs, and fixes all the code that currently goes against those rules
This commit is contained in:
parent
c08538ba84
commit
6dc1d08590
@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-shadow */
|
||||
|
||||
import AuthFrame from './AuthFrame';
|
||||
import ContentBox from './components/ContentBox';
|
||||
import PopupBox from './components/PopupBox';
|
||||
@ -32,7 +34,7 @@ const App: React.FC<AppProps> = ({scriptTag}) => {
|
||||
siteUrl: options.siteUrl,
|
||||
apiUrl: options.apiUrl!,
|
||||
apiKey: options.apiKey!
|
||||
})
|
||||
});
|
||||
}, [options]);
|
||||
|
||||
const [adminApi, setAdminApi] = useState<AdminApi|null>(null);
|
||||
@ -45,7 +47,7 @@ const App: React.FC<AppProps> = ({scriptTag}) => {
|
||||
return {
|
||||
...state,
|
||||
...newState
|
||||
}
|
||||
};
|
||||
});
|
||||
}, [setFullState]);
|
||||
|
||||
@ -55,7 +57,7 @@ const App: React.FC<AppProps> = ({scriptTag}) => {
|
||||
// because updates to state may be asynchronous
|
||||
// so calling dispatchAction('counterUp') multiple times, may yield unexpected results if we don't use a callback function
|
||||
setState((state) => {
|
||||
return SyncActionHandler({action, data, state, api, adminApi: adminApi!, options})
|
||||
return SyncActionHandler({action, data, state, api, adminApi: adminApi!, options});
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -66,7 +68,7 @@ const App: React.FC<AppProps> = ({scriptTag}) => {
|
||||
setState((state) => {
|
||||
ActionHandler({action, data, state, api, adminApi: adminApi!, options}).then((updatedState) => {
|
||||
setState({...updatedState});
|
||||
}).catch(console.error);
|
||||
}).catch(console.error); // eslint-disable-line no-console
|
||||
|
||||
// No immediate changes
|
||||
return {};
|
||||
@ -125,7 +127,7 @@ const App: React.FC<AppProps> = ({scriptTag}) => {
|
||||
pagination: data.meta.pagination,
|
||||
count: count
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/** Initialize comments setup on load, fetch data and setup state*/
|
||||
const initSetup = async () => {
|
||||
|
@ -10,5 +10,5 @@ const AuthFrame: React.FC<Props> = ({adminUrl, onLoad}) => {
|
||||
return (
|
||||
<iframe data-frame="admin-auth" src={adminUrl + 'auth-frame/'} style={iframeStyle} title="auth-frame" onLoad={onLoad}></iframe>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default AuthFrame;
|
||||
|
@ -37,7 +37,7 @@ export default class IFrame extends Component<any> {
|
||||
|
||||
if (this.props.onResize) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
(new ResizeObserver(_ => {
|
||||
(new ResizeObserver((_) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.props.onResize(this.iframeRoot);
|
||||
});
|
||||
|
@ -18,7 +18,7 @@ const Pagination = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const text = left === 1 ? t('Show 1 previous comment') : t('Show {{amount}} previous comments', {amount: formatNumber(left)})
|
||||
const text = left === 1 ? t('Show 1 previous comment') : t('Show {{amount}} previous comments', {amount: formatNumber(left)});
|
||||
|
||||
return (
|
||||
<button className="text-md group mb-10 flex w-full items-center px-0 pb-2 pt-0 text-left font-sans font-semibold text-neutral-700 dark:text-white" data-testid="pagination-component" type="button" onClick={loadMore}>
|
||||
|
@ -8,7 +8,7 @@ type Props = {
|
||||
};
|
||||
const RepliesPagination: React.FC<Props> = ({loadMore, count}) => {
|
||||
const {t} = useAppContext();
|
||||
const text = count === 1 ? t('Show 1 more reply') : t('Show {{amount}} more replies', {amount: formatNumber(count)})
|
||||
const text = count === 1 ? t('Show 1 more reply') : t('Show {{amount}} more replies', {amount: formatNumber(count)});
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center justify-start">
|
||||
|
@ -69,7 +69,7 @@ export function formatRelativeTime(dateString: string, t: TranslationFunction):
|
||||
}
|
||||
|
||||
// Diff in months
|
||||
diff = diff * 7 / 30
|
||||
diff = diff * 7 / 30;
|
||||
if (diff < 12) {
|
||||
if (Math.floor(diff) === 1) {
|
||||
// Special case, we should compare based on dates in the future instead
|
||||
|
@ -52,10 +52,10 @@ export abstract class BookshelfRepository<IDType, T extends Entity<IDType>> {
|
||||
|
||||
const existing = await this.Model.findOne({id: entity.id}, {require: false});
|
||||
if (existing) {
|
||||
existing.set(this.toPrimitive(entity))
|
||||
existing.set(this.toPrimitive(entity));
|
||||
await existing.save({}, {autoRefresh: false, method: 'update'});
|
||||
} else {
|
||||
await this.Model.add(this.toPrimitive(entity))
|
||||
await this.Model.add(this.toPrimitive(entity));
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ export abstract class BookshelfRepository<IDType, T extends Entity<IDType>> {
|
||||
order: this.#orderToString(order),
|
||||
limit,
|
||||
page
|
||||
})
|
||||
});
|
||||
return (await Promise.all(models.map(model => this.modelToEntity(model)))).filter(entity => !!entity) as T[];
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ const messages = {
|
||||
};
|
||||
|
||||
function validateFilter(filter: string | null, type: 'manual' | 'automatic', isAllowedEmpty = false) {
|
||||
const allowedProperties = ['featured', 'published_at', 'tag', 'tags']
|
||||
const allowedProperties = ['featured', 'published_at', 'tag', 'tags'];
|
||||
if (type === 'manual') {
|
||||
if (filter !== null) {
|
||||
throw new ValidationError({
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import {Collection} from './Collection';
|
||||
import {Knex} from "knex";
|
||||
import {Knex} from 'knex';
|
||||
|
||||
export interface CollectionRepository {
|
||||
createTransaction(fn: (transaction: Knex.Transaction) => Promise<any>): Promise<any>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import logging from '@tryghost/logging';
|
||||
import tpl from '@tryghost/tpl';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import {Knex} from "knex";
|
||||
import {Knex} from 'knex';
|
||||
import {
|
||||
PostsBulkUnpublishedEvent,
|
||||
PostsBulkFeaturedEvent,
|
||||
PostsBulkUnfeaturedEvent,
|
||||
PostsBulkAddTagsEvent
|
||||
} from "@tryghost/post-events";
|
||||
} from '@tryghost/post-events';
|
||||
import debugModule from '@tryghost/debug';
|
||||
import {Collection} from './Collection';
|
||||
import {CollectionRepository} from './CollectionRepository';
|
||||
@ -280,7 +280,7 @@ export class CollectionsService {
|
||||
featured: postEditEvent.previous.featured,
|
||||
published_at: postEditEvent.previous.published_at,
|
||||
tags: postEditEvent.previous.tags
|
||||
}
|
||||
};
|
||||
|
||||
return !isEqual(current, previous);
|
||||
}
|
||||
@ -289,7 +289,7 @@ export class CollectionsService {
|
||||
return await this.collectionsRepository.createTransaction(async (transaction) => {
|
||||
const collections = await this.collectionsRepository.getAll({
|
||||
transaction
|
||||
})
|
||||
});
|
||||
|
||||
for (const collection of collections) {
|
||||
if (collection.type === 'automatic' && collection.filter) {
|
||||
@ -423,7 +423,7 @@ export class CollectionsService {
|
||||
await this.collectionsRepository.save(collection, {transaction});
|
||||
}
|
||||
|
||||
collectionsChangeLog += `Post ${postEdit.id} was updated and added to collection ${collection.slug} with filter ${collection.filter}\n`
|
||||
collectionsChangeLog += `Post ${postEdit.id} was updated and added to collection ${collection.slug} with filter ${collection.filter}\n`;
|
||||
} else {
|
||||
debug(`Post ${postEdit.id} was updated but did not update any collections`);
|
||||
}
|
||||
@ -443,7 +443,7 @@ export class CollectionsService {
|
||||
});
|
||||
|
||||
// only process collections that have a filter that includes published_at
|
||||
collections = collections.filter((collection) => collection.filter?.includes('published_at'));
|
||||
collections = collections.filter(collection => collection.filter?.includes('published_at'));
|
||||
|
||||
if (!collections.length) {
|
||||
return;
|
||||
@ -461,7 +461,7 @@ export class CollectionsService {
|
||||
});
|
||||
|
||||
// only process collections that have a filter that includes featured
|
||||
collections = collections.filter((collection) => collection.filter?.includes('featured'));
|
||||
collections = collections.filter(collection => collection.filter?.includes('featured'));
|
||||
|
||||
if (!collections.length) {
|
||||
return;
|
||||
|
@ -20,12 +20,12 @@ type DonationEventModelInstance = BookshelfModelInstance & {
|
||||
referrer_medium: string | null;
|
||||
referrer_url: string | null;
|
||||
}
|
||||
type DonationPaymentEventModel = BookshelfModel<DonationEventModelInstance>;
|
||||
type DonationPaymentEventBookshelfModel = BookshelfModel<DonationEventModelInstance>;
|
||||
|
||||
export class DonationBookshelfRepository implements DonationRepository {
|
||||
#Model: DonationPaymentEventModel;
|
||||
#Model: DonationPaymentEventBookshelfModel;
|
||||
|
||||
constructor({DonationPaymentEventModel}: {DonationPaymentEventModel: DonationPaymentEventModel}) {
|
||||
constructor({DonationPaymentEventModel}: {DonationPaymentEventModel: DonationPaymentEventBookshelfModel}) {
|
||||
this.#Model = DonationPaymentEventModel;
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ export class DonationBookshelfRepository implements DonationRepository {
|
||||
attribution_type: event.attributionType,
|
||||
referrer_source: event.referrerSource,
|
||||
referrer_medium: event.referrerMedium,
|
||||
referrer_url: event.referrerUrl,
|
||||
referrer_url: event.referrerUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {DonationPaymentEvent} from "./DonationPaymentEvent";
|
||||
import {DonationPaymentEvent} from './DonationPaymentEvent';
|
||||
|
||||
export type DonationRepository = {
|
||||
save(event: DonationPaymentEvent): Promise<void>;
|
||||
|
@ -8,7 +8,7 @@ import {MailEventRepository} from './MailEventRepository';
|
||||
/**
|
||||
* @see https://documentation.mailgun.com/en/latest/user_manual.html#events-1
|
||||
*/
|
||||
enum EventType {
|
||||
enum EventType { // eslint-disable-line no-shadow
|
||||
CLICKED = 'clicked',
|
||||
COMPLAINED = 'complained',
|
||||
DELIVERED = 'delivered',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Recommendation} from "./Recommendation";
|
||||
import {RecommendationRepository} from "./RecommendationRepository";
|
||||
import {Recommendation} from './Recommendation';
|
||||
import {RecommendationRepository} from './RecommendationRepository';
|
||||
import {BookshelfRepository, ModelClass, ModelInstance} from '@tryghost/bookshelf-repository';
|
||||
import logger from '@tryghost/logging';
|
||||
|
||||
@ -26,8 +26,8 @@ export class BookshelfRecommendationRepository extends BookshelfRepository<strin
|
||||
url: entity.url.toString(),
|
||||
one_click_subscribe: entity.oneClickSubscribe,
|
||||
created_at: entity.createdAt,
|
||||
updated_at: entity.updatedAt,
|
||||
}
|
||||
updated_at: entity.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
modelToEntity(model: ModelInstance<string>): Recommendation | null {
|
||||
@ -42,8 +42,8 @@ export class BookshelfRecommendationRepository extends BookshelfRepository<strin
|
||||
url: new URL(model.get('url') as string),
|
||||
oneClickSubscribe: model.get('one_click_subscribe') as boolean,
|
||||
createdAt: model.get('created_at') as Date,
|
||||
updatedAt: model.get('updated_at') as Date | null,
|
||||
})
|
||||
updatedAt: model.get('updated_at') as Date | null
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
this.sentry?.captureException(err);
|
||||
@ -62,7 +62,7 @@ export class BookshelfRecommendationRepository extends BookshelfRepository<strin
|
||||
url: 'url',
|
||||
oneClickSubscribe: 'one_click_subscribe',
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
updatedAt: 'updated_at'
|
||||
} as Record<keyof Recommendation, string>;
|
||||
return mapping[field];
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Recommendation} from "./Recommendation";
|
||||
import {RecommendationRepository} from "./RecommendationRepository";
|
||||
import {Recommendation} from './Recommendation';
|
||||
import {RecommendationRepository} from './RecommendationRepository';
|
||||
import {InMemoryRepository} from '@tryghost/in-memory-repository';
|
||||
|
||||
export class InMemoryRecommendationRepository extends InMemoryRepository<string, Recommendation> implements RecommendationRepository {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import ObjectId from "bson-objectid";
|
||||
import errors from "@tryghost/errors";
|
||||
import ObjectId from 'bson-objectid';
|
||||
import errors from '@tryghost/errors';
|
||||
|
||||
export type AddRecommendation = {
|
||||
title: string
|
||||
@ -16,16 +16,16 @@ type RecommendationConstructorData = AddRecommendation & {id: string, createdAt:
|
||||
export type RecommendationCreateData = AddRecommendation & {id?: string, createdAt?: Date, updatedAt?: Date|null}
|
||||
|
||||
export class Recommendation {
|
||||
id: string
|
||||
title: string
|
||||
reason: string|null
|
||||
excerpt: string|null // Fetched from the site meta data
|
||||
featuredImage: URL|null // Fetched from the site meta data
|
||||
favicon: URL|null // Fetched from the site meta data
|
||||
url: URL
|
||||
oneClickSubscribe: boolean
|
||||
createdAt: Date
|
||||
updatedAt: Date|null
|
||||
id: string;
|
||||
title: string;
|
||||
reason: string|null;
|
||||
excerpt: string|null; // Fetched from the site meta data
|
||||
featuredImage: URL|null; // Fetched from the site meta data
|
||||
favicon: URL|null; // Fetched from the site meta data
|
||||
url: URL;
|
||||
oneClickSubscribe: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date|null;
|
||||
|
||||
#deleted: boolean;
|
||||
|
||||
@ -50,14 +50,14 @@ export class Recommendation {
|
||||
static validate(properties: AddRecommendation) {
|
||||
if (properties.url.protocol !== 'http:' && properties.url.protocol !== 'https:') {
|
||||
throw new errors.ValidationError({
|
||||
message: 'url must be a valid URL',
|
||||
message: 'url must be a valid URL'
|
||||
});
|
||||
}
|
||||
|
||||
if (properties.featuredImage !== null) {
|
||||
if (properties.featuredImage.protocol !== 'http:' && properties.featuredImage.protocol !== 'https:') {
|
||||
throw new errors.ValidationError({
|
||||
message: 'Featured image must be a valid URL',
|
||||
message: 'Featured image must be a valid URL'
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -65,32 +65,32 @@ export class Recommendation {
|
||||
if (properties.favicon !== null) {
|
||||
if (properties.favicon.protocol !== 'http:' && properties.favicon.protocol !== 'https:') {
|
||||
throw new errors.ValidationError({
|
||||
message: 'Favicon must be a valid URL',
|
||||
message: 'Favicon must be a valid URL'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.title.length === 0) {
|
||||
throw new errors.ValidationError({
|
||||
message: 'Title must not be empty',
|
||||
message: 'Title must not be empty'
|
||||
});
|
||||
}
|
||||
|
||||
if (properties.title.length > 2000) {
|
||||
throw new errors.ValidationError({
|
||||
message: 'Title must be less than 2000 characters',
|
||||
message: 'Title must be less than 2000 characters'
|
||||
});
|
||||
}
|
||||
|
||||
if (properties.reason && properties.reason.length > 2000) {
|
||||
throw new errors.ValidationError({
|
||||
message: 'Reason must be less than 2000 characters',
|
||||
message: 'Reason must be less than 2000 characters'
|
||||
});
|
||||
}
|
||||
|
||||
if (properties.excerpt && properties.excerpt.length > 2000) {
|
||||
throw new errors.ValidationError({
|
||||
message: 'Excerpt must be less than 2000 characters',
|
||||
message: 'Excerpt must be less than 2000 characters'
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -117,7 +117,7 @@ export class Recommendation {
|
||||
url: data.url,
|
||||
oneClickSubscribe: data.oneClickSubscribe,
|
||||
createdAt: data.createdAt ?? new Date(),
|
||||
updatedAt: data.updatedAt ?? null,
|
||||
updatedAt: data.updatedAt ?? null
|
||||
};
|
||||
|
||||
this.validate(d);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import {AddRecommendation, EditRecommendation, Recommendation} from "./Recommendation";
|
||||
import {RecommendationService} from "./RecommendationService";
|
||||
import {AddRecommendation, EditRecommendation, Recommendation} from './Recommendation';
|
||||
import {RecommendationService} from './RecommendationService';
|
||||
import errors from '@tryghost/errors';
|
||||
|
||||
type Frame = {
|
||||
@ -19,7 +19,7 @@ function validateString(object: any, key: string, {required = true, nullable = f
|
||||
}
|
||||
|
||||
if (object[key] !== undefined && object[key] !== null) {
|
||||
if (typeof object[key] !== "string") {
|
||||
if (typeof object[key] !== 'string') {
|
||||
throw new errors.BadRequestError({message: `${key} must be a string`});
|
||||
}
|
||||
return object[key];
|
||||
@ -33,7 +33,7 @@ function validateBoolean(object: any, key: string, {required = true} = {}): bool
|
||||
throw new errors.BadRequestError({message: `${key} must be an object`});
|
||||
}
|
||||
if (object[key] !== undefined) {
|
||||
if (typeof object[key] !== "boolean") {
|
||||
if (typeof object[key] !== 'boolean') {
|
||||
throw new errors.BadRequestError({message: `${key} must be a boolean`});
|
||||
}
|
||||
return object[key];
|
||||
@ -66,7 +66,7 @@ function validateInteger(object: any, key: string, {required = true, nullable =
|
||||
}
|
||||
|
||||
if (object[key] !== undefined && object[key] !== null) {
|
||||
if (typeof object[key] === "string") {
|
||||
if (typeof object[key] === 'string') {
|
||||
// Try to cast to a number
|
||||
const parsed = parseInt(object[key]);
|
||||
if (isNaN(parsed) || !isFinite(parsed)) {
|
||||
@ -75,7 +75,7 @@ function validateInteger(object: any, key: string, {required = true, nullable =
|
||||
return parsed;
|
||||
}
|
||||
|
||||
if (typeof object[key] !== "number") {
|
||||
if (typeof object[key] !== 'number') {
|
||||
throw new errors.BadRequestError({message: `${key} must be a number`});
|
||||
}
|
||||
return object[key];
|
||||
@ -84,7 +84,6 @@ function validateInteger(object: any, key: string, {required = true, nullable =
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class RecommendationController {
|
||||
service: RecommendationService;
|
||||
|
||||
@ -106,23 +105,22 @@ export class RecommendationController {
|
||||
}
|
||||
|
||||
#getFramePage(frame: Frame): number {
|
||||
const page = validateInteger(frame.options, "page", {required: false, nullable: true}) ?? 1;
|
||||
const page = validateInteger(frame.options, 'page', {required: false, nullable: true}) ?? 1;
|
||||
if (page < 1) {
|
||||
throw new errors.BadRequestError({message: "page must be greater or equal to 1"});
|
||||
throw new errors.BadRequestError({message: 'page must be greater or equal to 1'});
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
#getFrameLimit(frame: Frame, defaultLimit = 15): number {
|
||||
const limit = validateInteger(frame.options, "limit", {required: false, nullable: true}) ?? defaultLimit;
|
||||
const limit = validateInteger(frame.options, 'limit', {required: false, nullable: true}) ?? defaultLimit;
|
||||
if (limit < 1) {
|
||||
throw new errors.BadRequestError({message: "limit must be greater or equal to 1"});
|
||||
throw new errors.BadRequestError({message: 'limit must be greater or equal to 1'});
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
|
||||
#getFrameRecommendation(frame: Frame): AddRecommendation {
|
||||
if (!frame.data || !frame.data.recommendations || !frame.data.recommendations[0]) {
|
||||
throw new errors.BadRequestError();
|
||||
@ -131,15 +129,15 @@ export class RecommendationController {
|
||||
const recommendation = frame.data.recommendations[0];
|
||||
|
||||
const cleanedRecommendation: AddRecommendation = {
|
||||
title: validateString(recommendation, "title") ?? '',
|
||||
url: validateURL(recommendation, "url")!,
|
||||
title: validateString(recommendation, 'title') ?? '',
|
||||
url: validateURL(recommendation, 'url')!,
|
||||
|
||||
// Optional fields
|
||||
oneClickSubscribe: validateBoolean(recommendation, "one_click_subscribe", {required: false}) ?? false,
|
||||
reason: validateString(recommendation, "reason", {required: false, nullable: true}) ?? null,
|
||||
excerpt: validateString(recommendation, "excerpt", {required: false, nullable: true}) ?? null,
|
||||
featuredImage: validateURL(recommendation, "featured_image", {required: false, nullable: true}) ?? null,
|
||||
favicon: validateURL(recommendation, "favicon", {required: false, nullable: true}) ?? null,
|
||||
oneClickSubscribe: validateBoolean(recommendation, 'one_click_subscribe', {required: false}) ?? false,
|
||||
reason: validateString(recommendation, 'reason', {required: false, nullable: true}) ?? null,
|
||||
excerpt: validateString(recommendation, 'excerpt', {required: false, nullable: true}) ?? null,
|
||||
featuredImage: validateURL(recommendation, 'featured_image', {required: false, nullable: true}) ?? null,
|
||||
favicon: validateURL(recommendation, 'favicon', {required: false, nullable: true}) ?? null
|
||||
};
|
||||
|
||||
// Create a new recommendation
|
||||
@ -153,23 +151,22 @@ export class RecommendationController {
|
||||
|
||||
const recommendation = frame.data.recommendations[0];
|
||||
const cleanedRecommendation: EditRecommendation = {
|
||||
title: validateString(recommendation, "title", {required: false}) ?? undefined,
|
||||
url: validateURL(recommendation, "url", {required: false}) ?? undefined,
|
||||
oneClickSubscribe: validateBoolean(recommendation, "one_click_subscribe", {required: false}),
|
||||
reason: validateString(recommendation, "reason", {required: false, nullable: true}),
|
||||
excerpt: validateString(recommendation, "excerpt", {required: false, nullable: true}),
|
||||
featuredImage: validateURL(recommendation, "featured_image", {required: false, nullable: true}),
|
||||
favicon: validateURL(recommendation, "favicon", {required: false, nullable: true}),
|
||||
title: validateString(recommendation, 'title', {required: false}) ?? undefined,
|
||||
url: validateURL(recommendation, 'url', {required: false}) ?? undefined,
|
||||
oneClickSubscribe: validateBoolean(recommendation, 'one_click_subscribe', {required: false}),
|
||||
reason: validateString(recommendation, 'reason', {required: false, nullable: true}),
|
||||
excerpt: validateString(recommendation, 'excerpt', {required: false, nullable: true}),
|
||||
featuredImage: validateURL(recommendation, 'featured_image', {required: false, nullable: true}),
|
||||
favicon: validateURL(recommendation, 'favicon', {required: false, nullable: true})
|
||||
};
|
||||
|
||||
// Create a new recommendation
|
||||
return cleanedRecommendation;
|
||||
}
|
||||
|
||||
|
||||
#returnRecommendations(recommendations: Recommendation[], meta?: any) {
|
||||
return {
|
||||
data: recommendations.map(r => {
|
||||
data: recommendations.map((r) => {
|
||||
return {
|
||||
id: r.id,
|
||||
title: r.title,
|
||||
@ -184,7 +181,7 @@ export class RecommendationController {
|
||||
};
|
||||
}),
|
||||
meta
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#buildPagination({page, limit, count}: {page: number, limit: number, count: number}) {
|
||||
@ -197,7 +194,7 @@ export class RecommendationController {
|
||||
pages,
|
||||
prev: page > 1 ? page - 1 : null,
|
||||
next: page < pages ? page + 1 : null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async addRecommendation(frame: Frame) {
|
||||
@ -226,8 +223,8 @@ export class RecommendationController {
|
||||
const limit = this.#getFrameLimit(frame, 5);
|
||||
const order = [
|
||||
{
|
||||
field: "createdAt" as const,
|
||||
direction: "desc" as const
|
||||
field: 'createdAt' as const,
|
||||
direction: 'desc' as const
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {OrderOption} from "@tryghost/bookshelf-repository";
|
||||
import {Recommendation} from "./Recommendation";
|
||||
import {OrderOption} from '@tryghost/bookshelf-repository';
|
||||
import {Recommendation} from './Recommendation';
|
||||
|
||||
export interface RecommendationRepository {
|
||||
save(entity: Recommendation): Promise<void>;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {OrderOption} from "@tryghost/bookshelf-repository";
|
||||
import {AddRecommendation, Recommendation} from "./Recommendation";
|
||||
import {RecommendationRepository} from "./RecommendationRepository";
|
||||
import {WellknownService} from "./WellknownService";
|
||||
import errors from "@tryghost/errors";
|
||||
import tpl from "@tryghost/tpl";
|
||||
import {OrderOption} from '@tryghost/bookshelf-repository';
|
||||
import {AddRecommendation, Recommendation} from './Recommendation';
|
||||
import {RecommendationRepository} from './RecommendationRepository';
|
||||
import {WellknownService} from './WellknownService';
|
||||
import errors from '@tryghost/errors';
|
||||
import tpl from '@tryghost/tpl';
|
||||
|
||||
type MentionSendingService = {
|
||||
sendAll(options: {url: URL, links: URL[]}): Promise<void>
|
||||
@ -15,8 +15,8 @@ type RecommendationEnablerService = {
|
||||
}
|
||||
|
||||
const messages = {
|
||||
notFound: "Recommendation with id {id} not found"
|
||||
}
|
||||
notFound: 'Recommendation with id {id} not found'
|
||||
};
|
||||
|
||||
export class RecommendationService {
|
||||
repository: RecommendationRepository;
|
||||
@ -62,7 +62,7 @@ export class RecommendationService {
|
||||
links: [
|
||||
recommendation.url
|
||||
]
|
||||
}).catch(console.error);
|
||||
}).catch(console.error); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
async addRecommendation(addRecommendation: AddRecommendation) {
|
||||
@ -118,12 +118,12 @@ export class RecommendationService {
|
||||
|
||||
async listRecommendations({page, limit, filter, order}: { page: number; limit: number | 'all', filter?: string, order?: OrderOption<Recommendation> } = {page: 1, limit: 'all'}) {
|
||||
if (limit === 'all') {
|
||||
return await this.repository.getAll({filter, order})
|
||||
return await this.repository.getAll({filter, order});
|
||||
}
|
||||
return await this.repository.getPage({page, limit, filter, order})
|
||||
return await this.repository.getPage({page, limit, filter, order});
|
||||
}
|
||||
|
||||
async countRecommendations({filter}: { filter?: string }) {
|
||||
return await this.repository.getCount({filter})
|
||||
return await this.repository.getCount({filter});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Recommendation} from "./Recommendation"
|
||||
import fs from "fs/promises";
|
||||
import {Recommendation} from './Recommendation';
|
||||
import fs from 'fs/promises';
|
||||
import _path from 'path';
|
||||
|
||||
type UrlUtils = {
|
||||
@ -14,8 +14,8 @@ type Options = {
|
||||
}
|
||||
|
||||
export class WellknownService {
|
||||
dir: string
|
||||
urlUtils: UrlUtils
|
||||
dir: string;
|
||||
urlUtils: UrlUtils;
|
||||
|
||||
constructor({dir, urlUtils}: Options) {
|
||||
this.dir = dir;
|
||||
@ -27,8 +27,8 @@ export class WellknownService {
|
||||
url: recommendation.url,
|
||||
reason: recommendation.reason,
|
||||
updated_at: (recommendation.updatedAt ?? recommendation.createdAt).toISOString(),
|
||||
created_at: (recommendation.createdAt).toISOString(),
|
||||
}
|
||||
created_at: (recommendation.createdAt).toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
getPath() {
|
||||
|
@ -106,7 +106,7 @@
|
||||
"devDependencies": {
|
||||
"concurrently": "8.2.1",
|
||||
"eslint": "8.44.0",
|
||||
"eslint-plugin-ghost": "3.3.0",
|
||||
"eslint-plugin-ghost": "3.3.2",
|
||||
"eslint-plugin-react": "7.33.0",
|
||||
"husky": "8.0.3",
|
||||
"lint-staged": "14.0.1",
|
||||
|
145
yarn.lock
145
yarn.lock
@ -8854,90 +8854,89 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.1.0.tgz#96f3ca6615717659d06c9f7161a1d14ab0c49c66"
|
||||
integrity sha512-qg7Bm5TyP/I7iilGyp6DRqqkt8na00lI6HbjWZObgk3FFSzH5ypRwAHXJhJkwiRtTcfn+xYQIMOR5kJgpo6upw==
|
||||
"@typescript-eslint/eslint-plugin@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.6.0.tgz#19ba09aa34fd504696445100262e5a9e1b1d7024"
|
||||
integrity sha512-CW9YDGTQnNYMIo5lMeuiIG08p4E0cXrXTbcZ2saT/ETE7dWUrNxlijsQeU04qAAKkILiLzdQz+cGFxCJjaZUmA==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.5.1"
|
||||
"@typescript-eslint/scope-manager" "6.1.0"
|
||||
"@typescript-eslint/type-utils" "6.1.0"
|
||||
"@typescript-eslint/utils" "6.1.0"
|
||||
"@typescript-eslint/visitor-keys" "6.1.0"
|
||||
"@typescript-eslint/scope-manager" "6.6.0"
|
||||
"@typescript-eslint/type-utils" "6.6.0"
|
||||
"@typescript-eslint/utils" "6.6.0"
|
||||
"@typescript-eslint/visitor-keys" "6.6.0"
|
||||
debug "^4.3.4"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.2.4"
|
||||
natural-compare "^1.4.0"
|
||||
natural-compare-lite "^1.4.0"
|
||||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/parser@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.1.0.tgz#3135bf65dca5340d8650703eb8cb83113e156ee5"
|
||||
integrity sha512-hIzCPvX4vDs4qL07SYzyomamcs2/tQYXg5DtdAfj35AyJ5PIUqhsLf4YrEIFzZcND7R2E8tpQIZKayxg8/6Wbw==
|
||||
"@typescript-eslint/parser@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.6.0.tgz#fe323a7b4eafb6d5ea82b96216561810394a739e"
|
||||
integrity sha512-setq5aJgUwtzGrhW177/i+DMLqBaJbdwGj2CPIVFFLE0NCliy5ujIdLHd2D1ysmlmsjdL2GWW+hR85neEfc12w==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "6.1.0"
|
||||
"@typescript-eslint/types" "6.1.0"
|
||||
"@typescript-eslint/typescript-estree" "6.1.0"
|
||||
"@typescript-eslint/visitor-keys" "6.1.0"
|
||||
"@typescript-eslint/scope-manager" "6.6.0"
|
||||
"@typescript-eslint/types" "6.6.0"
|
||||
"@typescript-eslint/typescript-estree" "6.6.0"
|
||||
"@typescript-eslint/visitor-keys" "6.6.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.1.0.tgz#a6cdbe11630614f8c04867858a42dd56590796ed"
|
||||
integrity sha512-AxjgxDn27hgPpe2rQe19k0tXw84YCOsjDJ2r61cIebq1t+AIxbgiXKvD4999Wk49GVaAcdJ/d49FYel+Pp3jjw==
|
||||
"@typescript-eslint/scope-manager@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.6.0.tgz#57105d4419d6de971f7d2c30a2ff4ac40003f61a"
|
||||
integrity sha512-pT08u5W/GT4KjPUmEtc2kSYvrH8x89cVzkA0Sy2aaOUIw6YxOIjA8ilwLr/1fLjOedX1QAuBpG9XggWqIIfERw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.1.0"
|
||||
"@typescript-eslint/visitor-keys" "6.1.0"
|
||||
"@typescript-eslint/types" "6.6.0"
|
||||
"@typescript-eslint/visitor-keys" "6.6.0"
|
||||
|
||||
"@typescript-eslint/type-utils@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.1.0.tgz#21cc6c3bc1980b03f9eb4e64580d0c5be6f08215"
|
||||
integrity sha512-kFXBx6QWS1ZZ5Ni89TyT1X9Ag6RXVIVhqDs0vZE/jUeWlBv/ixq2diua6G7ece6+fXw3TvNRxP77/5mOMusx2w==
|
||||
"@typescript-eslint/type-utils@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.6.0.tgz#14f651d13b884915c4fca0d27adeb652a4499e86"
|
||||
integrity sha512-8m16fwAcEnQc69IpeDyokNO+D5spo0w1jepWWY2Q6y5ZKNuj5EhVQXjtVAeDDqvW6Yg7dhclbsz6rTtOvcwpHg==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "6.1.0"
|
||||
"@typescript-eslint/utils" "6.1.0"
|
||||
"@typescript-eslint/typescript-estree" "6.6.0"
|
||||
"@typescript-eslint/utils" "6.6.0"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/types@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.1.0.tgz#2d607c62827bb416ada5c96ebfa2ef84e45a8dfa"
|
||||
integrity sha512-+Gfd5NHCpDoHDOaU/yIF3WWRI2PcBRKKpP91ZcVbL0t5tQpqYWBs3z/GGhvU+EV1D0262g9XCnyqQh19prU0JQ==
|
||||
"@typescript-eslint/types@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.6.0.tgz#95e7ea650a2b28bc5af5ea8907114a48f54618c2"
|
||||
integrity sha512-CB6QpJQ6BAHlJXdwUmiaXDBmTqIE2bzGTDLADgvqtHWuhfNP3rAOK7kAgRMAET5rDRr9Utt+qAzRBdu3AhR3sg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.1.0.tgz#ea382f6482ba698d7e993a88ce5391ea7a66c33d"
|
||||
integrity sha512-nUKAPWOaP/tQjU1IQw9sOPCDavs/iU5iYLiY/6u7gxS7oKQoi4aUxXS1nrrVGTyBBaGesjkcwwHkbkiD5eBvcg==
|
||||
"@typescript-eslint/typescript-estree@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.6.0.tgz#373c420d2e12c28220f4a83352280a04823a91b7"
|
||||
integrity sha512-hMcTQ6Al8MP2E6JKBAaSxSVw5bDhdmbCEhGW/V8QXkb9oNsFkA4SBuOMYVPxD3jbtQ4R/vSODBsr76R6fP3tbA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.1.0"
|
||||
"@typescript-eslint/visitor-keys" "6.1.0"
|
||||
"@typescript-eslint/types" "6.6.0"
|
||||
"@typescript-eslint/visitor-keys" "6.6.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/utils@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.1.0.tgz#1641843792b4e3451cc692e2c73055df8b26f453"
|
||||
integrity sha512-wp652EogZlKmQoMS5hAvWqRKplXvkuOnNzZSE0PVvsKjpexd/XznRVHAtrfHFYmqaJz0DFkjlDsGYC9OXw+OhQ==
|
||||
"@typescript-eslint/utils@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.6.0.tgz#2d686c0f0786da6362d909e27a9de1c13ba2e7dc"
|
||||
integrity sha512-mPHFoNa2bPIWWglWYdR0QfY9GN0CfvvXX1Sv6DlSTive3jlMTUy+an67//Gysc+0Me9pjitrq0LJp0nGtLgftw==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@types/json-schema" "^7.0.12"
|
||||
"@types/semver" "^7.5.0"
|
||||
"@typescript-eslint/scope-manager" "6.1.0"
|
||||
"@typescript-eslint/types" "6.1.0"
|
||||
"@typescript-eslint/typescript-estree" "6.1.0"
|
||||
"@typescript-eslint/scope-manager" "6.6.0"
|
||||
"@typescript-eslint/types" "6.6.0"
|
||||
"@typescript-eslint/typescript-estree" "6.6.0"
|
||||
semver "^7.5.4"
|
||||
|
||||
"@typescript-eslint/visitor-keys@6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.1.0.tgz#d2b84dff6b58944d3257ea03687e269a788c73be"
|
||||
integrity sha512-yQeh+EXhquh119Eis4k0kYhj9vmFzNpbhM3LftWQVwqVjipCkwHBQOZutcYW+JVkjtTG9k8nrZU1UoNedPDd1A==
|
||||
"@typescript-eslint/visitor-keys@6.6.0":
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.6.0.tgz#1109088b4346c8b2446f3845db526374d9a3bafc"
|
||||
integrity sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.1.0"
|
||||
"@typescript-eslint/types" "6.6.0"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
|
||||
"@tyriar/fibonacci-heap@^2.0.7":
|
||||
@ -16773,10 +16772,10 @@ eslint-plugin-babel@5.3.1:
|
||||
dependencies:
|
||||
eslint-rule-composer "^0.3.0"
|
||||
|
||||
eslint-plugin-ember@11.10.0:
|
||||
version "11.10.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-ember/-/eslint-plugin-ember-11.10.0.tgz#46a696ebabcfefcf8212eb0eb2b11d61360c70fc"
|
||||
integrity sha512-/5VanfpfzIdmWgXWyQ6ylAJWITu8mXivRce06h0uoifVpUoGaBdAkwuto/PLGfDxWdi43xWUFLb5Tpkhx2MoFg==
|
||||
eslint-plugin-ember@11.11.1:
|
||||
version "11.11.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-ember/-/eslint-plugin-ember-11.11.1.tgz#bc49d76ed8ec43e646d222f15181f4d18c3c3218"
|
||||
integrity sha512-dvsDa4LkDkGqCE2bzBIguRMi1g40JVwRWMSHmn8S7toRDxSOU3M7yromgi5eSAJX2O2vEvJZ9QnR15YDbvNfVQ==
|
||||
dependencies:
|
||||
"@ember-data/rfc395-data" "^0.0.4"
|
||||
"@glimmer/syntax" "^0.84.2"
|
||||
@ -16809,21 +16808,21 @@ eslint-plugin-filenames@allouis/eslint-plugin-filenames#15dc354f4e3d155fc2d6ae08
|
||||
lodash.snakecase "4.1.1"
|
||||
lodash.upperfirst "4.3.1"
|
||||
|
||||
eslint-plugin-ghost@3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-ghost/-/eslint-plugin-ghost-3.3.0.tgz#576fcbb308e94b5e601ce8806ec63d354c5c114d"
|
||||
integrity sha512-VpL9FVI3kvY5BqM06qer67oDboDO/iHxl2mWnOZY1SwUtdcWuSfb81OQvmggilUc3UhjWahLuCHM9QzVtcOObA==
|
||||
eslint-plugin-ghost@3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-ghost/-/eslint-plugin-ghost-3.3.2.tgz#b789c20e645d285743bc8c852f92b9898474eb98"
|
||||
integrity sha512-Ae3u4lTo2ApB8wBdaEShJVEuqNSYG1IiarAFvee8bSx8Ykwj0vRxpyy9MRgwaFc6Lre7dcaIJ60iWVBoO4MwwA==
|
||||
dependencies:
|
||||
"@kapouer/eslint-plugin-no-return-in-loop" "1.0.0"
|
||||
"@typescript-eslint/eslint-plugin" "6.1.0"
|
||||
"@typescript-eslint/parser" "6.1.0"
|
||||
eslint-plugin-ember "11.10.0"
|
||||
"@typescript-eslint/eslint-plugin" "6.6.0"
|
||||
"@typescript-eslint/parser" "6.6.0"
|
||||
eslint-plugin-ember "11.11.1"
|
||||
eslint-plugin-filenames allouis/eslint-plugin-filenames#15dc354f4e3d155fc2d6ae082dbfc26377539a18
|
||||
eslint-plugin-mocha "7.0.1"
|
||||
eslint-plugin-n "16.0.1"
|
||||
eslint-plugin-n "16.0.2"
|
||||
eslint-plugin-sort-imports-es6-autofix "0.6.0"
|
||||
eslint-plugin-unicorn "42.0.0"
|
||||
typescript "5.1.6"
|
||||
typescript "5.2.2"
|
||||
|
||||
eslint-plugin-i18next@6.0.3:
|
||||
version "6.0.3"
|
||||
@ -16841,10 +16840,10 @@ eslint-plugin-mocha@7.0.1:
|
||||
eslint-utils "^2.0.0"
|
||||
ramda "^0.27.0"
|
||||
|
||||
eslint-plugin-n@16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.0.1.tgz#baa62bb3af52940a53ba15386348ad9b0b425ada"
|
||||
integrity sha512-CDmHegJN0OF3L5cz5tATH84RPQm9kG+Yx39wIqIwPR2C0uhBGMWfbbOtetR83PQjjidA5aXMu+LEFw1jaSwvTA==
|
||||
eslint-plugin-n@16.0.2:
|
||||
version "16.0.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.0.2.tgz#5b2c0ad8dd9b724244d30fad2cc49ff4308a2152"
|
||||
integrity sha512-Y66uDfUNbBzypsr0kELWrIz+5skicECrLUqlWuXawNSLUq3ltGlCwu6phboYYOTSnoTdHgTLrc+5Ydo6KjzZog==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
builtins "^5.0.1"
|
||||
@ -23801,11 +23800,6 @@ napi-build-utils@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
|
||||
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
|
||||
|
||||
natural-compare-lite@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
|
||||
integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
@ -30512,11 +30506,6 @@ typescript-memoize@^1.0.0-alpha.3, typescript-memoize@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/typescript-memoize/-/typescript-memoize-1.1.1.tgz#02737495d5df6ebf72c07ba0d002e8f4cf5ccfa0"
|
||||
integrity sha512-GQ90TcKpIH4XxYTI2F98yEQYZgjNMOGPpOgdjIBhaLaWji5HPWlRnZ4AeA1hfBxtY7bCGDJsqDDHk/KaHOl5bA==
|
||||
|
||||
typescript@5.1.6:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
|
||||
integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
|
||||
|
||||
typescript@5.2.2, typescript@^5.0.4:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"
|
||||
|
Loading…
Reference in New Issue
Block a user