@m5kdev/backend 0.5.0 → 0.6.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/dist/src/modules/auth/auth.lib.d.ts +7 -7
- package/dist/src/modules/auth/auth.trpc.d.ts +15 -15
- package/dist/src/modules/base/base.abstract.d.ts +3 -2
- package/dist/src/modules/base/base.abstract.js +10 -1
- package/dist/src/modules/base/base.procedure.d.ts +112 -0
- package/dist/src/modules/base/base.procedure.js +289 -0
- package/dist/src/modules/base/base.repository.d.ts +1 -0
- package/dist/src/modules/base/base.repository.js +12 -2
- package/dist/src/modules/base/base.service.d.ts +17 -5
- package/dist/src/modules/base/base.service.js +7 -0
- package/dist/src/modules/base/base.service.test.d.ts +1 -0
- package/dist/src/modules/base/base.service.test.js +415 -0
- package/dist/src/modules/connect/connect.repository.d.ts +3 -3
- package/dist/src/modules/connect/connect.service.d.ts +6 -6
- package/dist/src/modules/connect/connect.trpc.d.ts +2 -2
- package/dist/src/modules/recurrence/recurrence.service.d.ts +29 -8
- package/dist/src/modules/recurrence/recurrence.service.js +3 -4
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts +2 -2
- package/dist/src/modules/recurrence/recurrence.trpc.js +1 -1
- package/dist/src/modules/tag/tag.repository.js +27 -26
- package/dist/src/modules/tag/tag.service.d.ts +86 -15
- package/dist/src/modules/tag/tag.service.js +20 -12
- package/dist/src/modules/tag/tag.trpc.d.ts +2 -2
- package/dist/src/types.d.ts +15 -15
- package/dist/src/utils/trpc.d.ts +4 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -3260,13 +3260,13 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
|
|
|
3260
3260
|
$Infer: {
|
|
3261
3261
|
body: ({
|
|
3262
3262
|
permission: {
|
|
3263
|
-
readonly user?: ("get" | "
|
|
3263
|
+
readonly user?: ("get" | "set-role" | "create" | "update" | "delete" | "list" | "ban" | "impersonate" | "set-password")[] | undefined;
|
|
3264
3264
|
readonly session?: ("delete" | "list" | "revoke")[] | undefined;
|
|
3265
3265
|
};
|
|
3266
3266
|
permissions?: never | undefined;
|
|
3267
3267
|
} | {
|
|
3268
3268
|
permissions: {
|
|
3269
|
-
readonly user?: ("get" | "
|
|
3269
|
+
readonly user?: ("get" | "set-role" | "create" | "update" | "delete" | "list" | "ban" | "impersonate" | "set-password")[] | undefined;
|
|
3270
3270
|
readonly session?: ("delete" | "list" | "revoke")[] | undefined;
|
|
3271
3271
|
};
|
|
3272
3272
|
permission?: never | undefined;
|
|
@@ -3528,7 +3528,7 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
|
|
|
3528
3528
|
id: string;
|
|
3529
3529
|
organizationId: string;
|
|
3530
3530
|
email: string;
|
|
3531
|
-
role: "
|
|
3531
|
+
role: "admin" | "member" | "owner";
|
|
3532
3532
|
status: import("better-auth/plugins", { with: { "resolution-mode": "import" } }).InvitationStatus;
|
|
3533
3533
|
inviterId: string;
|
|
3534
3534
|
expiresAt: Date;
|
|
@@ -3538,7 +3538,7 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
|
|
|
3538
3538
|
Member: {
|
|
3539
3539
|
id: string;
|
|
3540
3540
|
organizationId: string;
|
|
3541
|
-
role: "
|
|
3541
|
+
role: "admin" | "member" | "owner";
|
|
3542
3542
|
createdAt: Date;
|
|
3543
3543
|
userId: string;
|
|
3544
3544
|
teamId?: string | undefined | undefined;
|
|
@@ -3566,7 +3566,7 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
|
|
|
3566
3566
|
members: {
|
|
3567
3567
|
id: string;
|
|
3568
3568
|
organizationId: string;
|
|
3569
|
-
role: "
|
|
3569
|
+
role: "admin" | "member" | "owner";
|
|
3570
3570
|
createdAt: Date;
|
|
3571
3571
|
userId: string;
|
|
3572
3572
|
teamId?: string | undefined | undefined;
|
|
@@ -3581,7 +3581,7 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
|
|
|
3581
3581
|
id: string;
|
|
3582
3582
|
organizationId: string;
|
|
3583
3583
|
email: string;
|
|
3584
|
-
role: "
|
|
3584
|
+
role: "admin" | "member" | "owner";
|
|
3585
3585
|
status: import("better-auth/plugins", { with: { "resolution-mode": "import" } }).InvitationStatus;
|
|
3586
3586
|
inviterId: string;
|
|
3587
3587
|
expiresAt: Date;
|
|
@@ -4811,7 +4811,7 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
|
|
|
4811
4811
|
} | undefined;
|
|
4812
4812
|
verification?: {
|
|
4813
4813
|
modelName?: string;
|
|
4814
|
-
fields?: Partial<Record<"
|
|
4814
|
+
fields?: Partial<Record<"createdAt" | "updatedAt" | "expiresAt" | "value" | "identifier", string>>;
|
|
4815
4815
|
additionalFields?: {
|
|
4816
4816
|
[key: string]: import("better-auth", { with: { "resolution-mode": "import" } }).DBFieldAttribute;
|
|
4817
4817
|
};
|
|
@@ -49,10 +49,10 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
49
49
|
input: void;
|
|
50
50
|
output: {
|
|
51
51
|
id: string;
|
|
52
|
-
status: string;
|
|
53
52
|
createdAt: Date;
|
|
54
53
|
updatedAt: Date | null;
|
|
55
54
|
expiresAt: Date | null;
|
|
55
|
+
status: string;
|
|
56
56
|
claimUserId: string | null;
|
|
57
57
|
claimedAt: Date | null;
|
|
58
58
|
claimedEmail: string | null;
|
|
@@ -67,11 +67,11 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
67
67
|
output: {
|
|
68
68
|
id: string;
|
|
69
69
|
email: string;
|
|
70
|
-
url: string;
|
|
71
70
|
createdAt: Date;
|
|
72
|
-
userId: string;
|
|
73
71
|
expiresAt: Date | null;
|
|
72
|
+
userId: string;
|
|
74
73
|
claimId: string;
|
|
74
|
+
url: string;
|
|
75
75
|
};
|
|
76
76
|
meta: any;
|
|
77
77
|
}>;
|
|
@@ -82,11 +82,11 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
82
82
|
output: {
|
|
83
83
|
id: string;
|
|
84
84
|
email: string;
|
|
85
|
-
url: string;
|
|
86
85
|
createdAt: Date;
|
|
87
|
-
userId: string;
|
|
88
86
|
expiresAt: Date | null;
|
|
87
|
+
userId: string;
|
|
89
88
|
claimId: string;
|
|
89
|
+
url: string;
|
|
90
90
|
}[];
|
|
91
91
|
meta: any;
|
|
92
92
|
}>;
|
|
@@ -139,11 +139,11 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
139
139
|
input: void;
|
|
140
140
|
output: {
|
|
141
141
|
id: string;
|
|
142
|
-
email: string | null;
|
|
143
142
|
name: string | null;
|
|
144
|
-
|
|
143
|
+
email: string | null;
|
|
145
144
|
createdAt: Date;
|
|
146
145
|
updatedAt: Date | null;
|
|
146
|
+
status: string;
|
|
147
147
|
}[];
|
|
148
148
|
meta: any;
|
|
149
149
|
}>;
|
|
@@ -153,11 +153,11 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
153
153
|
};
|
|
154
154
|
output: {
|
|
155
155
|
id: string;
|
|
156
|
-
email: string | null;
|
|
157
156
|
name: string | null;
|
|
158
|
-
|
|
157
|
+
email: string | null;
|
|
159
158
|
createdAt: Date;
|
|
160
159
|
updatedAt: Date | null;
|
|
160
|
+
status: string;
|
|
161
161
|
};
|
|
162
162
|
meta: any;
|
|
163
163
|
}>;
|
|
@@ -184,11 +184,11 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
184
184
|
};
|
|
185
185
|
output: {
|
|
186
186
|
id: string;
|
|
187
|
-
email: string | null;
|
|
188
187
|
name: string | null;
|
|
189
|
-
|
|
188
|
+
email: string | null;
|
|
190
189
|
createdAt: Date;
|
|
191
190
|
updatedAt: Date | null;
|
|
191
|
+
status: string;
|
|
192
192
|
};
|
|
193
193
|
meta: any;
|
|
194
194
|
}>;
|
|
@@ -198,11 +198,11 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
198
198
|
};
|
|
199
199
|
output: {
|
|
200
200
|
id: string;
|
|
201
|
-
email: string | null;
|
|
202
201
|
name: string | null;
|
|
203
|
-
|
|
202
|
+
email: string | null;
|
|
204
203
|
createdAt: Date;
|
|
205
204
|
updatedAt: Date | null;
|
|
205
|
+
status: string;
|
|
206
206
|
};
|
|
207
207
|
meta: any;
|
|
208
208
|
}>;
|
|
@@ -212,11 +212,11 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
|
|
|
212
212
|
};
|
|
213
213
|
output: {
|
|
214
214
|
id: string;
|
|
215
|
-
email: string | null;
|
|
216
215
|
name: string | null;
|
|
217
|
-
|
|
216
|
+
email: string | null;
|
|
218
217
|
createdAt: Date;
|
|
219
218
|
updatedAt: Date | null;
|
|
219
|
+
status: string;
|
|
220
220
|
};
|
|
221
221
|
meta: any;
|
|
222
222
|
}>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { TRPC_ERROR_CODE_KEY } from "@trpc/server";
|
|
2
|
-
import type { ServerResult, ServerResultAsync } from "./base.dto";
|
|
3
|
-
import type { ServerErrorLayer } from "./base.types";
|
|
4
2
|
import { ServerError } from "../../utils/errors";
|
|
5
3
|
import { logger } from "../../utils/logger";
|
|
4
|
+
import type { ServerResult, ServerResultAsync } from "./base.dto";
|
|
5
|
+
import type { ServerErrorLayer } from "./base.types";
|
|
6
6
|
export declare abstract class Base {
|
|
7
7
|
layer: ServerErrorLayer;
|
|
8
8
|
logger: ReturnType<typeof logger.child>;
|
|
@@ -15,4 +15,5 @@ export declare abstract class Base {
|
|
|
15
15
|
handleUnknownError(error: unknown): ServerError;
|
|
16
16
|
throwable<T>(fn: () => ServerResult<T>): ServerResult<T>;
|
|
17
17
|
throwableAsync<T>(fn: () => ServerResultAsync<T>): ServerResultAsync<T>;
|
|
18
|
+
throwablePromise<T>(fn: () => Promise<T>, errorHandler?: (error: unknown) => ServerError): ServerResultAsync<T>;
|
|
18
19
|
}
|
|
@@ -43,11 +43,20 @@ class Base {
|
|
|
43
43
|
}
|
|
44
44
|
async throwableAsync(fn) {
|
|
45
45
|
try {
|
|
46
|
-
return fn();
|
|
46
|
+
return await fn();
|
|
47
47
|
}
|
|
48
48
|
catch (error) {
|
|
49
49
|
return (0, neverthrow_1.err)(this.handleUnknownError(error));
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
async throwablePromise(fn, errorHandler) {
|
|
53
|
+
try {
|
|
54
|
+
const result = await fn();
|
|
55
|
+
return (0, neverthrow_1.ok)(result);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return (0, neverthrow_1.err)(errorHandler ? errorHandler(error) : this.handleUnknownError(error));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
52
61
|
}
|
|
53
62
|
exports.Base = Base;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { QueryInput } from "@m5kdev/commons/modules/schemas/query.schema";
|
|
2
|
+
import type { TRPC_ERROR_CODE_KEY } from "@trpc/server";
|
|
3
|
+
import type { ServerError } from "../../utils/errors";
|
|
4
|
+
import type { logger } from "../../utils/logger";
|
|
5
|
+
import type { Context, Session, User } from "../auth/auth.lib";
|
|
6
|
+
import type { Base } from "./base.abstract";
|
|
7
|
+
import type { ServerResult, ServerResultAsync } from "./base.dto";
|
|
8
|
+
import type { Entity, ResourceActionGrant } from "./base.grants";
|
|
9
|
+
type ServiceLogger = ReturnType<typeof logger.child>;
|
|
10
|
+
type RepositoryMap = Record<string, Base>;
|
|
11
|
+
type ServiceMap = Record<string, Base>;
|
|
12
|
+
export type ServiceProcedureContext = {
|
|
13
|
+
user?: User | null;
|
|
14
|
+
session?: Session | null;
|
|
15
|
+
} & Record<string, unknown>;
|
|
16
|
+
export type ServiceProcedureState = Record<string, unknown>;
|
|
17
|
+
export type ServiceProcedureStoredValue<T> = [T] extends [undefined] ? undefined : Awaited<T>;
|
|
18
|
+
export type ServiceProcedureResultLike<T> = T | ServerResult<T> | Promise<T | ServerResult<T>>;
|
|
19
|
+
export type ServiceProcedureContextFilterScope = "user" | "organization" | "team";
|
|
20
|
+
export type ServiceProcedureContextFilteredInput<TInput> = Extract<NonNullable<TInput>, QueryInput>;
|
|
21
|
+
export type ServiceProcedure<TInput, TCtx extends ServiceProcedureContext, TOutput> = (input: TInput, ctx: TCtx) => ServerResultAsync<TOutput>;
|
|
22
|
+
export type ServiceProcedureArgs<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState> = {
|
|
23
|
+
input: TInput;
|
|
24
|
+
ctx: TCtx;
|
|
25
|
+
state: State;
|
|
26
|
+
repository: Repositories;
|
|
27
|
+
service: Services;
|
|
28
|
+
logger: ServiceLogger;
|
|
29
|
+
};
|
|
30
|
+
export type ServiceProcedureStep<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState, TOutput = undefined> = (args: ServiceProcedureArgs<TInput, TCtx, Repositories, Services, State>) => ServiceProcedureResultLike<ServiceProcedureStoredValue<TOutput>>;
|
|
31
|
+
export type ServiceProcedureInputMapper<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState, TNextInput> = (args: ServiceProcedureArgs<TInput, TCtx, Repositories, Services, State>) => ServiceProcedureResultLike<ServiceProcedureStoredValue<TNextInput>>;
|
|
32
|
+
export type ServiceProcedureHandler<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState, TOutput> = (args: ServiceProcedureArgs<TInput, TCtx, Repositories, Services, State>) => ServiceProcedureResultLike<TOutput>;
|
|
33
|
+
export type ServiceProcedureEntityResolver<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState, TEntities extends Entity | Entity[] | undefined> = TEntities | ((args: ServiceProcedureArgs<TInput, TCtx, Repositories, Services, State>) => ServiceProcedureResultLike<TEntities>);
|
|
34
|
+
type ServiceProcedureAccessBaseConfig = {
|
|
35
|
+
action: string;
|
|
36
|
+
grants?: ResourceActionGrant[];
|
|
37
|
+
};
|
|
38
|
+
export type ServiceProcedureEntityStepName<State extends ServiceProcedureState> = Extract<{
|
|
39
|
+
[Key in keyof State]: State[Key] extends Entity | Entity[] | undefined ? Key : never;
|
|
40
|
+
}[keyof State], string>;
|
|
41
|
+
export type ServiceProcedureAccessEntitiesConfig<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState, TEntities extends Entity | Entity[] | undefined = undefined> = ServiceProcedureAccessBaseConfig & {
|
|
42
|
+
entities?: ServiceProcedureEntityResolver<TInput, TCtx, Repositories, Services, State, TEntities>;
|
|
43
|
+
entityStep?: never;
|
|
44
|
+
};
|
|
45
|
+
export type ServiceProcedureAccessStateConfig<State extends ServiceProcedureState, StepName extends ServiceProcedureEntityStepName<State>> = ServiceProcedureAccessBaseConfig & {
|
|
46
|
+
entityStep: StepName;
|
|
47
|
+
entities?: never;
|
|
48
|
+
};
|
|
49
|
+
export type ServiceProcedureAccessConfig<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState, TEntities extends Entity | Entity[] | undefined = undefined> = ServiceProcedureAccessEntitiesConfig<TInput, TCtx, Repositories, Services, State, TEntities> | ServiceProcedureAccessStateConfig<State, ServiceProcedureEntityStepName<State>>;
|
|
50
|
+
export interface ServiceProcedureBuilder<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState = Record<string, never>> {
|
|
51
|
+
use<StepName extends string, TOutput = void>(stepName: StepName, step: ServiceProcedureStep<TInput, TCtx, Repositories, Services, State, TOutput>): ServiceProcedureBuilder<TInput, TCtx, Repositories, Services, State & Record<StepName, ServiceProcedureStoredValue<TOutput>>>;
|
|
52
|
+
mapInput<StepName extends string, TNextInput>(stepName: StepName, step: ServiceProcedureInputMapper<TInput, TCtx, Repositories, Services, State, TNextInput>): ServiceProcedureBuilder<ServiceProcedureStoredValue<TNextInput>, TCtx, Repositories, Services, State & Record<StepName, ServiceProcedureStoredValue<TNextInput>>>;
|
|
53
|
+
addContextFilter(include?: readonly ServiceProcedureContextFilterScope[]): ServiceProcedureBuilder<ServiceProcedureContextFilteredInput<TInput>, TCtx & Context, Repositories, Services, State & {
|
|
54
|
+
contextFilter: ServiceProcedureContextFilteredInput<TInput>;
|
|
55
|
+
}>;
|
|
56
|
+
requireAuth(): ServiceProcedureBuilder<TInput, TCtx & Context, Repositories, Services, State>;
|
|
57
|
+
handle<TOutput>(handler: ServiceProcedureHandler<TInput, TCtx, Repositories, Services, State, TOutput>): ServiceProcedure<TInput, TCtx, TOutput>;
|
|
58
|
+
}
|
|
59
|
+
export interface PermissionServiceProcedureBuilder<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState = Record<string, never>> extends ServiceProcedureBuilder<TInput, TCtx, Repositories, Services, State> {
|
|
60
|
+
use<StepName extends string, TOutput = void>(stepName: StepName, step: ServiceProcedureStep<TInput, TCtx, Repositories, Services, State, TOutput>): PermissionServiceProcedureBuilder<TInput, TCtx, Repositories, Services, State & Record<StepName, ServiceProcedureStoredValue<TOutput>>>;
|
|
61
|
+
mapInput<StepName extends string, TNextInput>(stepName: StepName, step: ServiceProcedureInputMapper<TInput, TCtx, Repositories, Services, State, TNextInput>): PermissionServiceProcedureBuilder<ServiceProcedureStoredValue<TNextInput>, TCtx, Repositories, Services, State & Record<StepName, ServiceProcedureStoredValue<TNextInput>>>;
|
|
62
|
+
addContextFilter(include?: readonly ServiceProcedureContextFilterScope[]): PermissionServiceProcedureBuilder<ServiceProcedureContextFilteredInput<TInput>, TCtx & Context, Repositories, Services, State & {
|
|
63
|
+
contextFilter: ServiceProcedureContextFilteredInput<TInput>;
|
|
64
|
+
}>;
|
|
65
|
+
requireAuth(): PermissionServiceProcedureBuilder<TInput, TCtx & Context, Repositories, Services, State>;
|
|
66
|
+
access(config: ServiceProcedureAccessEntitiesConfig<TInput, TCtx, Repositories, Services, State>): PermissionServiceProcedureBuilder<TInput, TCtx & Context, Repositories, Services, State>;
|
|
67
|
+
access<TEntities extends Entity | Entity[] | undefined>(config: ServiceProcedureAccessEntitiesConfig<TInput, TCtx, Repositories, Services, State, TEntities>): PermissionServiceProcedureBuilder<TInput, TCtx & Context, Repositories, Services, State & {
|
|
68
|
+
access: TEntities;
|
|
69
|
+
}>;
|
|
70
|
+
access<StepName extends ServiceProcedureEntityStepName<State>>(config: ServiceProcedureAccessStateConfig<State, StepName>): PermissionServiceProcedureBuilder<TInput, TCtx & Context, Repositories, Services, State & {
|
|
71
|
+
access: State[StepName];
|
|
72
|
+
}>;
|
|
73
|
+
}
|
|
74
|
+
type BaseServiceProcedureHost<Repositories extends RepositoryMap, Services extends ServiceMap> = {
|
|
75
|
+
repository: Repositories;
|
|
76
|
+
service: Services;
|
|
77
|
+
logger: ServiceLogger;
|
|
78
|
+
addContextFilter(ctx: Context, include?: {
|
|
79
|
+
user?: boolean;
|
|
80
|
+
organization?: boolean;
|
|
81
|
+
team?: boolean;
|
|
82
|
+
}, query?: QueryInput): QueryInput;
|
|
83
|
+
error(code: TRPC_ERROR_CODE_KEY, message?: string, options?: {
|
|
84
|
+
cause?: unknown;
|
|
85
|
+
clientMessage?: string;
|
|
86
|
+
log?: boolean;
|
|
87
|
+
}): ServerResult<never>;
|
|
88
|
+
throwableAsync<T>(fn: () => ServerResultAsync<T>): ServerResultAsync<T>;
|
|
89
|
+
handleUnknownError(error: unknown): ServerError;
|
|
90
|
+
};
|
|
91
|
+
type PermissionServiceProcedureHost<Repositories extends RepositoryMap, Services extends ServiceMap> = BaseServiceProcedureHost<Repositories, Services> & {
|
|
92
|
+
checkPermission<T extends Entity>(ctx: {
|
|
93
|
+
session: Session;
|
|
94
|
+
user: User;
|
|
95
|
+
}, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): boolean;
|
|
96
|
+
checkPermissionAsync<T extends Entity>(ctx: {
|
|
97
|
+
session: Session;
|
|
98
|
+
user: User;
|
|
99
|
+
}, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<boolean>;
|
|
100
|
+
};
|
|
101
|
+
type ProcedureRuntimeStep<Repositories extends RepositoryMap, Services extends ServiceMap> = {
|
|
102
|
+
stage: "use" | "input" | "auth" | "access";
|
|
103
|
+
stepName: string;
|
|
104
|
+
run: (args: ServiceProcedureArgs<unknown, ServiceProcedureContext, Repositories, Services, ServiceProcedureState>) => Promise<ServerResult<unknown>>;
|
|
105
|
+
};
|
|
106
|
+
type ProcedureBuilderConfig<Repositories extends RepositoryMap, Services extends ServiceMap> = {
|
|
107
|
+
name: string;
|
|
108
|
+
steps: ProcedureRuntimeStep<Repositories, Services>[];
|
|
109
|
+
};
|
|
110
|
+
export declare function createServiceProcedureBuilder<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState = Record<string, never>>(host: BaseServiceProcedureHost<Repositories, Services>, config: ProcedureBuilderConfig<Repositories, Services>): ServiceProcedureBuilder<TInput, TCtx, Repositories, Services, State>;
|
|
111
|
+
export declare function createPermissionServiceProcedureBuilder<TInput, TCtx extends ServiceProcedureContext, Repositories extends RepositoryMap, Services extends ServiceMap, State extends ServiceProcedureState = Record<string, never>>(host: PermissionServiceProcedureHost<Repositories, Services>, config: ProcedureBuilderConfig<Repositories, Services>): PermissionServiceProcedureBuilder<TInput, TCtx, Repositories, Services, State>;
|
|
112
|
+
export {};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createServiceProcedureBuilder = createServiceProcedureBuilder;
|
|
4
|
+
exports.createPermissionServiceProcedureBuilder = createPermissionServiceProcedureBuilder;
|
|
5
|
+
const neverthrow_1 = require("neverthrow");
|
|
6
|
+
const DEFAULT_CONTEXT_FILTER_INCLUDE = [
|
|
7
|
+
"user",
|
|
8
|
+
];
|
|
9
|
+
function isServerResult(value) {
|
|
10
|
+
return (typeof value === "object" &&
|
|
11
|
+
value !== null &&
|
|
12
|
+
"isErr" in value &&
|
|
13
|
+
typeof value.isErr === "function" &&
|
|
14
|
+
"isOk" in value &&
|
|
15
|
+
typeof value.isOk === "function");
|
|
16
|
+
}
|
|
17
|
+
async function normalizeProcedureResult(result) {
|
|
18
|
+
const resolved = await result;
|
|
19
|
+
return isServerResult(resolved) ? resolved : (0, neverthrow_1.ok)(resolved);
|
|
20
|
+
}
|
|
21
|
+
function assertUniqueStepName(steps, stepName) {
|
|
22
|
+
if (steps.some((step) => step.stepName === stepName)) {
|
|
23
|
+
throw new Error(`Duplicate service procedure step name: ${stepName}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function hasStepName(steps, stepName) {
|
|
27
|
+
return steps.some((step) => step.stepName === stepName);
|
|
28
|
+
}
|
|
29
|
+
function getContextFilterInclude(include = DEFAULT_CONTEXT_FILTER_INCLUDE) {
|
|
30
|
+
return {
|
|
31
|
+
user: include.includes("user"),
|
|
32
|
+
organization: include.includes("organization"),
|
|
33
|
+
team: include.includes("team"),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function getFailureStage(code) {
|
|
37
|
+
return code === "FORBIDDEN" || code === "UNAUTHORIZED" ? "forbidden" : "error";
|
|
38
|
+
}
|
|
39
|
+
function logProcedureStage(host, procedureName, ctx, stage, { stepName, durationMs, errorCode, } = {}) {
|
|
40
|
+
host.logger.debug({
|
|
41
|
+
procedureName,
|
|
42
|
+
stage,
|
|
43
|
+
stepName,
|
|
44
|
+
durationMs,
|
|
45
|
+
errorCode,
|
|
46
|
+
hasUser: Boolean(ctx.user),
|
|
47
|
+
hasSession: Boolean(ctx.session),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function createRequireAuthStep(host) {
|
|
51
|
+
return {
|
|
52
|
+
stage: "auth",
|
|
53
|
+
stepName: "auth",
|
|
54
|
+
run: async ({ ctx }) => {
|
|
55
|
+
if (!ctx.user || !ctx.session) {
|
|
56
|
+
return host.error("UNAUTHORIZED", "Unauthorized");
|
|
57
|
+
}
|
|
58
|
+
return (0, neverthrow_1.ok)(true);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function createUseStep(stepName, step) {
|
|
63
|
+
return {
|
|
64
|
+
stage: "use",
|
|
65
|
+
stepName,
|
|
66
|
+
run: async (args) => normalizeProcedureResult(step(args)),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function createInputStep(stepName, step) {
|
|
70
|
+
return {
|
|
71
|
+
stage: "input",
|
|
72
|
+
stepName,
|
|
73
|
+
run: async (args) => normalizeProcedureResult(step(args)),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function createContextFilterStep(host, include) {
|
|
77
|
+
const contextInclude = getContextFilterInclude(include);
|
|
78
|
+
return {
|
|
79
|
+
stage: "input",
|
|
80
|
+
stepName: "contextFilter",
|
|
81
|
+
run: async ({ input, ctx }) => (0, neverthrow_1.ok)(host.addContextFilter(ctx, contextInclude, input)),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function createAccessStep(host, config) {
|
|
85
|
+
return {
|
|
86
|
+
stage: "access",
|
|
87
|
+
stepName: "access",
|
|
88
|
+
run: async (args) => {
|
|
89
|
+
const typedArgs = args;
|
|
90
|
+
if (!typedArgs.ctx.user || !typedArgs.ctx.session) {
|
|
91
|
+
return host.error("UNAUTHORIZED", "Unauthorized");
|
|
92
|
+
}
|
|
93
|
+
const permissionContext = {
|
|
94
|
+
user: typedArgs.ctx.user,
|
|
95
|
+
session: typedArgs.ctx.session,
|
|
96
|
+
};
|
|
97
|
+
if ("entityStep" in config && typeof config.entityStep === "string") {
|
|
98
|
+
const entities = typedArgs.state[config.entityStep];
|
|
99
|
+
const hasPermission = host.checkPermission(permissionContext, config.action, entities, config.grants);
|
|
100
|
+
if (!hasPermission) {
|
|
101
|
+
return host.error("FORBIDDEN");
|
|
102
|
+
}
|
|
103
|
+
return (0, neverthrow_1.ok)(entities);
|
|
104
|
+
}
|
|
105
|
+
if (typeof config.entities === "function") {
|
|
106
|
+
const resolveEntities = config.entities;
|
|
107
|
+
let loadedEntities;
|
|
108
|
+
const permission = await host.checkPermissionAsync(permissionContext, config.action, async () => {
|
|
109
|
+
const entityResult = await normalizeProcedureResult(resolveEntities(typedArgs));
|
|
110
|
+
if (entityResult.isOk()) {
|
|
111
|
+
loadedEntities = entityResult.value;
|
|
112
|
+
}
|
|
113
|
+
return entityResult;
|
|
114
|
+
}, config.grants);
|
|
115
|
+
if (permission.isErr()) {
|
|
116
|
+
return permission;
|
|
117
|
+
}
|
|
118
|
+
if (!permission.value) {
|
|
119
|
+
return host.error("FORBIDDEN");
|
|
120
|
+
}
|
|
121
|
+
return (0, neverthrow_1.ok)(loadedEntities);
|
|
122
|
+
}
|
|
123
|
+
const entities = config.entities;
|
|
124
|
+
const hasPermission = host.checkPermission(permissionContext, config.action, entities, config.grants);
|
|
125
|
+
if (!hasPermission) {
|
|
126
|
+
return host.error("FORBIDDEN");
|
|
127
|
+
}
|
|
128
|
+
return (0, neverthrow_1.ok)(entities);
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function createProcedureHandler(host, config, handler) {
|
|
133
|
+
return async (input, ctx) => host.throwableAsync(async () => {
|
|
134
|
+
const state = {};
|
|
135
|
+
const startTime = Date.now();
|
|
136
|
+
const typedCtx = ctx;
|
|
137
|
+
let currentInput = input;
|
|
138
|
+
logProcedureStage(host, config.name, typedCtx, "start");
|
|
139
|
+
try {
|
|
140
|
+
for (const step of config.steps) {
|
|
141
|
+
const stepResult = await step.run({
|
|
142
|
+
input: currentInput,
|
|
143
|
+
ctx: typedCtx,
|
|
144
|
+
state,
|
|
145
|
+
repository: host.repository,
|
|
146
|
+
service: host.service,
|
|
147
|
+
logger: host.logger,
|
|
148
|
+
});
|
|
149
|
+
if (stepResult.isErr()) {
|
|
150
|
+
logProcedureStage(host, config.name, typedCtx, getFailureStage(stepResult.error.code), {
|
|
151
|
+
stepName: step.stepName,
|
|
152
|
+
durationMs: Date.now() - startTime,
|
|
153
|
+
errorCode: stepResult.error.code,
|
|
154
|
+
});
|
|
155
|
+
return stepResult;
|
|
156
|
+
}
|
|
157
|
+
state[step.stepName] = stepResult.value;
|
|
158
|
+
if (step.stage === "input") {
|
|
159
|
+
currentInput = stepResult.value;
|
|
160
|
+
}
|
|
161
|
+
if (step.stage === "auth") {
|
|
162
|
+
logProcedureStage(host, config.name, typedCtx, "auth_passed", {
|
|
163
|
+
stepName: step.stepName,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
if (step.stage === "access") {
|
|
167
|
+
logProcedureStage(host, config.name, typedCtx, "access_passed", {
|
|
168
|
+
stepName: step.stepName,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const handlerResult = await normalizeProcedureResult(handler({
|
|
173
|
+
input: currentInput,
|
|
174
|
+
ctx: ctx,
|
|
175
|
+
state: state,
|
|
176
|
+
repository: host.repository,
|
|
177
|
+
service: host.service,
|
|
178
|
+
logger: host.logger,
|
|
179
|
+
}));
|
|
180
|
+
if (handlerResult.isErr()) {
|
|
181
|
+
logProcedureStage(host, config.name, typedCtx, getFailureStage(handlerResult.error.code), {
|
|
182
|
+
durationMs: Date.now() - startTime,
|
|
183
|
+
errorCode: handlerResult.error.code,
|
|
184
|
+
});
|
|
185
|
+
return handlerResult;
|
|
186
|
+
}
|
|
187
|
+
logProcedureStage(host, config.name, typedCtx, "success", {
|
|
188
|
+
durationMs: Date.now() - startTime,
|
|
189
|
+
});
|
|
190
|
+
return handlerResult;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const serverError = host.handleUnknownError(error);
|
|
194
|
+
logProcedureStage(host, config.name, typedCtx, getFailureStage(serverError.code), {
|
|
195
|
+
durationMs: Date.now() - startTime,
|
|
196
|
+
errorCode: serverError.code,
|
|
197
|
+
});
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function createServiceProcedureBuilder(host, config) {
|
|
203
|
+
function addContextFilter(include) {
|
|
204
|
+
const steps = hasStepName(config.steps, "auth")
|
|
205
|
+
? config.steps
|
|
206
|
+
: [...config.steps, createRequireAuthStep(host)];
|
|
207
|
+
assertUniqueStepName(steps, "contextFilter");
|
|
208
|
+
return createServiceProcedureBuilder(host, {
|
|
209
|
+
...config,
|
|
210
|
+
steps: [...steps, createContextFilterStep(host, include)],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const builder = {
|
|
214
|
+
use(stepName, step) {
|
|
215
|
+
assertUniqueStepName(config.steps, stepName);
|
|
216
|
+
return createServiceProcedureBuilder(host, {
|
|
217
|
+
...config,
|
|
218
|
+
steps: [...config.steps, createUseStep(stepName, step)],
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
mapInput(stepName, step) {
|
|
222
|
+
assertUniqueStepName(config.steps, stepName);
|
|
223
|
+
return createServiceProcedureBuilder(host, {
|
|
224
|
+
...config,
|
|
225
|
+
steps: [...config.steps, createInputStep(stepName, step)],
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
addContextFilter,
|
|
229
|
+
requireAuth() {
|
|
230
|
+
assertUniqueStepName(config.steps, "auth");
|
|
231
|
+
return createServiceProcedureBuilder(host, {
|
|
232
|
+
...config,
|
|
233
|
+
steps: [...config.steps, createRequireAuthStep(host)],
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
handle(handler) {
|
|
237
|
+
return createProcedureHandler(host, config, handler);
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
return builder;
|
|
241
|
+
}
|
|
242
|
+
function createPermissionServiceProcedureBuilder(host, config) {
|
|
243
|
+
function addContextFilter(include) {
|
|
244
|
+
const steps = hasStepName(config.steps, "auth")
|
|
245
|
+
? config.steps
|
|
246
|
+
: [...config.steps, createRequireAuthStep(host)];
|
|
247
|
+
assertUniqueStepName(steps, "contextFilter");
|
|
248
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
249
|
+
...config,
|
|
250
|
+
steps: [...steps, createContextFilterStep(host, include)],
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function access(accessConfig) {
|
|
254
|
+
assertUniqueStepName(config.steps, "access");
|
|
255
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
256
|
+
...config,
|
|
257
|
+
steps: [...config.steps, createAccessStep(host, accessConfig)],
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const builder = {
|
|
261
|
+
use(stepName, step) {
|
|
262
|
+
assertUniqueStepName(config.steps, stepName);
|
|
263
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
264
|
+
...config,
|
|
265
|
+
steps: [...config.steps, createUseStep(stepName, step)],
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
mapInput(stepName, step) {
|
|
269
|
+
assertUniqueStepName(config.steps, stepName);
|
|
270
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
271
|
+
...config,
|
|
272
|
+
steps: [...config.steps, createInputStep(stepName, step)],
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
addContextFilter,
|
|
276
|
+
requireAuth() {
|
|
277
|
+
assertUniqueStepName(config.steps, "auth");
|
|
278
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
279
|
+
...config,
|
|
280
|
+
steps: [...config.steps, createRequireAuthStep(host)],
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
access,
|
|
284
|
+
handle(handler) {
|
|
285
|
+
return createProcedureHandler(host, config, handler);
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
return builder;
|
|
289
|
+
}
|
|
@@ -32,6 +32,7 @@ export declare class BaseRepository<O extends LibSQLDatabase<any>, S extends Rec
|
|
|
32
32
|
getConditionBuilder(): ConditionBuilder;
|
|
33
33
|
getConditionBuilder(table: undefined): ConditionBuilder;
|
|
34
34
|
getConditionBuilder<TTable extends SQLiteTableWithColumns<any>>(table: TTable): TableConditionBuilder<TTable>;
|
|
35
|
+
throwableQuery<T>(fn: () => Promise<T>): ServerResultAsync<T>;
|
|
35
36
|
withPagination<TQuery>(query: TQuery, { page, limit }: Pick<QueryInput, "page" | "limit">): TQuery;
|
|
36
37
|
withSorting<TTable extends SQLiteTableWithColumns<any>, TQuery>(query: TQuery, { sort, order }: Pick<QueryInput, "sort" | "order">, table?: TTable): TQuery;
|
|
37
38
|
withSortingAndPagination<TTable extends SQLiteTableWithColumns<any>, TQuery>(query: TQuery, { sort, order, page, limit }: Pick<QueryInput, "sort" | "order" | "page" | "limit">, table?: TTable): TQuery;
|
|
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BaseExternaRepository = exports.BaseTableRepository = exports.BaseRepository = exports.arrayContains = exports.TableConditionBuilder = exports.ConditionBuilder = void 0;
|
|
4
4
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
5
5
|
const neverthrow_1 = require("neverthrow");
|
|
6
|
-
const
|
|
7
|
-
const base_dto_1 = require("./base.dto");
|
|
6
|
+
const errors_1 = require("../../utils/errors");
|
|
8
7
|
const applyPagination_1 = require("../utils/applyPagination");
|
|
9
8
|
const applySorting_1 = require("../utils/applySorting");
|
|
10
9
|
const getConditionsFromFilters_1 = require("../utils/getConditionsFromFilters");
|
|
10
|
+
const base_abstract_1 = require("./base.abstract");
|
|
11
|
+
const base_dto_1 = require("./base.dto");
|
|
11
12
|
class ConditionBuilder {
|
|
12
13
|
conditions;
|
|
13
14
|
constructor(conditions = []) {
|
|
@@ -66,6 +67,15 @@ class BaseRepository extends base_abstract_1.Base {
|
|
|
66
67
|
}
|
|
67
68
|
return new TableConditionBuilder(table);
|
|
68
69
|
}
|
|
70
|
+
throwableQuery(fn) {
|
|
71
|
+
return this.throwablePromise(() => fn(), (error) => new errors_1.ServerError({
|
|
72
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
73
|
+
layer: "repository",
|
|
74
|
+
layerName: this.constructor.name,
|
|
75
|
+
message: "Database query failed",
|
|
76
|
+
cause: error,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
69
79
|
withPagination(query, { page, limit }) {
|
|
70
80
|
return (0, applyPagination_1.applyPagination)(query, limit, page);
|
|
71
81
|
}
|