@devmoods/express-extras 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import { type Connection } from '../../postgres.js';
2
+ export declare function up(query: Connection['query']): Promise<void>;
3
+ export declare function down(query: Connection['query']): Promise<void>;
@@ -0,0 +1,8 @@
1
+ export async function up(query) {
2
+ await query('CREATE TABLE baz (id serial primary key)');
3
+ await query('CREATE TABLE foo (id serial primary key)');
4
+ }
5
+ export async function down(query) {
6
+ await query('DROP TABLE foo');
7
+ await query('DROP TABLE baz');
8
+ }
package/dist/email.d.ts CHANGED
@@ -12,4 +12,5 @@ export interface SendEmailOptions extends nodemailer.SendMailOptions {
12
12
  }
13
13
  export declare function createEmail({ host, port, user, pass, senderName, senderAddress, }: EmailOptions): {
14
14
  send: ({ to, subject, message, ...rest }: SendEmailOptions) => Promise<void>;
15
+ transporter: nodemailer.Transporter<import("nodemailer/lib/smtp-transport/index.js").SentMessageInfo>;
15
16
  };
package/dist/email.js CHANGED
@@ -17,5 +17,5 @@ export function createEmail({ host, port, user, pass, senderName, senderAddress,
17
17
  });
18
18
  getLogger('core').info('email-sent', { email: result });
19
19
  };
20
- return { send };
20
+ return { send, transporter };
21
21
  }
