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