@miguelmorales13/nestkit 0.1.1 → 0.3.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 (57) hide show
  1. package/README.md +305 -1
  2. package/dist/auth/auth-user.entity.d.ts +8 -0
  3. package/dist/auth/auth-user.port.d.ts +16 -0
  4. package/dist/auth/auth.controller.d.ts +25 -0
  5. package/dist/auth/auth.dto.d.ts +13 -0
  6. package/dist/auth/auth.service.d.ts +24 -0
  7. package/dist/auth/authenticated-user.d.ts +7 -0
  8. package/dist/auth/current-tenant.decorator.d.ts +2 -0
  9. package/dist/auth/current-user.decorator.d.ts +1 -0
  10. package/dist/auth/hash.util.d.ts +2 -0
  11. package/dist/auth/index.cjs +265 -0
  12. package/dist/auth/index.d.ts +16 -0
  13. package/dist/auth/index.js +265 -0
  14. package/dist/auth/jwt-auth.guard.d.ts +5 -0
  15. package/dist/auth/require-tenant.guard.d.ts +9 -0
  16. package/dist/auth/roles.decorator.d.ts +3 -0
  17. package/dist/auth/roles.guard.d.ts +11 -0
  18. package/dist/auth/token.service.d.ts +7 -0
  19. package/dist/bootstrap/index.cjs +4 -3
  20. package/dist/bootstrap/index.js +3 -2
  21. package/dist/{chunk-4IDLJMQA.cjs → chunk-7FQQDWOZ.cjs} +6 -14
  22. package/dist/chunk-7SOM7EZP.cjs +1 -0
  23. package/dist/chunk-DQYAIQQ5.js +0 -0
  24. package/dist/{chunk-AR3EKJBR.cjs → chunk-FDNGAYTZ.cjs} +6 -6
  25. package/dist/{chunk-AUA4X3RE.cjs → chunk-O2ZMI3EK.cjs} +2 -2
  26. package/dist/{chunk-HGJX2GPE.js → chunk-ORWJ7LES.js} +1 -1
  27. package/dist/{chunk-EQXYK6AL.js → chunk-Q45IEPWU.js} +5 -13
  28. package/dist/chunk-R7BVS6CI.cjs +13 -0
  29. package/dist/{chunk-VPS5CLHT.js → chunk-RHNQGTAT.js} +1 -1
  30. package/dist/chunk-YFYHLYHN.js +13 -0
  31. package/dist/email/nodemailer/index.cjs +40 -0
  32. package/dist/email/nodemailer/index.d.ts +1 -0
  33. package/dist/email/nodemailer/index.js +40 -0
  34. package/dist/email/nodemailer/nodemailer.module.d.ts +4 -0
  35. package/dist/email/resend/index.cjs +31 -0
  36. package/dist/email/resend/index.d.ts +1 -0
  37. package/dist/email/resend/index.js +31 -0
  38. package/dist/email/resend/resend.module.d.ts +4 -0
  39. package/dist/errors/index.cjs +7 -4
  40. package/dist/errors/index.js +6 -3
  41. package/dist/index.cjs +17 -14
  42. package/dist/index.js +24 -21
  43. package/dist/stripe/index.cjs +78 -0
  44. package/dist/stripe/index.d.ts +3 -0
  45. package/dist/stripe/index.js +78 -0
  46. package/dist/stripe/stripe-webhook.controller.d.ts +20 -0
  47. package/dist/stripe/stripe.module.d.ts +4 -0
  48. package/dist/telegram/index.cjs +31 -0
  49. package/dist/telegram/index.d.ts +1 -0
  50. package/dist/telegram/index.js +31 -0
  51. package/dist/telegram/telegram.module.d.ts +8 -0
  52. package/dist/whatsapp/index.cjs +68 -0
  53. package/dist/whatsapp/index.d.ts +3 -0
  54. package/dist/whatsapp/index.js +68 -0
  55. package/dist/whatsapp/whatsapp-client.d.ts +21 -0
  56. package/dist/whatsapp/whatsapp.module.d.ts +4 -0
  57. package/package.json +74 -10
