@devmoods/express-extras 0.63.1 → 0.65.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.
Files changed (75) hide show
  1. package/README.md +0 -111
  2. package/dist/auth/emailAndPassword.js +2 -1
  3. package/dist/auth/google.d.ts +4 -4
  4. package/dist/auth/google.js +14 -11
  5. package/dist/auth/passwordless.js +2 -1
  6. package/dist/auth/token.js +6 -6
  7. package/dist/auth/types.d.ts +1 -1
  8. package/dist/auth.d.ts +1 -1
  9. package/dist/auth.js +17 -10
  10. package/dist/cache.d.ts +3 -0
  11. package/dist/cache.js +3 -2
  12. package/dist/channels.d.ts +9 -5
  13. package/dist/channels.js +115 -23
  14. package/dist/cli.js +1 -1
  15. package/dist/config.d.ts +66 -66
  16. package/dist/config.js +35 -36
  17. package/dist/context.d.ts +5 -2
  18. package/dist/context.js +10 -9
  19. package/dist/csrf.js +6 -6
  20. package/dist/email.d.ts +1 -1
  21. package/dist/email.js +3 -3
  22. package/dist/errors.js +1 -1
  23. package/dist/faktory.d.ts +1 -1
  24. package/dist/faktory.js +5 -15
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +4 -3
  27. package/dist/listen.d.ts +5 -4
  28. package/dist/listen.js +47 -45
  29. package/dist/logger.d.ts +1 -0
  30. package/dist/logger.js +11 -5
  31. package/dist/manage.d.ts +4 -6
  32. package/dist/manage.js +41 -37
  33. package/dist/middleware.d.ts +3 -0
  34. package/dist/middleware.js +24 -7
  35. package/dist/rate-limit.d.ts +1 -0
  36. package/dist/rate-limit.js +25 -3
  37. package/dist/redis.d.ts +3 -0
  38. package/dist/redis.js +25 -2
  39. package/dist/sentry.js +7 -3
  40. package/dist/setupTests.global.d.ts +2 -0
  41. package/dist/setupTests.global.js +4 -0
  42. package/dist/setupTests.js +5 -1
  43. package/dist/slack.js +2 -2
  44. package/dist/utils.d.ts +2 -14
  45. package/dist/utils.js +2 -21
  46. package/dist/validate.js +7 -4
  47. package/dist/vite/__tests__/fixtures/src/entry-server.d.ts +5 -0
  48. package/dist/vite/__tests__/fixtures/src/entry-server.js +15 -0
  49. package/dist/vite/defineViteConfig.js +11 -1
  50. package/dist/vite/index.d.ts +1 -0
  51. package/dist/vite/index.js +3 -0
  52. package/dist/vite/ssr.d.ts +1 -0
  53. package/dist/vite/ssr.js +9 -4
  54. package/dist/vitest/index.d.ts +6 -0
  55. package/dist/vitest/index.js +25 -0
  56. package/dist/vitest/matchers.d.ts +11 -0
  57. package/dist/vitest/matchers.js +65 -0
  58. package/dist/vitest/testClient.d.ts +30 -0
  59. package/dist/vitest/testClient.js +98 -0
  60. package/dist/webapi.js +4 -0
  61. package/package.json +38 -43
  62. package/dist/__tests__/__migrations__/1_a.d.ts +0 -3
  63. package/dist/__tests__/__migrations__/1_a.js +0 -9
  64. package/dist/migrate.d.ts +0 -47
  65. package/dist/migrate.js +0 -255
  66. package/dist/postgres-ts-generator.d.ts +0 -11
  67. package/dist/postgres-ts-generator.js +0 -114
  68. package/dist/postgres.d.ts +0 -71
  69. package/dist/postgres.js +0 -261
  70. package/dist/sql.d.ts +0 -33
  71. package/dist/sql.js +0 -135
  72. package/dist/vite/vitest.d.ts +0 -1
  73. package/dist/vite/vitest.js +0 -1
  74. package/dist/vite.d.ts +0 -1
  75. package/dist/vite.js +0 -1