package/dist/migrate.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { createPostgres } from './postgres.js';
1
+ import { type Connection, createPostgres } from './postgres.js';
2
2
  interface Migration {
3
3
  name: string;
4
4
  filename: string;
@@ -23,9 +23,9 @@ export declare class Migrator {
23
23
  runMigrations(direction: 'up' | 'down'): Promise<number>;
24
24
  log(...args: any[]): void;
25
25
  executeMigration(migrations: Migration[], currentVersion: number, direction: 'up' | 'down'): Promise<number>;
26
- queryWithLog(sql: string): Promise<import("pg").QueryResult<import("pg").QueryResultRow>>;
27
- migrate(migration: Migration, direction: 'up' | 'down'): Promise<any>;
28
- updateCurrentVersion(version: number): Promise<import("pg").QueryResult<import("pg").QueryResultRow>>;
26
+ readMigrationFile(filename: string): Promise<any>;
27
+ migrate(tx: Connection, migration: Migration, direction: 'up' | 'down'): Promise<void>;
28
+ updateCurrentVersion(connection: Connection, version: number): Promise<import("pg").QueryResult<import("pg").QueryResultRow>>;
29
29
  getCurrentVersion(): Promise<number>;
30
30
  getMigrations(): Promise<{
31
31
  filename: string;
package/dist/migrate.js CHANGED
@@ -54,7 +54,7 @@ Usage
54
54
  }
55
55
  export class Migrator {
56
56
  constructor({ postgres = createPostgres(), pathToMigrations = path.resolve(process.cwd(), 'db', 'migrations'), migrationTableName = 'migrations', verbose = true, } = {}) {
57
- this.filenameRegExp = /(\d+)_(.*)\.(js|ts|mjs|mts)/;
57
+ this.filenameRegExp = /(\d+)_(.*)\.(js|ts|mjs|mts|sql)/;
58
58
  this.postgres = postgres;
59
59
  this.pathToMigrations = pathToMigrations;
60
60
  this.migrationTableName = migrationTableName;
@@ -104,8 +104,10 @@ export class Migrator {
104
104
  }
105
105
  for (const migration of pendingMigrations) {
106
106
  this.log('Running migration', migration.filename);
107
- await this.migrate(migration, direction);
108
- await this.updateCurrentVersion(migration.version);
107
+ await this.postgres.transaction(async (tx) => {
108
+ await this.migrate(tx, migration, direction);
109
+ await this.updateCurrentVersion(tx, migration.version);
110
+ });
109
111
  }
110
112
  this.log(chalk.green('Done!'));
111
113
  return await this.getCurrentVersion();
@@ -120,29 +122,55 @@ export class Migrator {
120
122
  return currentVersion;
121
123
  }
122
124
  this.log('Rolling back migration', migrationToRollback.filename);
123
- await this.migrate(migrationToRollback, 'down');
124
- await this.updateCurrentVersion(updatedVersion);
125
+ await this.postgres.transaction(async (tx) => {
126
+ await this.migrate(tx, migrationToRollback, 'down');
127
+ await this.updateCurrentVersion(tx, updatedVersion);
128
+ });
125
129
  return await this.getCurrentVersion();
126
130
  }
127
131
  throw new Error(`Invalid direction ${direction}`);
128
132
  }
129
- queryWithLog(sql) {
130
- this.log(chalk.yellow(sql.toString()));
131
- return this.postgres.query(sql);
133
+ async readMigrationFile(filename) {
134
+ if (!filename.endsWith('.sql')) {
135
+ return import(filename);
136
+ }
137
+ const content = await fs.readFile(filename, 'utf-8');
138
+ const [up, down] = content.split('-- ROLLBACK --');
139
+ return {
140
+ up() {
141
+ return up.trim();
142
+ },
143
+ down() {
144
+ return (down || '').trim();
145
+ },
146
+ };
132
147
  }
133
- async migrate(migration, direction) {
134
- const migrator = await import(migration.filename);
148
+ async migrate(tx, migration, direction) {
149
+ const migrator = await this.readMigrationFile(migration.filename);
135
150
  if (!migrator.up || !migrator.down) {
136
151
  throw new Error('Migrations must export both an up() and a down() function.');
137
152
  }
138
- const returnValue = migrator[direction](this.queryWithLog.bind(this));
139
- if (typeof returnValue === 'string') {
140
- return this.queryWithLog(returnValue);
153
+ try {
154
+ const queryWithLog = (sql) => {
155
+ this.log(chalk.yellow(sql.toString()));
156
+ return tx.query(sql);
157
+ };
158
+ const returnValue = await migrator[direction](queryWithLog.bind(tx));
159
+ if (typeof returnValue === 'string') {
160
+ if (returnValue !== '') {
161
+ await queryWithLog(returnValue);
162
+ }
163
+ }
164
+ else {
165
+ await returnValue;
166
+ }
167
+ }
168
+ catch (error) {
169
+ throw new Error(`Migration failed: ${error.message}`);
141
170
  }
142
- return returnValue;
143
171
  }
144
- updateCurrentVersion(version) {
145
- return this.postgres.query(`INSERT INTO ${this.migrationTableName} (version, run_at) VALUES ($1, $2)`, [version, new Date()]);
172
+ updateCurrentVersion(connection, version) {
173
+ return connection.query(`INSERT INTO ${this.migrationTableName} (version, run_at) VALUES ($1, $2)`, [version, new Date()]);
146
174
  }
147
175
  async getCurrentVersion() {
148
176
  const result = await this.postgres.first(`SELECT version FROM ${this.migrationTableName} ORDER BY run_at DESC LIMIT 1`);
@@ -10,7 +10,7 @@ export declare class MultipleRowsReturnedError extends Error {
10
10
  constructor(message: string);
11
11
  }
12
12
  type Value = null | string | number | Date;
13
- declare class API {
13
+ export declare class Connection {
14
14
  client: PoolClient;
15
15
  constructor(client: PoolClient);
16
16
  query<T extends QueryResultRow>(text: string | SQLStatement, values?: Value[]): Promise<QueryResult<T>>;
@@ -19,6 +19,8 @@ declare class API {
19
19
  first<T>(text: string | SQLStatement, values?: Value[]): Promise<T | null>;
20
20
  get<T>(text: string | SQLStatement, values?: Value[]): Promise<T>;
21
21
  }
22
+ export declare class Transaction extends Connection {
23
+ }
22
24
  /**
23
25
  * Create a new Postgres connection pool with
24
26
  * conveience methods for executing queries using
@@ -32,8 +34,8 @@ export declare function createPostgres({ onError, ...config }?: PostgresOptions)
32
34
  first: <T_2>(text: string | SQLStatement, values?: Value[] | undefined) => Promise<T_2 | null>;
33
35
  all: <T_3>(text: string | SQLStatement, values?: Value[] | undefined) => Promise<T_3[]>;
34
36
  get: <T_4>(text: string | SQLStatement, values?: Value[] | undefined) => Promise<T_4>;
35
- withConnection: <T_5>(callback: (connection: API) => Promise<T_5>) => Promise<T_5>;
36
- transaction: <T_6>(callback: (connection: API) => T_6 | Promise<T_6>) => Promise<T_6>;
37
+ withConnection: <T_5>(callback: (connection: Connection) => Promise<T_5>) => Promise<T_5>;
38
+ transaction: <T_6>(callback: (connection: Transaction) => T_6 | Promise<T_6>) => Promise<T_6>;
37
39
  instance: pg.Pool;
38
40
  clear: (force?: boolean) => Promise<void>;
39
41
  end(): Promise<void>;
package/dist/postgres.js CHANGED
@@ -12,7 +12,7 @@ export class MultipleRowsReturnedError extends Error {
12
12
  this.name = 'MultipleRowsReturnedError';
13
13
  }
14
14
  }
15
- class API {
15
+ export class Connection {
16
16
  constructor(client) {
17
17
  this.client = client;
18
18
  }
@@ -44,6 +44,8 @@ class API {
44
44
  return result.rows[0];
45
45
  }
46
46
  }
47
+ export class Transaction extends Connection {
48
+ }
47
49
  /**
48
50
  * Create a new Postgres connection pool with
49
51
  * conveience methods for executing queries using
@@ -57,7 +59,7 @@ export function createPostgres({ onError = () => { }, ...config } = {}) {
57
59
  async function withConnection(callback) {
58
60
  const client = await pool.connect();
59
61
  try {
60
- const result = await callback(new API(client));
62
+ const result = await callback(new Connection(client));
61
63
  return result;
62
64
  }
63
65
  finally {
@@ -82,7 +84,7 @@ export function createPostgres({ onError = () => { }, ...config } = {}) {
82
84
  const client = await pool.connect();
83
85
  try {
84
86
  await client.query('BEGIN');
85
- const result = await callback(new API(client));
87
+ const result = await callback(new Transaction(client));
86
88
  await client.query('COMMIT');
87
89
  return result;
88
90
  }
package/dist/sentry.d.ts CHANGED
@@ -22,9 +22,9 @@ export declare function createSentryOptions(app: Application | null, options?: S
22
22
  release?: string | undefined;
23
23
  environment?: string | undefined;
24
24
  debug?: boolean | undefined;
25
- dsn: string | undefined;
26
25
  instrumenter?: import("@sentry/types").Instrumenter | undefined;
27
26
  sampleRate?: number | undefined;
27
+ dsn: string | undefined;
28
28
  enabled?: boolean | undefined;
29
29
  attachStacktrace?: boolean | undefined;
30
30
  autoSessionTracking?: boolean | undefined;
@@ -32,6 +32,7 @@ export declare function createSentryOptions(app: Application | null, options?: S
32
32
  dist?: string | undefined;
33
33
  transportOptions?: Partial<import("@sentry/node/types/transports/http.js").NodeTransportOptions> | undefined;
34
34
  tracesSampleRate: number;
35
+ enableTracing?: boolean | undefined;
35
36
  initialScope?: import("@sentry/types").CaptureContext | undefined;
36
37
  maxBreadcrumbs?: number | undefined;
37
38
  maxValueLength?: number | undefined;
@@ -39,6 +40,7 @@ export declare function createSentryOptions(app: Application | null, options?: S
39
40
  normalizeMaxBreadth?: number | undefined;
40
41
  shutdownTimeout?: number | undefined;
41
42
  ignoreErrors?: (string | RegExp)[] | undefined;
43
+ ignoreTransactions?: (string | RegExp)[] | undefined;
42
44
  tunnel?: string | undefined;
43
45
  sendDefaultPii?: boolean | undefined;
44
46
  _metadata?: import("@sentry/types").SdkMetadata | undefined;
@@ -52,6 +54,7 @@ export declare function createSentryOptions(app: Application | null, options?: S
52
54
  beforeSendTransaction?: ((event: import("@sentry/types").TransactionEvent, hint: Sentry.EventHint) => Sentry.Event | PromiseLike<Sentry.Event | null> | null) | undefined;
53
55
  beforeBreadcrumb?: ((breadcrumb: Sentry.Breadcrumb, hint?: Sentry.BreadcrumbHint | undefined) => Sentry.Breadcrumb | null) | undefined;
54
56
  profilesSampleRate?: number | undefined;
57
+ profilesSampler?: ((samplingContext: import("@sentry/types").SamplingContext) => number | boolean) | undefined;
55
58
  serverName?: string | undefined;
56
59
  includeLocalVariables?: boolean | undefined;
57
60
  tracePropagationTargets?: import("@sentry/types").TracePropagationTargets | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devmoods/express-extras",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Kubernetes-ready utilities for rapid Node.js application development",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,13 +34,13 @@
34
34
  ],
35
35
  "license": "ISC",
36
36
  "devDependencies": {
37
- "@devmoods/eslint-config": "^1.0.0",
38
- "@types/node": "18.11.18",
37
+ "@devmoods/eslint-config": "^1.1.0",
38
+ "@types/node": "20.2.5",
39
39
  "@types/supertest": "2.0.12",
40
- "@typescript-eslint/eslint-plugin": "^5.50.0",
41
- "@typescript-eslint/parser": "^5.50.0",
40
+ "@typescript-eslint/eslint-plugin": "^5.59.7",
41
+ "@typescript-eslint/parser": "^5.59.7",
42
42
  "chalk": "^5.2.0",
43
- "eslint": "^8.33.0",
43
+ "eslint": "^8.41.0",
44
44
  "eslint-plugin-jsx-a11y": "^6.7.1",
45
45
  "eslint-plugin-prettier": "^4.2.1",
46
46
  "eslint-plugin-react": "^7.32.2",
@@ -48,26 +48,26 @@
48
48
  "import-sort-cli": "6.0.0",
49
49
  "import-sort-style-module": "6.0.0",
50
50
  "isomorphic-fetch": "3.0.0",
51
- "prettier": "2.8.3",
51
+ "prettier": "2.8.8",
52
52
  "supertest": "6.3.3",
53
53
  "ts-node": "10.9.1",
54
- "typescript": "4.9.5",
55
- "vitest": "^0.28.3"
54
+ "typescript": "5.0.4",
55
+ "vitest": "^0.31.1"
56
56
  },
57
57
  "dependencies": {
58
58
  "@devmoods/fetch": "^3.3.0",
59
- "@godaddy/terminus": "^4.11.2",
60
- "@sentry/node": "^7.36.0",
61
- "@sentry/tracing": "^7.36.0",
59
+ "@godaddy/terminus": "^4.12.0",
60
+ "@sentry/node": "^7.53.1",
61
+ "@sentry/tracing": "^7.53.1",
62
62
  "@types/bcrypt": "^5.0.0",
63
63
  "@types/bunyan": "^1.8.8",
64
64
  "@types/cls-hooked": "^4.3.3",
65
65
  "@types/cookie-parser": "^1.4.3",
66
- "@types/express": "^4.17.16",
67
- "@types/jsonwebtoken": "^9.0.1",
68
- "@types/nodemailer": "^6.4.7",
69
- "@types/pg": "^8.6.6",
70
- "@types/uuid": "^9.0.0",
66
+ "@types/express": "^4.17.17",
67
+ "@types/jsonwebtoken": "^9.0.2",
68
+ "@types/nodemailer": "^6.4.8",
69
+ "@types/pg": "^8.10.1",
70
+ "@types/uuid": "^9.0.1",
71
71
  "ajv": "^8.12.0",
72
72
  "bcrypt": "^5.1.0",
73
73
  "bunyan": "^1.8.15",
@@ -76,12 +76,12 @@
76
76
  "cookie-parser": "^1.4.6",
77
77
  "dotenv": "^16.0.3",
78
78
  "express": "^4.18.2",
79
- "helmet": "^6.0.1",
79
+ "helmet": "^7.0.0",
80
80
  "jsonwebtoken": "^9.0.0",
81
- "meow": "^11.0.0",
82
- "nodemailer": "^6.9.1",
83
- "pg": "^8.9.0",
84
- "redis": "^4.6.4",
81
+ "meow": "^12.0.1",
82
+ "nodemailer": "^6.9.3",
83
+ "pg": "^8.11.0",
84
+ "redis": "^4.6.6",
85
85
  "sql-template-strings": "^2.2.2",
86
86
  "uuid": "^9.0.0"
87
87
  },
@@ -1,2 +0,0 @@
1
- export declare function up(query: any): Promise<void>;
2
- export declare function down(query: any): Promise<void>;
@@ -1,7 +0,0 @@
1
- // Generated Fri Feb 03 2023 07:32:26 GMT+0100 (Central European Standard Time)
2
- export async function up(query) {
3
- await query('SELECT 1');
4
- }
5
- export async function down(query) {
6
- await query('SELECT 1');
7
- }