@groundbrick/express-adapter 0.2.1 → 0.2.2

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.
package/README.md CHANGED
@@ -11,6 +11,7 @@ Express.js integration adapter para o microframework TypeScript com middleware e
11
11
  - **📊 Response Helpers** - Formatação padronizada de respostas da API
12
12
  - **✅ Validation Utilities** - Helpers de validação e sanitização
13
13
  - **📄 Pagination Support** - Utilitários de paginação e ordenação
14
+ - **📡 Sentry (Error Tracking + APM)** - Observabilidade opt-in, ativada por variáveis de ambiente
14
15
 
15
16
  ## Instalação
16
17
 
@@ -369,6 +370,53 @@ A classe `ExpressApp` configura automaticamente:
369
370
  - **Request Logging**: Log automático de requests com timing
370
371
  - **Response Formatting**: Formatação padronizada de respostas
371
372
  - **Error Handling**: Tratamento global de erros
373
+ - **Sentry**: Error handler do Sentry (quando ativado por `SENTRY_DSN`), antes do handler global
374
+
375
+ ## Sentry (Error Tracking + APM)
376
+
377
+ O adapter integra o [Sentry Node SDK](https://docs.sentry.io/platforms/javascript/guides/node/)
378
+ como uma capacidade **opt-in**, ativada simplesmente pela presença da variável `SENTRY_DSN`.
379
+ Cobre captura de erros, tracing (HTTP, Express e PostgreSQL `pg`) e profiling de CPU, dando
380
+ visibilidade sobre erros e gargalos de performance (endpoints e queries lentas). Se `SENTRY_DSN`
381
+ não estiver definida, é um no-op completo (apps sem DSN e testes não são afetados).
382
+
383
+ ### Como funciona
384
+
385
+ Em ESM, o `Sentry.init()` tem de correr **antes** de qualquer módulo instrumentado (`http`,
386
+ `express`, `pg`) ser carregado. Por isso o package expõe um módulo side-effecting que deve ser
387
+ pré-carregado via a flag `--import` do Node, e o `ExpressApp` regista automaticamente o error
388
+ handler do Sentry (antes do error handler global) quando o SDK foi inicializado.
389
+
390
+ ### Ativação
391
+
392
+ 1. Pré-carregar o módulo de instrumentação nos scripts do app:
393
+
394
+ ```jsonc
395
+ // package.json do app
396
+ {
397
+ "scripts": {
398
+ "dev": "tsx watch --import @groundbrick/express-adapter/instrument src/server.ts",
399
+ "start": "node --import @groundbrick/express-adapter/instrument dist/src/server.js"
400
+ }
401
+ }
402
+ ```
403
+
404
+ 2. Definir as variáveis de ambiente (DSN vazio = Sentry desativado):
405
+
406
+ ```bash
407
+ # Sentry (error tracking + APM). SENTRY_DSN vazio = desativado.
408
+ SENTRY_DSN=https://<key>@<org>.ingest.sentry.io/<project>
409
+ SENTRY_ENVIRONMENT=development
410
+ # Tracing: % de transações capturadas (1.0 = 100%). Baixar em produção (ex.: 0.1).
411
+ SENTRY_TRACES_SAMPLE_RATE=1.0
412
+ # Profiling: % das transações amostradas que também são profiled.
413
+ SENTRY_PROFILES_SAMPLE_RATE=1.0
414
+ ```
415
+
416
+ Não é preciso código no `server.ts`: o error handler do Sentry é montado pelo `ExpressApp`
417
+ (depois das rotas, antes do error handler global), pelo que as respostas de erro continuam a sair
418
+ no formato padrão `{ success: false, error: { ... } }`. Em testes (sem o preload `--import`) o
419
+ Sentry nunca inicializa, logo a integração fica inerte.
372
420
 
373
421
  ## Exemplo Completo com Autenticação
374
422
 
@@ -435,6 +483,9 @@ Este adapter funciona perfeitamente com:
435
483
  - `@groundbrick/logger` - Logging functionality
436
484
  - `@groundbrick/service-base` - Service base classes
437
485
  - `@groundbrick/db-core` - Database core abstractions
486
+ - `@sentry/node` - Error tracking + APM (Sentry Node SDK)
487
+ - `@sentry/profiling-node` - CPU profiling integration para o Sentry
488
+ - `dotenv` - Carrega `.env` no preload de instrumentação do Sentry
438
489
 
439
490
  ### Peer Dependencies
440
491
  - `express` - Express.js framework
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cookies/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAMvD,MAAM,WAAW,gBAAgB;IAC7B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,YAAY,EAAE,OAAO,CAAC;IACtB,wCAAwC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACrC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CAiB/E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC;IAI7E;;OAEG;aACM,QAAQ,SAAS,MAAM,YAAY,MAAM,GAAG,IAAI;IAQzD;;;OAGG;eACQ,QAAQ,GAAG,IAAI;IAK1B;;OAEG;0BACmB,MAAM,GAAG,aAAa;EAOnD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,MAAc,GAAG,MAAM,CAWpE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,GAAE,MAAc,GAAG,MAAM,CAE3E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cookies/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAMvD,MAAM,WAAW,gBAAgB;IAC7B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,YAAY,EAAE,OAAO,CAAC;IACtB,wCAAwC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACrC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CAsC/E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC;IAI7E;;OAEG;aACM,QAAQ,SAAS,MAAM,YAAY,MAAM,GAAG,IAAI;IAQzD;;;OAGG;eACQ,QAAQ,GAAG,IAAI;IAK1B;;OAEG;0BACmB,MAAM,GAAG,aAAa;EAOnD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,MAAc,GAAG,MAAM,CAWpE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,GAAE,MAAc,GAAG,MAAM,CAE3E"}
@@ -6,8 +6,24 @@
6
6
  * These options MUST be consistent between set and clear operations.
7
7
  */
8
8
  export function createAuthCookieOptions(config) {
9
- const secure = config.secure ?? config.isProduction;
10
- const sameSite = config.sameSite ?? (config.isProduction ? 'strict' : 'lax');
9
+ let secure = config.secure ?? config.isProduction;
10
+ let sameSite = config.sameSite ?? (config.isProduction ? 'strict' : 'lax');
11
+ // Production hardening: even if env vars try to lower security, force secure
12
+ // cookie flags. This prevents accidental misconfiguration (e.g. an operator
13
+ // setting AUTH_COOKIE_SECURE=false to debug something and forgetting to revert)
14
+ // from shipping an insecure cookie in production.
15
+ if (config.isProduction) {
16
+ if (secure !== true) {
17
+ throw new Error('Refusing to issue auth cookie with secure=false in production. ' +
18
+ 'Unset AUTH_COOKIE_SECURE or set it to true.');
19
+ }
20
+ if (sameSite !== 'strict' && sameSite !== 'lax') {
21
+ // sameSite='none' would require secure (already enforced) but is too
22
+ // permissive for auth cookies; force strict.
23
+ throw new Error(`Refusing to issue auth cookie with sameSite="${sameSite}" in production. ` +
24
+ 'Unset AUTH_COOKIE_SAMESITE or set it to "strict" (recommended) or "lax".');
25
+ }
26
+ }
11
27
  const options = {
12
28
  path: '/',
13
29
  domain: config.domain,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cookies/index.ts"],"names":[],"mappings":"AAmBA,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAwB;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC;IACpD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAE7E,MAAM,OAAO,GAAkB;QAC3B,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,IAAI;QACd,MAAM;QACN,QAAQ;KACX,CAAC;IAEF,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;IACrC,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAA8C;IACjF,MAAM,QAAQ,GAAG,YAAY,CAAC;IAE9B,OAAO;QACH;;WAEG;QACH,GAAG,CAAC,GAAa,EAAE,KAAa,EAAE,QAAgB;YAC9C,MAAM,OAAO,GAAG,uBAAuB,CAAC;gBACpC,GAAG,UAAU;gBACb,QAAQ;aACX,CAAC,CAAC;YACH,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QAED;;;WAGG;QACH,KAAK,CAAC,GAAa;YACf,MAAM,OAAO,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;YACpD,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QAED;;WAEG;QACH,UAAU,CAAC,QAAiB;YACxB,OAAO,uBAAuB,CAAC;gBAC3B,GAAG,UAAU;gBACb,QAAQ;aACX,CAAC,CAAC;QACP,CAAC;KACJ,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,YAAoB,KAAK;IACxD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7C,QAAQ,IAAI,EAAE,CAAC;QACX,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,cAAc;QACrD,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;QACzD,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,gBAAgB;QAClD,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,GAAG,IAAI,CAAC,CAAC,gBAAgB;QAC7C,OAAO,CAAC,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,mBAAmB;IAC7E,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,YAAoB,KAAK;IAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cookies/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpressApp.d.ts","sourceRoot":"","sources":["../../src/core/ExpressApp.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAO9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAK3D,qBAAa,UAAU;IACnB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,MAAM,GAAE,aAAkB;IAqCtC,OAAO,CAAC,eAAe;IA+DvB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,WAAW;IAoBZ,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI;IAS1C,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAItC,MAAM,IAAI,OAAO;IAIjB,kBAAkB,IAAI,IAAI;IAoB1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA6BhC"}
1
+ {"version":3,"file":"ExpressApp.d.ts","sourceRoot":"","sources":["../../src/core/ExpressApp.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAQ9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAK3D,qBAAa,UAAU;IACnB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,MAAM,GAAE,aAAkB;IAqCtC,OAAO,CAAC,eAAe;IA+DvB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,WAAW;IAoBZ,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI;IAS1C,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAItC,MAAM,IAAI,OAAO;IAIjB,kBAAkB,IAAI,IAAI;IA4B1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA6BhC"}
@@ -2,6 +2,7 @@ import express from 'express';
2
2
  import cors from 'cors';
3
3
  import helmet from 'helmet';
4
4
  import cookieParser from 'cookie-parser';
5
+ import * as Sentry from '@sentry/node';
5
6
  // import rateLimit, { RateLimitRequestHandler } from 'express-rate-limit';
6
7
  import { createLogger } from '@groundbrick/logger';
7
8
  import { ErrorHandler } from '../middleware/ErrorHandler.js';
@@ -153,6 +154,13 @@ export class ExpressApp {
153
154
  }
154
155
  });
155
156
  });