package/dist/migrate.js DELETED
@@ -1,255 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import * as path from 'node:path';
3
- import chalk from 'chalk';
4
- import {} from 'commander';
5
- import {} from './cli.js';
6
- import { createPostgres } from './postgres.js';
7
- import { formatSql, sql } from './sql.js';
8
- export const asCli = (command, cli) => {
9
- command.command('up').action(cli.wrap(async () => {
10
- const migrator = new Migrator();
11
- try {
12
- await migrator.runMigrations('up');
13
- }
14
- finally {
15
- await migrator.end();
16
- }
17
- }));
18
- command.command('down').action(cli.wrap(async () => {
19
- const migrator = new Migrator();
20
- try {
21
- await migrator.runMigrations('down');
22
- }
23
- finally {
24
- await migrator.end();
25
- }
26
- }));
27
- command.command('init').action(cli.wrap(async () => {
28
- const migrator = new Migrator();
29
- try {
30
- const created = await migrator.init();
31
- if (created) {
32
- cli.greenLine('Migrations table was successfully setup.');
33
- }
34
- else {
35
- cli.greenLine('Migrations table already existing.');
36
- }
37
- }
38
- finally {
39
- await migrator.end();
40
- }
41
- }));
42
- command
43
- .command('create')
44
- .argument('<string>', 'name of migration')
45
- .option('--typescript', 'use typescript')
46
- .action(cli.wrap(async (name, flags) => {
47
- const migrator = new Migrator();
48
- try {
49
- const createdMigration = await migrator.create(name, flags.typescript ? 'ts' : 'sql');
50
- migrator.log(chalk.green('Created migration', createdMigration));
51
- }
52
- finally {
53
- await migrator.end();
54
- }
55
- }));
56
- };
57
- export class Migrator {
58
- postgres;
59
- migrationTableName;
60
- pathToMigrations;
61
- filenameRegExp = /(\d+)_(.*)\.(js|ts|mjs|mts|sql)/;
62
- verbose;
63
- constructor({ postgres = createPostgres({ onLogStatement: () => { } }), pathToMigrations = path.resolve(process.cwd(), 'db', 'migrations'), migrationTableName = 'migrations', verbose = true, } = {}) {
64
- this.postgres = postgres;
65
- this.pathToMigrations = pathToMigrations;
66
- this.migrationTableName = migrationTableName;
67
- this.verbose = verbose;
68
- }
69
- end() {
70
- return this.postgres.end();
71
- }
72
- async init() {
73
- if (await this.isInstalled()) {
74
- return false;
75
- }
76
- await fs.mkdir(this.pathToMigrations, { recursive: true });
77
- await this.postgres.query(sql `
78
- CREATE TABLE IF NOT EXISTS ${sql.identifier(this.migrationTableName)} (
79
- id bigserial PRIMARY KEY,
80
- version int NOT NULL,
81
- run_at timestamp with time zone NOT NULL
82
- );
83
- `);
84
- return true;
85
- }
86
- async isInstalled() {
87
- try {
88
- await this.postgres.query(sql `SELECT * FROM ${sql.identifier(this.migrationTableName)}`);
89
- await fs.stat(this.pathToMigrations);
90
- return true;
91
- }
92
- catch {
93
- return false;
94
- }
95
- }
96
- async create(name, format = 'sql') {
97
- if (!name) {
98
- throw new Error('Missing positional argument <name>');
99
- }
100
- if (!(await this.isInstalled())) {
101
- await this.init();
102
- }
103
- const existingMigrations = await this.getMigrations();
104
- const lastVersion = existingMigrations.length
105
- ? existingMigrations[existingMigrations.length - 1].version
106
- : 0;
107
- const filename = path.join(this.pathToMigrations, `${lastVersion + 1}_${name}.${format}`);
108
- const getTemplate = () => {
109
- switch (format) {
110
- case 'sql':
111
- return `-- Migration ${name} created ${new Date()}\n\nSELECT 1;\n\n-- ROLLBACK --\n\nSELECT 1;\n`;
112
- case 'ts':
113
- return `// Migration ${name} created ${new Date()}\n\nexport function up(query: any) {}\n\nexport function down(query: any) {}\n`;
114
- }
115
- };
116
- await fs.writeFile(filename, getTemplate());
117
- return filename;
118
- }
119
- async runMigrations(direction) {
120
- if (direction !== 'up' && direction !== 'down') {
121
- throw new Error('Only up and down are supported.');
122
- }
123
- if (!(await this.isInstalled())) {
124
- await this.init();
125
- }
126
- const migrations = await this.getMigrations();
127
- const currentVersion = await this.getCurrentVersion();
128
- return this.executeMigration(migrations, currentVersion, direction);
129
- }
130
- log(...args) {
131
- if (this.verbose) {
132
- console.log(...args);
133
- }
134
- }
135
- async executeMigration(migrations, currentVersion, direction) {
136
- if (direction === 'up') {
137
- const pendingMigrations = this.getPendingMigrations(migrations, currentVersion);
138
- if (!pendingMigrations.length) {
139
- this.log(chalk.green('No pending migrations'));
140
- return currentVersion;
141
- }
142
- for (const migration of pendingMigrations) {
143
- this.log('Running migration', migration.filename);
144
- const migrator = await this.readMigrationFile(migration.filename);
145
- if (migrator.useTransaction !== false) {
146
- await this.postgres.transaction(async () => {
147
- await this.migrate(migrator, direction);
148
- await this.updateCurrentVersion(migration.version);
149
- });
150
- }
151
- else {
152
- await this.postgres.withConnection(async () => {
153
- await this.migrate(migrator, direction);
154
- await this.updateCurrentVersion(migration.version);
155
- });
156
- }
157
- }
158
- this.log(chalk.green('Done!'));
159
- return await this.getCurrentVersion();
160
- }
161
- else if (direction === 'down') {
162
- const previousMigrations = this.getPreviousMigrations(migrations, currentVersion);
163
- const previousMigration = previousMigrations[0];
164
- const updatedVersion = previousMigration ? previousMigration.version : 0;
165
- const migrationToRollback = migrations.find((migration) => migration.version === currentVersion);
166
- if (!migrationToRollback) {
167
- this.log(chalk.green('No migrations have been run, so nothing to rollback to.'));
168
- return currentVersion;
169
- }
170
- this.log('Rolling back migration', migrationToRollback.filename);
171
- const migrator = await this.readMigrationFile(migrationToRollback.filename);
172
- if (migrator.useTransaction !== false) {
173
- await this.postgres.transaction(async () => {
174
- await this.migrate(migrator, 'down');
175
- await this.updateCurrentVersion(updatedVersion);
176
- });
177
- }
178
- else {
179
- await this.postgres.withConnection(async () => {
180
- await this.migrate(migrator, 'down');
181
- await this.updateCurrentVersion(updatedVersion);
182
- });
183
- }
184
- return await this.getCurrentVersion();
185
- /* c8 ignore next 2 */
186
- }
187
- throw new Error(`Invalid direction ${direction}`);
188
- }
189
- async readMigrationFile(filename) {
190
- if (!filename.endsWith('.sql')) {
191
- return import(filename);
192
- }
193
- const content = await fs.readFile(filename, 'utf-8');
194
- const [up, down] = content.split(/[-\s]+ROLLBACK[-\s]+/i);
195
- return {
196
- useTransaction: !/[-\s]+NO[\s]*TRANSACTION[-\s]+/i.test(content),
197
- up() {
198
- return up.trim();
199
- },
200
- down() {
201
- return (down || '').trim();
202
- },
203
- };
204
- }
205
- async migrate(migrator, direction) {
206
- try {
207
- const queryWithLog = (statement) => {
208
- this.log(chalk.yellow(formatSql(statement)));
209
- return this.postgres.query(statement);
210
- };
211
- const returnValue = await migrator[direction](queryWithLog.bind(this.postgres));
212
- if (typeof returnValue === 'string') {
213
- if (returnValue !== '') {
214
- await queryWithLog(returnValue);
215
- }
216
- }
217
- else {
218
- await returnValue;
219
- }
220
- /* c8 ignore next 3 */
221
- }
222
- catch (error) {
223
- throw new Error(`Migration failed: ${error.message}`);
224
- }
225
- }
226
- updateCurrentVersion(version) {
227
- return this.postgres.query(`INSERT INTO ${this.migrationTableName} (version, run_at) VALUES ($1, $2)`, [version, new Date()]);
228
- }
229
- async getCurrentVersion() {
230
- const result = await this.postgres.first(`SELECT version FROM ${this.migrationTableName} ORDER BY run_at DESC LIMIT 1`);
231
- return result ? result.version : 0;
232
- }
233
- async getMigrations() {
234
- const files = await fs.readdir(this.pathToMigrations);
235
- return files
236
- .map((filename) => filename.match(this.filenameRegExp))
237
- .filter(notNullOrUndefined)
238
- .map(([filename, version, name]) => ({
239
- filename: path.join(this.pathToMigrations, filename),
240
- version: parseInt(version, 10),
241
- name,
242
- }))
243
- .sort((a, b) => a.version - b.version);
244
- }
245
- getPreviousMigrations(migrations, currentVersion) {
246
- const reversedMigrations = migrations.reverse();
247
- return reversedMigrations.filter((migration) => migration.version < currentVersion);
248
- }
249
- getPendingMigrations(migrations, currentVersion) {
250
- return migrations.filter((migration) => migration.version > currentVersion);
251
- }
252
- }
253
- function notNullOrUndefined(x) {
254
- return x != null;
255
- }
@@ -1,11 +0,0 @@
1
- export interface GenerateDatabaseTypesOptions {
2
- outputPath: string;
3
- connectionString: string;
4
- schema?: string;
5
- excludeTables?: string[];
6
- includeTables?: string[];
7
- }
8
- /**
9
- * Generates complete TypeScript definitions from database schema
10
- */
11
- export declare function generateDatabaseTypes(options: GenerateDatabaseTypesOptions): Promise<void>;
@@ -1,114 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import { resolve, dirname } from 'node:path';
3
- import {} from 'pg-structure';
4
- import { pgStructure } from 'pg-structure/dist/main.js';
5
- /**
6
- * Converts PostgreSQL data type to TypeScript type
7
- */
8
- function pgTypeToTsType(pgType, isNullable) {
9
- const nullableSuffix = isNullable ? ' | null' : '';
10
- switch (pgType.name.toLowerCase()) {
11
- case 'uuid':
12
- return `string${nullableSuffix}`;
13
- case 'text':
14
- case 'varchar':
15
- case 'char':
16
- case 'character varying':
17
- return `string${nullableSuffix}`;
18
- case 'integer':
19
- case 'int':
20
- case 'int2':
21
- case 'int4':
22
- case 'int8':
23
- case 'serial':
24
- case 'smallint':
25
- return `number${nullableSuffix}`;
26
- case 'bigint':
27
- case 'bigserial':
28
- case 'decimal':
29
- case 'numeric':
30
- return `string${nullableSuffix}`;
31
- case 'real':
32
- case 'double precision':
33
- case 'float':
34
- case 'float4':
35
- case 'float8':
36
- return `number${nullableSuffix}`;
37
- case 'boolean':
38
- case 'bool':
39
- return `boolean${nullableSuffix}`;
40
- case 'timestamp':
41
- case 'timestamp with time zone':
42
- case 'timestamptz':
43
- return `Date${nullableSuffix}`;
44
- case 'date':
45
- return `Date${nullableSuffix}`;
46
- case 'time':
47
- case 'time with time zone':
48
- return `string${nullableSuffix}`;
49
- case 'json':
50
- case 'jsonb':
51
- return `Record<string, any>${nullableSuffix}`;
52
- case 'bytea':
53
- return `Buffer${nullableSuffix}`;
54
- case 'inet':
55
- case 'cidr':
56
- return `string${nullableSuffix}`;
57
- case 'array':
58
- return `any[]${nullableSuffix}`;
59
- default:
60
- return `any${nullableSuffix}`;
61
- }
62
- }
63
- /**
64
- * Generates TypeScript interface for a table
65
- */
66
- function generateTableInterface(table, tableName) {
67
- const interfaceName = tableName.charAt(0).toUpperCase() + tableName.slice(1);
68
- let interfaceCode = `\n/**\n * ${interfaceName} table interface\n */\nexport interface ${interfaceName} {\n`;
69
- for (const column of table.columns) {
70
- const tsType = pgTypeToTsType(column.type, !column.notNull);
71
- const comment = column.comment ? ` /** ${column.comment} */` : '';
72
- interfaceCode += ` /** ${column.name} */\n ${column.name}: ${tsType};${comment}\n`;
73
- }
74
- interfaceCode += '}\n';
75
- return interfaceCode;
76
- }
77
- /**
78
- * Generates complete TypeScript definitions from database schema
79
- */
80
- export async function generateDatabaseTypes(options) {
81
- const { outputPath, connectionString, schema = 'public', excludeTables = ['migrations', 'migration_lock'], includeTables, } = options;
82
- // Connect to database and get schema
83
- const db = await pgStructure(connectionString);
84
- let output = `/**
85
- * TypeScript definitions for database tables
86
- * Auto-generated from database schema
87
- */
88
-
89
- `;
90
- const tables = db.schemas.get(schema)?.tables || [];
91
- const filteredTables = tables.filter((table) => {
92
- if (includeTables && includeTables.length > 0) {
93
- return includeTables.includes(table.name);
94
- }
95
- return !excludeTables.includes(table.name);
96
- });
97
- // Generate table interfaces
98
- for (const table of filteredTables) {
99
- output += generateTableInterface(table, table.name);
100
- }
101
- // Generate DB interface
102
- output +=
103
- '\n/**\n * Database schema interface\n * Maps table names to their respective interfaces\n */\nexport interface DB {\n';
104
- for (const table of filteredTables) {
105
- const interfaceName = table.name.charAt(0).toUpperCase() + table.name.slice(1);
106
- output += ` ${table.name}: ${interfaceName};\n`;
107
- }
108
- output += '}\n';
109
- // Ensure output directory exists
110
- const outputDir = dirname(resolve(outputPath));
111
- await fs.mkdir(outputDir, { recursive: true });
112
- // Write the generated types
113
- await fs.writeFile(resolve(outputPath), output, 'utf-8');
114
- }
@@ -1,71 +0,0 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
- import { type RequestHandler } from 'express';
3
- import { type PoolClient, type PoolConfig, type QueryResult, type QueryResultRow } from 'pg';
4
- import { type SqlStatement } from './sql.js';
5
- export declare const escapePostgresIdentifier: typeof import("pg").escapeIdentifier;
6
- export declare const escapePostgresLiteral: typeof import("pg").escapeLiteral;
7
- export interface PostgresOptions extends PoolConfig, ConnectionOptions {
8
- onError?: (error: Error, client: any) => void;
9
- url?: PoolConfig['connectionString'];
10
- }
11
- export declare class DoesNotExistError extends Error {
12
- constructor(message: string);
13
- }
14
- export declare class MultipleRowsReturnedError extends Error {
15
- constructor(message: string);
16
- }
17
- export declare class IntegrityError extends Error {
18
- constructor(message: string);
19
- }
20
- type Value = null | string | number | Date;
21
- export interface ConnectionOptions {
22
- onLogStatement?: (statement: SqlStatement | string, values: unknown[] | undefined, duration: number) => void;
23
- }
24
- export interface TransactionContext {
25
- connection: Connection;
26
- savepoints: string[];
27
- isCommitted: boolean;
28
- isRolledBack: boolean;
29
- }
30
- export declare const transactionStorage: AsyncLocalStorage<TransactionContext>;
31
- export declare class Connection {
32
- client: PoolClient;
33
- options: ConnectionOptions;
34
- constructor(client: PoolClient, options: ConnectionOptions);
35
- private logStatement;
36
- query<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<QueryResult<T>>;
37
- all<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T[]>;
38
- insert<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<QueryResult<T>>;
39
- first<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T | null>;
40
- get<T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]): Promise<T>;
41
- }
42
- export declare class Transaction extends Connection {
43
- savepoints: string[];
44
- isCommitted: boolean;
45
- isRolledBack: boolean;
46
- createSavepoint(): Promise<string>;
47
- rollbackToSavepoint(savepointName: string): Promise<void>;
48
- releaseSavepoint(savepointName: string): Promise<void>;
49
- }
50
- /**
51
- * Create a new Postgres connection pool with
52
- * conveience methods for executing queries using
53
- * one of the available connections.
54
- *
55
- * @param config
56
- */
57
- export declare function createPostgres({ onError, url, ...postgresOptions }?: PostgresOptions): {
58
- insert: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<QueryResult<T>>;
59
- query: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<QueryResult<T>>;
60
- first: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T | null>;
61
- all: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T[]>;
62
- get: <T extends QueryResultRow>(text: string | SqlStatement, values?: Value[]) => Promise<T>;
63
- withConnection: <T>(callback: (connection: Connection) => Promise<T>) => Promise<T>;
64
- transaction: <T>(callback: () => Promise<T> | T) => Promise<T>;
65
- getCurrentTransaction: () => Transaction | null;
66
- instance: import("pg").Pool;
67
- clear: (force?: boolean) => Promise<void>;
68
- end(): Promise<void>;
69
- };
70
- export declare function withTransaction(postgres: ReturnType<typeof createPostgres>): RequestHandler;
71
- export {};