@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,415 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const neverthrow_1 = require("neverthrow");
4
+ const errors_1 = require("../../utils/errors");
5
+ const base_service_1 = require("./base.service");
6
+ function createUser(overrides = {}) {
7
+ return {
8
+ id: "user-1",
9
+ role: "member",
10
+ email: "user@example.com",
11
+ name: "User",
12
+ createdAt: new Date(),
13
+ updatedAt: new Date(),
14
+ banned: false,
15
+ banReason: null,
16
+ banExpires: null,
17
+ emailVerified: true,
18
+ image: null,
19
+ onboarding: null,
20
+ preferences: null,
21
+ flags: null,
22
+ stripeCustomerId: null,
23
+ paymentCustomerId: null,
24
+ paymentPlanTier: null,
25
+ paymentPlanExpiresAt: null,
26
+ ...overrides,
27
+ };
28
+ }
29
+ function createSession(overrides = {}) {
30
+ return {
31
+ id: "session-1",
32
+ createdAt: new Date(),
33
+ updatedAt: new Date(),
34
+ expiresAt: new Date(Date.now() + 1000 * 60 * 60),
35
+ token: "token",
36
+ ipAddress: null,
37
+ userAgent: null,
38
+ userId: "user-1",
39
+ activeOrganizationId: null,
40
+ activeOrganizationRole: null,
41
+ activeTeamId: null,
42
+ activeTeamRole: null,
43
+ impersonatedBy: null,
44
+ ...overrides,
45
+ };
46
+ }
47
+ describe("BaseService procedure builder", () => {
48
+ it("runs chained steps in order and exposes state by step name", async () => {
49
+ const events = [];
50
+ class PipelineService extends base_service_1.BaseService {
51
+ run = this.procedure("run")
52
+ .use("trimmed", ({ input }) => {
53
+ events.push("trimmed");
54
+ return input.value.trim();
55
+ })
56
+ .use("upper", ({ state }) => {
57
+ events.push("upper");
58
+ return state.trimmed.toUpperCase();
59
+ })
60
+ .handle(({ state }) => {
61
+ events.push("handler");
62
+ return (0, neverthrow_1.ok)({
63
+ trimmed: state.trimmed,
64
+ upper: state.upper,
65
+ });
66
+ });
67
+ }
68
+ const service = new PipelineService();
69
+ const result = await service.run({ value: " hello " }, {});
70
+ expect(result.isOk()).toBe(true);
71
+ if (result.isOk()) {
72
+ expect(result.value).toEqual({
73
+ trimmed: "hello",
74
+ upper: "HELLO",
75
+ });
76
+ }
77
+ expect(events).toEqual(["trimmed", "upper", "handler"]);
78
+ });
79
+ it("addContextFilter wraps auth and maps query input with the default step name", async () => {
80
+ class QueryService extends base_service_1.BaseService {
81
+ run = this.procedure("run")
82
+ .addContextFilter(["user", "organization"])
83
+ .handle(({ input, state }) => (0, neverthrow_1.ok)({
84
+ input,
85
+ stateMatches: state.contextFilter === input,
86
+ }));
87
+ }
88
+ const service = new QueryService();
89
+ const unauthorized = await service.run({ search: "hello" }, {});
90
+ expect(unauthorized.isErr()).toBe(true);
91
+ if (unauthorized.isErr()) {
92
+ expect(unauthorized.error.code).toBe("UNAUTHORIZED");
93
+ }
94
+ const authorized = await service.run({ search: "hello" }, {
95
+ user: createUser(),
96
+ session: createSession({
97
+ activeOrganizationId: "org-1",
98
+ }),
99
+ });
100
+ expect(authorized.isOk()).toBe(true);
101
+ if (authorized.isOk()) {
102
+ expect(authorized.value.input.search).toBe("hello");
103
+ expect(authorized.value.stateMatches).toBe(true);
104
+ expect(authorized.value.input.filters).toEqual([
105
+ {
106
+ columnId: "userId",
107
+ type: "string",
108
+ method: "equals",
109
+ value: "user-1",
110
+ },
111
+ {
112
+ columnId: "organizationId",
113
+ type: "string",
114
+ method: "equals",
115
+ value: "org-1",
116
+ },
117
+ ]);
118
+ }
119
+ });
120
+ it("mapInput updates the input seen by later steps and the handler", async () => {
121
+ class QueryService extends base_service_1.BaseService {
122
+ run = this.procedure("run")
123
+ .requireAuth()
124
+ .mapInput("scopedQuery", ({ input, ctx }) => this.addContextFilter(ctx, { user: true, organization: true, team: true }, input, {
125
+ userId: {
126
+ columnId: "authorUserId",
127
+ method: "equals",
128
+ },
129
+ organizationId: {
130
+ columnId: "organizationId",
131
+ method: "equals",
132
+ },
133
+ teamId: {
134
+ columnId: "teamId",
135
+ method: "equals",
136
+ },
137
+ }))
138
+ .use("filterCount", ({ input }) => input.filters?.length ?? 0)
139
+ .handle(({ input, state }) => (0, neverthrow_1.ok)({
140
+ input,
141
+ filterCount: state.filterCount,
142
+ scopedInputMatches: state.scopedQuery === input,
143
+ }));
144
+ }
145
+ const service = new QueryService();
146
+ const result = await service.run({ search: "hello" }, {
147
+ user: createUser(),
148
+ session: createSession({
149
+ activeOrganizationId: "org-1",
150
+ activeTeamId: "team-1",
151
+ }),
152
+ });
153
+ expect(result.isOk()).toBe(true);
154
+ if (result.isOk()) {
155
+ expect(result.value.input.search).toBe("hello");
156
+ expect(result.value.filterCount).toBe(3);
157
+ expect(result.value.scopedInputMatches).toBe(true);
158
+ expect(result.value.input.filters).toEqual([
159
+ {
160
+ columnId: "authorUserId",
161
+ type: "string",
162
+ method: "equals",
163
+ value: "user-1",
164
+ },
165
+ {
166
+ columnId: "organizationId",
167
+ type: "string",
168
+ method: "equals",
169
+ value: "org-1",
170
+ },
171
+ {
172
+ columnId: "teamId",
173
+ type: "string",
174
+ method: "equals",
175
+ value: "team-1",
176
+ },
177
+ ]);
178
+ }
179
+ });
180
+ it("uses the base service default context type for procedures", async () => {
181
+ class ContextService extends base_service_1.BaseService {
182
+ run = this.procedure("run")
183
+ .requireAuth()
184
+ .handle(({ input, ctx }) => (0, neverthrow_1.ok)(`${ctx.requestId}:${ctx.user.id}:${input.value}`));
185
+ }
186
+ const service = new ContextService();
187
+ const result = await service.run({ value: "ok" }, {
188
+ requestId: "req-1",
189
+ user: createUser(),
190
+ session: createSession(),
191
+ });
192
+ expect(result.isOk()).toBe(true);
193
+ if (result.isOk()) {
194
+ expect(result.value).toBe("req-1:user-1:ok");
195
+ }
196
+ });
197
+ it("requireAuth rejects missing auth context", async () => {
198
+ class ProtectedService extends base_service_1.BaseService {
199
+ run = this.procedure("run")
200
+ .requireAuth()
201
+ .handle(({ input, ctx }) => (0, neverthrow_1.ok)(`${ctx.user.id}:${input.value}`));
202
+ }
203
+ const service = new ProtectedService();
204
+ const result = await service.run({ value: "x" }, {});
205
+ expect(result.isErr()).toBe(true);
206
+ if (result.isErr()) {
207
+ expect(result.error.code).toBe("UNAUTHORIZED");
208
+ }
209
+ });
210
+ it("requireAuth allows valid context", async () => {
211
+ class ProtectedService extends base_service_1.BaseService {
212
+ run = this.procedure("run")
213
+ .requireAuth()
214
+ .handle(({ input, ctx }) => (0, neverthrow_1.ok)(`${ctx.user.id}:${input.value}`));
215
+ }
216
+ const service = new ProtectedService();
217
+ const result = await service.run({ value: "ok" }, { user: createUser(), session: createSession() });
218
+ expect(result.isOk()).toBe(true);
219
+ if (result.isOk()) {
220
+ expect(result.value).toBe("user-1:ok");
221
+ }
222
+ });
223
+ it("normalizes thrown async errors through throwableAsync", async () => {
224
+ class ThrowingService extends base_service_1.BaseService {
225
+ run = this.procedure("run").handle(async () => {
226
+ throw new Error("boom");
227
+ });
228
+ }
229
+ const service = new ThrowingService();
230
+ const result = await service.run(undefined, {});
231
+ expect(result.isErr()).toBe(true);
232
+ if (result.isErr()) {
233
+ expect(result.error.code).toBe("INTERNAL_SERVER_ERROR");
234
+ expect(result.error.cause).toBeInstanceOf(Error);
235
+ }
236
+ });
237
+ it("logs metadata stages without input or context payloads", async () => {
238
+ class LoggedService extends base_service_1.BaseService {
239
+ run = this.procedure("run")
240
+ .requireAuth()
241
+ .handle(({ ctx }) => (0, neverthrow_1.ok)(ctx.user.id));
242
+ }
243
+ const service = new LoggedService();
244
+ const debugSpy = jest.fn();
245
+ service.logger.debug = debugSpy;
246
+ const result = await service.run({ secret: "top-secret" }, {
247
+ user: createUser(),
248
+ session: createSession(),
249
+ token: "super-token",
250
+ });
251
+ expect(result.isOk()).toBe(true);
252
+ expect(debugSpy).toHaveBeenCalledTimes(3);
253
+ const payloads = debugSpy.mock.calls.map(([payload]) => payload);
254
+ expect(payloads.map((payload) => payload.stage)).toEqual(["start", "auth_passed", "success"]);
255
+ const serializedPayloads = JSON.stringify(payloads);
256
+ expect(serializedPayloads).not.toContain("top-secret");
257
+ expect(serializedPayloads).not.toContain("super-token");
258
+ expect(payloads.every((payload) => !("input" in payload))).toBe(true);
259
+ expect(payloads.every((payload) => !("ctx" in payload))).toBe(true);
260
+ });
261
+ });
262
+ describe("BasePermissionService procedure builder", () => {
263
+ it("passes loaded entities into the handler when access is granted", async () => {
264
+ const grants = [
265
+ {
266
+ action: "read",
267
+ level: "user",
268
+ role: "member",
269
+ access: "own",
270
+ },
271
+ ];
272
+ class PermissionService extends base_service_1.BasePermissionService {
273
+ constructor() {
274
+ super({}, {}, grants);
275
+ }
276
+ run = this.procedure("run")
277
+ .access({
278
+ action: "read",
279
+ entities: ({ input }) => ({
280
+ userId: input.ownerId,
281
+ }),
282
+ })
283
+ .handle(({ state }) => (0, neverthrow_1.ok)(state.access?.userId ?? null));
284
+ }
285
+ const service = new PermissionService();
286
+ const result = await service.run({ ownerId: "user-1" }, { user: createUser(), session: createSession() });
287
+ expect(result.isOk()).toBe(true);
288
+ if (result.isOk()) {
289
+ expect(result.value).toBe("user-1");
290
+ }
291
+ });
292
+ it("reuses a typed state step directly when entityStep is provided", async () => {
293
+ const grants = [
294
+ {
295
+ action: "read",
296
+ level: "user",
297
+ role: "member",
298
+ access: "own",
299
+ },
300
+ ];
301
+ class PermissionService extends base_service_1.BasePermissionService {
302
+ constructor() {
303
+ super({}, {}, grants);
304
+ }
305
+ run = this.procedure("run")
306
+ .use("record", ({ input }) => (0, neverthrow_1.ok)({
307
+ id: "resource-1",
308
+ userId: input.ownerId,
309
+ teamId: null,
310
+ organizationId: null,
311
+ }))
312
+ .access({
313
+ action: "read",
314
+ entityStep: "record",
315
+ })
316
+ .handle(({ state }) => (0, neverthrow_1.ok)(state.access === state.record));
317
+ }
318
+ const service = new PermissionService();
319
+ const result = await service.run({ ownerId: "user-1" }, { user: createUser(), session: createSession() });
320
+ expect(result.isOk()).toBe(true);
321
+ if (result.isOk()) {
322
+ expect(result.value).toBe(true);
323
+ }
324
+ });
325
+ it("returns FORBIDDEN when access validation fails", async () => {
326
+ const grants = [
327
+ {
328
+ action: "read",
329
+ level: "user",
330
+ role: "member",
331
+ access: "own",
332
+ },
333
+ ];
334
+ class PermissionService extends base_service_1.BasePermissionService {
335
+ constructor() {
336
+ super({}, {}, grants);
337
+ }
338
+ run = this.procedure("run")
339
+ .access({
340
+ action: "read",
341
+ entities: ({ input }) => ({
342
+ userId: input.ownerId,
343
+ }),
344
+ })
345
+ .handle(() => (0, neverthrow_1.ok)("allowed"));
346
+ }
347
+ const service = new PermissionService();
348
+ const result = await service.run({ ownerId: "other-user" }, { user: createUser(), session: createSession() });
349
+ expect(result.isErr()).toBe(true);
350
+ if (result.isErr()) {
351
+ expect(result.error.code).toBe("FORBIDDEN");
352
+ }
353
+ });
354
+ it("propagates entity loader failures through access", async () => {
355
+ const grants = [
356
+ {
357
+ action: "read",
358
+ level: "user",
359
+ role: "member",
360
+ access: "own",
361
+ },
362
+ ];
363
+ class PermissionService extends base_service_1.BasePermissionService {
364
+ constructor() {
365
+ super({}, {}, grants);
366
+ }
367
+ run = this.procedure("run")
368
+ .access({
369
+ action: "read",
370
+ entities: async () => (0, neverthrow_1.err)(new errors_1.ServerError({
371
+ code: "NOT_FOUND",
372
+ layer: "service",
373
+ layerName: "PermissionService",
374
+ message: "Missing entity",
375
+ })),
376
+ })
377
+ .handle(() => (0, neverthrow_1.ok)("allowed"));
378
+ }
379
+ const service = new PermissionService();
380
+ const result = await service.run({ ownerId: "user-1" }, { user: createUser(), session: createSession() });
381
+ expect(result.isErr()).toBe(true);
382
+ if (result.isErr()) {
383
+ expect(result.error.code).toBe("INTERNAL_SERVER_ERROR");
384
+ }
385
+ });
386
+ it("skips entity loading when grants already allow all access", async () => {
387
+ const grants = [
388
+ {
389
+ action: "read",
390
+ level: "user",
391
+ role: "member",
392
+ access: "all",
393
+ },
394
+ ];
395
+ const resolveEntity = jest.fn(async () => ({ userId: "user-1" }));
396
+ class PermissionService extends base_service_1.BasePermissionService {
397
+ constructor() {
398
+ super({}, {}, grants);
399
+ }
400
+ run = this.procedure("run")
401
+ .access({
402
+ action: "read",
403
+ entities: resolveEntity,
404
+ })
405
+ .handle(() => (0, neverthrow_1.ok)("allowed"));
406
+ }
407
+ const service = new PermissionService();
408
+ const result = await service.run({ ownerId: "other-user" }, { user: createUser(), session: createSession() });
409
+ expect(result.isOk()).toBe(true);
410
+ if (result.isOk()) {
411
+ expect(result.value).toBe("allowed");
412
+ }
413
+ expect(resolveEntity).not.toHaveBeenCalled();
414
+ });
415
+ });
@@ -389,22 +389,22 @@ export declare class ConnectRepository extends BaseTableRepository<Orm, Schema,
389
389
  updatedAt: Date | null;
390
390
  }[]>>;
391
391
  upsert(data: ConnectInsert, tx?: Orm): Promise<import("../base/base.dto").ServerResult<{
392
- provider: string;
393
392
  id: string;
394
393
  createdAt: Date;
395
394
  updatedAt: Date | null;
396
- userId: string;
397
395
  expiresAt: Date | null;
396
+ userId: string;
397
+ parentId: string | null;
398
398
  accessToken: string;
399
399
  refreshToken: string | null;
400
400
  scope: string | null;
401
+ provider: string;
401
402
  accountType: string;
402
403
  providerAccountId: string;
403
404
  handle: string | null;
404
405
  displayName: string | null;
405
406
  avatarUrl: string | null;
406
407
  tokenType: string | null;
407
- parentId: string | null;
408
408
  metadataJson: unknown;
409
409
  revokedAt: Date | null;
410
410
  lastRefreshedAt: Date | null;
@@ -16,19 +16,19 @@ export declare class ConnectService extends BaseService<{
16
16
  url: string;
17
17
  }>;
18
18
  handleCallback(user: User, sessionId: string, providerId: string, code: string, state: string): Promise<import("../base/base.dto").ServerResult<{
19
- handle: string | null;
20
19
  id: string;
21
20
  createdAt: Date;
22
21
  updatedAt: Date | null;
22
+ expiresAt: Date | null;
23
23
  userId: string;
24
24
  parentId: string | null;
25
- expiresAt: Date | null;
26
25
  accessToken: string;
27
26
  refreshToken: string | null;
28
27
  scope: string | null;
29
28
  provider: string;
30
29
  accountType: string;
31
30
  providerAccountId: string;
31
+ handle: string | null;
32
32
  displayName: string | null;
33
33
  avatarUrl: string | null;
34
34
  tokenType: string | null;
@@ -37,19 +37,19 @@ export declare class ConnectService extends BaseService<{
37
37
  lastRefreshedAt: Date | null;
38
38
  }>>;
39
39
  refreshToken(connectionId: string): Promise<import("../base/base.dto").ServerResult<{
40
- handle: string | null;
41
40
  id: string;
42
41
  createdAt: Date;
43
42
  updatedAt: Date | null;
43
+ expiresAt: Date | null;
44
44
  userId: string;
45
45
  parentId: string | null;
46
- expiresAt: Date | null;
47
46
  accessToken: string;
48
47
  refreshToken: string | null;
49
48
  scope: string | null;
50
49
  provider: string;
51
50
  accountType: string;
52
51
  providerAccountId: string;
52
+ handle: string | null;
53
53
  displayName: string | null;
54
54
  avatarUrl: string | null;
55
55
  tokenType: string | null;
@@ -18,11 +18,11 @@ export declare function createConnectTRPC({ router, privateProcedure: procedure
18
18
  provider: string;
19
19
  accountType: string;
20
20
  providerAccountId: string;
21
- handle?: string | null | undefined;
22
21
  updatedAt?: Date | null | undefined;
23
- parentId?: string | null | undefined;
24
22
  expiresAt?: Date | null | undefined;
23
+ parentId?: string | null | undefined;
25
24
  scope?: string | null | undefined;
25
+ handle?: string | null | undefined;
26
26
  displayName?: string | null | undefined;
27
27
  avatarUrl?: string | null | undefined;
28
28
  tokenType?: string | null | undefined;
@@ -1,20 +1,41 @@
1
1
  import type { CreateRecurrenceSchema, DeleteRecurrenceRulesSchema, DeleteRecurrenceSchema, UpdateRecurrenceRulesSchema, UpdateRecurrenceSchema } from "@m5kdev/commons/modules/recurrence/recurrence.schema";
2
- import type { QueryInput } from "@m5kdev/commons/modules/schemas/query.schema";
3
- import type { Context, User } from "../auth/auth.lib";
2
+ import type { Context } from "../auth/auth.lib";
4
3
  import type { ServerResultAsync } from "../base/base.dto";
5
4
  import { BaseService } from "../base/base.service";
6
5
  import type { CreateWithRulesResult, RecurrenceRepository, RecurrenceRulesRepository } from "./recurrence.repository";
7
6
  export declare class RecurrenceService extends BaseService<{
8
7
  recurrence: RecurrenceRepository;
9
8
  recurrenceRules: RecurrenceRulesRepository;
10
- }, Record<string, never>> {
11
- create(data: CreateRecurrenceSchema, ctx: Context): ServerResultAsync<CreateWithRulesResult>;
12
- list(query?: QueryInput, ctx?: {
13
- user?: User;
14
- }): ServerResultAsync<{
15
- rows: CreateWithRulesResult["recurrence"][];
9
+ }, Record<string, never>, Context> {
10
+ readonly list: import("../base/base.procedure").ServiceProcedure<{
11
+ page?: number | undefined;
12
+ limit?: number | undefined;
13
+ sort?: string | undefined;
14
+ order?: "asc" | "desc" | undefined;
15
+ filters?: {
16
+ columnId: string;
17
+ type: "string" | "number" | "boolean" | "date" | "enum";
18
+ method: "on" | "contains" | "equals" | "starts_with" | "ends_with" | "greater_than" | "less_than" | "between" | "before" | "after" | "oneOf" | "intersect" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
19
+ value: string | number | boolean | string[];
20
+ valueTo?: string | undefined;
21
+ endColumnId?: string | undefined;
22
+ }[] | undefined;
23
+ }, Context, {
24
+ rows: {
25
+ id: string;
26
+ name: string | null;
27
+ createdAt: Date;
28
+ updatedAt: Date;
29
+ metadata: Record<string, any> | null;
30
+ userId: string | null;
31
+ teamId: string | null;
32
+ organizationId: string | null;
33
+ enabled: boolean;
34
+ kind: string | null;
35
+ }[];
16
36
  total: number;
17
37
  }>;
38
+ create(data: CreateRecurrenceSchema, ctx: Context): ServerResultAsync<CreateWithRulesResult>;
18
39
  findById(id: string): ServerResultAsync<CreateWithRulesResult["recurrence"] | null>;
19
40
  update(data: UpdateRecurrenceSchema & {
20
41
  id: string;
@@ -31,6 +31,9 @@ function mapRuleToInsert(rule) {
31
31
  return out;
32
32
  }
33
33
  class RecurrenceService extends base_service_1.BaseService {
34
+ list = this.procedure("list")
35
+ .addContextFilter(["user"])
36
+ .handle(({ input }) => this.repository.recurrence.queryList(input));
34
37
  async create(data, ctx) {
35
38
  const recurrenceData = {
36
39
  name: data.name,
@@ -44,10 +47,6 @@ class RecurrenceService extends base_service_1.BaseService {
44
47
  const rulesData = data.recurrenceRules.map(mapRuleToInsert);
45
48
  return this.repository.recurrence.createWithRules(recurrenceData, rulesData);
46
49
  }
47
- async list(query, ctx) {
48
- const queryWithUser = ctx?.user ? this.addUserFilter(ctx.user.id, query, "userId") : query;
49
- return this.repository.recurrence.queryList(queryWithUser);
50
- }
51
50
  async findById(id) {
52
51
  const result = await this.repository.recurrence.findById(id);
53
52
  if (result.isErr())
@@ -1,5 +1,5 @@
1
- import type { RecurrenceService } from "./recurrence.service";
2
1
  import { type TRPCMethods } from "../../utils/trpc";
2
+ import type { RecurrenceService } from "./recurrence.service";
3
3
  export declare function createRecurrenceTRPC({ router, privateProcedure: procedure }: TRPCMethods, recurrenceService: RecurrenceService): import("@trpc/server").TRPCBuiltRouter<{
4
4
  ctx: import("../auth/auth.lib").Context;
5
5
  meta: any;
@@ -11,11 +11,11 @@ export declare function createRecurrenceTRPC({ router, privateProcedure: procedu
11
11
  page?: number | undefined;
12
12
  limit?: number | undefined;
13
13
  sort?: string | undefined;
14
- order?: "desc" | "asc" | undefined;
14
+ order?: "asc" | "desc" | undefined;
15
15
  filters?: {
16
16
  columnId: string;
17
17
  type: "string" | "number" | "boolean" | "date" | "enum";
18
- method: "intersect" | "contains" | "equals" | "starts_with" | "ends_with" | "greater_than" | "less_than" | "on" | "between" | "before" | "after" | "oneOf" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
18
+ method: "on" | "contains" | "equals" | "starts_with" | "ends_with" | "greater_than" | "less_than" | "between" | "before" | "after" | "oneOf" | "intersect" | "isEmpty" | "isNotEmpty" | "is_null" | "is_not_null";
19
19
  value: string | number | boolean | string[];
20
20
  valueTo?: string | undefined;
21
21
  endColumnId?: string | undefined;
@@ -20,7 +20,7 @@ const deleteRecurrenceOutputSchema = zod_1.z.object({ id: zod_1.z.string() });
20
20
  function createRecurrenceTRPC({ router, privateProcedure: procedure }, recurrenceService) {
21
21
  return router({
22
22
  list: procedure
23
- .input(query_schema_1.querySchema.optional())
23
+ .input(query_schema_1.querySchema.default({}))
24
24
  .output(listRecurrenceOutputSchema)
25
25
  .query(async ({ ctx, input }) => {
26
26
  return (0, trpc_1.handleTRPCResult)(await recurrenceService.list(input, ctx));
@@ -36,7 +36,7 @@ exports.taggings = (0, sqlite_core_1.sqliteTable)("taggings", {
36
36
  .$default(() => new Date()),
37
37
  tagId: (0, sqlite_core_1.text)("tag_id")
38
38
  .notNull()
39
- .references(() => exports.tags.id),
39
+ .references(() => exports.tags.id, { onDelete: "cascade" }),
40
40
  resourceType: (0, sqlite_core_1.text)("resource_type").notNull(), // e.g., "post", "image"
41
41
  resourceId: (0, sqlite_core_1.text)("resource_id").notNull(), // id in the resource table
42
42
  });