157
+ // Sentry captures unhandled route exceptions and forwards them (next(err))
158
+ // to the groundbrick ErrorHandler below, which still formats the response.
159
+ // Only attaches when Sentry was initialized (i.e. SENTRY_DSN present, via the
160
+ // @groundbrick/express-adapter/instrument preload). No-op otherwise (and in tests).
161
+ if (Sentry.isInitialized()) {
162
+ Sentry.setupExpressErrorHandler(this.app);
163
+ }
156
164
  // Global error handler - must be last
157
165
  this.app.use(this.errorHandler.handle);
158
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ExpressApp.js","sourceRoot":"","sources":["../../src/core/ExpressApp.ts"],"names":[],"mappings":"AAAA,OAAO,OAAuC,MAAM,SAAS,CAAC;AAC9D,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,MAAyB,MAAM,QAAQ,CAAC;AAE/C,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,2EAA2E;AAC3E,OAAO,EAAE,YAAY,EAAU,MAAM,qBAAqB,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEvE,MAAM,OAAO,UAAU;IACX,GAAG,CAAU;IACb,MAAM,CAAS;IACf,MAAM,CAAgB;IACtB,YAAY,CAAe;IAC3B,aAAa,CAAgB;IAErC,YAAY,SAAwB,EAAE;QAClC,IAAI,CAAC,MAAM,GAAG;YACV,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,SAAS;YACf,IAAI,EAAE;gBACF,MAAM,EAAE,IAAI;gBACZ,WAAW,EAAE,IAAI;aACpB;YACD,QAAQ,EAAE;gBACN,MAAM,EAAE,IAAI;gBACZ,eAAe;gBACf,8CAA8C;gBAC9C,6DAA6D;gBAC7D,IAAI;aACP;YACD,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE;gBACR,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;gBACvB,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;aAChD;YACD,UAAU,EAAE,KAAK;YACjB,GAAG,MAAM;SACZ,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC;YACvB,OAAO,EAAE,aAAa;YACtB,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM;SACxB,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClD,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAEtF,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC;IAEO,eAAe;QACnB,4BAA4B;QAC5B,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACxD,CAAC;QAED,sBAAsB;QACtB,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;YAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjD,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;gBACxB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,YAA6B,CAAC,CAAC,CAAC;YACxD,CAAC;QACL,CAAC;QAED,mBAAmB;QACnB,yCAAyC;QACzC,2DAA2D;QAC3D,6DAA6D;QAC7D,mDAAmD;QACnD,iFAAiF;QACjF,UAAU;QACV,yDAAyD;QACzD,IAAI;QAEJ,OAAO;QACP,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,CAAC;QAED,gDAAgD;QAChD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;QAE7B,iBAAiB;QACjB,iCAAiC;QACjC,+CAA+C;QAC/C,IAAI;QAEJ,iDAAiD;QACjD,4EAA4E;QAC5E,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;YACtB,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI;YAC/B,IAAI,EAAE,kBAAkB;SAC3B,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC;YAC5B,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU;YACrC,IAAI,EAAE,mCAAmC;SAC5C,CAAC,CAAC,CAAC;QAEJ,kBAAkB;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAErC,sBAAsB;QACtB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAEvC,sBAAsB;QACtB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IAEO,gBAAgB;QACpB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE;gBAC7D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC9C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8BAA8B,SAAS,SAAS,IAAI,GAAG,CAAC,CAAC;YAC9E,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAEO,WAAW;QACf,wBAAwB;QACxB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;YACrD,GAAG,CAAC,IAAI,CAAC;gBACL,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;aAC3B,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,gBAAgB;QAChB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;YAC/C,GAAG,CAAC,IAAI,CAAC;gBACL,OAAO,EAAE,uCAAuC;gBAChD,OAAO,EAAE,OAAO;gBAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACtC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;IAEM,SAAS,CAAC,MAAyB;QACtC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YACnB,MAAM,WAAW,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAElE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;IACP,CAAC;IAEM,QAAQ,CAAC,KAAsB;QAClC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5B,CAAC;IAEM,MAAM;QACT,OAAO,IAAI,CAAC,GAAG,CAAC;IACpB,CAAC;IAEM,kBAAkB;QACrB,yCAAyC;QACzC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;YAC9C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACjB,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE;oBACH,OAAO,EAAE,iBAAiB;oBAC1B,IAAI,EAAE,WAAW;iBACpB;gBACD,IAAI,EAAE;oBACF,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,cAAc,CAAW;iBACnD;aACJ,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,sCAAsC;QACtC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAEM,KAAK;QACR,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAE1B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;gBAC5C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;gBACtD,OAAO,EAAE,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,oBAAoB;YACpB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;gBAC/D,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;oBACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACpB,CAAC,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;gBAC9D,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;oBACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;oBAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACpB,CAAC,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ"}
1
+ {"version":3,"file":"ExpressApp.js","sourceRoot":"","sources":["../../src/core/ExpressApp.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,9 @@ export { ErrorHandler } from './middleware/ErrorHandler.js';
4
4
  export { RequestLogger } from './middleware/RequestLogger.js';
5
5
  export { ResponseFormatter } from './middleware/ResponseFormatter.js';
6
6
  export { JwtMiddleware } from './middleware/JwtMiddleware.js';
7
+ export { createRateLimitMiddleware, type CreateRateLimitOptions } from './middleware/RateLimitMiddleware.js';
8
+ export { InMemoryRateLimiter, type TrackResult } from './middleware/InMemoryRateLimiter.js';
9
+ export { createTurnstileMiddleware, type CreateTurnstileOptions } from './middleware/TurnstileMiddleware.js';
7
10
  export type * from './types/index.js';
8
11
  export { ResponseHelper, ValidationHelper, PaginationHelper, RequestHelper, SecurityHelper } from './utils/index.js';
9
12
  export * from './cookies/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAG9D,mBAAmB,kBAAkB,CAAC;AAGtC,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACf,MAAM,kBAAkB,CAAC;AAG1B,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7G,OAAO,EAAE,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAC5F,OAAO,EAAE,yBAAyB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAG7G,mBAAmB,kBAAkB,CAAC;AAGtC,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACf,MAAM,kBAAkB,CAAC;AAG1B,cAAc,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ export { ErrorHandler } from './middleware/ErrorHandler.js';
6
6
  export { RequestLogger } from './middleware/RequestLogger.js';
7
7
  export { ResponseFormatter } from './middleware/ResponseFormatter.js';
8
8
  export { JwtMiddleware } from './middleware/JwtMiddleware.js';
9
+ export { createRateLimitMiddleware } from './middleware/RateLimitMiddleware.js';
10
+ export { InMemoryRateLimiter } from './middleware/InMemoryRateLimiter.js';
11
+ export { createTurnstileMiddleware } from './middleware/TurnstileMiddleware.js';
9
12
  // Utility helpers
10
13
  export { ResponseHelper, ValidationHelper, PaginationHelper, RequestHelper, SecurityHelper } from './utils/index.js';
11
14
  // Cookie utilities
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,eAAe;AACf,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,aAAa;AACb,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAK9D,kBAAkB;AAClB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACf,MAAM,kBAAkB,CAAC;AAE1B,mBAAmB;AACnB,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,eAAe;AACf,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,aAAa;AACb,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAA+B,MAAM,qCAAqC,CAAC;AAC7G,OAAO,EAAE,mBAAmB,EAAoB,MAAM,qCAAqC,CAAC;AAC5F,OAAO,EAAE,yBAAyB,EAA+B,MAAM,qCAAqC,CAAC;AAK7G,kBAAkB;AAClB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACf,MAAM,kBAAkB,CAAC;AAE1B,mBAAmB;AACnB,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=instrument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument.d.ts","sourceRoot":"","sources":["../src/instrument.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ // Sentry instrumentation entry point.
2
+ //
3
+ // This module is side-effecting: importing it initializes the Sentry Node SDK.
4
+ // It MUST be preloaded before any application code so that the SDK can patch
5
+ // `http`, `express` and `pg` for tracing. In ESM that means referencing it via
6
+ // Node's `--import` flag, e.g.:
7
+ //
8
+ // node --import @groundbrick/express-adapter/instrument dist/src/server.js
9
+ //
10
+ // Activation is opt-in and env-driven: if `SENTRY_DSN` is not set, this is a
11
+ // complete no-op, so apps (and tests) that don't configure Sentry are unaffected.
12
+ import * as dotenv from 'dotenv';
13
+ // The preload runs before the app loads its own config, so the .env file has not
14
+ // been read yet. Load it here so SENTRY_* variables are available.
15
+ dotenv.config();
16
+ import * as Sentry from '@sentry/node';
17
+ import { nodeProfilingIntegration } from '@sentry/profiling-node';
18
+ const dsn = process.env.SENTRY_DSN;
19
+ if (dsn) {
20
+ const environment = process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV || 'development';
21
+ Sentry.init({
22
+ dsn,
23
+ environment,
24
+ // Send structured logs to Sentry.
25
+ enableLogs: true,
26
+ // CPU profiling (function-level) to surface performance bottlenecks.
27
+ integrations: [nodeProfilingIntegration()],
28
+ // Tracing: percentage of transactions captured. 1.0 = 100% (ideal for
29
+ // diagnosis/trial). Lower in production via the env var below.
30
+ tracesSampleRate: Number(process.env.SENTRY_TRACES_SAMPLE_RATE ?? '1.0'),
31
+ // Profiling: percentage of already-sampled transactions that are also profiled.
32
+ profilesSampleRate: Number(process.env.SENTRY_PROFILES_SAMPLE_RATE ?? '1.0'),
33
+ });
34
+ // Runs before the app logger is configured, so a plain console line is the right
35
+ // tool here. Confirms at boot that error tracking + APM are active.
36
+ console.log(`[sentry] initialized (environment=${environment})`);
37
+ }
38
+ //# sourceMappingURL=instrument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument.js","sourceRoot":"","sources":["../src/instrument.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,+EAA+E;AAC/E,gCAAgC;AAChC,EAAE;AACF,6EAA6E;AAC7E,EAAE;AACF,6EAA6E;AAC7E,kFAAkF;AAClF,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,iFAAiF;AACjF,mEAAmE;AACnE,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAElE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;AAEnC,IAAI,GAAG,EAAE,CAAC;IACR,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC;IAE5F,MAAM,CAAC,IAAI,CAAC;QACV,GAAG;QACH,WAAW;QACX,kCAAkC;QAClC,UAAU,EAAE,IAAI;QAChB,qEAAqE;QACrE,YAAY,EAAE,CAAC,wBAAwB,EAAE,CAAC;QAC1C,sEAAsE;QACtE,+DAA+D;QAC/D,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,KAAK,CAAC;QACxE,gFAAgF;QAChF,kBAAkB,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,KAAK,CAAC;KAC7E,CAAC,CAAC;IAEH,iFAAiF;IACjF,oEAAoE;IACpE,OAAO,CAAC,GAAG,CAAC,qCAAqC,WAAW,GAAG,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Process-local rate limiter for use INSIDE services (not as Express middleware).
3
+ *
4
+ * Use case: throttling notifications, internal job dispatch, or any side-effect that
5
+ * isn't a public endpoint and so doesn't fit an HTTP rate-limit middleware. The
6
+ * service calls `.track(key, max, windowMs)` before performing the action and only
7
+ * proceeds if `allowed` is true.
8
+ *
9
+ * Caveats:
10
+ * - Process-local. Multiple Node workers each have their own counters; the global
11
+ * rate equals N × max where N is the number of replicas. Acceptable as a first
12
+ * pass; swap for Redis-backed if scaling horizontally.
13
+ * - No persistence. Counters reset on restart.
14
+ * - Uses sliding window (oldest entries are pruned lazily on each `.track` call).
15
+ */
16
+ export interface TrackResult {
17
+ /** Whether the action is allowed to proceed. */
18
+ allowed: boolean;
19
+ /** Remaining attempts in the current window. */
20
+ remaining: number;
21
+ /** Timestamp (ms epoch) when the oldest attempt in the window will fall out. */
22
+ resetAt: number;
23
+ }
24
+ export declare class InMemoryRateLimiter {
25
+ private buckets;
26
+ /**
27
+ * Record an attempt for `key` and tell the caller whether it is allowed.
28
+ *
29
+ * Sliding-window algorithm: keep timestamps of the last `max` attempts that
30
+ * are still inside `[now - windowMs, now]`. If we already have `max` such
31
+ * attempts, deny; otherwise add the current timestamp and allow.
32
+ */
33
+ track(key: string, max: number, windowMs: number): TrackResult;
34
+ /** Manually drop a key's history. Used after a successful login resets brute-force tracking. */
35
+ reset(key: string): void;
36
+ /** Snapshot for tests/observability. */
37
+ size(): number;
38
+ }
39
+ //# sourceMappingURL=InMemoryRateLimiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryRateLimiter.d.ts","sourceRoot":"","sources":["../../src/middleware/InMemoryRateLimiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,WAAW;IACxB,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,OAAO,CAA+B;IAE9C;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW;IAyB9D,gGAAgG;IAChG,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIxB,wCAAwC;IACxC,IAAI,IAAI,MAAM;CAGjB"}
@@ -0,0 +1,41 @@
1
+ export class InMemoryRateLimiter {
2
+ buckets = new Map();
3
+ /**
4
+ * Record an attempt for `key` and tell the caller whether it is allowed.
5
+ *
6
+ * Sliding-window algorithm: keep timestamps of the last `max` attempts that
7
+ * are still inside `[now - windowMs, now]`. If we already have `max` such
8
+ * attempts, deny; otherwise add the current timestamp and allow.
9
+ */
10
+ track(key, max, windowMs) {
11
+ const now = Date.now();
12
+ const cutoff = now - windowMs;
13
+ const entries = (this.buckets.get(key) ?? []).filter((ts) => ts > cutoff);
14
+ if (entries.length >= max) {
15
+ // Don't append the rejected attempt — otherwise a tight loop would push
16
+ // resetAt forever forwards. We only count actual passes.
17
+ this.buckets.set(key, entries);
18
+ return {
19
+ allowed: false,
20
+ remaining: 0,
21
+ resetAt: entries[0] + windowMs
22
+ };
23
+ }
24
+ entries.push(now);
25
+ this.buckets.set(key, entries);
26
+ return {
27
+ allowed: true,
28
+ remaining: max - entries.length,
29
+ resetAt: entries[0] + windowMs
30
+ };
31
+ }
32
+ /** Manually drop a key's history. Used after a successful login resets brute-force tracking. */
33
+ reset(key) {
34
+ this.buckets.delete(key);
35
+ }
36
+ /** Snapshot for tests/observability. */
37
+ size() {
38
+ return this.buckets.size;
39
+ }
40
+ }
41
+ //# sourceMappingURL=InMemoryRateLimiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryRateLimiter.js","sourceRoot":"","sources":["../../src/middleware/InMemoryRateLimiter.ts"],"names":[],"mappings":"AAwBA,MAAM,OAAO,mBAAmB;IACpB,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE9C;;;;;;OAMG;IACH,KAAK,CAAC,GAAW,EAAE,GAAW,EAAE,QAAgB;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC;QAC9B,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;QAE1E,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACxB,wEAAwE;YACxE,yDAAyD;YACzD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/B,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,CAAC;gBACZ,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;aACjC,CAAC;QACN,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/B,OAAO;YACH,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM;YAC/B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;SACjC,CAAC;IACN,CAAC;IAED,gGAAgG;IAChG,KAAK,CAAC,GAAW;QACb,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,wCAAwC;IACxC,IAAI;QACA,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC7B,CAAC;CACJ"}
@@ -0,0 +1,32 @@
1
+ import type { Request, Response, NextFunction, RequestHandler } from 'express';
2
+ import { type Options as ExpressRateLimitOptions } from 'express-rate-limit';
3
+ export interface CreateRateLimitOptions {
4
+ /** Time window in milliseconds. */
5
+ windowMs: number;
6
+ /** Maximum number of requests allowed within the window. */
7
+ max: number;
8
+ /** Build the rate-limit key from the request. Default: client IP. */
9
+ keyGenerator?: (req: Request) => string;
10
+ /** Skip counting on successful (status < 400) responses. Useful for /login so legit users don't burn their budget. */
11
+ skipSuccessfulRequests?: boolean;
12
+ /** Override the handler called when the limit is exceeded. Default: 429 JSON. */
13
+ handler?: (req: Request, res: Response, next: NextFunction, options: ExpressRateLimitOptions) => void;
14
+ /** Optional message returned in the default 429 body. */
15
+ message?: string;
16
+ /** Standard RateLimit-* headers (RFC). Default: true. */
17
+ standardHeaders?: boolean;
18
+ }
19
+ /**
20
+ * Build an Express rate-limit middleware with sane defaults for AgendaPet-style APIs.
21
+ *
22
+ * Defaults:
23
+ * standardHeaders: true (modern RateLimit-* headers)
24
+ * legacyHeaders: false (no X-RateLimit-*)
25
+ * keyGenerator: req.ip (uses Express's trust-proxy chain)
26
+ *
27
+ * Store: in-memory (process-local). Each Node worker has its own counters; not safe
28
+ * across horizontally-scaled replicas. For multi-instance deployments inject a shared
29
+ * store (e.g. rate-limit-redis) via the underlying express-rate-limit API.
30
+ */
31
+ export declare function createRateLimitMiddleware(options: CreateRateLimitOptions): RequestHandler;
32
+ //# sourceMappingURL=RateLimitMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateLimitMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/RateLimitMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAkB,EAAE,KAAK,OAAO,IAAI,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAExF,MAAM,WAAW,sBAAsB;IACnC,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,sHAAsH;IACtH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iFAAiF;IACjF,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACtG,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,cAAc,CAgBzF"}
@@ -0,0 +1,31 @@
1
+ import rateLimit from 'express-rate-limit';
2
+ /**
3
+ * Build an Express rate-limit middleware with sane defaults for AgendaPet-style APIs.
4
+ *
5
+ * Defaults:
6
+ * standardHeaders: true (modern RateLimit-* headers)
7
+ * legacyHeaders: false (no X-RateLimit-*)
8
+ * keyGenerator: req.ip (uses Express's trust-proxy chain)
9
+ *
10
+ * Store: in-memory (process-local). Each Node worker has its own counters; not safe
11
+ * across horizontally-scaled replicas. For multi-instance deployments inject a shared
12
+ * store (e.g. rate-limit-redis) via the underlying express-rate-limit API.
13
+ */
14
+ export function createRateLimitMiddleware(options) {
15
+ return rateLimit({
16
+ windowMs: options.windowMs,
17
+ max: options.max,
18
+ standardHeaders: options.standardHeaders ?? true,
19
+ legacyHeaders: false,
20
+ keyGenerator: options.keyGenerator ?? ((req) => req.ip ?? 'unknown'),
21
+ skipSuccessfulRequests: options.skipSuccessfulRequests ?? false,
22
+ handler: options.handler ?? ((req, res) => {
23
+ res.status(429).json({
24
+ success: false,
25
+ message: options.message ?? 'Demasiados pedidos. Aguarde alguns minutos.',
26
+ code: 'RATE_LIMITED'
27
+ });
28
+ })
29
+ });
30
+ }
31
+ //# sourceMappingURL=RateLimitMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateLimitMiddleware.js","sourceRoot":"","sources":["../../src/middleware/RateLimitMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,SAAsD,MAAM,oBAAoB,CAAC;AAmBxF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA+B;IACrE,OAAO,SAAS,CAAC;QACb,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,IAAI;QAChD,aAAa,EAAE,KAAK;QACpB,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;QAC7E,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,IAAI,KAAK;QAC/D,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACtC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACjB,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,6CAA6C;gBACzE,IAAI,EAAE,cAAc;aACvB,CAAC,CAAC;QACP,CAAC,CAAC;KACL,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { RequestHandler } from 'express';
2
+ export interface CreateTurnstileOptions {
3
+ /** Cloudflare Turnstile secret key. If missing or empty, middleware short-circuits to next() (no-op). */
4
+ secretKey: string | undefined;
5
+ /** Body field where the client puts the Turnstile token. Default: 'turnstile_token'. */
6
+ tokenField?: string;
7
+ /** Verify endpoint (override for testing). */
8
+ verifyUrl?: string;
9
+ }
10
+ /**
11
+ * Express middleware that validates a Cloudflare Turnstile token submitted with the
12
+ * request body. On valid token: next(). On missing or invalid token: 400.
13
+ *
14
+ * Designed to be safe when not yet configured: if secretKey is undefined or empty,
15
+ * the middleware is a no-op (next()). This lets the same code run in dev/staging
16
+ * without Cloudflare credentials, and lets ops flip Turnstile on in production by
17
+ * just setting the env var.
18
+ *
19
+ * The IP of the caller is forwarded to Cloudflare so they can apply geographic /
20
+ * behavioural rules. We use req.ip which respects Express's trust-proxy setting.
21
+ *
22
+ * Docs: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
23
+ */
24
+ export declare function createTurnstileMiddleware(options: CreateTurnstileOptions): RequestHandler;
25
+ //# sourceMappingURL=TurnstileMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TurnstileMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/TurnstileMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,MAAM,WAAW,sBAAsB;IACnC,yGAAyG;IACzG,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,cAAc,CAkFzF"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Express middleware that validates a Cloudflare Turnstile token submitted with the
3
+ * request body. On valid token: next(). On missing or invalid token: 400.
4
+ *
5
+ * Designed to be safe when not yet configured: if secretKey is undefined or empty,
6
+ * the middleware is a no-op (next()). This lets the same code run in dev/staging
7
+ * without Cloudflare credentials, and lets ops flip Turnstile on in production by
8
+ * just setting the env var.
9
+ *
10
+ * The IP of the caller is forwarded to Cloudflare so they can apply geographic /
11
+ * behavioural rules. We use req.ip which respects Express's trust-proxy setting.
12
+ *
13
+ * Docs: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
14
+ */
15
+ export function createTurnstileMiddleware(options) {
16
+ const verifyUrl = options.verifyUrl ?? 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
17
+ const tokenField = options.tokenField ?? 'turnstile_token';
18
+ return async (req, res, next) => {
19
+ const secret = options.secretKey;
20
+ if (!secret) {
21
+ // Not configured — skip silently. Safe default for dev/staging.
22
+ return next();
23
+ }
24
+ const token = req.body?.[tokenField];
25
+ if (!token || typeof token !== 'string') {
26
+ // Most common production cause: the frontend never sent a token, usually
27
+ // because the matching PUBLIC site key is not configured on the client.
28
+ console.warn('[turnstile] request blocked: token missing', {
29
+ path: req.path,
30
+ ip: req.ip,
31
+ tokenField,
32
+ tokenType: typeof token
33
+ });
34
+ res.status(400).json({
35
+ success: false,
36
+ code: 'CAPTCHA_REQUIRED',
37
+ message: 'Validação anti-bot em falta.'
38
+ });
39
+ return;
40
+ }
41
+ try {
42
+ const params = new URLSearchParams();
43
+ params.set('secret', secret);
44
+ params.set('response', token);
45
+ if (req.ip) {
46
+ params.set('remoteip', req.ip);
47
+ }
48
+ const response = await fetch(verifyUrl, {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
51
+ body: params
52
+ });
53
+ const result = (await response.json());
54
+ if (!result.success) {
55
+ // Log the full Cloudflare verdict so misconfigurations are diagnosable
56
+ // from the backend logs. Common error-codes:
57
+ // invalid-input-secret -> wrong/missing TURNSTILE_SECRET_KEY
58
+ // invalid-input-response-> token bad, expired or already used
59
+ // timeout-or-duplicate -> token replayed
60
+ // A hostname that doesn't match the deployed domain means the site key
61
+ // belongs to a Turnstile widget configured for a different domain.
62
+ console.warn('[turnstile] verification rejected by Cloudflare', {
63
+ path: req.path,
64
+ ip: req.ip,
65
+ errorCodes: result['error-codes'] ?? [],
66
+ hostname: result.hostname,
67
+ action: result.action
68
+ });
69
+ res.status(400).json({
70
+ success: false,
71
+ code: 'CAPTCHA_INVALID',
72
+ message: 'Validação anti-bot falhou. Tente novamente.'
73
+ });
74
+ return;
75
+ }
76
+ return next();
77
+ }
78
+ catch (error) {
79
+ // Don't block legit users if Cloudflare is unreachable. Log and pass through;
80
+ // the deeper rate-limit and honeypot defences still apply.
81
+ // Caller can adjust to fail-closed in higher-risk endpoints.
82
+ console.error('[turnstile] verify call failed, falling through to allow:', error);
83
+ return next();
84
+ }
85
+ };
86
+ }
87
+ //# sourceMappingURL=TurnstileMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TurnstileMiddleware.js","sourceRoot":"","sources":["../../src/middleware/TurnstileMiddleware.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundbrick/express-adapter",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Express.js integration adapter with middleware, controllers, and error handling",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -10,6 +10,10 @@
10
10
  "import": "./dist/index.js",
11
11
  "require": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts"
13
+ },
14
+ "./instrument": {
15
+ "import": "./dist/instrument.js",
16
+ "types": "./dist/instrument.d.ts"
13
17
  }
14
18
  },
15
19
  "files": [
@@ -45,7 +49,10 @@
45
49
  "@groundbrick/db-core": "^0.2.0",
46
50
  "@groundbrick/logger": "^0.4.0",
47
51
  "@groundbrick/service-base": "^0.2.0",
48
- "@types/ms": "^2.1.0"
52
+ "@sentry/node": "^10.58.0",
53
+ "@sentry/profiling-node": "^10.58.0",
54
+ "@types/ms": "^2.1.0",
55
+ "dotenv": "^16.6.1"
49
56
  },
50
57
  "peerDependencies": {
51
58
  "compression": "^1.7.4",