@m5kdev/backend 0.6.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 (45) 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 +4 -8
  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 +1 -1
  9. package/dist/src/modules/base/base.actor.d.ts +68 -0
  10. package/dist/src/modules/base/base.actor.js +99 -0
  11. package/dist/src/modules/base/base.actor.test.d.ts +1 -0
  12. package/dist/src/modules/base/base.actor.test.js +58 -0
  13. package/dist/src/modules/base/base.grants.d.ts +3 -7
  14. package/dist/src/modules/base/base.grants.js +22 -10
  15. package/dist/src/modules/base/base.grants.test.js +16 -45
  16. package/dist/src/modules/base/base.procedure.d.ts +17 -20
  17. package/dist/src/modules/base/base.procedure.js +36 -24
  18. package/dist/src/modules/base/base.service.d.ts +7 -19
  19. package/dist/src/modules/base/base.service.js +19 -12
  20. package/dist/src/modules/base/base.service.test.js +89 -61
  21. package/dist/src/modules/billing/billing.service.d.ts +4 -25
  22. package/dist/src/modules/billing/billing.service.js +6 -6
  23. package/dist/src/modules/billing/billing.trpc.d.ts +2 -2
  24. package/dist/src/modules/billing/billing.trpc.js +4 -6
  25. package/dist/src/modules/connect/connect.service.d.ts +19 -11
  26. package/dist/src/modules/connect/connect.service.js +10 -8
  27. package/dist/src/modules/connect/connect.trpc.d.ts +2 -2
  28. package/dist/src/modules/recurrence/recurrence.service.d.ts +36 -6
  29. package/dist/src/modules/recurrence/recurrence.service.js +13 -10
  30. package/dist/src/modules/recurrence/recurrence.trpc.d.ts +1 -1
  31. package/dist/src/modules/social/social.service.d.ts +3 -4
  32. package/dist/src/modules/social/social.service.js +3 -3
  33. package/dist/src/modules/tag/tag.service.d.ts +16 -12
  34. package/dist/src/modules/tag/tag.service.js +4 -4
  35. package/dist/src/modules/tag/tag.trpc.d.ts +1 -1
  36. package/dist/src/modules/workflow/workflow.service.d.ts +48 -8
  37. package/dist/src/modules/workflow/workflow.service.js +6 -6
  38. package/dist/src/modules/workflow/workflow.trpc.d.ts +2 -2
  39. package/dist/src/types.d.ts +4 -4
  40. package/dist/src/utils/trpc.d.ts +31 -41
  41. package/dist/src/utils/trpc.js +95 -0
  42. package/dist/src/utils/trpc.test.d.ts +1 -0
  43. package/dist/src/utils/trpc.test.js +154 -0
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +3 -3
@@ -6,7 +6,7 @@ import type { OpenRouterProvider } from "@openrouter/ai-sdk-provider";
6
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 { Context, User } from "../auth/auth.lib";
9
+ import type { RequiredServiceActor } from "../base/base.actor";
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";
@@ -26,17 +26,20 @@ type GenerateTextInput = {
26
26
  messages: ModelMessage[];
27
27
  prompt?: never;
28
28
  };
29
+ type AIServiceActorContext = {
30
+ actor: RequiredServiceActor<"user">;
31
+ };
29
32
  type AIServiceGenerateTextParams = Omit<GenerateTextParams, "model" | "prompt" | "messages"> & GenerateTextInput & {
30
33
  model: string;
31
34
  removeMDash?: boolean;
32
- ctx?: Context;
35
+ ctx?: AIServiceActorContext;
33
36
  };
34
37
  type AIServiceGenerateObjectParams<T extends ZodType> = Omit<GenerateTextParams, "model" | "prompt" | "messages" | "output"> & GenerateTextInput & {
35
38
  model: string;
36
39
  schema: T;
37
40
  repairAttempts?: number;
38
41
  repairModel?: string;
39
- ctx?: Context;
42
+ ctx?: AIServiceActorContext;
40
43
  };
41
44
  export declare class AIService<MastraInstance extends Mastra> extends BaseService<{
42
45
  aiUsage?: AiUsageRepository;
@@ -64,38 +67,33 @@ export declare class AIService<MastraInstance extends Mastra> extends BaseServic
64
67
  agentUse(agent: string, options: MastraAgentGenerateOptions & {
65
68
  prompt?: string;
66
69
  messages?: MessageListInput;
67
- }, ctx?: {
68
- user: User;
70
+ }, ctx?: AIServiceActorContext & {
69
71
  model?: string;
70
72
  }): ServerResultAsync<Awaited<ReturnType<MastraModelOutput<any>["getFullOutput"]>>>;
71
73
  agentText(agent: string, options: MastraAgentGenerateOptions & {
72
74
  prompt?: string;
73
75
  messages?: MessageListInput;
74
- }, ctx?: {
75
- user: User;
76
+ }, ctx?: AIServiceActorContext & {
76
77
  model?: string;
77
78
  }): ServerResultAsync<string>;
78
79
  agentTextResult(agent: string, options: MastraAgentGenerateOptions & {
79
80
  prompt?: string;
80
81
  messages?: MessageListInput;
81
- }, ctx?: {
82
- user: User;
82
+ }, ctx?: AIServiceActorContext & {
83
83
  model?: string;
84
84
  }): ServerResultAsync<FullOutput<any>>;
85
85
  agentObject<T extends ZodType<any>>(agent: string, options: MastraAgentGenerateOptions & {
86
86
  schema: T;
87
87
  prompt?: string;
88
88
  messages?: MessageListInput;
89
- }, ctx?: {
90
- user: User;
89
+ }, ctx?: AIServiceActorContext & {
91
90
  model?: string;
92
91
  }): ServerResultAsync<z.infer<T>>;
93
92
  agentObjectResult<T extends ZodType<any>>(agent: string, options: MastraAgentGenerateOptions & {
94
93
  schema: T;
95
94
  prompt?: string;
96
95
  messages?: MessageListInput;
97
- }, ctx?: {
98
- user: User;
96
+ }, ctx?: AIServiceActorContext & {
99
97
  model?: string;
100
98
  }): ServerResultAsync<FullOutput<any> & {
101
99
  object: z.infer<T>;
@@ -55,8 +55,8 @@ class AIService extends base_service_1.BaseService {
55
55
  if (!payload)
56
56
  return this.error("BAD_REQUEST", "No prompt or messages provided");
57
57
  const requestContext = options.requestContext ?? new request_context_1.RequestContext();
58
- if (ctx?.user) {
59
- requestContext.set("userId", ctx.user.id);
58
+ if (ctx?.actor) {
59
+ requestContext.set("userId", ctx.actor.userId);
60
60
  }
61
61
  if (ctx?.model) {
62
62
  requestContext.set("model", ctx.model);
@@ -69,7 +69,7 @@ class AIService extends base_service_1.BaseService {
69
69
  this.logger.info("AGENT USE DONE");
70
70
  if (this.repository.aiUsage) {
71
71
  const createUsageResult = await this.repository.aiUsage.create({
72
- userId: ctx?.user?.id,
72
+ userId: ctx?.actor?.userId,
73
73
  model: ctx?.model ?? "unknown",
74
74
  provider: "openrouter",
75
75
  feature: agent,
@@ -159,7 +159,7 @@ class AIService extends base_service_1.BaseService {
159
159
  const result = await (0, ai_1.generateText)(request);
160
160
  if (this.repository.aiUsage) {
161
161
  const createUsageResult = await this.repository.aiUsage.create({
162
- userId: ctx?.user?.id,
162
+ userId: ctx?.actor?.userId,
163
163
  model,
164
164
  provider: "openrouter",
165
165
  feature: "generateText",
@@ -194,7 +194,7 @@ class AIService extends base_service_1.BaseService {
194
194
  const result = await (0, ai_1.generateText)(request);
195
195
  if (this.repository.aiUsage) {
196
196
  const createUsageResult = await this.repository.aiUsage.create({
197
- userId: ctx?.user?.id,
197
+ userId: ctx?.actor?.userId,
198
198
  model,
199
199
  provider: "openrouter",
200
200
  feature: "generateObject",
@@ -213,7 +213,7 @@ class AIService extends base_service_1.BaseService {
213
213
  if (ai_1.NoObjectGeneratedError.isInstance(error)) {
214
214
  if (this.repository.aiUsage) {
215
215
  const createUsageResult = await this.repository.aiUsage.create({
216
- userId: ctx?.user?.id,
216
+ userId: ctx?.actor?.userId,
217
217
  model,
218
218
  provider: "openrouter",
219
219
  feature: "generateObject",
@@ -2,7 +2,7 @@ import type { Mastra } from "@mastra/core";
2
2
  import type { AIService } from "./ai.service";
3
3
  import { type TRPCMethods } from "../../utils/trpc";
4
4
  export declare function createAITRPC<MastraInstance extends Mastra>({ router, adminProcedure }: TRPCMethods, aiService: AIService<MastraInstance>): import("@trpc/server").TRPCBuiltRouter<{
5
- ctx: import("../auth/auth.lib").Context;
5
+ ctx: import("../../utils/trpc").Context;
6
6
  meta: any;
7
7
  errorShape: import("@trpc/server").TRPCDefaultErrorShape;
8
8
  transformer: true;
@@ -1,9 +1,9 @@
1
1
  import { type BetterAuthOptions, betterAuth } from "better-auth";
2
2
  import { type InferSelectModel } from "drizzle-orm";
3
3
  import type { LibSQLDatabase } from "drizzle-orm/libsql";
4
- import * as auth from "./auth.db";
5
4
  import type { BillingService } from "../billing/billing.service";
6
5
  import type { EmailService } from "../email/email.service";
6
+ import * as auth from "./auth.db";
7
7
  declare const schema: {
8
8
  users: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
9
9
  name: "users";
@@ -2345,10 +2345,6 @@ type Schema = typeof schema;
2345
2345
  export type Orm = LibSQLDatabase<Schema>;
2346
2346
  export type User = InferSelectModel<typeof auth.users>;
2347
2347
  export type Session = InferSelectModel<typeof auth.sessions>;
2348
- export type Context = {
2349
- session: Session;
2350
- user: User;
2351
- };
2352
2348
  export type BetterAuth = ReturnType<typeof betterAuth>;
2353
2349
  type CreateBetterAuthParams<O extends Orm, S extends Schema, E extends EmailService, B extends BillingService> = {
2354
2350
  orm: O;
@@ -3260,13 +3256,13 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
3260
3256
  $Infer: {
3261
3257
  body: ({
3262
3258
  permission: {
3263
- readonly user?: ("get" | "set-role" | "create" | "update" | "delete" | "list" | "ban" | "impersonate" | "set-password")[] | undefined;
3259
+ readonly user?: ("update" | "get" | "set-role" | "create" | "delete" | "list" | "ban" | "impersonate" | "set-password")[] | undefined;
3264
3260
  readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3265
3261
  };
3266
3262
  permissions?: never | undefined;
3267
3263
  } | {
3268
3264
  permissions: {
3269
- readonly user?: ("get" | "set-role" | "create" | "update" | "delete" | "list" | "ban" | "impersonate" | "set-password")[] | undefined;
3265
+ readonly user?: ("update" | "get" | "set-role" | "create" | "delete" | "list" | "ban" | "impersonate" | "set-password")[] | undefined;
3270
3266
  readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3271
3267
  };
3272
3268
  permission?: never | undefined;
@@ -4811,7 +4807,7 @@ export declare function createBetterAuth<O extends Orm, S extends Schema, E exte
4811
4807
  } | undefined;
4812
4808
  verification?: {
4813
4809
  modelName?: string;
4814
- fields?: Partial<Record<"createdAt" | "updatedAt" | "expiresAt" | "value" | "identifier", string>>;
4810
+ fields?: Partial<Record<"createdAt" | "updatedAt" | "expiresAt" | "identifier" | "value", string>>;
4815
4811
  additionalFields?: {
4816
4812
  [key: string]: import("better-auth", { with: { "resolution-mode": "import" } }).DBFieldAttribute;
4817
4813
  };
@@ -7,10 +7,10 @@ const drizzle_1 = require("better-auth/adapters/drizzle");
7
7
  const api_1 = require("better-auth/api");
8
8
  const plugins_1 = require("better-auth/plugins");
9
9
  const drizzle_orm_1 = require("drizzle-orm");
10
- const auth = tslib_1.__importStar(require("./auth.db"));
11
- const auth_utils_1 = require("./auth.utils");
12
10
  const logger_1 = require("../../utils/logger");
13
11
  const posthog_1 = require("../../utils/posthog");
12
+ const auth = tslib_1.__importStar(require("./auth.db"));
13
+ const auth_utils_1 = require("./auth.utils");
14
14
  const schema = { ...auth };
15
15
  function createBetterAuth({ orm, schema, services, hooks, options, config }) {
16
16
  const { email: emailService, billing: billingService } = services;
@@ -1,9 +1,9 @@
1
+ import type { Context } from "../../utils/trpc";
1
2
  import type { ServerResultAsync } from "../base/base.dto";
2
3
  import { BaseService } from "../base/base.service";
3
4
  import type { BillingService } from "../billing/billing.service";
4
5
  import type { EmailService } from "../email/email.service";
5
6
  import type { AccountClaim, AccountClaimMagicLinkOutput, AccountClaimOutput, Waitlist, WaitlistOutput } from "./auth.dto";
6
- import type { Context, User } from "./auth.lib";
7
7
  import type { AuthRepository } from "./auth.repository";
8
8
  type AuthServiceDependencies = {
9
9
  email: EmailService;
@@ -15,42 +15,22 @@ export declare class AuthService extends BaseService<{
15
15
  auth: AuthRepository;
16
16
  }, AuthServiceDependencies> {
17
17
  private getBillingService;
18
- private getActiveOrganizationId;
19
- getUserWaitlistCount({ user }: {
20
- user: User;
21
- }): ServerResultAsync<number>;
22
- getOnboarding({ user }: {
23
- user: User;
24
- }): ServerResultAsync<number>;
25
- setOnboarding(onboarding: number, { user }: {
26
- user: User;
27
- }): ServerResultAsync<number>;
28
- getPreferences({ user }: {
29
- user: User;
30
- }): ServerResultAsync<Record<string, unknown>>;
31
- setPreferences(preferences: Record<string, unknown>, { user }: {
32
- user: User;
33
- }): ServerResultAsync<Record<string, unknown>>;
18
+ private organizationActorFromCtx;
19
+ getUserWaitlistCount(ctx: Context): ServerResultAsync<number>;
20
+ getOnboarding(ctx: Context): ServerResultAsync<number>;
21
+ setOnboarding(onboarding: number, ctx: Context): ServerResultAsync<number>;
22
+ getPreferences(ctx: Context): ServerResultAsync<Record<string, unknown>>;
23
+ setPreferences(preferences: Record<string, unknown>, ctx: Context): ServerResultAsync<Record<string, unknown>>;
34
24
  getOrganizationPreferences(ctx: Context): ServerResultAsync<Record<string, unknown>>;
35
25
  setOrganizationPreferences(preferences: Record<string, unknown>, ctx: Context): ServerResultAsync<Record<string, unknown>>;
36
- getMetadata({ user }: {
37
- user: User;
38
- }): ServerResultAsync<Record<string, unknown>>;
39
- setMetadata(metadata: Record<string, unknown>, { user }: {
40
- user: User;
41
- }): ServerResultAsync<Record<string, unknown>>;
42
- getFlags({ user }: {
43
- user: User;
44
- }): ServerResultAsync<string[]>;
26
+ getMetadata(ctx: Context): ServerResultAsync<Record<string, unknown>>;
27
+ setMetadata(metadata: Record<string, unknown>, ctx: Context): ServerResultAsync<Record<string, unknown>>;
28
+ getFlags(ctx: Context): ServerResultAsync<string[]>;
45
29
  getOrganizationFlags(ctx: Context): ServerResultAsync<string[]>;
46
- setFlags(flags: string[], { user }: {
47
- user: User;
48
- }): ServerResultAsync<string[]>;
30
+ setFlags(flags: string[], ctx: Context): ServerResultAsync<string[]>;
49
31
  setOrganizationFlags(flags: string[], ctx: Context): ServerResultAsync<string[]>;
50
32
  listAdminWaitlist(): ServerResultAsync<WaitlistOutput[]>;
51
- listWaitlist({ user }: {
52
- user: User;
53
- }): ServerResultAsync<Waitlist[]>;
33
+ listWaitlist(ctx: Context): ServerResultAsync<Waitlist[]>;
54
34
  addToWaitlist({ email }: {
55
35
  email: string;
56
36
  }): ServerResultAsync<WaitlistOutput>;
@@ -60,14 +40,10 @@ export declare class AuthService extends BaseService<{
60
40
  inviteToWaitlist({ email, name }: {
61
41
  email: string;
62
42
  name?: string;
63
- }, { user }: {
64
- user: User;
65
- }): ServerResultAsync<Waitlist>;
43
+ }, ctx: Context): ServerResultAsync<Waitlist>;
66
44
  createInvitationCode({ name }: {
67
45
  name?: string;
68
- }, { user }: {
69
- user: User;
70
- }): ServerResultAsync<Waitlist>;
46
+ }, ctx: Context): ServerResultAsync<Waitlist>;
71
47
  joinWaitlist({ email }: {
72
48
  email: string;
73
49
  }): ServerResultAsync<WaitlistOutput>;
@@ -82,19 +58,13 @@ export declare class AuthService extends BaseService<{
82
58
  expiresInHours?: number;
83
59
  }): ServerResultAsync<AccountClaim>;
84
60
  listAccountClaims(): ServerResultAsync<AccountClaimOutput[]>;
85
- getMyAccountClaimStatus({ user }: {
86
- user: User;
87
- }): ServerResultAsync<AccountClaim | null>;
61
+ getMyAccountClaimStatus(ctx: Context): ServerResultAsync<AccountClaim | null>;
88
62
  setMyAccountClaimEmail({ email }: {
89
63
  email: string;
90
- }, { user }: {
91
- user: User;
92
- }): ServerResultAsync<{
64
+ }, ctx: Context): ServerResultAsync<{
93
65
  status: boolean;
94
66
  }>;
95
- acceptMyAccountClaim({ user }: {
96
- user: User;
97
- }): ServerResultAsync<{
67
+ acceptMyAccountClaim(ctx: Context): ServerResultAsync<{
98
68
  status: boolean;
99
69
  }>;
100
70
  generateAccountClaimMagicLink({ claimId, email, }: {
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthService = void 0;
4
4
  const neverthrow_1 = require("neverthrow");
5
5
  const posthog_1 = require("../../utils/posthog");
6
+ const errors_1 = require("../../utils/errors");
7
+ const base_actor_1 = require("../base/base.actor");
6
8
  const base_service_1 = require("../base/base.service");
7
9
  class AuthService extends base_service_1.BaseService {
8
10
  getBillingService() {
@@ -10,104 +12,111 @@ class AuthService extends base_service_1.BaseService {
10
12
  return null;
11
13
  return this.service.billing;
12
14
  }
13
- getActiveOrganizationId(ctx) {
14
- const organizationId = ctx.session.activeOrganizationId;
15
- if (!organizationId) {
16
- return this.repository.auth.error("FORBIDDEN", "No active organization");
15
+ organizationActorFromCtx(ctx) {
16
+ try {
17
+ return (0, neverthrow_1.ok)((0, base_actor_1.createActorFromContext)({ user: ctx.user, session: ctx.session }, "organization"));
18
+ }
19
+ catch (e) {
20
+ if (e instanceof errors_1.ServerError)
21
+ return (0, neverthrow_1.err)(e);
22
+ throw e;
17
23
  }
18
- return (0, neverthrow_1.ok)(organizationId);
19
24
  }
20
- async getUserWaitlistCount({ user }) {
21
- if (user.role === "admin")
25
+ async getUserWaitlistCount(ctx) {
26
+ if (ctx.actor.userRole === "admin")
22
27
  return (0, neverthrow_1.ok)(0);
23
- return this.repository.auth.getUserWaitlistCount(user.id);
28
+ return this.repository.auth.getUserWaitlistCount(ctx.actor.userId);
24
29
  }
25
- async getOnboarding({ user }) {
26
- return this.repository.auth.getOnboarding(user.id);
30
+ async getOnboarding(ctx) {
31
+ return this.repository.auth.getOnboarding(ctx.actor.userId);
27
32
  }
28
- async setOnboarding(onboarding, { user }) {
33
+ async setOnboarding(onboarding, ctx) {
29
34
  (0, posthog_1.posthogCapture)({
30
- distinctId: user.id,
35
+ distinctId: ctx.actor.userId,
31
36
  event: "onboarding_set",
32
37
  properties: {
33
38
  onboarding,
34
39
  },
35
40
  });
36
- return this.repository.auth.setOnboarding(user.id, onboarding);
41
+ return this.repository.auth.setOnboarding(ctx.actor.userId, onboarding);
37
42
  }
38
- async getPreferences({ user }) {
39
- return this.repository.auth.getPreferences(user.id);
43
+ async getPreferences(ctx) {
44
+ return this.repository.auth.getPreferences(ctx.actor.userId);
40
45
  }
41
- async setPreferences(preferences, { user }) {
46
+ async setPreferences(preferences, ctx) {
42
47
  (0, posthog_1.posthogCapture)({
43
- distinctId: user.id,
48
+ distinctId: ctx.actor.userId,
44
49
  event: "preferences_set",
45
50
  });
46
- return this.repository.auth.setPreferences(user.id, preferences);
51
+ return this.repository.auth.setPreferences(ctx.actor.userId, preferences);
47
52
  }
48
53
  async getOrganizationPreferences(ctx) {
49
- const organizationId = this.getActiveOrganizationId(ctx);
50
- if (organizationId.isErr())
51
- return (0, neverthrow_1.err)(organizationId.error);
52
- return this.repository.auth.getOrganizationPreferences(ctx.user.id, organizationId.value);
54
+ const org = this.organizationActorFromCtx(ctx);
55
+ if (org.isErr())
56
+ return (0, neverthrow_1.err)(org.error);
57
+ const actor = org.value;
58
+ return this.repository.auth.getOrganizationPreferences(actor.userId, actor.organizationId);
53
59
  }
54
60
  async setOrganizationPreferences(preferences, ctx) {
55
- const organizationId = this.getActiveOrganizationId(ctx);
56
- if (organizationId.isErr())
57
- return (0, neverthrow_1.err)(organizationId.error);
61
+ const org = this.organizationActorFromCtx(ctx);
62
+ if (org.isErr())
63
+ return (0, neverthrow_1.err)(org.error);
64
+ const actor = org.value;
58
65
  (0, posthog_1.posthogCapture)({
59
- distinctId: ctx.user.id,
66
+ distinctId: actor.userId,
60
67
  event: "organization_preferences_set",
61
68
  properties: {
62
- organizationId: organizationId.value,
69
+ organizationId: actor.organizationId,
63
70
  },
64
71
  });
65
- return this.repository.auth.setOrganizationPreferences(ctx.user.id, organizationId.value, preferences);
72
+ return this.repository.auth.setOrganizationPreferences(actor.userId, actor.organizationId, preferences);
66
73
  }
67
- async getMetadata({ user }) {
68
- return this.repository.auth.getMetadata(user.id);
74
+ async getMetadata(ctx) {
75
+ return this.repository.auth.getMetadata(ctx.actor.userId);
69
76
  }
70
- async setMetadata(metadata, { user }) {
77
+ async setMetadata(metadata, ctx) {
71
78
  (0, posthog_1.posthogCapture)({
72
- distinctId: user.id,
79
+ distinctId: ctx.actor.userId,
73
80
  event: "metadata_set",
74
81
  });
75
- return this.repository.auth.setMetadata(user.id, metadata);
82
+ return this.repository.auth.setMetadata(ctx.actor.userId, metadata);
76
83
  }
77
- async getFlags({ user }) {
78
- return this.repository.auth.getFlags(user.id);
84
+ async getFlags(ctx) {
85
+ return this.repository.auth.getFlags(ctx.actor.userId);
79
86
  }
80
87
  async getOrganizationFlags(ctx) {
81
- const organizationId = this.getActiveOrganizationId(ctx);
82
- if (organizationId.isErr())
83
- return (0, neverthrow_1.err)(organizationId.error);
84
- return this.repository.auth.getOrganizationFlags(ctx.user.id, organizationId.value);
88
+ const org = this.organizationActorFromCtx(ctx);
89
+ if (org.isErr())
90
+ return (0, neverthrow_1.err)(org.error);
91
+ const actor = org.value;
92
+ return this.repository.auth.getOrganizationFlags(actor.userId, actor.organizationId);
85
93
  }
86
- async setFlags(flags, { user }) {
94
+ async setFlags(flags, ctx) {
87
95
  (0, posthog_1.posthogCapture)({
88
- distinctId: user.id,
96
+ distinctId: ctx.actor.userId,
89
97
  event: "flags_set",
90
98
  });
91
- return this.repository.auth.setFlags(user.id, flags);
99
+ return this.repository.auth.setFlags(ctx.actor.userId, flags);
92
100
  }
93
101
  async setOrganizationFlags(flags, ctx) {
94
- const organizationId = this.getActiveOrganizationId(ctx);
95
- if (organizationId.isErr())
96
- return (0, neverthrow_1.err)(organizationId.error);
102
+ const org = this.organizationActorFromCtx(ctx);
103
+ if (org.isErr())
104
+ return (0, neverthrow_1.err)(org.error);
105
+ const actor = org.value;
97
106
  (0, posthog_1.posthogCapture)({
98
- distinctId: ctx.user.id,
107
+ distinctId: actor.userId,
99
108
  event: "organization_flags_set",
100
109
  properties: {
101
- organizationId: organizationId.value,
110
+ organizationId: actor.organizationId,
102
111
  },
103
112
  });
104
- return this.repository.auth.setOrganizationFlags(ctx.user.id, organizationId.value, flags);
113
+ return this.repository.auth.setOrganizationFlags(actor.userId, actor.organizationId, flags);
105
114
  }
106
115
  async listAdminWaitlist() {
107
116
  return this.repository.auth.listAdminWaitlist();
108
117
  }
109
- async listWaitlist({ user }) {
110
- return this.repository.auth.listWaitlist(user.id);
118
+ async listWaitlist(ctx) {
119
+ return this.repository.auth.listWaitlist(ctx.actor.userId);
111
120
  }
112
121
  async addToWaitlist({ email }) {
113
122
  return this.repository.auth.addToWaitlist(email);
@@ -123,20 +132,24 @@ class AuthService extends base_service_1.BaseService {
123
132
  await this.service.email.sendWaitlistInvite(waitlist.value.email, waitlist.value.code);
124
133
  return (0, neverthrow_1.ok)(waitlist.value);
125
134
  }
126
- async inviteToWaitlist({ email, name }, { user }) {
127
- const count = await this.repository.auth.getUserWaitlistCount(user.id);
135
+ async inviteToWaitlist({ email, name }, ctx) {
136
+ const count = await this.repository.auth.getUserWaitlistCount(ctx.user.id);
128
137
  if (count.isErr())
129
138
  return (0, neverthrow_1.err)(count.error);
130
139
  if (count.value >= 3)
131
140
  return this.repository.auth.error("BAD_REQUEST", "Run out of invites");
132
- const waitlist = await this.repository.auth.inviteToWaitlist({ email, userId: user.id, name });
141
+ const waitlist = await this.repository.auth.inviteToWaitlist({
142
+ email,
143
+ userId: ctx.user.id,
144
+ name,
145
+ });
133
146
  if (waitlist.isErr())
134
147
  return (0, neverthrow_1.err)(waitlist.error);
135
148
  if (!waitlist.value.code)
136
149
  return this.repository.auth.error("BAD_REQUEST");
137
- await this.service.email.sendWaitlistUserInvite(email, waitlist.value.code, user.name, name);
150
+ await this.service.email.sendWaitlistUserInvite(email, waitlist.value.code, ctx.user.name, name);
138
151
  (0, posthog_1.posthogCapture)({
139
- distinctId: user.id,
152
+ distinctId: ctx.user.id,
140
153
  event: "waitlist_invite_sent",
141
154
  properties: {
142
155
  email,
@@ -145,15 +158,15 @@ class AuthService extends base_service_1.BaseService {
145
158
  });
146
159
  return (0, neverthrow_1.ok)(waitlist.value);
147
160
  }
148
- async createInvitationCode({ name }, { user }) {
161
+ async createInvitationCode({ name }, ctx) {
149
162
  (0, posthog_1.posthogCapture)({
150
- distinctId: user.id,
163
+ distinctId: ctx.actor.userId,
151
164
  event: "waitlist_invitation_code_created",
152
165
  properties: {
153
166
  name,
154
167
  },
155
168
  });
156
- return this.repository.auth.createInvitationCode({ userId: user.id, name });
169
+ return this.repository.auth.createInvitationCode({ userId: ctx.actor.userId, name });
157
170
  }
158
171
  async joinWaitlist({ email }) {
159
172
  const waitlist = await this.repository.auth.joinWaitlist(email);
@@ -174,23 +187,23 @@ class AuthService extends base_service_1.BaseService {
174
187
  async listAccountClaims() {
175
188
  return this.repository.auth.listAccountClaims();
176
189
  }
177
- async getMyAccountClaimStatus({ user }) {
178
- return this.repository.auth.findPendingAccountClaimForUser(user.id);
190
+ async getMyAccountClaimStatus(ctx) {
191
+ return this.repository.auth.findPendingAccountClaimForUser(ctx.actor.userId);
179
192
  }
180
- async setMyAccountClaimEmail({ email }, { user }) {
181
- return this.repository.auth.setAccountClaimEmail({ userId: user.id, email });
193
+ async setMyAccountClaimEmail({ email }, ctx) {
194
+ return this.repository.auth.setAccountClaimEmail({ userId: ctx.actor.userId, email });
182
195
  }
183
- async acceptMyAccountClaim({ user }) {
184
- const pendingClaim = await this.repository.auth.findPendingAccountClaimForUser(user.id);
196
+ async acceptMyAccountClaim(ctx) {
197
+ const pendingClaim = await this.repository.auth.findPendingAccountClaimForUser(ctx.user.id);
185
198
  if (pendingClaim.isErr())
186
199
  return (0, neverthrow_1.err)(pendingClaim.error);
187
- const accepted = await this.repository.auth.acceptAccountClaim(user.id);
200
+ const accepted = await this.repository.auth.acceptAccountClaim(ctx.user.id);
188
201
  if (accepted.isErr())
189
202
  return (0, neverthrow_1.err)(accepted.error);
190
203
  if (pendingClaim.value) {
191
204
  const billingService = this.getBillingService();
192
205
  if (billingService) {
193
- await billingService.createUserHook({ user });
206
+ await billingService.createUserHook({ user: ctx.user });
194
207
  }
195
208
  }
196
209
  return (0, neverthrow_1.ok)(accepted.value);
@@ -1,7 +1,7 @@
1
1
  import { type TRPCMethods } from "../../utils/trpc";
2
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
- ctx: import("./auth.lib").Context;
4
+ ctx: import("../../utils/trpc").Context;
5
5
  meta: any;
6
6
  errorShape: import("@trpc/server").TRPCDefaultErrorShape;
7
7
  transformer: true;
@@ -0,0 +1,68 @@
1
+ import type { Session, User } from "../auth/auth.lib";
2
+ export type UserActor = {
3
+ userId: string;
4
+ userRole: string;
5
+ organizationId: string | null;
6
+ organizationRole: string | null;
7
+ teamId: string | null;
8
+ teamRole: string | null;
9
+ };
10
+ export type OrganizationActor = {
11
+ userId: string;
12
+ userRole: string;
13
+ organizationId: string;
14
+ organizationRole: string;
15
+ teamId: string | null;
16
+ teamRole: string | null;
17
+ };
18
+ export type TeamActor = {
19
+ userId: string;
20
+ userRole: string;
21
+ organizationId: string;
22
+ organizationRole: string;
23
+ teamId: string;
24
+ teamRole: string;
25
+ };
26
+ export type AuthenticatedActor = UserActor | OrganizationActor | TeamActor;
27
+ /** @deprecated Prefer `AuthenticatedActor` — kept for grants and legacy call sites */
28
+ export type ServiceActor = AuthenticatedActor;
29
+ export type Actor = {
30
+ user: UserActor;
31
+ organization: OrganizationActor;
32
+ team: TeamActor;
33
+ authenticated: AuthenticatedActor;
34
+ };
35
+ export type ActorScope = "user" | "organization" | "team";
36
+ export type RequiredServiceActor<Scope extends ActorScope> = Actor[Scope];
37
+ /** Claims shape used by tests and factories */
38
+ export type ServiceActorClaims = {
39
+ userId: string;
40
+ userRole: string;
41
+ organizationId?: string | null;
42
+ organizationRole?: string | null;
43
+ teamId?: string | null;
44
+ teamRole?: string | null;
45
+ };
46
+ /** @deprecated Prefer `OrganizationActor` */
47
+ export type ServiceOrganizationActor = OrganizationActor;
48
+ /** @deprecated Prefer `TeamActor` */
49
+ export type ServiceTeamActor = TeamActor;
50
+ export declare function createActorFromContext(context: {
51
+ user: User;
52
+ session: Session;
53
+ }, scope: "team"): TeamActor;
54
+ export declare function createActorFromContext(context: {
55
+ user: User;
56
+ session: Session;
57
+ }, scope: "organization"): OrganizationActor;
58
+ export declare function createActorFromContext(context: {
59
+ user: User;
60
+ session: Session;
61
+ }, scope: "user"): UserActor;
62
+ export declare function validateActor(actor: AuthenticatedActor, scope: ActorScope): boolean;
63
+ /**
64
+ * Builds a flat actor for tests / grants without session. Validates that team scope implies organization.
65
+ */
66
+ export declare function createServiceActor(claims: ServiceActorClaims): AuthenticatedActor;
67
+ export declare function getServiceActorScope(actor: AuthenticatedActor): ActorScope;
68
+ export declare function hasServiceActorScope(actor: AuthenticatedActor, scope: ActorScope): boolean;