@m5kdev/backend 0.5.0 → 0.7.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 (53) hide show
  1. package/dist/src/modules/ai/ai.service.d.ts +11 -13
  2. package/dist/src/modules/ai/ai.service.js +6 -6
  3. package/dist/src/modules/ai/ai.trpc.d.ts +1 -1
  4. package/dist/src/modules/auth/auth.lib.d.ts +8 -12
  5. package/dist/src/modules/auth/auth.lib.js +2 -2
  6. package/dist/src/modules/auth/auth.service.d.ts +17 -47
  7. package/dist/src/modules/auth/auth.service.js +79 -66
  8. package/dist/src/modules/auth/auth.trpc.d.ts +16 -16
  9. package/dist/src/modules/base/base.abstract.d.ts +3 -2
  10. package/dist/src/modules/base/base.abstract.js +10 -1
  11. package/dist/src/modules/base/base.actor.d.ts +68 -0
  12. package/dist/src/modules/base/base.actor.js +99 -0
  13. package/dist/src/modules/base/base.actor.test.d.ts +1 -0
  14. package/dist/src/modules/base/base.actor.test.js +58 -0
  15. package/dist/src/modules/base/base.grants.d.ts +3 -7
  16. package/dist/src/modules/base/base.grants.js +22 -10
  17. package/dist/src/modules/base/base.grants.test.js +16 -45
  18. package/dist/src/modules/base/base.procedure.d.ts +109 -0
  19. package/dist/src/modules/base/base.procedure.js +301 -0
  20. package/dist/src/modules/base/base.repository.d.ts +1 -0
  21. package/dist/src/modules/base/base.repository.js +12 -2
  22. package/dist/src/modules/base/base.service.d.ts +23 -23
  23. package/dist/src/modules/base/base.service.js +26 -12
  24. package/dist/src/modules/base/base.service.test.d.ts +1 -0
  25. package/dist/src/modules/base/base.service.test.js +443 -0
  26. package/dist/src/modules/billing/billing.service.d.ts +4 -25
  27. package/dist/src/modules/billing/billing.service.js +6 -6
  28. package/dist/src/modules/billing/billing.trpc.d.ts +2 -2
  29. package/dist/src/modules/billing/billing.trpc.js +4 -6
  30. package/dist/src/modules/connect/connect.repository.d.ts +3 -3
  31. package/dist/src/modules/connect/connect.service.d.ts +21 -13
  32. package/dist/src/modules/connect/connect.service.js +10 -8
  33. package/dist/src/modules/connect/connect.trpc.d.ts +2 -2
  34. package/dist/src/modules/recurrence/recurrence.service.d.ts +59 -8
  35. package/dist/src/modules/recurrence/recurrence.service.js +16 -14
  36. package/dist/src/modules/recurrence/recurrence.trpc.d.ts +3 -3
  37. package/dist/src/modules/recurrence/recurrence.trpc.js +1 -1
  38. package/dist/src/modules/social/social.service.d.ts +3 -4
  39. package/dist/src/modules/social/social.service.js +3 -3
  40. package/dist/src/modules/tag/tag.repository.js +27 -26
  41. package/dist/src/modules/tag/tag.service.d.ts +90 -15
  42. package/dist/src/modules/tag/tag.service.js +20 -12
  43. package/dist/src/modules/tag/tag.trpc.d.ts +3 -3
  44. package/dist/src/modules/workflow/workflow.service.d.ts +48 -8
  45. package/dist/src/modules/workflow/workflow.service.js +6 -6
  46. package/dist/src/modules/workflow/workflow.trpc.d.ts +2 -2
  47. package/dist/src/types.d.ts +19 -19
  48. package/dist/src/utils/trpc.d.ts +31 -41
  49. package/dist/src/utils/trpc.js +95 -0
  50. package/dist/src/utils/trpc.test.d.ts +1 -0
  51. package/dist/src/utils/trpc.test.js +154 -0
  52. package/dist/tsconfig.tsbuildinfo +1 -1
  53. package/package.json +3 -3
@@ -0,0 +1,301 @@
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 base_actor_1 = require("./base.actor");
7
+ const DEFAULT_CONTEXT_FILTER_INCLUDE = [
8
+ "user",
9
+ ];
10
+ function isServerResult(value) {
11
+ return (typeof value === "object" &&
12
+ value !== null &&
13
+ "isErr" in value &&
14
+ typeof value.isErr === "function" &&
15
+ "isOk" in value &&
16
+ typeof value.isOk === "function");
17
+ }
18
+ async function normalizeProcedureResult(result) {
19
+ const resolved = await result;
20
+ return isServerResult(resolved) ? resolved : (0, neverthrow_1.ok)(resolved);
21
+ }
22
+ function assertUniqueStepName(steps, stepName) {
23
+ if (steps.some((step) => step.stepName === stepName)) {
24
+ throw new Error(`Duplicate service procedure step name: ${stepName}`);
25
+ }
26
+ }
27
+ function hasStepName(steps, stepName) {
28
+ return steps.some((step) => step.stepName === stepName);
29
+ }
30
+ function getContextFilterInclude(include = DEFAULT_CONTEXT_FILTER_INCLUDE) {
31
+ return {
32
+ user: include.includes("user"),
33
+ organization: include.includes("organization"),
34
+ team: include.includes("team"),
35
+ };
36
+ }
37
+ function getFailureStage(code) {
38
+ return code === "FORBIDDEN" || code === "UNAUTHORIZED" ? "forbidden" : "error";
39
+ }
40
+ function logProcedureStage(host, procedureName, ctx, stage, { stepName, durationMs, errorCode, } = {}) {
41
+ host.logger.debug({
42
+ procedureName,
43
+ stage,
44
+ stepName,
45
+ durationMs,
46
+ errorCode,
47
+ hasActor: Boolean(ctx.actor),
48
+ });
49
+ }
50
+ function requireProcedureActor(host, ctx, scope) {
51
+ if (!ctx.actor) {
52
+ return host.error("UNAUTHORIZED", "Unauthorized");
53
+ }
54
+ if (!(0, base_actor_1.validateActor)(ctx.actor, scope)) {
55
+ return host.error("FORBIDDEN", "Forbidden");
56
+ }
57
+ return (0, neverthrow_1.ok)(ctx.actor);
58
+ }
59
+ function createRequireAuthStep(host, scope = "user") {
60
+ return {
61
+ stage: "auth",
62
+ stepName: "auth",
63
+ run: async ({ ctx }) => {
64
+ return requireProcedureActor(host, ctx, scope);
65
+ },
66
+ };
67
+ }
68
+ function createUseStep(stepName, step) {
69
+ return {
70
+ stage: "use",
71
+ stepName,
72
+ run: async (args) => normalizeProcedureResult(step(args)),
73
+ };
74
+ }
75
+ function createInputStep(stepName, step) {
76
+ return {
77
+ stage: "input",
78
+ stepName,
79
+ run: async (args) => normalizeProcedureResult(step(args)),
80
+ };
81
+ }
82
+ function createContextFilterStep(host, include) {
83
+ const contextInclude = getContextFilterInclude(include);
84
+ const requiredScope = contextInclude.team
85
+ ? "team"
86
+ : contextInclude.organization
87
+ ? "organization"
88
+ : "user";
89
+ return {
90
+ stage: "input",
91
+ stepName: "contextFilter",
92
+ run: async ({ input, ctx }) => {
93
+ const actor = requireProcedureActor(host, ctx, requiredScope);
94
+ if (actor.isErr())
95
+ return actor;
96
+ return (0, neverthrow_1.ok)(host.addContextFilter(actor.value, contextInclude, input));
97
+ },
98
+ };
99
+ }
100
+ function createAccessStep(host, config) {
101
+ return {
102
+ stage: "access",
103
+ stepName: "access",
104
+ run: async (args) => {
105
+ const typedArgs = args;
106
+ const actor = requireProcedureActor(host, typedArgs.ctx, "user");
107
+ if (actor.isErr())
108
+ return actor;
109
+ if ("entityStep" in config && typeof config.entityStep === "string") {
110
+ const entities = typedArgs.state[config.entityStep];
111
+ const hasPermission = host.checkPermission(actor.value, config.action, entities, config.grants);
112
+ if (!hasPermission) {
113
+ return host.error("FORBIDDEN");
114
+ }
115
+ return (0, neverthrow_1.ok)(entities);
116
+ }
117
+ if (typeof config.entities === "function") {
118
+ const resolveEntities = config.entities;
119
+ let loadedEntities;
120
+ const permission = await host.checkPermissionAsync(actor.value, config.action, async () => {
121
+ const entityResult = await normalizeProcedureResult(resolveEntities(typedArgs));
122
+ if (entityResult.isOk()) {
123
+ loadedEntities = entityResult.value;
124
+ }
125
+ return entityResult;
126
+ }, config.grants);
127
+ if (permission.isErr()) {
128
+ return permission;
129
+ }
130
+ if (!permission.value) {
131
+ return host.error("FORBIDDEN");
132
+ }
133
+ return (0, neverthrow_1.ok)(loadedEntities);
134
+ }
135
+ const entities = config.entities;
136
+ const hasPermission = host.checkPermission(actor.value, config.action, entities, config.grants);
137
+ if (!hasPermission) {
138
+ return host.error("FORBIDDEN");
139
+ }
140
+ return (0, neverthrow_1.ok)(entities);
141
+ },
142
+ };
143
+ }
144
+ function createProcedureHandler(host, config, handler) {
145
+ return async (input, ctx) => host.throwableAsync(async () => {
146
+ const state = {};
147
+ const startTime = Date.now();
148
+ const typedCtx = ctx;
149
+ let currentInput = input;
150
+ logProcedureStage(host, config.name, typedCtx, "start");
151
+ try {
152
+ for (const step of config.steps) {
153
+ const stepResult = await step.run({
154
+ input: currentInput,
155
+ ctx: typedCtx,
156
+ state,
157
+ repository: host.repository,
158
+ service: host.service,
159
+ logger: host.logger,
160
+ });
161
+ if (stepResult.isErr()) {
162
+ logProcedureStage(host, config.name, typedCtx, getFailureStage(stepResult.error.code), {
163
+ stepName: step.stepName,
164
+ durationMs: Date.now() - startTime,
165
+ errorCode: stepResult.error.code,
166
+ });
167
+ return stepResult;
168
+ }
169
+ state[step.stepName] = stepResult.value;
170
+ if (step.stage === "input") {
171
+ currentInput = stepResult.value;
172
+ }
173
+ if (step.stage === "auth") {
174
+ logProcedureStage(host, config.name, typedCtx, "auth_passed", {
175
+ stepName: step.stepName,
176
+ });
177
+ }
178
+ if (step.stage === "access") {
179
+ logProcedureStage(host, config.name, typedCtx, "access_passed", {
180
+ stepName: step.stepName,
181
+ });
182
+ }
183
+ }
184
+ const handlerResult = await normalizeProcedureResult(handler({
185
+ input: currentInput,
186
+ ctx: ctx,
187
+ state: state,
188
+ repository: host.repository,
189
+ service: host.service,
190
+ logger: host.logger,
191
+ }));
192
+ if (handlerResult.isErr()) {
193
+ logProcedureStage(host, config.name, typedCtx, getFailureStage(handlerResult.error.code), {
194
+ durationMs: Date.now() - startTime,
195
+ errorCode: handlerResult.error.code,
196
+ });
197
+ return handlerResult;
198
+ }
199
+ logProcedureStage(host, config.name, typedCtx, "success", {
200
+ durationMs: Date.now() - startTime,
201
+ });
202
+ return handlerResult;
203
+ }
204
+ catch (error) {
205
+ const serverError = host.handleUnknownError(error);
206
+ logProcedureStage(host, config.name, typedCtx, getFailureStage(serverError.code), {
207
+ durationMs: Date.now() - startTime,
208
+ errorCode: serverError.code,
209
+ });
210
+ throw error;
211
+ }
212
+ });
213
+ }
214
+ function createServiceProcedureBuilder(host, config) {
215
+ function addContextFilter(include) {
216
+ const steps = hasStepName(config.steps, "auth")
217
+ ? config.steps
218
+ : [...config.steps, createRequireAuthStep(host, "user")];
219
+ assertUniqueStepName(steps, "contextFilter");
220
+ return createServiceProcedureBuilder(host, {
221
+ ...config,
222
+ steps: [...steps, createContextFilterStep(host, include)],
223
+ });
224
+ }
225
+ const builder = {
226
+ use(stepName, step) {
227
+ assertUniqueStepName(config.steps, stepName);
228
+ return createServiceProcedureBuilder(host, {
229
+ ...config,
230
+ steps: [...config.steps, createUseStep(stepName, step)],
231
+ });
232
+ },
233
+ mapInput(stepName, step) {
234
+ assertUniqueStepName(config.steps, stepName);
235
+ return createServiceProcedureBuilder(host, {
236
+ ...config,
237
+ steps: [...config.steps, createInputStep(stepName, step)],
238
+ });
239
+ },
240
+ addContextFilter,
241
+ requireAuth(scope) {
242
+ assertUniqueStepName(config.steps, "auth");
243
+ return createServiceProcedureBuilder(host, {
244
+ ...config,
245
+ steps: [...config.steps, createRequireAuthStep(host, scope ?? "user")],
246
+ });
247
+ },
248
+ handle(handler) {
249
+ return createProcedureHandler(host, config, handler);
250
+ },
251
+ };
252
+ return builder;
253
+ }
254
+ function createPermissionServiceProcedureBuilder(host, config) {
255
+ function addContextFilter(include) {
256
+ const steps = hasStepName(config.steps, "auth")
257
+ ? config.steps
258
+ : [...config.steps, createRequireAuthStep(host, "user")];
259
+ assertUniqueStepName(steps, "contextFilter");
260
+ return createPermissionServiceProcedureBuilder(host, {
261
+ ...config,
262
+ steps: [...steps, createContextFilterStep(host, include)],
263
+ });
264
+ }
265
+ function access(accessConfig) {
266
+ assertUniqueStepName(config.steps, "access");
267
+ return createPermissionServiceProcedureBuilder(host, {
268
+ ...config,
269
+ steps: [...config.steps, createAccessStep(host, accessConfig)],
270
+ });
271
+ }
272
+ const builder = {
273
+ use(stepName, step) {
274
+ assertUniqueStepName(config.steps, stepName);
275
+ return createPermissionServiceProcedureBuilder(host, {
276
+ ...config,
277
+ steps: [...config.steps, createUseStep(stepName, step)],
278
+ });
279
+ },
280
+ mapInput(stepName, step) {
281
+ assertUniqueStepName(config.steps, stepName);
282
+ return createPermissionServiceProcedureBuilder(host, {
283
+ ...config,
284
+ steps: [...config.steps, createInputStep(stepName, step)],
285
+ });
286
+ },
287
+ addContextFilter,
288
+ requireAuth(scope) {
289
+ assertUniqueStepName(config.steps, "auth");
290
+ return createPermissionServiceProcedureBuilder(host, {
291
+ ...config,
292
+ steps: [...config.steps, createRequireAuthStep(host, scope ?? "user")],
293
+ });
294
+ },
295
+ access,
296
+ handle(handler) {
297
+ return createProcedureHandler(host, config, handler);
298
+ },
299
+ };
300
+ return builder;
301
+ }
@@ -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 base_abstract_1 = require("./base.abstract");
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
  }
@@ -1,40 +1,40 @@
1
1
  import type { QueryFilter, QueryInput } from "@m5kdev/commons/modules/schemas/query.schema";
2
- import type { Context, Session, User } from "../auth/auth.lib";
3
2
  import { Base } from "./base.abstract";
3
+ import { type AuthenticatedActor } from "./base.actor";
4
4
  import type { ServerResult, ServerResultAsync } from "./base.dto";
5
5
  import { type Entity, type ResourceActionGrant, type ResourceGrant } from "./base.grants";
6
- import type { BaseExternaRepository, BaseRepository } from "./base.repository";
7
- export declare class BaseService<Repositories extends Record<string, BaseRepository<any, any, any> | BaseExternaRepository>, Services extends Record<string, BaseService<any, any>>> extends Base {
6
+ import { type PermissionServiceProcedureBuilder, type ServiceProcedureBuilder, type ServiceProcedureContext } from "./base.procedure";
7
+ export type { PermissionServiceProcedureBuilder, ServiceProcedure, ServiceProcedureAccessConfig, ServiceProcedureAccessEntitiesConfig, ServiceProcedureAccessStateConfig, ServiceProcedureArgs, ServiceProcedureBuilder, ServiceProcedureContext, ServiceProcedureContextFilteredInput, ServiceProcedureContextFilterScope, ServiceProcedureEntityStepName, ServiceProcedureInputMapper, } from "./base.procedure";
8
+ export declare class BaseService<Repositories extends Record<string, Base>, Services extends Record<string, Base>, DefaultContext extends ServiceProcedureContext = ServiceProcedureContext> extends Base {
8
9
  repository: Repositories;
9
10
  service: Services;
10
11
  constructor(repository?: Repositories, service?: Services);
11
- addUserFilter(value: string, query?: QueryInput, columnId?: string, method?: QueryFilter["method"]): QueryInput;
12
- addContextFilter(ctx: Context, include?: {
12
+ addUserFilter(value: string, query?: undefined, columnId?: string, method?: QueryFilter["method"]): QueryInput;
13
+ addUserFilter<TQuery extends QueryInput>(value: string, query: TQuery, columnId?: string, method?: QueryFilter["method"]): TQuery;
14
+ protected procedure<TInput, TCtx extends ServiceProcedureContext = DefaultContext>(name: string): ServiceProcedureBuilder<TInput, TCtx, Repositories, Services>;
15
+ addContextFilter(actor: AuthenticatedActor, include?: {
13
16
  user?: boolean;
14
17
  organization?: boolean;
15
18
  team?: boolean;
16
- }, query?: QueryInput, map?: Record<string, {
19
+ }, query?: undefined, map?: Record<string, {
17
20
  columnId: string;
18
21
  method: QueryFilter["method"];
19
22
  }>): QueryInput;
23
+ addContextFilter<TQuery extends QueryInput>(actor: AuthenticatedActor, include: {
24
+ user?: boolean;
25
+ organization?: boolean;
26
+ team?: boolean;
27
+ } | undefined, query: TQuery, map?: Record<string, {
28
+ columnId: string;
29
+ method: QueryFilter["method"];
30
+ }>): TQuery;
20
31
  }
21
- export declare class BasePermissionService<Repositories extends Record<string, BaseRepository<any, any, any> | BaseExternaRepository>, Services extends Record<string, BaseService<any, any>>> extends BaseService<Repositories, Services> {
32
+ export declare class BasePermissionService<Repositories extends Record<string, Base>, Services extends Record<string, Base>, DefaultContext extends ServiceProcedureContext = ServiceProcedureContext> extends BaseService<Repositories, Services, DefaultContext> {
22
33
  grants: ResourceGrant[];
23
34
  constructor(repository: Repositories, service: Services, grants?: ResourceGrant[]);
24
- accessGuard<T extends Entity>(ctx: {
25
- session: Session;
26
- user: User;
27
- }, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): ServerResult<true>;
28
- accessGuardAsync<T extends Entity>(ctx: {
29
- session: Session;
30
- user: User;
31
- }, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<true>;
32
- checkPermission<T extends Entity>(ctx: {
33
- session: Session;
34
- user: User;
35
- }, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): boolean;
36
- checkPermissionAsync<T extends Entity>(ctx: {
37
- session: Session;
38
- user: User;
39
- }, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<boolean>;
35
+ accessGuard<T extends Entity>(actor: AuthenticatedActor, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): ServerResult<true>;
36
+ accessGuardAsync<T extends Entity>(actor: AuthenticatedActor, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<true>;
37
+ protected procedure<TInput, TCtx extends ServiceProcedureContext = DefaultContext>(name: string): PermissionServiceProcedureBuilder<TInput, TCtx, Repositories, Services>;
38
+ checkPermission<T extends Entity>(actor: AuthenticatedActor, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): boolean;
39
+ checkPermissionAsync<T extends Entity>(actor: AuthenticatedActor, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<boolean>;
40
40
  }
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BasePermissionService = exports.BaseService = void 0;
4
4
  const neverthrow_1 = require("neverthrow");
5
5
  const base_abstract_1 = require("./base.abstract");
6
+ const base_actor_1 = require("./base.actor");
6
7
  const base_grants_1 = require("./base.grants");
8
+ const base_procedure_1 = require("./base.procedure");
7
9
  class BaseService extends base_abstract_1.Base {
8
10
  repository;
9
11
  service;
@@ -25,7 +27,10 @@ class BaseService extends base_abstract_1.Base {
25
27
  ? { ...query, filters: [...(query?.filters ?? []), userFilter] }
26
28
  : { filters: [userFilter] };
27
29
  }
28
- addContextFilter(ctx, include = {
30
+ procedure(name) {
31
+ return (0, base_procedure_1.createServiceProcedureBuilder)(this, { name, steps: [] });
32
+ }
33
+ addContextFilter(actor, include = {
29
34
  user: true,
30
35
  organization: false,
31
36
  team: false,
@@ -49,23 +54,29 @@ class BaseService extends base_abstract_1.Base {
49
54
  columnId: map.userId.columnId,
50
55
  type: "string",
51
56
  method: map.userId.method,
52
- value: ctx.user.id,
57
+ value: actor.userId,
53
58
  });
54
59
  }
55
60
  if (include.organization) {
61
+ if (!(0, base_actor_1.validateActor)(actor, "organization")) {
62
+ throw new Error("Organization-scoped context filter requires an organization actor");
63
+ }
56
64
  filters.push({
57
65
  columnId: map.organizationId.columnId,
58
66
  type: "string",
59
67
  method: map.organizationId.method,
60
- value: ctx.session.activeOrganizationId ?? "",
68
+ value: actor.organizationId,
61
69
  });
62
70
  }
63
71
  if (include.team) {
72
+ if (!(0, base_actor_1.validateActor)(actor, "team")) {
73
+ throw new Error("Team-scoped context filter requires a team actor");
74
+ }
64
75
  filters.push({
65
76
  columnId: map.teamId.columnId,
66
77
  type: "string",
67
78
  method: map.teamId.method,
68
- value: ctx.session.activeTeamId ?? "",
79
+ value: actor.teamId,
69
80
  });
70
81
  }
71
82
  return query ? { ...query, filters: [...(query?.filters ?? []), ...filters] } : { filters };
@@ -78,27 +89,30 @@ class BasePermissionService extends BaseService {
78
89
  super(repository, service);
79
90
  this.grants = grants;
80
91
  }
81
- accessGuard(ctx, action, entities, grants) {
82
- const hasPermission = this.checkPermission(ctx, action, entities, grants);
92
+ accessGuard(actor, action, entities, grants) {
93
+ const hasPermission = this.checkPermission(actor, action, entities, grants);
83
94
  if (!hasPermission)
84
95
  return this.error("FORBIDDEN");
85
96
  return (0, neverthrow_1.ok)(true);
86
97
  }
87
- async accessGuardAsync(ctx, action, getEntities, grants) {
88
- const hasPermission = await this.checkPermissionAsync(ctx, action, getEntities, grants);
98
+ async accessGuardAsync(actor, action, getEntities, grants) {
99
+ const hasPermission = await this.checkPermissionAsync(actor, action, getEntities, grants);
89
100
  if (hasPermission.isErr())
90
101
  return (0, neverthrow_1.err)(hasPermission.error);
91
102
  if (!hasPermission.value)
92
103
  return this.error("FORBIDDEN");
93
104
  return (0, neverthrow_1.ok)(true);
94
105
  }
95
- checkPermission(ctx, action, entities, grants) {
106
+ procedure(name) {
107
+ return (0, base_procedure_1.createPermissionServiceProcedureBuilder)(this, { name, steps: [] });
108
+ }
109
+ checkPermission(actor, action, entities, grants) {
96
110
  const actionGrants = grants ?? this.grants.filter((grant) => grant.action === action);
97
- return (0, base_grants_1.checkPermissionSync)(ctx, actionGrants, entities);
111
+ return (0, base_grants_1.checkPermissionSync)(actor, actionGrants, entities);
98
112
  }
99
- async checkPermissionAsync(ctx, action, getEntities, grants) {
113
+ async checkPermissionAsync(actor, action, getEntities, grants) {
100
114
  const actionGrants = grants ?? this.grants.filter((grant) => grant.action === action);
101
- const permission = await (0, base_grants_1.checkPermissionAsync)(ctx, actionGrants, getEntities);
115
+ const permission = await (0, base_grants_1.checkPermissionAsync)(actor, actionGrants, getEntities);
102
116
  if (permission.isErr())
103
117
  return this.error("INTERNAL_SERVER_ERROR", "Failed to check permission", {
104
118
  cause: permission.error,
@@ -0,0 +1 @@
1
+ export {};