package/README.md CHANGED
@@ -387,6 +387,304 @@ El idioma se resuelve por el `Accept-Language` del request (comportamiento por d
387
387
  regresa el `fallback`, así una traducción faltante no se convierte en un error 500 encima del error
388
388
  original.
389
389
 
390
+ ### `telegram` — cliente de salida para un bot específico
391
+
392
+ ```ts
393
+ import { Module } from '@nestjs/common';
394
+ import { TelegramModule } from '@miguelmorales13/nestkit/telegram';
395
+
396
+ @Module({ imports: [TelegramModule] })
397
+ export class AppModule {}
398
+ ```
399
+
400
+ ```ts
401
+ import { Inject, Injectable } from '@nestjs/common';
402
+ import { TELEGRAM_BOT } from '@miguelmorales13/nestkit/telegram';
403
+ import type { Telegram as TelegramClient } from 'telegraf';
404
+
405
+ @Injectable()
406
+ export class NotificationsService {
407
+ constructor(@Inject(TELEGRAM_BOT) private readonly bot: TelegramClient) {}
408
+
409
+ async notify(chatId: number | string, text: string) {
410
+ await this.bot.sendMessage(chatId, text);
411
+ }
412
+ }
413
+ ```
414
+
415
+ Requiere `TELEGRAM_BOT_TOKEN` en el entorno — falla explícito al arrancar si falta, igual que
416
+ `PgModule`/`SupabaseModule`. `TelegramModule` solo expone el cliente de **salida** (`Telegram`, la
417
+ clase de `telegraf` para llamar directo a la Bot API): no hay polling, no hay webhook, no hay manejo
418
+ de updates entrantes — es a propósito, este paquete no enruta mensajes recibidos. Importa el tipo del
419
+ cliente como `Telegram as TelegramClient` si tipas tu propia inyección — `telegraf` exporta una clase
420
+ llamada `Telegram`, que choca de nombre con el concepto genérico "Telegram" en tu propio código.
421
+
422
+ ### `whatsapp` — cliente de salida sobre la Cloud API de Meta
423
+
424
+ ```ts
425
+ import { Module } from '@nestjs/common';
426
+ import { WhatsAppModule } from '@miguelmorales13/nestkit/whatsapp';
427
+
428
+ @Module({ imports: [WhatsAppModule] })
429
+ export class AppModule {}
430
+ ```
431
+
432
+ ```ts
433
+ import { Inject, Injectable } from '@nestjs/common';
434
+ import { WHATSAPP_CLIENT, WhatsAppClient } from '@miguelmorales13/nestkit/whatsapp';
435
+
436
+ @Injectable()
437
+ export class NotificationsService {
438
+ constructor(@Inject(WHATSAPP_CLIENT) private readonly whatsapp: WhatsAppClient) {}
439
+
440
+ async notify(to: string, body: string) {
441
+ await this.whatsapp.sendTextMessage(to, body);
442
+ }
443
+ }
444
+ ```
445
+
446
+ Requiere `WHATSAPP_ACCESS_TOKEN` y `WHATSAPP_PHONE_NUMBER_ID`; `WHATSAPP_API_VERSION` es opcional
447
+ (default `v21.0`). No hay dependencia npm de por medio — Meta no publica un SDK oficial de Node
448
+ razonable para la Cloud API, que de todos modos es un REST plano, así que `WhatsAppClient` usa el
449
+ `fetch` global de Node (18+) directo. Solo manda mensajes de texto (`sendTextMessage`) — sin
450
+ templates, sin media, sin manejo de mensajes entrantes.
451
+
452
+ ### `stripe` — cliente de la SDK oficial
453
+
454
+ ```ts
455
+ import { Module } from '@nestjs/common';
456
+ import { StripeModule } from '@miguelmorales13/nestkit/stripe';
457
+
458
+ @Module({ imports: [StripeModule] })
459
+ export class AppModule {}
460
+ ```
461
+
462
+ ```ts
463
+ import { Inject, Injectable } from '@nestjs/common';
464
+ import { STRIPE_CLIENT } from '@miguelmorales13/nestkit/stripe';
465
+ import type Stripe from 'stripe';
466
+
467
+ @Injectable()
468
+ export class BillingService {
469
+ constructor(@Inject(STRIPE_CLIENT) private readonly stripe: Stripe) {}
470
+
471
+ async createCustomer(email: string) {
472
+ return this.stripe.customers.create({ email });
473
+ }
474
+ }
475
+ ```
476
+
477
+ Requiere `STRIPE_SECRET_KEY`. `STRIPE_API_VERSION` es opcional — si no la fijas, la SDK usa su propia
478
+ versión pineada por default (no la fuerces a mano salvo que sepas exactamente por qué). Como
479
+ Postgres/Supabase, solo expone el cliente crudo por DI — no envuelve `Stripe` en tipos propios de
480
+ `nestkit`, así que tipa tu inyección directo contra `Stripe` de la librería `stripe`.
481
+
482
+ #### Webhooks de Stripe
483
+
484
+ > **Requisito obligatorio**: tu `main.ts` tiene que crear la app con
485
+ > `NestFactory.create(AppModule, { rawBody: true })`. La verificación de firma de Stripe necesita los
486
+ > bytes crudos del request tal cual llegaron — `applyNestKitDefaults` corre **después** de crear la
487
+ > app y no puede habilitar `rawBody` en retrospectiva. Sin esto, el controller lanza un error
488
+ > explícito en cuanto le llega un webhook, en vez de fallar silencioso.
489
+
490
+ ```ts
491
+ // main.ts
492
+ const app = await NestFactory.create(AppModule, { rawBody: true });
493
+ ```
494
+
495
+ ```ts
496
+ import { Module } from '@nestjs/common';
497
+ import { createStripeWebhookController } from '@miguelmorales13/nestkit/stripe';
498
+
499
+ class StripeWebhookController extends createStripeWebhookController({
500
+ handlers: {
501
+ 'checkout.session.completed': async (event) => {
502
+ // event.data.object es el Checkout Session
503
+ },
504
+ 'customer.subscription.updated': async (event) => {
505
+ // actualiza el plan del tenant en tu propia DB
506
+ },
507
+ },
508
+ }) {}
509
+
510
+ @Module({ controllers: [StripeWebhookController] })
511
+ export class BillingModule {}
512
+ ```
513
+
514
+ Requiere `STRIPE_WEBHOOK_SECRET` además de `STRIPE_SECRET_KEY`. Los tipos de evento sin handler
515
+ registrado se acusan recibo (`{ received: true }`) y se ignoran — Stripe espera un 200 rápido,
516
+ no que manejes cada tipo de evento que te manda.
517
+
518
+ ### `auth` — flujo completo de registro/login, para empresa o para persona sola
519
+
520
+ Mismo espíritu que `crud`: `nestkit` define el puerto (`AuthUserPort`) y la orquestación
521
+ (`BaseAuthService`, `createAuthController`), vos traés tu propia entidad/persistencia. A diferencia
522
+ de los demás módulos, **no hay `Module` que importar** — son clases y guards planos que se usan
523
+ directo, sin registro en el contenedor de DI.
524
+
525
+ ```ts
526
+ // user.repository.ts
527
+ import { Injectable } from '@nestjs/common';
528
+ import type { AuthUser, AuthUserPort } from '@miguelmorales13/nestkit/auth';
529
+
530
+ interface User extends AuthUser {}
531
+
532
+ @Injectable()
533
+ export class UserRepository implements AuthUserPort<User> {
534
+ async findByEmail(email: string): Promise<User | null> { /* ... */ return null; }
535
+ async findById(id: string): Promise<User | null> { /* ... */ return null; }
536
+ async create(data: { email: string; passwordHash: string; tenantId?: string; roles: string[] }): Promise<User> {
537
+ /* insertar y devolver el user creado */
538
+ throw new Error('not implemented');
539
+ }
540
+ }
541
+ ```
542
+
543
+ ```ts
544
+ // auth.module.ts
545
+ import { Module } from '@nestjs/common';
546
+ import { BaseAuthService, createAuthController } from '@miguelmorales13/nestkit/auth';
547
+ import { UserRepository } from './user.repository';
548
+
549
+ const AUTH_SERVICE = 'AUTH_SERVICE';
550
+
551
+ class AuthController extends createAuthController(AUTH_SERVICE as any) {}
552
+
553
+ @Module({
554
+ controllers: [AuthController],
555
+ providers: [
556
+ UserRepository,
557
+ { provide: AUTH_SERVICE, useFactory: (users: UserRepository) => new BaseAuthService(users), inject: [UserRepository] },
558
+ ],
559
+ })
560
+ export class AuthModule {}
561
+ ```
562
+
563
+ > En la práctica, igual que con `crud`, usa una subclase concreta de `BaseAuthService` como
564
+ > provider/token para que TypeScript infiera los tipos sin castear.
565
+
566
+ Esto ya te da `POST /auth/register`, `POST /auth/login`, `POST /auth/refresh` funcionando. Para que
567
+ sirva **tanto para una empresa como para una persona que se registra sola**, `tenantId` es opcional
568
+ en todo el flujo: mandalo en el body de `register` si el usuario pertenece a una empresa, omitilo
569
+ para una cuenta individual — queda como `undefined` en el JWT y en la entidad, nada más se rompe.
570
+
571
+ **Proteger rutas** — el orden de los guards importa, `JwtAuthGuard` tiene que ir primero (es el que
572
+ puebla `request.user`):
573
+
574
+ ```ts
575
+ import { Controller, Get, UseGuards } from '@nestjs/common';
576
+ import {
577
+ JwtAuthGuard,
578
+ RolesGuard,
579
+ Roles,
580
+ RequireTenantGuard,
581
+ CurrentUser,
582
+ CurrentTenant,
583
+ type AuthenticatedUser,
584
+ } from '@miguelmorales13/nestkit/auth';
585
+
586
+ @Controller('widgets')
587
+ @UseGuards(JwtAuthGuard)
588
+ export class WidgetsController {
589
+ @Get('me')
590
+ me(@CurrentUser() user: AuthenticatedUser) {
591
+ return user;
592
+ }
593
+
594
+ @Get('admin-only')
595
+ @UseGuards(RolesGuard)
596
+ @Roles('admin')
597
+ adminOnly() { /* ... */ }
598
+
599
+ @Get('company-only')
600
+ @UseGuards(RequireTenantGuard)
601
+ companyOnly(@CurrentTenant() tenantId: string) { /* tenantId nunca es undefined acá */ }
602
+ }
603
+ ```
604
+
605
+ `RequireTenantGuard` es el opt-in para endpoints que **sí** deben ser exclusivos de cuentas de
606
+ empresa — sin él, cualquier ruta protegida por `JwtAuthGuard` funciona igual de bien para una cuenta
607
+ individual (`tenantId: undefined`) que para una de empresa.
608
+
609
+ **Integración con `crud`** — `tenantIdExtractor` de `createCrudController` lee directo del JWT que
610
+ `JwtAuthGuard` ya decodificó:
611
+
612
+ ```ts
613
+ class WidgetsController extends createCrudController(WIDGETS_SERVICE as any, {
614
+ path: 'widgets',
615
+ tenantIdExtractor: (req) => (req as { user?: AuthenticatedUser }).user?.tenantId,
616
+ }) {}
617
+ ```
618
+
619
+ Requiere `JWT_ACCESS_SECRET` y `JWT_REFRESH_SECRET`; `JWT_ACCESS_EXPIRES_IN` (default `15m`) y
620
+ `JWT_REFRESH_EXPIRES_IN` (default `30d`) son opcionales. Los passwords se hashean con `bcryptjs`
621
+ (oculto — no es peer dependency, no lo importás vos); los tokens usan `jsonwebtoken` por debajo,
622
+ también oculto. `login()` devuelve el mismo mensaje de error para "no existe el usuario" y
623
+ "contraseña incorrecta" — evita que alguien pueda usar el endpoint para enumerar emails registrados.
624
+
625
+ `register()` siempre asigna `roles: ['user']` — no hay forma de pasar otro rol inicial desde el
626
+ body (a propósito: dejarlo abierto desde el request sería que cualquiera se autoasigne `admin`). Si
627
+ necesitás otra lógica de roles al alta (ej. el primer usuario de un tenant nuevo es `owner`), extendé
628
+ `BaseAuthService` y sobreescribí `register()`, o llamá `users.create()` directo con los roles que
629
+ quieras desde tu propio controller.
630
+
631
+ ### `email/resend` y `email/nodemailer` — dos transportes, elegís uno por proyecto
632
+
633
+ Mismo patrón que `database/postgres` vs `database/supabase`: dos módulos independientes, no una
634
+ abstracción unificada — importás el que le convenga al proyecto.
635
+
636
+ ```ts
637
+ import { Module } from '@nestjs/common';
638
+ import { ResendModule } from '@miguelmorales13/nestkit/email/resend';
639
+
640
+ @Module({ imports: [ResendModule] })
641
+ export class AppModule {}
642
+ ```
643
+
644
+ ```ts
645
+ import { Inject, Injectable } from '@nestjs/common';
646
+ import { RESEND_CLIENT } from '@miguelmorales13/nestkit/email/resend';
647
+ import type { Resend } from 'resend';
648
+
649
+ @Injectable()
650
+ export class NotificationsService {
651
+ constructor(@Inject(RESEND_CLIENT) private readonly resend: Resend) {}
652
+
653
+ async notify(to: string, subject: string, html: string) {
654
+ await this.resend.emails.send({ from: 'noreply@tudominio.com', to, subject, html });
655
+ }
656
+ }
657
+ ```
658
+
659
+ Requiere `RESEND_API_KEY`. Elegí Resend para proyectos nuevos: una sola API key, SDK oficial liviano.
660
+
661
+ ```ts
662
+ import { Module } from '@nestjs/common';
663
+ import { NodemailerModule } from '@miguelmorales13/nestkit/email/nodemailer';
664
+
665
+ @Module({ imports: [NodemailerModule] })
666
+ export class AppModule {}
667
+ ```
668
+
669
+ ```ts
670
+ import { Inject, Injectable } from '@nestjs/common';
671
+ import { NODEMAILER_TRANSPORT } from '@miguelmorales13/nestkit/email/nodemailer';
672
+ import type { Transporter } from 'nodemailer';
673
+
674
+ @Injectable()
675
+ export class NotificationsService {
676
+ constructor(@Inject(NODEMAILER_TRANSPORT) private readonly mailer: Transporter) {}
677
+
678
+ async notify(to: string, subject: string, html: string) {
679
+ await this.mailer.sendMail({ from: 'noreply@tudominio.com', to, subject, html });
680
+ }
681
+ }
682
+ ```
683
+
684
+ Requiere `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`; `SMTP_SECURE` es opcional (`'true'`
685
+ para forzar TLS directo). Elegí Nodemailer cuando ya tenés un proveedor SMTP propio (Gmail, SES,
686
+ Mailgun, tu propio servidor) en vez de sumar otra cuenta de terceros.
687
+
390
688
  ## Subpaths disponibles
391
689
 
392
690
  | Subpath | Qué trae |
@@ -400,12 +698,18 @@ original.
400
698
  | `@miguelmorales13/nestkit/database/postgres` | `PgModule`, `PG_POOL`, `withTenantScope` |
401
699
  | `@miguelmorales13/nestkit/database/supabase` | `SupabaseModule`, `SUPABASE_ANON_CLIENT`, `SUPABASE_SERVICE_ROLE_CLIENT` |
402
700
  | `@miguelmorales13/nestkit/database/mongo` | Solo el contrato `MongoRepositoryPort` — sin implementación |
701
+ | `@miguelmorales13/nestkit/telegram` | `TelegramModule`, `TELEGRAM_BOT` |
702
+ | `@miguelmorales13/nestkit/whatsapp` | `WhatsAppModule`, `WHATSAPP_CLIENT`, `WhatsAppClient` |
703
+ | `@miguelmorales13/nestkit/stripe` | `StripeModule`, `STRIPE_CLIENT`, `createStripeWebhookController` |
704
+ | `@miguelmorales13/nestkit/auth` | `AuthUserPort`, `BaseAuthService`, `createAuthController`, `TokenService`, `JwtAuthGuard`, `RolesGuard`, `RequireTenantGuard`, `CurrentUser`, `CurrentTenant`, `Roles` |
705
+ | `@miguelmorales13/nestkit/email/resend` | `ResendModule`, `RESEND_CLIENT` |
706
+ | `@miguelmorales13/nestkit/email/nodemailer` | `NodemailerModule`, `NODEMAILER_TRANSPORT` |
403
707
  | `@miguelmorales13/nestkit/i18n` | `I18nModule` (wrapper de `nestjs-i18n`), `translateOr` |
404
708
  | `@miguelmorales13/nestkit/bootstrap` | `applyNestKitDefaults` |
405
709
 
406
710
  ## Estado del paquete
407
711
 
408
- `0.1.0`, no publicado en npm todavía (repo privado, se prueba localmente con `bun link` antes de
712
+ `0.3.0`, no publicado en npm todavía (repo privado, se prueba localmente con `bun link` antes de
409
713
  publicar). Sin adaptador Mongo real. Sin tests unitarios propios — es un paquete nuevo sin
410
714
  consumidor productivo todavía; la primera integración real (ej. en `hr-pymes-saas`) es la que va a
411
715
  ejercitar el código de verdad.
@@ -0,0 +1,8 @@
1
+ import type { BaseEntity } from '../entities/base.entity.js';
2
+ export interface AuthUser extends BaseEntity {
3
+ email: string;
4
+ passwordHash: string;
5
+ /** Absent for an individual/solo account — present for a company-owned user. */
6
+ tenantId?: string;
7
+ roles: string[];
8
+ }
@@ -0,0 +1,16 @@
1
+ import type { AuthUser } from './auth-user.entity.js';
2
+ /**
3
+ * Persistence port for the Auth module — parallel to crud's Repository<T>.
4
+ * The consumer implements this against their own storage; nestkit only
5
+ * orchestrates hashing/token issuance on top of it.
6
+ */
7
+ export interface AuthUserPort<U extends AuthUser = AuthUser> {
8
+ findByEmail(email: string): Promise<U | null>;
9
+ findById(id: string): Promise<U | null>;
10
+ create(data: {
11
+ email: string;
12
+ passwordHash: string;
13
+ tenantId?: string;
14
+ roles: string[];
15
+ }): Promise<U>;
16
+ }
@@ -0,0 +1,25 @@
1
+ import { type Type } from '@nestjs/common';
2
+ import { LoginDto, RefreshDto, RegisterDto } from './auth.dto.js';
3
+ import type { AuthUser } from './auth-user.entity.js';
4
+ import type { AuthTokens, BaseAuthService } from './auth.service.js';
5
+ export interface CreateAuthControllerOptions {
6
+ /** Route prefix for the generated controller. Defaults to 'auth'. */
7
+ path?: string;
8
+ }
9
+ /**
10
+ * Mixin factory (function returning a class) that wires up register/login/
11
+ * refresh to a BaseAuthService — composition, not forced inheritance, same
12
+ * shape as crud's createCrudController.
13
+ *
14
+ * Usage:
15
+ * class AuthController extends createAuthController(AUTH_SERVICE as any) {}
16
+ */
17
+ export declare function createAuthController<U extends AuthUser>(serviceToken: Type<BaseAuthService<U>>, options?: CreateAuthControllerOptions): Type<{
18
+ register(body: RegisterDto): Promise<{
19
+ user: U;
20
+ } & AuthTokens>;
21
+ login(body: LoginDto): Promise<{
22
+ user: U;
23
+ } & AuthTokens>;
24
+ refresh(body: RefreshDto): Promise<AuthTokens>;
25
+ }>;
@@ -0,0 +1,13 @@
1
+ export declare class RegisterDto {
2
+ email: string;
3
+ password: string;
4
+ /** Omit for an individual/solo signup — provide for a company-owned user. */
5
+ tenantId?: string;
6
+ }
7
+ export declare class LoginDto {
8
+ email: string;
9
+ password: string;
10
+ }
11
+ export declare class RefreshDto {
12
+ refreshToken: string;
13
+ }
@@ -0,0 +1,24 @@
1
+ import { TokenService } from './token.service.js';
2
+ import type { AuthUser } from './auth-user.entity.js';
3
+ import type { AuthUserPort } from './auth-user.port.js';
4
+ export interface AuthTokens {
5
+ accessToken: string;
6
+ refreshToken: string;
7
+ }
8
+ export declare class BaseAuthService<U extends AuthUser> {
9
+ protected readonly users: AuthUserPort<U>;
10
+ protected readonly tokens: TokenService;
11
+ constructor(users: AuthUserPort<U>, tokens?: TokenService);
12
+ register(data: {
13
+ email: string;
14
+ password: string;
15
+ tenantId?: string;
16
+ }): Promise<{
17
+ user: U;
18
+ } & AuthTokens>;
19
+ login(email: string, password: string): Promise<{
20
+ user: U;
21
+ } & AuthTokens>;
22
+ refresh(refreshToken: string): Promise<AuthTokens>;
23
+ private issueTokens;
24
+ }
@@ -0,0 +1,7 @@
1
+ /** Decoded JWT payload shape, attached to `request.user` by JwtAuthGuard. */
2
+ export interface AuthenticatedUser {
3
+ sub: string;
4
+ email: string;
5
+ tenantId?: string;
6
+ roles: string[];
7
+ }
@@ -0,0 +1,2 @@
1
+ /** Returns `undefined` for an individual/solo account — no tenantId to read. */
2
+ export declare const CurrentTenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
@@ -0,0 +1 @@
1
+ export declare const CurrentUser: (...dataOrPipes: unknown[]) => ParameterDecorator;
@@ -0,0 +1,2 @@
1
+ export declare function hashPassword(plain: string): Promise<string>;
2
+ export declare function verifyPassword(plain: string, passwordHash: string): Promise<boolean>;