@miguelmorales13/nestkit 0.4.1 → 0.5.1
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 +42 -4
- package/dist/index.cjs +11 -11
- package/dist/index.js +13 -13
- package/dist/storage/adapters/bunny.storage.d.ts +34 -0
- package/dist/storage/adapters/local.storage.d.ts +31 -0
- package/dist/storage/adapters/s3.storage.d.ts +37 -0
- package/dist/storage/index.cjs +528 -0
- package/dist/storage/index.d.ts +8 -0
- package/dist/storage/index.js +528 -0
- package/dist/storage/storage.module.d.ts +47 -0
- package/dist/storage/storage.options.d.ts +79 -0
- package/dist/storage/storage.port.d.ts +136 -0
- package/package.json +26 -2
package/README.md
CHANGED
|
@@ -685,6 +685,45 @@ Requiere `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`; `SMTP_SECURE` e
|
|
|
685
685
|
para forzar TLS directo). Elegí Nodemailer cuando ya tenés un proveedor SMTP propio (Gmail, SES,
|
|
686
686
|
Mailgun, tu propio servidor) en vez de sumar otra cuenta de terceros.
|
|
687
687
|
|
|
688
|
+
### `storage` — subida de archivos agnóstica del proveedor
|
|
689
|
+
|
|
690
|
+
Almacenamiento de archivos con un puerto único (`StoragePort`) y varios adaptadores intercambiables:
|
|
691
|
+
**local** (disco, para desarrollo), **S3-compatible** (AWS S3 y **Cloudflare R2**) y **Bunny.net**.
|
|
692
|
+
Sirve cualquier tipo de archivo — imágenes, PDF y otros documentos, video, audio — sin suponer nada
|
|
693
|
+
del contenido. Cambiar de R2 a Bunny (o al revés) es **una línea** en `StorageModule.forRoot(...)`:
|
|
694
|
+
nada depende de un proveedor concreto, todo depende del puerto.
|
|
695
|
+
|
|
696
|
+
Trae streaming en ambos sentidos (`putStream` para subir grandes/video sin cargarlos en memoria,
|
|
697
|
+
`getStream` con **rango de bytes** para el seek de video/audio), subida **directa firmada** al
|
|
698
|
+
proveedor (`signedUploadUrl`, para que un archivo pesado no pase por tu API), URLs de lectura
|
|
699
|
+
firmadas (`signedReadUrl`), y `stat`/`exists`/`list`/`delete`.
|
|
700
|
+
|
|
701
|
+
```ts
|
|
702
|
+
// En el módulo raíz — elegí el proveedor acá y nada más cambia:
|
|
703
|
+
StorageModule.forRoot({ provider: 'r2' }) // lee R2_* del entorno
|
|
704
|
+
StorageModule.forRoot({ provider: 'bunny' }) // lee BUNNY_* del entorno
|
|
705
|
+
StorageModule.forRoot({ provider: 'local', local: { root: '.storage' } })
|
|
706
|
+
StorageModule.forRootAsync({ inject: [Config], useFactory: (c) => ({ provider: c.storageProvider }) })
|
|
707
|
+
|
|
708
|
+
// En cualquier servicio:
|
|
709
|
+
constructor(@Inject(STORAGE) private readonly storage: StoragePort) {}
|
|
710
|
+
|
|
711
|
+
await this.storage.put(`${tenantId}/platillos/${id}.webp`, buffer, { contentType: 'image/webp' });
|
|
712
|
+
const { stream, totalSize, contentRange } = await this.storage.getStream(key, { start, end }); // video
|
|
713
|
+
const target = await this.storage.signedUploadUrl(key, { contentType: 'video/mp4' }); // subida directa
|
|
714
|
+
const url = this.storage.publicUrl(key) ?? (await this.storage.signedReadUrl(key));
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
Los adaptadores S3/R2 cargan `@aws-sdk/client-s3` **de forma diferida** (solo si se usa ese
|
|
718
|
+
proveedor), así que un proyecto con Bunny o local no necesita el SDK. Peer deps opcionales:
|
|
719
|
+
`@aws-sdk/client-s3`, `@aws-sdk/s3-request-presigner`, `@aws-sdk/lib-storage` (solo para S3/R2);
|
|
720
|
+
Bunny y local no requieren nada extra. Variables de entorno reconocidas: `STORAGE_PROVIDER`,
|
|
721
|
+
`STORAGE_LOCAL_ROOT`/`STORAGE_PUBLIC_BASE_URL`, `S3_*`, `R2_*`, `BUNNY_*` (ver `resolveStorageOptions`).
|
|
722
|
+
|
|
723
|
+
`signedUploadUrl` no existe en el adaptador local (un cliente no puede subir a tu disco) ni en Bunny
|
|
724
|
+
Storage (no hay firma que oculte la contraseña de la zona); en esos casos se sube por la API, o se
|
|
725
|
+
usa Bunny Stream/TUS para video grande.
|
|
726
|
+
|
|
688
727
|
## Subpaths disponibles
|
|
689
728
|
|
|
690
729
|
| Subpath | Qué trae |
|
|
@@ -702,6 +741,7 @@ Mailgun, tu propio servidor) en vez de sumar otra cuenta de terceros.
|
|
|
702
741
|
| `@miguelmorales13/nestkit/whatsapp` | `WhatsAppModule`, `WHATSAPP_CLIENT`, `WhatsAppClient` |
|
|
703
742
|
| `@miguelmorales13/nestkit/stripe` | `StripeModule`, `STRIPE_CLIENT`, `createStripeWebhookController` |
|
|
704
743
|
| `@miguelmorales13/nestkit/auth` | `AuthUserPort`, `BaseAuthService`, `createAuthController`, `TokenService`, `JwtAuthGuard`, `RolesGuard`, `RequireTenantGuard`, `CurrentUser`, `CurrentTenant`, `Roles` |
|
|
744
|
+
| `@miguelmorales13/nestkit/storage` | `StorageModule`, `STORAGE`, `StoragePort`, adaptadores `LocalStorage`/`S3Storage`/`BunnyStorage` |
|
|
705
745
|
| `@miguelmorales13/nestkit/email/resend` | `ResendModule`, `RESEND_CLIENT` |
|
|
706
746
|
| `@miguelmorales13/nestkit/email/nodemailer` | `NodemailerModule`, `NODEMAILER_TRANSPORT` |
|
|
707
747
|
| `@miguelmorales13/nestkit/i18n` | `I18nModule` (wrapper de `nestjs-i18n`), `translateOr` |
|
|
@@ -709,7 +749,5 @@ Mailgun, tu propio servidor) en vez de sumar otra cuenta de terceros.
|
|
|
709
749
|
|
|
710
750
|
## Estado del paquete
|
|
711
751
|
|
|
712
|
-
`0.
|
|
713
|
-
|
|
714
|
-
consumidor productivo todavía; la primera integración real (ej. en `hr-pymes-saas`) es la que va a
|
|
715
|
-
ejercitar el código de verdad.
|
|
752
|
+
`0.5.1`. Sin adaptador Mongo real. Sin tests unitarios propios — es un paquete nuevo; la primera
|
|
753
|
+
integración real (ej. en `hr-pymes-saas` o `cuota`) es la que ejercita el código de verdad.
|
package/dist/index.cjs
CHANGED
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
var _chunkRF75KC63cjs = require('./chunk-RF75KC63.cjs');
|
|
6
|
-
require('./chunk-MR2IFCZE.cjs');
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
var
|
|
12
|
-
require('./chunk-KVBQBT3D.cjs');
|
|
10
|
+
var _chunkSPTDXUEDcjs = require('./chunk-SPTDXUED.cjs');
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
|
|
14
|
+
var _chunkM3EL5O6Tcjs = require('./chunk-M3EL5O6T.cjs');
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
var _chunkV2M75FN3cjs = require('./chunk-V2M75FN3.cjs');
|
|
20
|
+
require('./chunk-MR2IFCZE.cjs');
|
|
21
|
+
require('./chunk-KVBQBT3D.cjs');
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
var _chunk54ZXIB5Tcjs = require('./chunk-54ZXIB5T.cjs');
|
|
@@ -25,6 +29,9 @@ var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
|
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
var _chunk3CLYZC3Tcjs = require('./chunk-3CLYZC3T.cjs');
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
var _chunkQS2W5XCQcjs = require('./chunk-QS2W5XCQ.cjs');
|
|
28
35
|
require('./chunk-7SOM7EZP.cjs');
|
|
29
36
|
|
|
30
37
|
|
|
@@ -42,13 +49,6 @@ var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
|
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
var _chunkM3EL5O6Tcjs = require('./chunk-M3EL5O6T.cjs');
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
var _chunkQS2W5XCQcjs = require('./chunk-QS2W5XCQ.cjs');
|
|
52
52
|
require('./chunk-2REOCMUD.cjs');
|
|
53
53
|
|
|
54
54
|
|
package/dist/index.js
CHANGED
|
@@ -3,18 +3,22 @@ import {
|
|
|
3
3
|
PgModule,
|
|
4
4
|
withTenantScope
|
|
5
5
|
} from "./chunk-VKOPDDCC.js";
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
SUPABASE_ANON_CLIENT,
|
|
8
|
+
SUPABASE_SERVICE_ROLE_CLIENT,
|
|
9
|
+
SupabaseModule
|
|
10
|
+
} from "./chunk-PA24P76K.js";
|
|
11
|
+
import {
|
|
12
|
+
I18nModule,
|
|
13
|
+
translateOr
|
|
14
|
+
} from "./chunk-IYUUYCP5.js";
|
|
7
15
|
import {
|
|
8
16
|
REQUEST_ID_HEADER,
|
|
9
17
|
RequestIdMiddleware,
|
|
10
18
|
TrackingModule
|
|
11
19
|
} from "./chunk-EYURGACO.js";
|
|
20
|
+
import "./chunk-NAK4WDKS.js";
|
|
12
21
|
import "./chunk-EPVKCBPT.js";
|
|
13
|
-
import {
|
|
14
|
-
SUPABASE_ANON_CLIENT,
|
|
15
|
-
SUPABASE_SERVICE_ROLE_CLIENT,
|
|
16
|
-
SupabaseModule
|
|
17
|
-
} from "./chunk-PA24P76K.js";
|
|
18
22
|
import {
|
|
19
23
|
applyNestKitDefaults
|
|
20
24
|
} from "./chunk-AOCF5QCZ.js";
|
|
@@ -25,6 +29,9 @@ import {
|
|
|
25
29
|
BaseCrudService,
|
|
26
30
|
createCrudController
|
|
27
31
|
} from "./chunk-JOVBJDJ2.js";
|
|
32
|
+
import {
|
|
33
|
+
BaseResponseDto
|
|
34
|
+
} from "./chunk-XX2HPTRU.js";
|
|
28
35
|
import "./chunk-DQYAIQQ5.js";
|
|
29
36
|
import {
|
|
30
37
|
ConflictAppException,
|
|
@@ -42,13 +49,6 @@ import {
|
|
|
42
49
|
import {
|
|
43
50
|
AppException
|
|
44
51
|
} from "./chunk-YFYHLYHN.js";
|
|
45
|
-
import {
|
|
46
|
-
I18nModule,
|
|
47
|
-
translateOr
|
|
48
|
-
} from "./chunk-IYUUYCP5.js";
|
|
49
|
-
import {
|
|
50
|
-
BaseResponseDto
|
|
51
|
-
} from "./chunk-XX2HPTRU.js";
|
|
52
52
|
import "./chunk-4MGIQFAJ.js";
|
|
53
53
|
export {
|
|
54
54
|
AppException,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import type { BunnyStorageOptions } from '../storage.options.js';
|
|
3
|
+
import type { ByteRange, ListResult, ObjectInfo, PutOptions, ReadStream, SignedUploadTarget, StoragePort } from '../storage.port.js';
|
|
4
|
+
/**
|
|
5
|
+
* Bunny.net Storage adapter. Objects live in a Storage Zone (write/read via the
|
|
6
|
+
* Storage API) and are served publicly through the Pull Zone bound to it.
|
|
7
|
+
*
|
|
8
|
+
* Uses `fetch` only — no SDK. Streaming works both ways: `putStream` streams the
|
|
9
|
+
* body up, `getStream` streams it down. Direct signed UPLOADS are not offered:
|
|
10
|
+
* Bunny Storage has no presigned-PUT that avoids exposing the zone password, so
|
|
11
|
+
* uploads go through your API (or use Bunny Stream/TUS for large video).
|
|
12
|
+
*/
|
|
13
|
+
export declare class BunnyStorage implements StoragePort {
|
|
14
|
+
private readonly base;
|
|
15
|
+
private readonly apiKey;
|
|
16
|
+
private readonly pullZoneUrl?;
|
|
17
|
+
private readonly tokenSecurityKey?;
|
|
18
|
+
constructor(options: BunnyStorageOptions);
|
|
19
|
+
private url;
|
|
20
|
+
put(key: string, data: Buffer | Uint8Array, options?: PutOptions): Promise<ObjectInfo>;
|
|
21
|
+
putStream(key: string, stream: Readable, options?: PutOptions): Promise<ObjectInfo>;
|
|
22
|
+
get(key: string): Promise<Buffer>;
|
|
23
|
+
getStream(key: string, range?: ByteRange): Promise<ReadStream>;
|
|
24
|
+
stat(key: string): Promise<ObjectInfo | null>;
|
|
25
|
+
exists(key: string): Promise<boolean>;
|
|
26
|
+
delete(key: string): Promise<void>;
|
|
27
|
+
private listRaw;
|
|
28
|
+
list(prefix: string, options?: {
|
|
29
|
+
limit?: number;
|
|
30
|
+
}): Promise<ListResult>;
|
|
31
|
+
publicUrl(key: string): string | null;
|
|
32
|
+
signedReadUrl(key: string, ttlSeconds?: number): Promise<string>;
|
|
33
|
+
signedUploadUrl(): Promise<SignedUploadTarget>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { LocalStorageOptions } from '../storage.options.js';
|
|
3
|
+
import type { ByteRange, ListResult, ObjectInfo, ReadStream, SignedUploadTarget, StoragePort } from '../storage.port.js';
|
|
4
|
+
/**
|
|
5
|
+
* Filesystem-backed storage for development. Serves streams and byte ranges from
|
|
6
|
+
* disk. Content-Type is not persisted (the filesystem has no place for it), so
|
|
7
|
+
* `stat`/`getStream` return it only when the caller can infer it elsewhere.
|
|
8
|
+
*
|
|
9
|
+
* Not for production: there is no durability story, no CDN, and `signedUploadUrl`
|
|
10
|
+
* is unsupported (a client can't PUT to your disk). Use S3/R2 or Bunny there.
|
|
11
|
+
*/
|
|
12
|
+
export declare class LocalStorage implements StoragePort {
|
|
13
|
+
private readonly root;
|
|
14
|
+
private readonly publicBaseUrl?;
|
|
15
|
+
constructor(options: LocalStorageOptions);
|
|
16
|
+
/** Resolve a key to an absolute path, refusing anything that escapes the root. */
|
|
17
|
+
private pathOf;
|
|
18
|
+
put(key: string, data: Buffer | Uint8Array): Promise<ObjectInfo>;
|
|
19
|
+
putStream(key: string, stream: Readable): Promise<ObjectInfo>;
|
|
20
|
+
get(key: string): Promise<Buffer>;
|
|
21
|
+
getStream(key: string, range?: ByteRange): Promise<ReadStream>;
|
|
22
|
+
stat(key: string): Promise<ObjectInfo | null>;
|
|
23
|
+
exists(key: string): Promise<boolean>;
|
|
24
|
+
delete(key: string): Promise<void>;
|
|
25
|
+
list(prefix: string, options?: {
|
|
26
|
+
limit?: number;
|
|
27
|
+
}): Promise<ListResult>;
|
|
28
|
+
publicUrl(key: string): string | null;
|
|
29
|
+
signedReadUrl(key: string): Promise<string>;
|
|
30
|
+
signedUploadUrl(): Promise<SignedUploadTarget>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { S3StorageOptions } from '../storage.options.js';
|
|
3
|
+
import type { ByteRange, ListResult, ObjectInfo, PutOptions, ReadStream, SignedUploadTarget, StoragePort } from '../storage.port.js';
|
|
4
|
+
/**
|
|
5
|
+
* S3-compatible storage. Works with AWS S3 and any S3 API implementation —
|
|
6
|
+
* notably Cloudflare R2 (see `createR2Storage`) and MinIO.
|
|
7
|
+
*
|
|
8
|
+
* The AWS SDK is imported LAZILY, the first time it's needed, so an app that only
|
|
9
|
+
* uses the Bunny or local adapter never loads `@aws-sdk/*`. Install
|
|
10
|
+
* `@aws-sdk/client-s3`, `@aws-sdk/s3-request-presigner` and `@aws-sdk/lib-storage`
|
|
11
|
+
* (optional peer deps) to use this adapter.
|
|
12
|
+
*/
|
|
13
|
+
export declare class S3Storage implements StoragePort {
|
|
14
|
+
private readonly bucket;
|
|
15
|
+
private readonly publicBaseUrl?;
|
|
16
|
+
private clientPromise?;
|
|
17
|
+
private readonly config;
|
|
18
|
+
constructor(options: S3StorageOptions);
|
|
19
|
+
/** Lazily construct (and memoize) the S3 client, loading the SDK on first use. */
|
|
20
|
+
private client;
|
|
21
|
+
put(key: string, data: Buffer | Uint8Array, options?: PutOptions): Promise<ObjectInfo>;
|
|
22
|
+
putStream(key: string, stream: Readable, options?: PutOptions): Promise<ObjectInfo>;
|
|
23
|
+
get(key: string): Promise<Buffer>;
|
|
24
|
+
getStream(key: string, range?: ByteRange): Promise<ReadStream>;
|
|
25
|
+
stat(key: string): Promise<ObjectInfo | null>;
|
|
26
|
+
exists(key: string): Promise<boolean>;
|
|
27
|
+
delete(key: string): Promise<void>;
|
|
28
|
+
list(prefix: string, options?: {
|
|
29
|
+
limit?: number;
|
|
30
|
+
cursor?: string;
|
|
31
|
+
}): Promise<ListResult>;
|
|
32
|
+
signedReadUrl(key: string, ttlSeconds?: number): Promise<string>;
|
|
33
|
+
signedUploadUrl(key: string, options?: PutOptions & {
|
|
34
|
+
ttlSeconds?: number;
|
|
35
|
+
}): Promise<SignedUploadTarget>;
|
|
36
|
+
publicUrl(key: string): string | null;
|
|
37
|
+
}
|