@miguelmorales13/nestkit 0.1.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.
- package/README.md +411 -0
- package/dist/bootstrap/apply-defaults.d.ts +23 -0
- package/dist/bootstrap/index.d.ts +2 -0
- package/dist/bootstrap/index.js +10 -0
- package/dist/chunk-4MGIQFAJ.js +16 -0
- package/dist/chunk-EBO6UKHL.js +20 -0
- package/dist/chunk-EPVKCBPT.js +0 -0
- package/dist/chunk-EQXYK6AL.js +81 -0
- package/dist/chunk-EYURGACO.js +40 -0
- package/dist/chunk-HGJX2GPE.js +39 -0
- package/dist/chunk-IYUUYCP5.js +36 -0
- package/dist/chunk-JOVBJDJ2.js +104 -0
- package/dist/chunk-KDAA6GFF.js +29 -0
- package/dist/chunk-KP7GRCZW.js +32 -0
- package/dist/chunk-NAK4WDKS.js +0 -0
- package/dist/chunk-PA24P76K.js +39 -0
- package/dist/chunk-VKOPDDCC.js +50 -0
- package/dist/chunk-XX2HPTRU.js +12 -0
- package/dist/crud/crud.controller.d.ts +39 -0
- package/dist/crud/crud.service.d.ts +29 -0
- package/dist/crud/index.d.ts +5 -0
- package/dist/crud/index.js +9 -0
- package/dist/crud/repository.port.d.ts +14 -0
- package/dist/database/mongo/index.d.ts +1 -0
- package/dist/database/mongo/index.js +1 -0
- package/dist/database/mongo/repository.port.d.ts +1 -0
- package/dist/database/postgres/index.d.ts +2 -0
- package/dist/database/postgres/index.js +11 -0
- package/dist/database/postgres/postgres.module.d.ts +4 -0
- package/dist/database/postgres/tenant-scope.d.ts +11 -0
- package/dist/database/supabase/index.d.ts +1 -0
- package/dist/database/supabase/index.js +11 -0
- package/dist/database/supabase/supabase.module.d.ts +6 -0
- package/dist/entities/base-response.dto.d.ts +15 -0
- package/dist/entities/base.entity.d.ts +11 -0
- package/dist/entities/index.d.ts +3 -0
- package/dist/entities/index.js +7 -0
- package/dist/entities/soft-delete.entity.d.ts +8 -0
- package/dist/errors/app.exception.d.ts +11 -0
- package/dist/errors/common.exceptions.d.ts +16 -0
- package/dist/errors/global-exception.filter.d.ts +13 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/index.js +22 -0
- package/dist/i18n/i18n.module.d.ts +18 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +9 -0
- package/dist/i18n/translate.helper.d.ts +7 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +75 -0
- package/dist/response/api-response.d.ts +20 -0
- package/dist/response/index.d.ts +2 -0
- package/dist/response/index.js +9 -0
- package/dist/response/response.interceptor.d.ts +13 -0
- package/dist/tracking/index.d.ts +4 -0
- package/dist/tracking/index.js +15 -0
- package/dist/tracking/request-context.d.ts +14 -0
- package/dist/tracking/request-id.middleware.d.ts +17 -0
- package/dist/tracking/tracking.module.d.ts +9 -0
- package/package.json +112 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__decorateClass,
|
|
3
|
+
__decorateParam
|
|
4
|
+
} from "./chunk-4MGIQFAJ.js";
|
|
5
|
+
|
|
6
|
+
// src/crud/crud.service.ts
|
|
7
|
+
var BaseCrudService = class {
|
|
8
|
+
constructor(repository, options = {}) {
|
|
9
|
+
this.repository = repository;
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
findById(id, tenantId) {
|
|
13
|
+
return this.repository.findById(id, tenantId);
|
|
14
|
+
}
|
|
15
|
+
findAll(filter, tenantId) {
|
|
16
|
+
return this.repository.findAll(filter, tenantId);
|
|
17
|
+
}
|
|
18
|
+
create(data, tenantId) {
|
|
19
|
+
return this.repository.create(data, tenantId);
|
|
20
|
+
}
|
|
21
|
+
update(id, data, tenantId) {
|
|
22
|
+
return this.repository.update(id, data, tenantId);
|
|
23
|
+
}
|
|
24
|
+
async delete(id, tenantId) {
|
|
25
|
+
if (this.options.deleteStrategy === "soft") {
|
|
26
|
+
await this.repository.update(id, { deletedAt: /* @__PURE__ */ new Date() }, tenantId);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await this.repository.delete(id, tenantId);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/crud/crud.controller.ts
|
|
34
|
+
import {
|
|
35
|
+
Body,
|
|
36
|
+
Controller,
|
|
37
|
+
Delete,
|
|
38
|
+
Get,
|
|
39
|
+
Inject,
|
|
40
|
+
Param,
|
|
41
|
+
Patch,
|
|
42
|
+
Post,
|
|
43
|
+
Query,
|
|
44
|
+
Req
|
|
45
|
+
} from "@nestjs/common";
|
|
46
|
+
function createCrudController(serviceToken, options = {}) {
|
|
47
|
+
const tenantIdOf = (req) => options.tenantIdExtractor?.(req);
|
|
48
|
+
let CrudControllerHost = class {
|
|
49
|
+
constructor(service) {
|
|
50
|
+
this.service = service;
|
|
51
|
+
}
|
|
52
|
+
findAll(filter, req) {
|
|
53
|
+
return this.service.findAll(filter, tenantIdOf(req));
|
|
54
|
+
}
|
|
55
|
+
findById(id, req) {
|
|
56
|
+
return this.service.findById(id, tenantIdOf(req));
|
|
57
|
+
}
|
|
58
|
+
create(data, req) {
|
|
59
|
+
return this.service.create(data, tenantIdOf(req));
|
|
60
|
+
}
|
|
61
|
+
update(id, data, req) {
|
|
62
|
+
return this.service.update(id, data, tenantIdOf(req));
|
|
63
|
+
}
|
|
64
|
+
remove(id, req) {
|
|
65
|
+
return this.service.delete(id, tenantIdOf(req));
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
__decorateClass([
|
|
69
|
+
Get(),
|
|
70
|
+
__decorateParam(0, Query()),
|
|
71
|
+
__decorateParam(1, Req())
|
|
72
|
+
], CrudControllerHost.prototype, "findAll", 1);
|
|
73
|
+
__decorateClass([
|
|
74
|
+
Get(":id"),
|
|
75
|
+
__decorateParam(0, Param("id")),
|
|
76
|
+
__decorateParam(1, Req())
|
|
77
|
+
], CrudControllerHost.prototype, "findById", 1);
|
|
78
|
+
__decorateClass([
|
|
79
|
+
Post(),
|
|
80
|
+
__decorateParam(0, Body()),
|
|
81
|
+
__decorateParam(1, Req())
|
|
82
|
+
], CrudControllerHost.prototype, "create", 1);
|
|
83
|
+
__decorateClass([
|
|
84
|
+
Patch(":id"),
|
|
85
|
+
__decorateParam(0, Param("id")),
|
|
86
|
+
__decorateParam(1, Body()),
|
|
87
|
+
__decorateParam(2, Req())
|
|
88
|
+
], CrudControllerHost.prototype, "update", 1);
|
|
89
|
+
__decorateClass([
|
|
90
|
+
Delete(":id"),
|
|
91
|
+
__decorateParam(0, Param("id")),
|
|
92
|
+
__decorateParam(1, Req())
|
|
93
|
+
], CrudControllerHost.prototype, "remove", 1);
|
|
94
|
+
CrudControllerHost = __decorateClass([
|
|
95
|
+
Controller(options.path ?? ""),
|
|
96
|
+
__decorateParam(0, Inject(serviceToken))
|
|
97
|
+
], CrudControllerHost);
|
|
98
|
+
return CrudControllerHost;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
BaseCrudService,
|
|
103
|
+
createCrudController
|
|
104
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RequestContext
|
|
3
|
+
} from "./chunk-EBO6UKHL.js";
|
|
4
|
+
import {
|
|
5
|
+
__decorateClass
|
|
6
|
+
} from "./chunk-4MGIQFAJ.js";
|
|
7
|
+
|
|
8
|
+
// src/response/response.interceptor.ts
|
|
9
|
+
import { Injectable } from "@nestjs/common";
|
|
10
|
+
import { map } from "rxjs/operators";
|
|
11
|
+
var ResponseInterceptor = class {
|
|
12
|
+
intercept(_context, next) {
|
|
13
|
+
return next.handle().pipe(
|
|
14
|
+
map((data) => ({
|
|
15
|
+
success: true,
|
|
16
|
+
data,
|
|
17
|
+
requestId: RequestContext.getRequestId() ?? "unknown",
|
|
18
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
19
|
+
}))
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
ResponseInterceptor = __decorateClass([
|
|
24
|
+
Injectable()
|
|
25
|
+
], ResponseInterceptor);
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
ResponseInterceptor
|
|
29
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ResponseInterceptor
|
|
3
|
+
} from "./chunk-KDAA6GFF.js";
|
|
4
|
+
import {
|
|
5
|
+
GlobalExceptionFilter
|
|
6
|
+
} from "./chunk-EQXYK6AL.js";
|
|
7
|
+
|
|
8
|
+
// src/bootstrap/apply-defaults.ts
|
|
9
|
+
import {
|
|
10
|
+
ValidationPipe
|
|
11
|
+
} from "@nestjs/common";
|
|
12
|
+
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
|
13
|
+
import helmet from "helmet";
|
|
14
|
+
function applyNestKitDefaults(app, options = {}) {
|
|
15
|
+
app.use(helmet());
|
|
16
|
+
app.enableCors(options.cors ?? { origin: process.env.CORS_ORIGIN?.split(",") ?? true });
|
|
17
|
+
app.useGlobalPipes(
|
|
18
|
+
new ValidationPipe({ whitelist: true, transform: true, ...options.validation })
|
|
19
|
+
);
|
|
20
|
+
app.useGlobalFilters(new GlobalExceptionFilter());
|
|
21
|
+
app.useGlobalInterceptors(new ResponseInterceptor());
|
|
22
|
+
if (options.swagger !== false) {
|
|
23
|
+
const swaggerOptions = options.swagger ?? { title: "API" };
|
|
24
|
+
const config = new DocumentBuilder().setTitle(swaggerOptions.title).build();
|
|
25
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
26
|
+
SwaggerModule.setup(swaggerOptions.path ?? "docs", app, document);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
applyNestKitDefaults
|
|
32
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__decorateClass
|
|
3
|
+
} from "./chunk-4MGIQFAJ.js";
|
|
4
|
+
|
|
5
|
+
// src/database/supabase/supabase.module.ts
|
|
6
|
+
import { Module } from "@nestjs/common";
|
|
7
|
+
import { createClient } from "@supabase/supabase-js";
|
|
8
|
+
var SUPABASE_ANON_CLIENT = /* @__PURE__ */ Symbol("SUPABASE_ANON_CLIENT");
|
|
9
|
+
var SUPABASE_SERVICE_ROLE_CLIENT = /* @__PURE__ */ Symbol("SUPABASE_SERVICE_ROLE_CLIENT");
|
|
10
|
+
function requireEnv(name) {
|
|
11
|
+
const value = process.env[name];
|
|
12
|
+
if (!value) {
|
|
13
|
+
throw new Error(`SupabaseModule: ${name} environment variable is not set`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
var SupabaseModule = class {
|
|
18
|
+
};
|
|
19
|
+
SupabaseModule = __decorateClass([
|
|
20
|
+
Module({
|
|
21
|
+
providers: [
|
|
22
|
+
{
|
|
23
|
+
provide: SUPABASE_ANON_CLIENT,
|
|
24
|
+
useFactory: () => createClient(requireEnv("SUPABASE_URL"), requireEnv("SUPABASE_ANON_KEY"))
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
provide: SUPABASE_SERVICE_ROLE_CLIENT,
|
|
28
|
+
useFactory: () => createClient(requireEnv("SUPABASE_URL"), requireEnv("SUPABASE_SERVICE_ROLE_KEY"))
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
exports: [SUPABASE_ANON_CLIENT, SUPABASE_SERVICE_ROLE_CLIENT]
|
|
32
|
+
})
|
|
33
|
+
], SupabaseModule);
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
SUPABASE_ANON_CLIENT,
|
|
37
|
+
SUPABASE_SERVICE_ROLE_CLIENT,
|
|
38
|
+
SupabaseModule
|
|
39
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__decorateClass
|
|
3
|
+
} from "./chunk-4MGIQFAJ.js";
|
|
4
|
+
|
|
5
|
+
// src/database/postgres/postgres.module.ts
|
|
6
|
+
import { Module } from "@nestjs/common";
|
|
7
|
+
import { Pool } from "pg";
|
|
8
|
+
var PG_POOL = /* @__PURE__ */ Symbol("PG_POOL");
|
|
9
|
+
var PgModule = class {
|
|
10
|
+
};
|
|
11
|
+
PgModule = __decorateClass([
|
|
12
|
+
Module({
|
|
13
|
+
providers: [
|
|
14
|
+
{
|
|
15
|
+
provide: PG_POOL,
|
|
16
|
+
useFactory: () => {
|
|
17
|
+
const connectionString = process.env.DATABASE_URL;
|
|
18
|
+
if (!connectionString) {
|
|
19
|
+
throw new Error("PgModule: DATABASE_URL environment variable is not set");
|
|
20
|
+
}
|
|
21
|
+
return new Pool({ connectionString });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
exports: [PG_POOL]
|
|
26
|
+
})
|
|
27
|
+
], PgModule);
|
|
28
|
+
|
|
29
|
+
// src/database/postgres/tenant-scope.ts
|
|
30
|
+
async function withTenantScope(pool, authUserId, fn) {
|
|
31
|
+
const client = await pool.connect();
|
|
32
|
+
try {
|
|
33
|
+
await client.query("BEGIN");
|
|
34
|
+
await client.query("SELECT set_config('request.jwt.claim.sub', $1, true)", [authUserId]);
|
|
35
|
+
const result = await fn(client);
|
|
36
|
+
await client.query("COMMIT");
|
|
37
|
+
return result;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
await client.query("ROLLBACK");
|
|
40
|
+
throw error;
|
|
41
|
+
} finally {
|
|
42
|
+
client.release();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
PG_POOL,
|
|
48
|
+
PgModule,
|
|
49
|
+
withTenantScope
|
|
50
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Type } from '@nestjs/common';
|
|
2
|
+
import type { BaseEntity } from '../entities/base.entity.js';
|
|
3
|
+
import type { BaseCrudService } from './crud.service.js';
|
|
4
|
+
export interface CreateCrudControllerOptions {
|
|
5
|
+
/** Route prefix for the generated controller. Defaults to no prefix. */
|
|
6
|
+
path?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Extracts the tenant id from the incoming request and forwards it to
|
|
9
|
+
* every service call (findAll/findById/create/update/remove). Required
|
|
10
|
+
* for multi-tenant use — without it, the generated controller behaves as
|
|
11
|
+
* single-tenant (no tenantId is ever passed to the Repository/service,
|
|
12
|
+
* same as omitting it manually). Typical implementation reads a value a
|
|
13
|
+
* guard already attached to the request, e.g. `(req) => req.companyId`.
|
|
14
|
+
*/
|
|
15
|
+
tenantIdExtractor?: (req: unknown) => string | undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Mixin factory (function returning a class) that wires up the standard
|
|
19
|
+
* REST verbs to a BaseCrudService — composition, not forced inheritance.
|
|
20
|
+
* Pass the service's class/token; Nest resolves it via constructor DI when
|
|
21
|
+
* the generated controller is instantiated, so the service must already be
|
|
22
|
+
* a provider in the module where this controller is declared.
|
|
23
|
+
*
|
|
24
|
+
* Usage (single-tenant):
|
|
25
|
+
* class WidgetsController extends createCrudController(WidgetsService, { path: 'widgets' }) {}
|
|
26
|
+
*
|
|
27
|
+
* Usage (multi-tenant, e.g. behind a guard that sets req.companyId):
|
|
28
|
+
* class WidgetsController extends createCrudController(WidgetsService, {
|
|
29
|
+
* path: 'widgets',
|
|
30
|
+
* tenantIdExtractor: (req) => (req as AuthenticatedRequest).companyId,
|
|
31
|
+
* }) {}
|
|
32
|
+
*/
|
|
33
|
+
export declare function createCrudController<T extends BaseEntity>(serviceToken: Type<BaseCrudService<T>>, options?: CreateCrudControllerOptions): Type<{
|
|
34
|
+
findAll(filter: Partial<T> | undefined, req: unknown): Promise<T[]>;
|
|
35
|
+
findById(id: string, req: unknown): Promise<T | null>;
|
|
36
|
+
create(data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>, req: unknown): Promise<T>;
|
|
37
|
+
update(id: string, data: Partial<T>, req: unknown): Promise<T>;
|
|
38
|
+
remove(id: string, req: unknown): Promise<void>;
|
|
39
|
+
}>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { BaseEntity } from '../entities/base.entity.js';
|
|
2
|
+
import type { Repository } from './repository.port.js';
|
|
3
|
+
export interface BaseCrudServiceOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 'hard' (default): delete() calls repository.delete().
|
|
6
|
+
* 'soft': delete() calls repository.update(id, { deletedAt: new Date() })
|
|
7
|
+
* instead. When using 'soft', T must actually carry a `deletedAt` field
|
|
8
|
+
* (conform to SoftDeleteEntity) — not enforced via a generic constraint
|
|
9
|
+
* here so this class stays usable with plain BaseEntity under 'hard'
|
|
10
|
+
* deletes too. Type your repository as Repository<YourSoftDeleteEntity>
|
|
11
|
+
* if you always soft-delete.
|
|
12
|
+
*/
|
|
13
|
+
deleteStrategy?: 'soft' | 'hard';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generic CRUD orchestration on top of a Repository<T> port. Optional to
|
|
17
|
+
* use directly — extend it to add business rules, or bypass it and consume
|
|
18
|
+
* the Repository port directly.
|
|
19
|
+
*/
|
|
20
|
+
export declare class BaseCrudService<T extends BaseEntity> {
|
|
21
|
+
protected readonly repository: Repository<T>;
|
|
22
|
+
protected readonly options: BaseCrudServiceOptions;
|
|
23
|
+
constructor(repository: Repository<T>, options?: BaseCrudServiceOptions);
|
|
24
|
+
findById(id: string, tenantId?: string): Promise<T | null>;
|
|
25
|
+
findAll(filter?: Partial<T>, tenantId?: string): Promise<T[]>;
|
|
26
|
+
create(data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>, tenantId?: string): Promise<T>;
|
|
27
|
+
update(id: string, data: Partial<T>, tenantId?: string): Promise<T>;
|
|
28
|
+
delete(id: string, tenantId?: string): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { Repository } from './repository.port.js';
|
|
2
|
+
export { BaseCrudService } from './crud.service.js';
|
|
3
|
+
export type { BaseCrudServiceOptions } from './crud.service.js';
|
|
4
|
+
export { createCrudController } from './crud.controller.js';
|
|
5
|
+
export type { CreateCrudControllerOptions } from './crud.controller.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BaseEntity } from '../entities/base.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* Persistence port every adapter (Postgres, Supabase, future Mongo, or a
|
|
4
|
+
* hand-rolled one) implements. `tenantId` is optional so the same port
|
|
5
|
+
* works for both multi-tenant (RLS-scoped) and single-tenant consumers —
|
|
6
|
+
* adapters that don't need it simply ignore the parameter.
|
|
7
|
+
*/
|
|
8
|
+
export interface Repository<T extends BaseEntity, ID = string> {
|
|
9
|
+
findById(id: ID, tenantId?: string): Promise<T | null>;
|
|
10
|
+
findAll(filter?: Partial<T>, tenantId?: string): Promise<T[]>;
|
|
11
|
+
create(data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>, tenantId?: string): Promise<T>;
|
|
12
|
+
update(id: ID, data: Partial<T>, tenantId?: string): Promise<T>;
|
|
13
|
+
delete(id: ID, tenantId?: string): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { MongoRepositoryPort } from './repository.port.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "../../chunk-EPVKCBPT.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { Repository as MongoRepositoryPort } from '../../crud/repository.port.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Pool, PoolClient } from 'pg';
|
|
2
|
+
/**
|
|
3
|
+
* Generalization of the RLS-scoped transaction pattern (`runAsOwner`):
|
|
4
|
+
* checks out a client, opens a transaction, sets
|
|
5
|
+
* `request.jwt.claim.sub = authUserId` via `set_config` (scoped to the
|
|
6
|
+
* transaction) so Postgres Row-Level-Security policies relying on
|
|
7
|
+
* `auth.uid()` see the correct tenant, runs `fn` with that client, commits
|
|
8
|
+
* on success / rolls back on error, and always releases the client back to
|
|
9
|
+
* the pool.
|
|
10
|
+
*/
|
|
11
|
+
export declare function withTenantScope<T>(pool: Pool, authUserId: string, fn: (client: PoolClient) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SupabaseModule, SUPABASE_ANON_CLIENT, SUPABASE_SERVICE_ROLE_CLIENT, } from './supabase.module.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** DI token for the anon-key Supabase client (respects RLS as the caller). */
|
|
2
|
+
export declare const SUPABASE_ANON_CLIENT: unique symbol;
|
|
3
|
+
/** DI token for the service-role Supabase client (bypasses RLS — server-only use). */
|
|
4
|
+
export declare const SUPABASE_SERVICE_ROLE_CLIENT: unique symbol;
|
|
5
|
+
export declare class SupabaseModule {
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BaseEntity } from './base.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* Base class for response DTOs that mirror an entity's public shape.
|
|
4
|
+
* Extend it, add/omit fields as needed, and reuse `fromEntity()` to map
|
|
5
|
+
* a domain entity onto the DTO without repeating boilerplate constructors.
|
|
6
|
+
*
|
|
7
|
+
* Uses the "polymorphic this" pattern so `fromEntity` returns an instance
|
|
8
|
+
* of the concrete subclass, not `BaseResponseDto` itself.
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class BaseResponseDto {
|
|
11
|
+
id: string;
|
|
12
|
+
createdAt: Date;
|
|
13
|
+
updatedAt: Date;
|
|
14
|
+
static fromEntity<TDto extends BaseResponseDto, TEntity extends BaseEntity>(this: new () => TDto, entity: TEntity): TDto;
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base shape every domain entity managed by nestkit must satisfy.
|
|
3
|
+
* Intentionally not a class (no ORM decorators) — this is a framework-agnostic
|
|
4
|
+
* contract. Consumers map their own persistence model (row, document, etc.)
|
|
5
|
+
* onto this shape in their repository adapters.
|
|
6
|
+
*/
|
|
7
|
+
export interface BaseEntity {
|
|
8
|
+
id: string;
|
|
9
|
+
createdAt: Date;
|
|
10
|
+
updatedAt: Date;
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BaseEntity } from './base.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* Entity shape for domains that use soft-delete instead of hard row/document
|
|
4
|
+
* removal. `deletedAt` is `null` while the entity is active.
|
|
5
|
+
*/
|
|
6
|
+
export interface SoftDeleteEntity extends BaseEntity {
|
|
7
|
+
deletedAt: Date | null;
|
|
8
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HttpException, type HttpStatus } from '@nestjs/common';
|
|
2
|
+
/**
|
|
3
|
+
* Base for all domain/application exceptions in nestkit-based services.
|
|
4
|
+
* Carries a stable machine-readable `code` (independent from the HTTP
|
|
5
|
+
* status) that clients can branch on, plus optional structured `details`.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AppException extends HttpException {
|
|
8
|
+
readonly code: string;
|
|
9
|
+
readonly details?: unknown;
|
|
10
|
+
constructor(code: string, message: string, status: HttpStatus, details?: unknown);
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AppException } from './app.exception.js';
|
|
2
|
+
export declare class NotFoundAppException extends AppException {
|
|
3
|
+
constructor(message?: string, details?: unknown, code?: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class ConflictAppException extends AppException {
|
|
6
|
+
constructor(message?: string, details?: unknown, code?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class ValidationAppException extends AppException {
|
|
9
|
+
constructor(message?: string, details?: unknown, code?: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class UnauthorizedAppException extends AppException {
|
|
12
|
+
constructor(message?: string, details?: unknown, code?: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class ForbiddenAppException extends AppException {
|
|
15
|
+
constructor(message?: string, details?: unknown, code?: string);
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ArgumentsHost, type ExceptionFilter } from '@nestjs/common';
|
|
2
|
+
/**
|
|
3
|
+
* Global filter that translates AppException, native Nest HttpException, and
|
|
4
|
+
* any unhandled error into the standard ApiErrorResponse envelope. Register
|
|
5
|
+
* with `app.useGlobalFilters(new GlobalExceptionFilter())` (or via
|
|
6
|
+
* `applyNestKitDefaults`). Never leaks a stack trace to the client — it only
|
|
7
|
+
* goes to the Nest Logger.
|
|
8
|
+
*/
|
|
9
|
+
export declare class GlobalExceptionFilter implements ExceptionFilter {
|
|
10
|
+
private readonly logger;
|
|
11
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
12
|
+
private resolve;
|
|
13
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { AppException } from './app.exception.js';
|
|
2
|
+
export { NotFoundAppException, ConflictAppException, ValidationAppException, UnauthorizedAppException, ForbiddenAppException, } from './common.exceptions.js';
|
|
3
|
+
export { GlobalExceptionFilter } from './global-exception.filter.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConflictAppException,
|
|
3
|
+
ForbiddenAppException,
|
|
4
|
+
NotFoundAppException,
|
|
5
|
+
UnauthorizedAppException,
|
|
6
|
+
ValidationAppException
|
|
7
|
+
} from "../chunk-HGJX2GPE.js";
|
|
8
|
+
import {
|
|
9
|
+
AppException,
|
|
10
|
+
GlobalExceptionFilter
|
|
11
|
+
} from "../chunk-EQXYK6AL.js";
|
|
12
|
+
import "../chunk-EBO6UKHL.js";
|
|
13
|
+
import "../chunk-4MGIQFAJ.js";
|
|
14
|
+
export {
|
|
15
|
+
AppException,
|
|
16
|
+
ConflictAppException,
|
|
17
|
+
ForbiddenAppException,
|
|
18
|
+
GlobalExceptionFilter,
|
|
19
|
+
NotFoundAppException,
|
|
20
|
+
UnauthorizedAppException,
|
|
21
|
+
ValidationAppException
|
|
22
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
+
export interface NestKitI18nOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Path to the folder containing per-language JSON namespaces, relative to
|
|
5
|
+
* the consumer's project root. Files are expected at
|
|
6
|
+
* `{path}/{lang}/{namespace}.json`. Defaults to `./i18n`.
|
|
7
|
+
*/
|
|
8
|
+
path?: string;
|
|
9
|
+
/** Defaults to 'es'. */
|
|
10
|
+
fallbackLanguage?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thin wrapper over nestjs-i18n so consumers don't have to know its
|
|
14
|
+
* loader/resolver configuration shape — just point it at a folder.
|
|
15
|
+
*/
|
|
16
|
+
export declare class I18nModule {
|
|
17
|
+
static forRoot(options?: NestKitI18nOptions): DynamicModule;
|
|
18
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { I18nService } from 'nestjs-i18n';
|
|
2
|
+
/**
|
|
3
|
+
* Translates `key` via the given I18nService, falling back to `fallback`
|
|
4
|
+
* when the key is missing or resolution throws — useful inside exceptions
|
|
5
|
+
* where a missing translation should never itself become the error.
|
|
6
|
+
*/
|
|
7
|
+
export declare function translateOr(i18n: I18nService, key: string, fallback: string, args?: Record<string, unknown>): string;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './entities/index.js';
|
|
2
|
+
export * from './response/index.js';
|
|
3
|
+
export * from './errors/index.js';
|
|
4
|
+
export * from './tracking/index.js';
|
|
5
|
+
export * from './crud/index.js';
|
|
6
|
+
export * from './database/postgres/index.js';
|
|
7
|
+
export * from './database/supabase/index.js';
|
|
8
|
+
export * from './database/mongo/index.js';
|
|
9
|
+
export * from './i18n/index.js';
|
|
10
|
+
export * from './bootstrap/index.js';
|