metamigo-common: build, fmt, lint

This commit is contained in:
Abel Luck 2023-03-13 11:46:12 +00:00
parent 2a1ced5383
commit 75fb3f84c4
21 changed files with 95 additions and 156 deletions

View file

@ -40,6 +40,7 @@ module.exports = {
"unicorn/no-useless-undefined": "off", "unicorn/no-useless-undefined": "off",
"unicorn/no-array-reduce": "off", "unicorn/no-array-reduce": "off",
"unicorn/no-array-for-each": "off", "unicorn/no-array-for-each": "off",
"unicorn/prefer-ternary": "off",
"function-call-argument-newline": "off", "function-call-argument-newline": "off",
"promise/param-names": "error", "promise/param-names": "error",
"promise/no-return-wrap": [ "promise/no-return-wrap": [

View file

@ -1,13 +1,7 @@
require('@digiresilience/eslint-config-metamigo/patch/modern-module-resolution'); require('eslint-config-link/patch/modern-module-resolution');
module.exports = { module.exports = {
extends: [ extends: [
"@digiresilience/eslint-config-metamigo/profile/node", "eslint-config-link/profile/node",
"@digiresilience/eslint-config-metamigo/profile/typescript" "eslint-config-link/profile/typescript"
], ]
rules: {
// TODO: enable this after jest fixes this issue https://github.com/nodejs/node/issues/38343
"unicorn/prefer-node-protocol": "off"
},
parserOptions: { tsconfigRootDir: __dirname }
}; };

View file

@ -10,13 +10,9 @@
"scripts": { "scripts": {
"build": "tsc -p tsconfig.json", "build": "tsc -p tsconfig.json",
"fix:lint": "eslint src --ext .ts --fix", "fix:lint": "eslint src --ext .ts --fix",
"fix:prettier": "prettier \"src/**/*.ts\" --write", "fmt": "prettier \"src/**/*.ts\" --write",
"test": "yarn test:jest && yarn test:lint && yarn test:prettier", "lint": "eslint src --ext .ts && prettier \"src/**/*.ts\" --list-different",
"test:lint": "eslint src --ext .ts", "doc": "typedoc src/ --exclude '**/*.test.ts' --exclude '**/*.spec.ts' --name $npm_package_name --readme README.md --target es2019 --mode file --out build/docs",
"test:prettier": "prettier \"src/**/*.ts\" --list-different",
"test:jest": "jest --coverage --forceExit --detectOpenHandles --reporters=default --reporters=jest-junit",
"doc": "yarn run doc:html",
"doc:html": "typedoc src/ --exclude '**/*.test.ts' --exclude '**/*.spec.ts' --name $npm_package_name --readme README.md --target es2019 --mode file --out build/docs",
"watch:build": "tsc -p tsconfig.json -w" "watch:build": "tsc -p tsconfig.json -w"
}, },
"devDependencies": { "devDependencies": {

View file

@ -11,14 +11,14 @@ export const AppMetaConfig: ConvictSchema<IAppMetaConfig> = {
doc: "The current application version", doc: "The current application version",
format: String, format: String,
env: "npm_package_version", env: "npm_package_version",
default: null, default: undefined,
skipGenerate: true, skipGenerate: true,
}, },
name: { name: {
doc: "Application name", doc: "Application name",
format: String, format: String,
env: "npm_package_name", env: "npm_package_name",
default: null, default: undefined,
skipGenerate: true, skipGenerate: true,
}, },
figletFont: { figletFont: {

View file

@ -1,7 +1,9 @@
import * as Joi from "joi"; import * as Joi from "joi";
import type { Format } from "convict"; import type { Format } from "convict";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const coerceString = (v: any): string => v.toString(); const coerceString = (v: any): string => v.toString();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const validator = (s: any) => (v: any) => Joi.assert(v, s); const validator = (s: any) => (v: any) => Joi.assert(v, s);
const url = Joi.string().uri({ const url = Joi.string().uri({

View file

@ -1,5 +1,6 @@
import convict from "convict"; import convict from "convict";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const visitLeaf = (acc: any, key: any, leaf: any) => { const visitLeaf = (acc: any, key: any, leaf: any) => {
if (leaf.skipGenerate) { if (leaf.skipGenerate) {
return; return;
@ -12,9 +13,11 @@ const visitLeaf = (acc: any, key: any, leaf: any) => {
} }
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const visitNode = (acc: any, node: any, key = "") => { const visitNode = (acc: any, node: any, key = "") => {
if (node._cvtProperties) { if (node._cvtProperties) {
const keys = Object.keys(node._cvtProperties); const keys = Object.keys(node._cvtProperties);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let subacc: any; let subacc: any;
if (key === "") { if (key === "") {
subacc = acc; subacc = acc;

View file

@ -1,4 +1,4 @@
import process from "process"; import process from "node:process";
import convict, { SchemaObj } from "convict"; import convict, { SchemaObj } from "convict";
import { IServerConfig, ServerConfig } from "./server"; import { IServerConfig, ServerConfig } from "./server";
import { IMetricsConfig, MetricsConfig } from "./metrics-server"; import { IMetricsConfig, MetricsConfig } from "./metrics-server";
@ -42,19 +42,14 @@ interface IMetamigoConfig {
isProd?: boolean; isProd?: boolean;
isTest?: boolean; isTest?: boolean;
isDev?: boolean; isDev?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
frontend: any; frontend: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
nextAuth: any; nextAuth: any;
} }
export type IMetamigoConvict = ExtendedConvict<IMetamigoConfig>; export type IMetamigoConvict = ExtendedConvict<IMetamigoConfig>;
export type { export type { IMetamigoConfig };
IServerConfig,
IMetricsConfig,
IAppMetaConfig,
ICorsConfig,
ILoggingConfig,
IMetamigoConfig,
};
export * from "./formats"; export * from "./formats";
export * from "./generate"; export * from "./generate";
@ -103,6 +98,7 @@ ${error}
try { try {
config.validate({ allowed: "strict" }); config.validate({ allowed: "strict" });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
const msg = ` const msg = `
@ -140,3 +136,9 @@ export const loadConfiguration = async <T extends IMetamigoConfig>(
const c = await loadConfigurationRaw(schema, override); const c = await loadConfigurationRaw(schema, override);
return c.getProperties(); return c.getProperties();
}; };
export { type IServerConfig } from "./server";
export { type IMetricsConfig } from "./metrics-server";
export { type IAppMetaConfig } from "./app-meta";
export { type ICorsConfig } from "./cors";
export { type ILoggingConfig } from "./logging";

View file

@ -1,6 +1,7 @@
import chalk from "chalk"; import chalk from "chalk";
import convict from "convict"; import convict from "convict";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const visitLeaf = (path: any, key: any, leaf: any) => { const visitLeaf = (path: any, key: any, leaf: any) => {
if (leaf.skipGenerate) { if (leaf.skipGenerate) {
return; return;
@ -20,6 +21,7 @@ const visitLeaf = (path: any, key: any, leaf: any) => {
console.log(`\tenv: ${leaf.env}`); console.log(`\tenv: ${leaf.env}`);
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const visitNode = (path: any, node: any, key = "") => { const visitNode = (path: any, node: any, key = "") => {
if (node._cvtProperties) { if (node._cvtProperties) {
const keys = Object.keys(node._cvtProperties); const keys = Object.keys(node._cvtProperties);

View file

@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/ban-types,@typescript-eslint/no-explicit-any,max-params */ /* eslint-disable @typescript-eslint/no-explicit-any,max-params */
import * as Boom from "@hapi/boom"; import * as Boom from "@hapi/boom";
import * as Hapi from "@hapi/hapi"; import * as Hapi from "@hapi/hapi";
import { CrudRepository } from "../records/crud-repository"; import { CrudRepository } from "../records/crud-repository";
@ -33,12 +33,8 @@ export abstract class AbstractCrudController<
*/ */
abstract repoName: string; abstract repoName: string;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment abstract paramsIdField;
// @ts-ignore abstract dbDecoration;
abstract paramsIdField = "id";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
abstract dbDecoration = "db";
abstract recordType: PgRecordInfo<TUnsavedR, TSavedR, IdKeyT>; abstract recordType: PgRecordInfo<TUnsavedR, TSavedR, IdKeyT>;
repo(request: Hapi.Request): CrudRepository<TUnsavedR, TSavedR, IdKeyT> { repo(request: Hapi.Request): CrudRepository<TUnsavedR, TSavedR, IdKeyT> {
@ -206,7 +202,6 @@ export abstract class AbstractCrudController<
}; };
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function unboundCrudController<TRecordInfo extends PgRecordInfo>( export function unboundCrudController<TRecordInfo extends PgRecordInfo>(
aRecordType: TRecordInfo aRecordType: TRecordInfo
) { ) {
@ -229,7 +224,6 @@ export function unboundCrudController<TRecordInfo extends PgRecordInfo>(
}; };
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function CrudControllerBase<Rec extends PgRecordInfo>(recordType: Rec) { export function CrudControllerBase<Rec extends PgRecordInfo>(recordType: Rec) {
return unboundCrudController<Rec>(recordType); return unboundCrudController<Rec>(recordType);
} }

View file

@ -1,9 +1,9 @@
/* eslint-disable unicorn/no-null,max-params */ /* eslint-disable unicorn/no-null,max-params */
import { createHash, randomBytes } from "crypto"; import { createHash, randomBytes } from "node:crypto";
import type { AdapterInstance } from "next-auth/adapters"; import type { AdapterInstance } from "next-auth/adapters";
import omit from "lodash/omit"; import omit from "lodash/omit";
import type { IMetamigoRepositories } from "../records"; import type { IMetamigoRepositories } from "../records";
import type { UnsavedAccount, SavedAccount } from "../records/account"; import type { UnsavedAccount } from "../records/account";
import type { UserId, UnsavedUser, SavedUser } from "../records/user"; import type { UserId, UnsavedUser, SavedUser } from "../records/user";
import type { UnsavedSession, SavedSession } from "../records/session"; import type { UnsavedSession, SavedSession } from "../records/session";
@ -12,6 +12,7 @@ export const defaultSessionMaxAge = 30 * 24 * 60 * 60 * 1000;
// Sessions updated only if session is greater than this value (0 = always) // Sessions updated only if session is greater than this value (0 = always)
export const defaulteSessionUpdateAge = 24 * 60 * 60 * 1000; export const defaulteSessionUpdateAge = 24 * 60 * 60 * 1000;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getCompoundId = (providerId: any, providerAccountId: any) => const getCompoundId = (providerId: any, providerAccountId: any) =>
createHash("sha256") createHash("sha256")
.update(`${providerId}:${providerAccountId}`) .update(`${providerId}:${providerAccountId}`)
@ -29,7 +30,7 @@ export class NextAuthAdapter<TRepositories extends IMetamigoRepositories>
) {} ) {}
async createUser(profile: UnsavedUser): Promise<SavedUser> { async createUser(profile: UnsavedUser): Promise<SavedUser> {
// @ts-expect-error // @ts-expect-error Typescript doesn't like lodash's omit()
return this.repos.users.upsert(omit(profile, ["isActive", "id"])); return this.repos.users.upsert(omit(profile, ["isActive", "id"]));
} }

View file

@ -1,5 +1,5 @@
import * as Hapi from "@hapi/hapi"; import * as Hapi from "@hapi/hapi";
import * as http from "http"; import * as http from "node:http";
import type { HttpTerminator } from "http-terminator"; import type { HttpTerminator } from "http-terminator";
import * as Glue from "@hapi/glue"; import * as Glue from "@hapi/glue";
import * as Promster from "@promster/hapi"; import * as Promster from "@promster/hapi";
@ -102,7 +102,6 @@ export const defaultPlugins = <T extends IMetamigoConfig>(
{ plugin: StatusPlugin }, { plugin: StatusPlugin },
{ plugin: Promster.createPlugin() }, { plugin: Promster.createPlugin() },
]; ];
// @ts-ignore
return plugins; return plugins;
}; };
@ -110,7 +109,7 @@ export const announce = async <T extends IMetamigoConfig>(
config: T config: T
): Promise<void> => ): Promise<void> =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
// @ts-expect-error // @ts-expect-error figlet doesn't have types
figlet.text( figlet.text(
config.meta.name, config.meta.name,
{ font: config.meta.figletFont }, { font: config.meta.figletFont },

View file

@ -1,4 +1,4 @@
import process from "process"; import process from "node:process";
import * as Hapi from "@hapi/hapi"; import * as Hapi from "@hapi/hapi";
import * as Joi from "joi"; import * as Joi from "joi";
import Hoek from "@hapi/hoek"; import Hoek from "@hapi/hoek";

View file

@ -12,12 +12,10 @@ import * as pino from "pino";
declare module "@hapi/hapi" { declare module "@hapi/hapi" {
interface Server { interface Server {
// @ts-ignore
logger: pino.Logger; logger: pino.Logger;
} }
interface Request { interface Request {
// @ts-ignore
logger: pino.Logger; logger: pino.Logger;
} }
} }

View file

@ -1,7 +1,9 @@
import pino, { LoggerOptions } from "pino"; import pino, { LoggerOptions } from "pino";
import { IMetamigoConfig } from "./config"; import { IMetamigoConfig } from "./config";
export const getPrettyPrint = <T extends IMetamigoConfig>(config: T): boolean => { export const getPrettyPrint = <T extends IMetamigoConfig>(
config: T
): boolean => {
const { prettyPrint } = config.logging; const { prettyPrint } = config.logging;
if (prettyPrint === "auto") return config?.isDev || false; if (prettyPrint === "auto") return config?.isDev || false;
return prettyPrint === true; return prettyPrint === true;

View file

@ -20,7 +20,6 @@ const register = async (
request.response.output.headers[header] = id; request.response.output.headers[header] = id;
} else { } else {
const id = request.headers[header] || uuid(); const id = request.headers[header] || uuid();
// @ts-ignore
request.response.header(header, id); request.response.header(header, id);
} }

View file

@ -1,12 +1,13 @@
import { Server, RouteOptionsAccess } from "@hapi/hapi"; import { Server, RouteOptionsAccess } from "@hapi/hapi";
import { Prometheus } from "@promster/hapi"; import { Prometheus } from "@promster/hapi";
import { Counter } from "prom-client";
interface StatusOptions { interface StatusOptions {
path?: string; path?: string;
auth?: RouteOptionsAccess; auth?: RouteOptionsAccess;
} }
const count = (statusCounter: any) => async () => { const count = (statusCounter: Counter) => async () => {
statusCounter.inc(); statusCounter.inc();
return "Incremented metamigo_status_test counter"; return "Incremented metamigo_status_test counter";
}; };

View file

@ -12,7 +12,7 @@ export type PgProtocol<T> = IDatabase<T> & T;
* @param aRecordType the record type runtime definition * @param aRecordType the record type runtime definition
*/ */
// haven't figured out a good return type for this function // haven't figured out a good return type for this function
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function unboundRepositoryBase< export function unboundRepositoryBase<
TRecordInfo extends PgRecordInfo, TRecordInfo extends PgRecordInfo,
TDatabaseExtension TDatabaseExtension
@ -48,7 +48,6 @@ export function unboundRepositoryBase<
}; };
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function RepositoryBase< export function RepositoryBase<
Rec extends PgRecordInfo, Rec extends PgRecordInfo,
TDatabaseExtension = unknown TDatabaseExtension = unknown

View file

@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/ban-types,@typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { TableName } from "pg-promise"; import { TableName } from "pg-promise";
import decamelcaseKeys from "decamelcase-keys"; import decamelcaseKeys from "decamelcase-keys";
import isObject from "lodash/isObject"; import isObject from "lodash/isObject";
@ -106,7 +106,7 @@ export abstract class CrudRepository<
`idsObj(${this.schemaTable}): passed record has multiple primary keys. the ids must be passed as an object or array. ${idValues}` `idsObj(${this.schemaTable}): passed record has multiple primary keys. the ids must be passed as an object or array. ${idValues}`
); );
} }
// @ts-ignore
ids[idKeys[0]] = idValues; ids[idKeys[0]] = idValues;
} }

View file

@ -1,54 +0,0 @@
export interface EntityType<TUnsaved = any, TSaved = any, TIds extends object = any> {
_saved: TSaved;
_unsaved: TUnsaved;
_idKeys: TIds;
idOf: (rec: TSaved) => TIds;
}
export declare type UnsavedR<T extends {
_unsaved: any;
}> = T["_unsaved"];
export declare type SavedR<T extends {
_saved: any;
}> = T["_saved"];
export declare type KeyType<R extends EntityType> = R["_idKeys"];
export interface PgRecordInfo<Unsaved = any, Saved extends Unsaved & IdType = any, IdType extends object = any> extends EntityType<Unsaved, Saved, IdType> {
tableName: string;
schemaName: string;
idKeys: (keyof Saved)[];
}
/**
* Extract the runtime key name from a recordInfo
*/
export declare function idKeysOf<RI extends PgRecordInfo>(recordInfoWithIdKey: RI): string[];
/**
* Turns a record type with possibly more fields than "id" into an array
*/
export declare function collectIdValues<RecordT extends PgRecordInfo>(idObj: KeyType<RecordT>, knexRecordType: RecordT): string[];
/**
*
* Creates a record descriptor that captures the table name, primary key name,
* unsaved type, and saved type of a database record type. Assumes "id" as the
* primary key name
*
*/
export declare function recordInfo<Unsaved, Saved extends Unsaved & {
id: any;
}>(schemaName: string, tableName: string): PgRecordInfo<Unsaved, Saved, Pick<Saved, "id">>;
export declare function recordInfo<Type extends {
id: string;
}>(schemaName: string, tableName: string): PgRecordInfo<Type, Type, Pick<Type, "id">>;
/**
*
* Creates a record descriptor that captures the table name, primary key name,
* unsaved type, and saved type of a database record type.
*
*/
export declare function recordInfo<Unsaved, Saved extends Unsaved, Id extends keyof Saved>(schemaName: string, tableName: string, idKey: Id[]): PgRecordInfo<Unsaved, Saved, Pick<Saved, Id>>;
/**
*
* Creates a record descriptor for records with composite primary keys
*
*/
export declare function compositeRecordType<TUnsaved, TSaved extends TUnsaved = TUnsaved>(schemaName: string, tableName: string): {
withCompositeKeys<TKeys extends keyof TSaved>(keys: TKeys[]): PgRecordInfo<TUnsaved, TSaved, Pick<TSaved, TKeys>>;
};

View file

@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/ban-types,@typescript-eslint/no-explicit-any,@typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/no-explicit-any */
import at from "lodash/at"; import at from "lodash/at";
import pick from "lodash/pick"; import pick from "lodash/pick";