metamigo-common: build, fmt, lint
This commit is contained in:
parent
2a1ced5383
commit
75fb3f84c4
21 changed files with 95 additions and 156 deletions
|
|
@ -1,13 +1,7 @@
|
|||
require('@digiresilience/eslint-config-metamigo/patch/modern-module-resolution');
|
||||
require('eslint-config-link/patch/modern-module-resolution');
|
||||
module.exports = {
|
||||
extends: [
|
||||
"@digiresilience/eslint-config-metamigo/profile/node",
|
||||
"@digiresilience/eslint-config-metamigo/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 }
|
||||
};
|
||||
|
||||
"eslint-config-link/profile/node",
|
||||
"eslint-config-link/profile/typescript"
|
||||
]
|
||||
};
|
||||
|
|
@ -10,13 +10,9 @@
|
|||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"fix:lint": "eslint src --ext .ts --fix",
|
||||
"fix:prettier": "prettier \"src/**/*.ts\" --write",
|
||||
"test": "yarn test:jest && yarn test:lint && yarn test:prettier",
|
||||
"test:lint": "eslint src --ext .ts",
|
||||
"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",
|
||||
"fmt": "prettier \"src/**/*.ts\" --write",
|
||||
"lint": "eslint src --ext .ts && prettier \"src/**/*.ts\" --list-different",
|
||||
"doc": "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"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ export const AppMetaConfig: ConvictSchema<IAppMetaConfig> = {
|
|||
doc: "The current application version",
|
||||
format: String,
|
||||
env: "npm_package_version",
|
||||
default: null,
|
||||
default: undefined,
|
||||
skipGenerate: true,
|
||||
},
|
||||
name: {
|
||||
doc: "Application name",
|
||||
format: String,
|
||||
env: "npm_package_name",
|
||||
default: null,
|
||||
default: undefined,
|
||||
skipGenerate: true,
|
||||
},
|
||||
figletFont: {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import * as Joi from "joi";
|
||||
import type { Format } from "convict";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
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 url = Joi.string().uri({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import convict from "convict";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const visitLeaf = (acc: any, key: any, leaf: any) => {
|
||||
if (leaf.skipGenerate) {
|
||||
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 = "") => {
|
||||
if (node._cvtProperties) {
|
||||
const keys = Object.keys(node._cvtProperties);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let subacc: any;
|
||||
if (key === "") {
|
||||
subacc = acc;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import process from "process";
|
||||
import process from "node:process";
|
||||
import convict, { SchemaObj } from "convict";
|
||||
import { IServerConfig, ServerConfig } from "./server";
|
||||
import { IMetricsConfig, MetricsConfig } from "./metrics-server";
|
||||
|
|
@ -42,19 +42,14 @@ interface IMetamigoConfig {
|
|||
isProd?: boolean;
|
||||
isTest?: boolean;
|
||||
isDev?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
frontend: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
nextAuth: any;
|
||||
}
|
||||
export type IMetamigoConvict = ExtendedConvict<IMetamigoConfig>;
|
||||
|
||||
export type {
|
||||
IServerConfig,
|
||||
IMetricsConfig,
|
||||
IAppMetaConfig,
|
||||
ICorsConfig,
|
||||
ILoggingConfig,
|
||||
IMetamigoConfig,
|
||||
};
|
||||
export type { IMetamigoConfig };
|
||||
|
||||
export * from "./formats";
|
||||
export * from "./generate";
|
||||
|
|
@ -103,6 +98,7 @@ ${error}
|
|||
|
||||
try {
|
||||
config.validate({ allowed: "strict" });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
const msg = `
|
||||
|
||||
|
|
@ -140,3 +136,9 @@ export const loadConfiguration = async <T extends IMetamigoConfig>(
|
|||
const c = await loadConfigurationRaw(schema, override);
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import chalk from "chalk";
|
||||
import convict from "convict";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const visitLeaf = (path: any, key: any, leaf: any) => {
|
||||
if (leaf.skipGenerate) {
|
||||
return;
|
||||
|
|
@ -20,6 +21,7 @@ const visitLeaf = (path: any, key: any, leaf: any) => {
|
|||
console.log(`\tenv: ${leaf.env}`);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const visitNode = (path: any, node: any, key = "") => {
|
||||
if (node._cvtProperties) {
|
||||
const keys = Object.keys(node._cvtProperties);
|
||||
|
|
|
|||
|
|
@ -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 Hapi from "@hapi/hapi";
|
||||
import { CrudRepository } from "../records/crud-repository";
|
||||
|
|
@ -33,12 +33,8 @@ export abstract class AbstractCrudController<
|
|||
*/
|
||||
|
||||
abstract repoName: string;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
abstract paramsIdField = "id";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
abstract dbDecoration = "db";
|
||||
abstract paramsIdField;
|
||||
abstract dbDecoration;
|
||||
abstract recordType: PgRecordInfo<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>(
|
||||
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) {
|
||||
return unboundCrudController<Rec>(recordType);
|
||||
}
|
||||
|
|
@ -241,53 +235,53 @@ export const crudRoutesFor = (
|
|||
idParam: string,
|
||||
validate: Record<string, Hapi.RouteOptionsValidate>
|
||||
): Hapi.ServerRoute[] => [
|
||||
{
|
||||
method: "POST",
|
||||
path: `${path}`,
|
||||
options: {
|
||||
handler: controller.create,
|
||||
validate: validate.create,
|
||||
description: `Method that creates a new ${name}.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
path: `${path}`,
|
||||
options: {
|
||||
handler: controller.create,
|
||||
validate: validate.create,
|
||||
description: `Method that creates a new ${name}.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
{
|
||||
method: "PUT",
|
||||
path: `${path}/{${idParam}}`,
|
||||
options: {
|
||||
handler: controller.updateById,
|
||||
validate: validate.updateById,
|
||||
description: `Method that updates a ${name} by its id.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "PUT",
|
||||
path: `${path}/{${idParam}}`,
|
||||
options: {
|
||||
handler: controller.updateById,
|
||||
validate: validate.updateById,
|
||||
description: `Method that updates a ${name} by its id.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
{
|
||||
method: "GET",
|
||||
path: `${path}/{${idParam}}`,
|
||||
options: {
|
||||
handler: controller.getById,
|
||||
validate: validate.getById,
|
||||
description: `Method that gets a ${name} by its id.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "GET",
|
||||
path: `${path}/{${idParam}}`,
|
||||
options: {
|
||||
handler: controller.getById,
|
||||
validate: validate.getById,
|
||||
description: `Method that gets a ${name} by its id.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
{
|
||||
method: "GET",
|
||||
path: `${path}`,
|
||||
options: {
|
||||
handler: controller.getAll,
|
||||
description: `Method that gets all ${name}s.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "GET",
|
||||
path: `${path}`,
|
||||
options: {
|
||||
handler: controller.getAll,
|
||||
description: `Method that gets all ${name}s.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `${path}/{${idParam}}`,
|
||||
options: {
|
||||
handler: controller.deleteById,
|
||||
validate: validate.deleteById,
|
||||
description: `Method that deletes a ${name} by its id.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `${path}/{${idParam}}`,
|
||||
options: {
|
||||
handler: controller.deleteById,
|
||||
validate: validate.deleteById,
|
||||
description: `Method that deletes a ${name} by its id.`,
|
||||
tags: ["api", name],
|
||||
},
|
||||
];
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/* 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 omit from "lodash/omit";
|
||||
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 { 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)
|
||||
export const defaulteSessionUpdateAge = 24 * 60 * 60 * 1000;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const getCompoundId = (providerId: any, providerAccountId: any) =>
|
||||
createHash("sha256")
|
||||
.update(`${providerId}:${providerAccountId}`)
|
||||
|
|
@ -26,10 +27,10 @@ export class NextAuthAdapter<TRepositories extends IMetamigoRepositories>
|
|||
private repos: TRepositories,
|
||||
private readonly sessionMaxAge = defaultSessionMaxAge,
|
||||
private readonly sessionUpdateAge = defaulteSessionUpdateAge
|
||||
) { }
|
||||
) {}
|
||||
|
||||
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"]));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as Hapi from "@hapi/hapi";
|
||||
import * as http from "http";
|
||||
import * as http from "node:http";
|
||||
import type { HttpTerminator } from "http-terminator";
|
||||
import * as Glue from "@hapi/glue";
|
||||
import * as Promster from "@promster/hapi";
|
||||
|
|
@ -102,7 +102,6 @@ export const defaultPlugins = <T extends IMetamigoConfig>(
|
|||
{ plugin: StatusPlugin },
|
||||
{ plugin: Promster.createPlugin() },
|
||||
];
|
||||
// @ts-ignore
|
||||
return plugins;
|
||||
};
|
||||
|
||||
|
|
@ -110,7 +109,7 @@ export const announce = async <T extends IMetamigoConfig>(
|
|||
config: T
|
||||
): Promise<void> =>
|
||||
new Promise((resolve, reject) => {
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error figlet doesn't have types
|
||||
figlet.text(
|
||||
config.meta.name,
|
||||
{ font: config.meta.figletFont },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import process from "process";
|
||||
import process from "node:process";
|
||||
import * as Hapi from "@hapi/hapi";
|
||||
import * as Joi from "joi";
|
||||
import Hoek from "@hapi/hoek";
|
||||
|
|
|
|||
|
|
@ -12,12 +12,10 @@ import * as pino from "pino";
|
|||
|
||||
declare module "@hapi/hapi" {
|
||||
interface Server {
|
||||
// @ts-ignore
|
||||
logger: pino.Logger;
|
||||
}
|
||||
|
||||
interface Request {
|
||||
// @ts-ignore
|
||||
logger: pino.Logger;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import pino, { LoggerOptions } from "pino";
|
||||
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;
|
||||
if (prettyPrint === "auto") return config?.isDev || false;
|
||||
return prettyPrint === true;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ const register = async (
|
|||
request.response.output.headers[header] = id;
|
||||
} else {
|
||||
const id = request.headers[header] || uuid();
|
||||
// @ts-ignore
|
||||
request.response.header(header, id);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { Server, RouteOptionsAccess } from "@hapi/hapi";
|
||||
import { Prometheus } from "@promster/hapi";
|
||||
import { Counter } from "prom-client";
|
||||
|
||||
interface StatusOptions {
|
||||
path?: string;
|
||||
auth?: RouteOptionsAccess;
|
||||
}
|
||||
|
||||
const count = (statusCounter: any) => async () => {
|
||||
const count = (statusCounter: Counter) => async () => {
|
||||
statusCounter.inc();
|
||||
return "Incremented metamigo_status_test counter";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export type PgProtocol<T> = IDatabase<T> & T;
|
|||
* @param aRecordType the record type runtime definition
|
||||
*/
|
||||
// haven't figured out a good return type for this function
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
|
||||
export function unboundRepositoryBase<
|
||||
TRecordInfo extends PgRecordInfo,
|
||||
TDatabaseExtension
|
||||
|
|
@ -48,7 +48,6 @@ export function unboundRepositoryBase<
|
|||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function RepositoryBase<
|
||||
Rec extends PgRecordInfo,
|
||||
TDatabaseExtension = unknown
|
||||
|
|
|
|||
|
|
@ -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 decamelcaseKeys from "decamelcase-keys";
|
||||
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}`
|
||||
);
|
||||
}
|
||||
// @ts-ignore
|
||||
|
||||
ids[idKeys[0]] = idValues;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>>;
|
||||
};
|
||||
|
|
@ -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 pick from "lodash/pick";
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue