@m5kdev/backend 0.4.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.
Files changed (38) hide show
  1. package/dist/src/modules/ai/ai.prompts.d.ts +5 -0
  2. package/dist/src/modules/ai/ai.prompts.js +16 -0
  3. package/dist/src/modules/ai/ai.service.d.ts +26 -12
  4. package/dist/src/modules/ai/ai.service.js +105 -15
  5. package/dist/src/modules/auth/auth.dto.d.ts +2 -2
  6. package/dist/src/modules/auth/auth.lib.d.ts +3 -3
  7. package/dist/src/modules/auth/auth.repository.d.ts +2 -2
  8. package/dist/src/modules/auth/auth.repository.js +1 -1
  9. package/dist/src/modules/auth/auth.service.d.ts +3 -3
  10. package/dist/src/modules/auth/auth.service.js +1 -1
  11. package/dist/src/modules/auth/auth.trpc.d.ts +6 -6
  12. package/dist/src/modules/auth/auth.trpc.js +1 -1
  13. package/dist/src/modules/base/base.abstract.d.ts +3 -2
  14. package/dist/src/modules/base/base.abstract.js +10 -1
  15. package/dist/src/modules/base/base.procedure.d.ts +112 -0
  16. package/dist/src/modules/base/base.procedure.js +289 -0
  17. package/dist/src/modules/base/base.repository.d.ts +1 -0
  18. package/dist/src/modules/base/base.repository.js +12 -2
  19. package/dist/src/modules/base/base.service.d.ts +17 -5
  20. package/dist/src/modules/base/base.service.js +7 -0
  21. package/dist/src/modules/base/base.service.test.d.ts +1 -0
  22. package/dist/src/modules/base/base.service.test.js +415 -0
  23. package/dist/src/modules/connect/connect.repository.d.ts +3 -3
  24. package/dist/src/modules/connect/connect.service.d.ts +4 -4
  25. package/dist/src/modules/connect/connect.trpc.d.ts +2 -2
  26. package/dist/src/modules/recurrence/recurrence.service.d.ts +29 -8
  27. package/dist/src/modules/recurrence/recurrence.service.js +3 -4
  28. package/dist/src/modules/recurrence/recurrence.trpc.d.ts +3 -3
  29. package/dist/src/modules/recurrence/recurrence.trpc.js +1 -1
  30. package/dist/src/modules/tag/tag.db.js +1 -1
  31. package/dist/src/modules/tag/tag.repository.js +27 -26
  32. package/dist/src/modules/tag/tag.service.d.ts +86 -15
  33. package/dist/src/modules/tag/tag.service.js +20 -12
  34. package/dist/src/modules/tag/tag.trpc.d.ts +3 -3
  35. package/dist/src/types.d.ts +5 -5
  36. package/dist/src/utils/trpc.d.ts +6 -6
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +6 -5
@@ -0,0 +1,5 @@
1
+ import { Prompt } from "./ai.prompt";
2
+ export declare const repairJsonPrompt: Prompt<{
3
+ text: string;
4
+ error: string;
5
+ }>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.repairJsonPrompt = void 0;
4
+ const ai_prompt_1 = require("./ai.prompt");
5
+ exports.repairJsonPrompt = new ai_prompt_1.Prompt(`
6
+ You are a JSON repair expert. You are given a JSON string that is invalid, error message and a schema that is used to parse the JSON string. You need to repair the JSON string and return the repaired JSON adher to the schema.
7
+
8
+ ## Text
9
+
10
+ {{text}}
11
+
12
+ ## Error
13
+
14
+ {{error}}
15
+
16
+ `);
@@ -3,10 +3,10 @@ import type { Mastra } from "@mastra/core";
3
3
  import type { FullOutput, MastraModelOutput } from "@mastra/core/stream";
4
4
  import { MDocument } from "@mastra/rag";
5
5
  import type { OpenRouterProvider } from "@openrouter/ai-sdk-provider";
6
- import { generateObject, generateText } from "ai";
6
+ import { generateText, type ModelMessage } from "ai";
7
7
  import type Replicate from "replicate";
8
8
  import type { ZodType, z } from "zod";
9
- import type { User } from "../auth/auth.lib";
9
+ import type { Context, User } from "../auth/auth.lib";
10
10
  import type { ServerResultAsync } from "../base/base.dto";
11
11
  import { BaseService } from "../base/base.service";
12
12
  import type { AiUsageRepository, AiUsageRow } from "./ai.repository";
@@ -18,6 +18,26 @@ type MessageListInput = {
18
18
  role: "user" | "assistant" | "system";
19
19
  content: string;
20
20
  }[];
21
+ type GenerateTextParams = Parameters<typeof generateText>[0];
22
+ type GenerateTextInput = {
23
+ prompt: string | ModelMessage[];
24
+ messages?: never;
25
+ } | {
26
+ messages: ModelMessage[];
27
+ prompt?: never;
28
+ };
29
+ type AIServiceGenerateTextParams = Omit<GenerateTextParams, "model" | "prompt" | "messages"> & GenerateTextInput & {
30
+ model: string;
31
+ removeMDash?: boolean;
32
+ ctx?: Context;
33
+ };
34
+ type AIServiceGenerateObjectParams<T extends ZodType> = Omit<GenerateTextParams, "model" | "prompt" | "messages" | "output"> & GenerateTextInput & {
35
+ model: string;
36
+ schema: T;
37
+ repairAttempts?: number;
38
+ repairModel?: string;
39
+ ctx?: Context;
40
+ };
21
41
  export declare class AIService<MastraInstance extends Mastra> extends BaseService<{
22
42
  aiUsage?: AiUsageRepository;
23
43
  }, {
@@ -39,8 +59,8 @@ export declare class AIService<MastraInstance extends Mastra> extends BaseServic
39
59
  replicate?: Replicate;
40
60
  });
41
61
  getMastra(): MastraInstance;
42
- prepareModel(model: string): any;
43
- prepareEmbeddingModel(model: string): any;
62
+ prepareModel(model: string): ReturnType<OpenRouterProvider["chat"]>;
63
+ prepareEmbeddingModel(model: string): ReturnType<OpenRouterProvider["textEmbeddingModel"]>;
44
64
  agentUse(agent: string, options: MastraAgentGenerateOptions & {
45
65
  prompt?: string;
46
66
  messages?: MessageListInput;
@@ -94,14 +114,8 @@ export declare class AIService<MastraInstance extends Mastra> extends BaseServic
94
114
  }[], model?: string): ServerResultAsync<{
95
115
  embeddings: number[][];
96
116
  }>;
97
- generateText(params: Omit<Parameters<typeof generateText>[0], "model"> & {
98
- model: string;
99
- removeMDash?: boolean;
100
- }): ServerResultAsync<string>;
101
- generateObject<T extends ZodType>(params: Omit<Parameters<typeof generateObject<T>>[0], "model" | "schema"> & {
102
- model: string;
103
- schema: T;
104
- }): ServerResultAsync<z.infer<T>>;
117
+ generateText(params: AIServiceGenerateTextParams): ServerResultAsync<string>;
118
+ generateObject<T extends ZodType>(params: AIServiceGenerateObjectParams<T>): ServerResultAsync<z.infer<T>>;
105
119
  generateReplicate(model: Parameters<Replicate["run"]>[0], options: Parameters<Replicate["run"]>[1]): ServerResultAsync<object>;
106
120
  generateTranscript(file_url: string): ServerResultAsync<{
107
121
  text: string;
@@ -6,8 +6,10 @@ const ai_utils_1 = require("@m5kdev/commons/modules/ai/ai.utils");
6
6
  const request_context_1 = require("@mastra/core/request-context");
7
7
  const rag_1 = require("@mastra/rag");
8
8
  const ai_1 = require("ai");
9
+ const jsonrepair_1 = require("jsonrepair");
9
10
  const neverthrow_1 = require("neverthrow");
10
11
  const base_service_1 = require("../base/base.service");
12
+ const ai_prompts_1 = require("./ai.prompts");
11
13
  class AIService extends base_service_1.BaseService {
12
14
  helpers = {
13
15
  arrayToPseudoXML: ai_utils_1.arrayToPseudoXML,
@@ -31,15 +33,19 @@ class AIService extends base_service_1.BaseService {
31
33
  if (!this.openrouter) {
32
34
  throw new Error("OpenRouter is not configured");
33
35
  }
34
- const openrouterModel = this.openrouter.chat(model);
35
- return openrouterModel;
36
+ return this.openrouter.chat(model, {
37
+ usage: {
38
+ include: true,
39
+ },
40
+ });
36
41
  }
37
42
  prepareEmbeddingModel(model) {
38
43
  if (!this.openrouter) {
39
44
  throw new Error("OpenRouter is not configured");
40
45
  }
41
- const openrouterModel = this.openrouter.textEmbeddingModel(model);
42
- return openrouterModel;
46
+ const openrouter = this.openrouter;
47
+ return (openrouter.embeddingModel?.(model) ??
48
+ openrouter.textEmbeddingModel(model));
43
49
  }
44
50
  async agentUse(agent, options, ctx) {
45
51
  return this.throwableAsync(async () => {
@@ -146,21 +152,105 @@ class AIService extends base_service_1.BaseService {
146
152
  }
147
153
  async generateText(params) {
148
154
  return this.throwableAsync(async () => {
149
- const { removeMDash = true, model, ...rest } = params;
150
- const result = await (0, ai_1.generateText)({ ...rest, model: this.prepareModel(model) });
155
+ const { removeMDash = true, model, prompt, messages, ctx, ...rest } = params;
156
+ const request = messages
157
+ ? { ...rest, model: this.prepareModel(model), messages }
158
+ : { ...rest, model: this.prepareModel(model), prompt };
159
+ const result = await (0, ai_1.generateText)(request);
160
+ if (this.repository.aiUsage) {
161
+ const createUsageResult = await this.repository.aiUsage.create({
162
+ userId: ctx?.user?.id,
163
+ model,
164
+ provider: "openrouter",
165
+ feature: "generateText",
166
+ traceId: result.providerMetadata?.openrouter?.traceId?.toString(),
167
+ inputTokens: result.usage.inputTokens,
168
+ outputTokens: result.usage.outputTokens,
169
+ totalTokens: result.usage.totalTokens,
170
+ cost: result?.providerMetadata?.openrouter?.usage?.cost ?? 0,
171
+ });
172
+ if (createUsageResult.isErr())
173
+ return (0, neverthrow_1.err)(createUsageResult.error);
174
+ }
151
175
  return (0, neverthrow_1.ok)(removeMDash ? result.text.replace(/\u2013|\u2014/g, "-") : result.text);
152
176
  });
153
177
  }
154
178
  async generateObject(params) {
155
- return this.throwableAsync(async () => {
156
- const model = this.prepareModel(params.model);
157
- const result = await (0, ai_1.generateObject)({
158
- ...params,
159
- model,
160
- schema: params.schema,
161
- });
162
- return (0, neverthrow_1.ok)(result.object);
163
- });
179
+ const { model, schema, prompt, messages, repairAttempts = 0, repairModel, ctx, ...rest } = params;
180
+ const request = messages
181
+ ? {
182
+ ...rest,
183
+ model: this.prepareModel(model),
184
+ messages,
185
+ output: ai_1.Output.object({ schema }),
186
+ }
187
+ : {
188
+ ...rest,
189
+ model: this.prepareModel(model),
190
+ prompt,
191
+ output: ai_1.Output.object({ schema }),
192
+ };
193
+ try {
194
+ const result = await (0, ai_1.generateText)(request);
195
+ if (this.repository.aiUsage) {
196
+ const createUsageResult = await this.repository.aiUsage.create({
197
+ userId: ctx?.user?.id,
198
+ model,
199
+ provider: "openrouter",
200
+ feature: "generateObject",
201
+ traceId: result.providerMetadata?.openrouter?.traceId?.toString(),
202
+ inputTokens: result.usage.inputTokens,
203
+ outputTokens: result.usage.outputTokens,
204
+ totalTokens: result.usage.totalTokens,
205
+ cost: result?.providerMetadata?.openrouter?.usage?.cost ?? 0,
206
+ });
207
+ if (createUsageResult.isErr())
208
+ return (0, neverthrow_1.err)(createUsageResult.error);
209
+ }
210
+ return (0, neverthrow_1.ok)(result.output);
211
+ }
212
+ catch (error) {
213
+ if (ai_1.NoObjectGeneratedError.isInstance(error)) {
214
+ if (this.repository.aiUsage) {
215
+ const createUsageResult = await this.repository.aiUsage.create({
216
+ userId: ctx?.user?.id,
217
+ model,
218
+ provider: "openrouter",
219
+ feature: "generateObject",
220
+ traceId: null,
221
+ inputTokens: error?.usage?.inputTokens,
222
+ outputTokens: error?.usage?.outputTokens,
223
+ totalTokens: error?.usage?.totalTokens,
224
+ cost: 0,
225
+ });
226
+ if (createUsageResult.isErr())
227
+ return (0, neverthrow_1.err)(createUsageResult.error);
228
+ }
229
+ if (error.text) {
230
+ const repairedText = (0, jsonrepair_1.jsonrepair)(error.text);
231
+ const parsed = schema.safeParse(repairedText);
232
+ if (parsed.success)
233
+ return (0, neverthrow_1.ok)(parsed.data);
234
+ if (repairAttempts === 0)
235
+ return this.error("PARSE_ERROR", "AI: Agent object failed", { cause: error });
236
+ return this.generateObject({
237
+ ...rest,
238
+ prompt: ai_prompts_1.repairJsonPrompt.compile({
239
+ text: error.text,
240
+ error: JSON.stringify(error.cause ?? "Unknown error"),
241
+ }),
242
+ repairAttempts: repairAttempts - 1,
243
+ model: repairModel ?? model,
244
+ schema,
245
+ ctx,
246
+ });
247
+ }
248
+ return this.error("PARSE_ERROR", "AI: Agent object failed without text", {
249
+ cause: error,
250
+ });
251
+ }
252
+ return this.error("BAD_REQUEST", "AI: Provided failed to generate object", { cause: error });
253
+ }
164
254
  }
165
255
  async generateReplicate(model, options) {
166
256
  return this.throwableAsync(async () => {
@@ -10,9 +10,9 @@ export declare const waitlistSchema: z.ZodObject<{
10
10
  expiresAt: z.ZodNullable<z.ZodDate>;
11
11
  }, z.core.$strip>;
12
12
  export declare const waitlistOutputSchema: z.ZodObject<{
13
- name: z.ZodNullable<z.ZodString>;
14
13
  id: z.ZodString;
15
14
  email: z.ZodNullable<z.ZodString>;
15
+ name: z.ZodNullable<z.ZodString>;
16
16
  status: z.ZodString;
17
17
  createdAt: z.ZodDate;
18
18
  updatedAt: z.ZodNullable<z.ZodDate>;
@@ -53,9 +53,9 @@ export declare const accountClaimMagicLinkSchema: z.ZodObject<{
53
53
  createdAt: z.ZodDate;
54
54
  }, z.core.$strip>;
55
55
  export declare const accountClaimMagicLinkOutputSchema: z.ZodObject<{
56
- url: z.ZodString;
57
56
  id: z.ZodString;
58
57
  email: z.ZodString;
58
+ url: z.ZodString;
59
59
  createdAt: z.ZodDate;
60
60
  userId: z.ZodString;
61
61
  expiresAt: z.ZodNullable<z.ZodDate>;
@@ -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" | "delete" | "list" | "create" | "update" | "set-role" | "ban" | "impersonate" | "set-password")[] | undefined;
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" | "delete" | "list" | "create" | "update" | "set-role" | "ban" | "impersonate" | "set-password")[] | undefined;
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;
@@ -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<"value" | "createdAt" | "updatedAt" | "expiresAt" | "identifier", string>>;
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
  };
@@ -1,8 +1,8 @@
1
1
  import type { LibSQLDatabase } from "drizzle-orm/libsql";
2
- import * as auth from "./auth.db";
3
- import type { AccountClaim, AccountClaimMagicLink, AccountClaimMagicLinkOutput, AccountClaimOutput, Waitlist, WaitlistOutput } from "./auth.dto";
4
2
  import type { ServerResultAsync } from "../base/base.dto";
5
3
  import { BaseRepository } from "../base/base.repository";
4
+ import * as auth from "./auth.db";
5
+ import type { AccountClaim, AccountClaimMagicLink, AccountClaimMagicLinkOutput, AccountClaimOutput, Waitlist, WaitlistOutput } from "./auth.dto";
6
6
  declare const schema: {
7
7
  users: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
8
8
  name: "users";
@@ -5,8 +5,8 @@ const tslib_1 = require("tslib");
5
5
  const drizzle_orm_1 = require("drizzle-orm");
6
6
  const neverthrow_1 = require("neverthrow");
7
7
  const uuid_1 = require("uuid");
8
- const auth = tslib_1.__importStar(require("./auth.db"));
9
8
  const base_repository_1 = require("../base/base.repository");
9
+ const auth = tslib_1.__importStar(require("./auth.db"));
10
10
  const schema = { ...auth };
11
11
  function parseOrganizationMetadata(metadata) {
12
12
  if (!metadata)
@@ -1,10 +1,10 @@
1
- import type { AccountClaim, AccountClaimMagicLinkOutput, AccountClaimOutput, Waitlist, WaitlistOutput } from "./auth.dto";
2
- import type { Context, User } from "./auth.lib";
3
- import type { AuthRepository } from "./auth.repository";
4
1
  import type { ServerResultAsync } from "../base/base.dto";
5
2
  import { BaseService } from "../base/base.service";
6
3
  import type { BillingService } from "../billing/billing.service";
7
4
  import type { EmailService } from "../email/email.service";
5
+ import type { AccountClaim, AccountClaimMagicLinkOutput, AccountClaimOutput, Waitlist, WaitlistOutput } from "./auth.dto";
6
+ import type { Context, User } from "./auth.lib";
7
+ import type { AuthRepository } from "./auth.repository";
8
8
  type AuthServiceDependencies = {
9
9
  email: EmailService;
10
10
  } | {
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthService = void 0;
4
4
  const neverthrow_1 = require("neverthrow");
5
- const base_service_1 = require("../base/base.service");
6
5
  const posthog_1 = require("../../utils/posthog");
6
+ const base_service_1 = require("../base/base.service");
7
7
  class AuthService extends base_service_1.BaseService {
8
8
  getBillingService() {
9
9
  if (!("billing" in this.service))
@@ -1,5 +1,5 @@
1
- import type { AuthService } from "./auth.service";
2
1
  import { type TRPCMethods } from "../../utils/trpc";
2
+ import type { AuthService } from "./auth.service";
3
3
  export declare function createAuthTRPC({ router, publicProcedure, privateProcedure: procedure, adminProcedure }: TRPCMethods, authService: AuthService): import("@trpc/server").TRPCBuiltRouter<{
4
4
  ctx: import("./auth.lib").Context;
5
5
  meta: any;
@@ -138,8 +138,8 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
138
138
  listAdminWaitlist: import("@trpc/server").TRPCQueryProcedure<{
139
139
  input: void;
140
140
  output: {
141
- name: string | null;
142
141
  id: string;
142
+ name: string | null;
143
143
  email: string | null;
144
144
  createdAt: Date;
145
145
  updatedAt: Date | null;
@@ -152,8 +152,8 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
152
152
  email: string;
153
153
  };
154
154
  output: {
155
- name: string | null;
156
155
  id: string;
156
+ name: string | null;
157
157
  email: string | null;
158
158
  createdAt: Date;
159
159
  updatedAt: Date | null;
@@ -183,8 +183,8 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
183
183
  id: string;
184
184
  };
185
185
  output: {
186
- name: string | null;
187
186
  id: string;
187
+ name: string | null;
188
188
  email: string | null;
189
189
  createdAt: Date;
190
190
  updatedAt: Date | null;
@@ -197,8 +197,8 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
197
197
  id: string;
198
198
  };
199
199
  output: {
200
- name: string | null;
201
200
  id: string;
201
+ name: string | null;
202
202
  email: string | null;
203
203
  createdAt: Date;
204
204
  updatedAt: Date | null;
@@ -211,8 +211,8 @@ export declare function createAuthTRPC({ router, publicProcedure, privateProcedu
211
211
  email: string;
212
212
  };
213
213
  output: {
214
- name: string | null;
215
214
  id: string;
215
+ name: string | null;
216
216
  email: string | null;
217
217
  createdAt: Date;
218
218
  updatedAt: Date | null;
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAuthTRPC = createAuthTRPC;
4
4
  const zod_1 = require("zod");
5
- const auth_dto_1 = require("./auth.dto");
6
5
  const trpc_1 = require("../../utils/trpc");
6
+ const auth_dto_1 = require("./auth.dto");
7
7
  function createAuthTRPC({ router, publicProcedure, privateProcedure: procedure, adminProcedure }, authService) {
8
8
  return router({
9
9
  getUserWaitlistCount: procedure.output(zod_1.z.number()).query(async ({ ctx }) => {
@@ -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 {};