jazz-tools 0.18.15 → 0.18.17

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 (91) hide show
  1. package/.svelte-kit/__package__/media/image.svelte +104 -98
  2. package/.svelte-kit/__package__/media/image.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/tests/media/image.svelte.test.js +16 -2
  4. package/.turbo/turbo-build.log +48 -38
  5. package/CHANGELOG.md +20 -0
  6. package/dist/better-auth/database-adapter/index.d.ts +50 -0
  7. package/dist/better-auth/database-adapter/index.d.ts.map +1 -0
  8. package/dist/better-auth/database-adapter/index.js +920 -0
  9. package/dist/better-auth/database-adapter/index.js.map +1 -0
  10. package/dist/better-auth/database-adapter/repository/account.d.ts +24 -0
  11. package/dist/better-auth/database-adapter/repository/account.d.ts.map +1 -0
  12. package/dist/better-auth/database-adapter/repository/generic.d.ts +45 -0
  13. package/dist/better-auth/database-adapter/repository/generic.d.ts.map +1 -0
  14. package/dist/better-auth/database-adapter/repository/index.d.ts +6 -0
  15. package/dist/better-auth/database-adapter/repository/index.d.ts.map +1 -0
  16. package/dist/better-auth/database-adapter/repository/session.d.ts +29 -0
  17. package/dist/better-auth/database-adapter/repository/session.d.ts.map +1 -0
  18. package/dist/better-auth/database-adapter/repository/user.d.ts +30 -0
  19. package/dist/better-auth/database-adapter/repository/user.d.ts.map +1 -0
  20. package/dist/better-auth/database-adapter/repository/verification.d.ts +18 -0
  21. package/dist/better-auth/database-adapter/repository/verification.d.ts.map +1 -0
  22. package/dist/better-auth/database-adapter/schema.d.ts +27 -0
  23. package/dist/better-auth/database-adapter/schema.d.ts.map +1 -0
  24. package/dist/better-auth/database-adapter/tests/index.test.d.ts +2 -0
  25. package/dist/better-auth/database-adapter/tests/index.test.d.ts.map +1 -0
  26. package/dist/better-auth/database-adapter/tests/repository/account.test.d.ts +2 -0
  27. package/dist/better-auth/database-adapter/tests/repository/account.test.d.ts.map +1 -0
  28. package/dist/better-auth/database-adapter/tests/repository/generic.test.d.ts +2 -0
  29. package/dist/better-auth/database-adapter/tests/repository/generic.test.d.ts.map +1 -0
  30. package/dist/better-auth/database-adapter/tests/repository/session.test.d.ts +2 -0
  31. package/dist/better-auth/database-adapter/tests/repository/session.test.d.ts.map +1 -0
  32. package/dist/better-auth/database-adapter/tests/repository/user.test.d.ts +2 -0
  33. package/dist/better-auth/database-adapter/tests/repository/user.test.d.ts.map +1 -0
  34. package/dist/better-auth/database-adapter/tests/repository/verification.test.d.ts +2 -0
  35. package/dist/better-auth/database-adapter/tests/repository/verification.test.d.ts.map +1 -0
  36. package/dist/better-auth/database-adapter/tests/sync-utils.d.ts +16 -0
  37. package/dist/better-auth/database-adapter/tests/sync-utils.d.ts.map +1 -0
  38. package/dist/better-auth/database-adapter/tests/utils.test.d.ts +2 -0
  39. package/dist/better-auth/database-adapter/tests/utils.test.d.ts.map +1 -0
  40. package/dist/better-auth/database-adapter/utils.d.ts +16 -0
  41. package/dist/better-auth/database-adapter/utils.d.ts.map +1 -0
  42. package/dist/{chunk-GRN6OAUX.js → chunk-OTWWOZMB.js} +73 -4
  43. package/dist/chunk-OTWWOZMB.js.map +1 -0
  44. package/dist/index.js +1 -1
  45. package/dist/react/index.js +2 -0
  46. package/dist/react/index.js.map +1 -1
  47. package/dist/react/media/image.d.ts.map +1 -1
  48. package/dist/react-native-core/index.js +3 -1
  49. package/dist/react-native-core/index.js.map +1 -1
  50. package/dist/react-native-core/media/image.d.ts.map +1 -1
  51. package/dist/svelte/media/image.svelte +104 -98
  52. package/dist/svelte/media/image.svelte.d.ts.map +1 -1
  53. package/dist/svelte/tests/media/image.svelte.test.js +16 -2
  54. package/dist/testing.js +1 -1
  55. package/dist/tools/implementation/refs.d.ts +1 -1
  56. package/dist/tools/implementation/refs.d.ts.map +1 -1
  57. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts +4 -0
  58. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
  59. package/dist/tools/subscribe/SubscriptionScope.d.ts +7 -0
  60. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  61. package/dist/tools/subscribe/index.d.ts.map +1 -1
  62. package/jazz-tools-0.18.6.tgz +0 -0
  63. package/package.json +10 -5
  64. package/src/better-auth/database-adapter/index.ts +228 -0
  65. package/src/better-auth/database-adapter/repository/account.ts +131 -0
  66. package/src/better-auth/database-adapter/repository/generic.ts +297 -0
  67. package/src/better-auth/database-adapter/repository/index.ts +5 -0
  68. package/src/better-auth/database-adapter/repository/session.ts +190 -0
  69. package/src/better-auth/database-adapter/repository/user.ts +158 -0
  70. package/src/better-auth/database-adapter/repository/verification.ts +37 -0
  71. package/src/better-auth/database-adapter/schema.ts +222 -0
  72. package/src/better-auth/database-adapter/tests/index.test.ts +690 -0
  73. package/src/better-auth/database-adapter/tests/repository/account.test.ts +149 -0
  74. package/src/better-auth/database-adapter/tests/repository/generic.test.ts +183 -0
  75. package/src/better-auth/database-adapter/tests/repository/session.test.ts +419 -0
  76. package/src/better-auth/database-adapter/tests/repository/user.test.ts +673 -0
  77. package/src/better-auth/database-adapter/tests/repository/verification.test.ts +101 -0
  78. package/src/better-auth/database-adapter/tests/sync-utils.ts +127 -0
  79. package/src/better-auth/database-adapter/tests/utils.test.ts +787 -0
  80. package/src/better-auth/database-adapter/utils.ts +178 -0
  81. package/src/react/media/image.tsx +2 -0
  82. package/src/react/tests/media/image.test.tsx +20 -2
  83. package/src/react-native-core/media/image.tsx +4 -1
  84. package/src/svelte/media/image.svelte +104 -98
  85. package/src/svelte/tests/media/image.svelte.test.ts +18 -2
  86. package/src/tools/implementation/refs.ts +27 -3
  87. package/src/tools/subscribe/CoValueCoreSubscription.ts +14 -0
  88. package/src/tools/subscribe/SubscriptionScope.ts +62 -1
  89. package/src/tools/subscribe/index.ts +8 -0
  90. package/tsup.config.ts +7 -0
  91. package/dist/chunk-GRN6OAUX.js.map +0 -1
@@ -0,0 +1,131 @@
1
+ import { CleanedWhere } from "better-auth/adapters";
2
+ import { co, z } from "jazz-tools";
3
+ import { JazzRepository } from "./generic";
4
+ import { isWhereBySingleField } from "../utils";
5
+ import type { TableItem } from "../schema";
6
+
7
+ /**
8
+ * This list stores an array of covalues ID
9
+ * mapped by the accountID property.
10
+ * Because accountId is not unique, and it is used on every social login
11
+ */
12
+ const AccountIdIndex = co.list(z.string());
13
+
14
+ export class AccountRepository extends JazzRepository {
15
+ /**
16
+ * Custom logic:
17
+ * - keep sync accountId index
18
+ */
19
+ async create(
20
+ model: string,
21
+ data: Record<string, any>,
22
+ uniqueId?: string,
23
+ ): Promise<TableItem> {
24
+ const account = await super.create(model, data, uniqueId);
25
+
26
+ await this.updateAccountIdIndex(
27
+ account[this.getAccountIdProperty()],
28
+ account.$jazz.id,
29
+ );
30
+
31
+ return account;
32
+ }
33
+
34
+ /**
35
+ * Custom logic:
36
+ * - if the accountId is in the where clause, get the ids from the index
37
+ */
38
+ async findMany(
39
+ model: string,
40
+ where: CleanedWhere[] | undefined,
41
+ limit?: number,
42
+ sortBy?: { field: string; direction: "asc" | "desc" },
43
+ offset?: number,
44
+ ): Promise<TableItem[]> {
45
+ if (isWhereBySingleField(this.getAccountIdProperty(), where)) {
46
+ const accountIdIndex = await this.getAccountIdIndex(where[0].value);
47
+
48
+ const ids = accountIdIndex ?? [];
49
+
50
+ if (ids.length === 0) {
51
+ return [];
52
+ }
53
+
54
+ // except for accountId clashing from different social providers,
55
+ // ids should contain a single id, max two
56
+ const results = await Promise.all(
57
+ ids.map((id) =>
58
+ super.findById(model, [
59
+ { field: "id", operator: "eq", value: id, connector: "AND" },
60
+ ]),
61
+ ),
62
+ );
63
+
64
+ return results.filter((value) => value !== null);
65
+ }
66
+
67
+ return super.findMany(model, where, limit, sortBy, offset);
68
+ }
69
+
70
+ async deleteValue(model: string, where: CleanedWhere[]): Promise<number> {
71
+ const nodes = await this.findMany(model, where);
72
+
73
+ const deleted = await super.deleteValue(model, where);
74
+
75
+ for (const node of nodes) {
76
+ const accountId = node.$jazz.raw.get(this.getAccountIdProperty()) as
77
+ | string
78
+ | undefined;
79
+
80
+ if (accountId) {
81
+ await this.deleteAccountIdIndex(accountId, node.$jazz.id);
82
+ }
83
+ }
84
+
85
+ return deleted;
86
+ }
87
+
88
+ private async getAccountIdIndex(accountId: string) {
89
+ const indexUnique = this.getAccountIdProperty() + "_" + accountId;
90
+
91
+ const accountIdIndex = await AccountIdIndex.loadUnique(
92
+ indexUnique,
93
+ this.owner.$jazz.id,
94
+ {
95
+ loadAs: this.worker,
96
+ },
97
+ );
98
+
99
+ return accountIdIndex;
100
+ }
101
+
102
+ private async updateAccountIdIndex(accountId: string, entityId: string) {
103
+ const accountIdIndex = await this.getAccountIdIndex(accountId);
104
+
105
+ const ids = accountIdIndex ?? [];
106
+
107
+ await AccountIdIndex.upsertUnique({
108
+ value: [...ids, entityId],
109
+ unique: this.getAccountIdProperty() + "_" + accountId,
110
+ owner: this.owner,
111
+ });
112
+ }
113
+
114
+ private async deleteAccountIdIndex(accountId: string, entityId: string) {
115
+ const accountIdIndex = await this.getAccountIdIndex(accountId);
116
+
117
+ const ids = accountIdIndex ?? [];
118
+
119
+ await AccountIdIndex.upsertUnique({
120
+ value: ids.filter((id) => id !== entityId),
121
+ unique: this.getAccountIdProperty() + "_" + accountId,
122
+ owner: this.owner,
123
+ });
124
+ }
125
+
126
+ private getAccountIdProperty(): string {
127
+ return (
128
+ this.betterAuthSchema.account?.fields.accountId?.fieldName || "accountId"
129
+ );
130
+ }
131
+ }
@@ -0,0 +1,297 @@
1
+ import { CleanedWhere } from "better-auth/adapters";
2
+ import { BetterAuthDbSchema } from "better-auth/db";
3
+ import { Account, CoList, CoMap, Group, co } from "jazz-tools";
4
+ import type { Database, TableItem } from "../schema.js";
5
+ import {
6
+ filterListByWhere,
7
+ isWhereBySingleField,
8
+ paginateList,
9
+ sortListByField,
10
+ } from "../utils.js";
11
+
12
+ export class JazzRepository {
13
+ protected databaseSchema: Database;
14
+ protected databaseRoot: co.loaded<Database, { group: true }>;
15
+ protected worker: Account;
16
+ protected owner: Group;
17
+ protected betterAuthSchema: BetterAuthDbSchema;
18
+
19
+ private coValuesTracker:
20
+ | {
21
+ done: () => Set<`co_z${string}`>;
22
+ }
23
+ | undefined = undefined;
24
+
25
+ constructor(
26
+ databaseSchema: Database,
27
+ databaseRoot: co.loaded<Database, { group: true }>,
28
+ worker: Account,
29
+ betterAuthSchema: BetterAuthDbSchema = {},
30
+ ensureSync: boolean = false,
31
+ ) {
32
+ this.databaseSchema = databaseSchema;
33
+ this.databaseRoot = databaseRoot;
34
+ this.worker = worker;
35
+ this.owner = databaseRoot.group;
36
+ this.betterAuthSchema = betterAuthSchema;
37
+
38
+ if (ensureSync)
39
+ this.coValuesTracker =
40
+ worker.$jazz.raw.core.node.syncManager.trackDirtyCoValues();
41
+ }
42
+
43
+ ensureSync() {
44
+ if (!this.coValuesTracker)
45
+ throw new Error("Repository wasn't initialized with ensureSync option");
46
+
47
+ return Promise.all(
48
+ Array.from(this.coValuesTracker.done(), (id) =>
49
+ this.worker.$jazz.raw.core.node.syncManager.waitForSync(id),
50
+ ),
51
+ );
52
+ }
53
+
54
+ async create(
55
+ model: string,
56
+ data: Record<string, any>,
57
+ uniqueId?: string,
58
+ ): Promise<TableItem> {
59
+ const schema = this.getSchema(model);
60
+
61
+ const resolved = await this.databaseRoot.$jazz.ensureLoaded({
62
+ resolve: {
63
+ tables: {
64
+ [model]: true,
65
+ },
66
+ },
67
+ });
68
+
69
+ const list = resolved.tables?.[model] as unknown as CoList<CoMap>;
70
+
71
+ if (!uniqueId) {
72
+ // Use the same owner of the table.
73
+ const node = schema.create(data, {
74
+ owner: list.$jazz.owner,
75
+ });
76
+
77
+ list.$jazz.push(node);
78
+
79
+ return node;
80
+ }
81
+
82
+ // If we have a unique id, we must check for soft deleted items first
83
+ const existingNode = (await schema.loadUnique(
84
+ uniqueId,
85
+ list.$jazz.owner.$jazz.id,
86
+ {
87
+ loadAs: this.worker,
88
+ },
89
+ )) as CoMap | null;
90
+
91
+ // if the entity exists and is not soft deleted, we must throw an error
92
+ if (existingNode && existingNode.$jazz?.raw.get("_deleted") !== true) {
93
+ throw new Error("Entity already exists");
94
+ }
95
+
96
+ // create the entity or update the deleted one
97
+ const node = await schema.upsertUnique({
98
+ value: {
99
+ ...data,
100
+ _deleted: false,
101
+ },
102
+ owner: list.$jazz.owner,
103
+ unique: uniqueId,
104
+ });
105
+
106
+ if (!node) {
107
+ throw new Error("Unable to create entity");
108
+ }
109
+
110
+ list.$jazz.push(node);
111
+
112
+ return node;
113
+ }
114
+
115
+ async findOne(
116
+ model: string,
117
+ where: CleanedWhere[],
118
+ ): Promise<TableItem | null> {
119
+ return this.findMany(model, where, 1).then((users) => users?.at(0) ?? null);
120
+ }
121
+
122
+ async findById(
123
+ model: string,
124
+ where: [{ field: "id"; operator: "eq"; value: string; connector: "AND" }],
125
+ ): Promise<TableItem | null> {
126
+ const id = where[0]!.value;
127
+
128
+ if (!id.startsWith("co_")) {
129
+ return null;
130
+ }
131
+
132
+ const node = await this.getSchema(model).load(id, { loadAs: this.worker });
133
+
134
+ if (!node) {
135
+ return null;
136
+ }
137
+
138
+ if (node.$jazz.raw.get("_deleted")) {
139
+ return null;
140
+ }
141
+
142
+ return node;
143
+ }
144
+
145
+ async findByUnique(
146
+ model: string,
147
+ where: [{ field: string; operator: "eq"; value: string; connector: "AND" }],
148
+ ): Promise<TableItem | null> {
149
+ const value = where[0]!.value;
150
+
151
+ const node = await this.getSchema(model).loadUnique(
152
+ value,
153
+ this.owner.$jazz.id,
154
+ {
155
+ loadAs: this.worker,
156
+ },
157
+ );
158
+
159
+ if (!node) {
160
+ return null;
161
+ }
162
+
163
+ if (node.$jazz.raw.get("_deleted")) {
164
+ return null;
165
+ }
166
+
167
+ return node;
168
+ }
169
+
170
+ async findMany(
171
+ model: string,
172
+ where: CleanedWhere[] | undefined,
173
+ limit?: number,
174
+ sortBy?: { field: string; direction: "asc" | "desc" },
175
+ offset?: number,
176
+ ): Promise<TableItem[]> {
177
+ // ensure schema exists
178
+ this.getSchema(model);
179
+
180
+ if (isWhereBySingleField("id", where)) {
181
+ return this.findById(model, where).then((node) => (node ? [node] : []));
182
+ }
183
+
184
+ const resolvedRoot = await this.databaseRoot.$jazz.ensureLoaded({
185
+ resolve: {
186
+ tables: {
187
+ [model]: {
188
+ $each: true,
189
+ },
190
+ },
191
+ },
192
+ });
193
+
194
+ const list = resolvedRoot.tables?.[model] as CoList<CoMap> | undefined;
195
+
196
+ if (!list) {
197
+ return [];
198
+ }
199
+
200
+ return this.filterSortPaginateList(list, where, limit, sortBy, offset);
201
+ }
202
+
203
+ async update(
204
+ model: string,
205
+ where: CleanedWhere[],
206
+ update: Record<string, any>,
207
+ ): Promise<TableItem[]> {
208
+ const nodes = await this.findMany(model, where);
209
+
210
+ if (nodes.length === 0) {
211
+ return [];
212
+ }
213
+
214
+ for (const node of nodes) {
215
+ for (const [key, value] of Object.entries(
216
+ update as Record<string, any>,
217
+ )) {
218
+ node.$jazz.set(key, value);
219
+ }
220
+ }
221
+
222
+ return nodes;
223
+ }
224
+
225
+ async deleteValue(model: string, where: CleanedWhere[]): Promise<number> {
226
+ const items = await this.findMany(model, where);
227
+
228
+ if (items.length === 0) {
229
+ return 0;
230
+ }
231
+
232
+ const resolved = await this.databaseRoot.$jazz.ensureLoaded({
233
+ resolve: {
234
+ tables: {
235
+ [model]: {
236
+ $each: true,
237
+ },
238
+ },
239
+ },
240
+ });
241
+
242
+ if (!resolved) {
243
+ throw new Error("Unable to load values");
244
+ }
245
+
246
+ const list = resolved?.tables?.[model] as unknown as CoList<CoMap>;
247
+
248
+ for (const toBeDeleted of items) {
249
+ // Get entries without trigger the shallow load
250
+ const index = [...list.entries()].findIndex(
251
+ ([_, value]) => value && value.$jazz.id === toBeDeleted.$jazz.id,
252
+ );
253
+
254
+ toBeDeleted.$jazz.set("_deleted", true);
255
+
256
+ if (index !== -1) {
257
+ list.$jazz.remove(index);
258
+ }
259
+ }
260
+
261
+ return items.length;
262
+ }
263
+
264
+ async count(
265
+ model: string,
266
+ where: CleanedWhere[] | undefined,
267
+ ): Promise<number> {
268
+ return this.findMany(model, where).then((values) => values.length);
269
+ }
270
+
271
+ protected getSchema(model: string) {
272
+ const schema = this.databaseSchema.shape.tables.shape[model]?.element;
273
+ if (!schema) {
274
+ throw new Error(`Schema for model "${model}" not found`);
275
+ }
276
+ return schema;
277
+ }
278
+
279
+ protected filterSortPaginateList<T extends TableItem>(
280
+ list: CoList<CoMap | null>,
281
+ where: CleanedWhere[] | undefined,
282
+ limit?: number,
283
+ sortBy?: { field: string; direction: "asc" | "desc" },
284
+ offset?: number,
285
+ ): T[] {
286
+ // ignore nullable values and soft deleted items
287
+ return [
288
+ list.filter(
289
+ (item) => item !== null && item.$jazz.raw.get("_deleted") !== true,
290
+ ),
291
+ ]
292
+ .map((list) => filterListByWhere(list, where))
293
+ .map((list) => sortListByField(list, sortBy))
294
+ .map((list) => paginateList(list, limit, offset))
295
+ .at(0)! as T[];
296
+ }
297
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./generic.js";
2
+ export * from "./user.js";
3
+ export * from "./session.js";
4
+ export * from "./verification.js";
5
+ export * from "./account.js";
@@ -0,0 +1,190 @@
1
+ import { BetterAuthDbSchema } from "better-auth/db";
2
+ import { CleanedWhere } from "better-auth/adapters";
3
+ import { co, Account } from "jazz-tools";
4
+ import { JazzRepository } from "./generic";
5
+ import { UserRepository } from "./user";
6
+ import {
7
+ containWhereByField,
8
+ extractWhereByField,
9
+ filterListByWhere,
10
+ isWhereBySingleField,
11
+ } from "../utils";
12
+ import type { Database, TableItem } from "../schema";
13
+
14
+ type UserSchema = co.Map<{
15
+ sessions: co.List<co.Map<any>>;
16
+ }>;
17
+
18
+ export class SessionRepository extends JazzRepository {
19
+ protected userRepository: UserRepository;
20
+
21
+ constructor(
22
+ databaseSchema: Database,
23
+ databaseRoot: co.loaded<Database, { group: true }>,
24
+ worker: Account,
25
+ betterAuthSchema: BetterAuthDbSchema = {},
26
+ ensureSync: boolean = false,
27
+ ) {
28
+ super(databaseSchema, databaseRoot, worker, betterAuthSchema, ensureSync);
29
+
30
+ this.userRepository = new UserRepository(
31
+ databaseSchema,
32
+ databaseRoot,
33
+ worker,
34
+ betterAuthSchema,
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Custom logic: sessions are stored inside the user object
40
+ */
41
+ async create(
42
+ model: string,
43
+ data: Record<string, any>,
44
+ uniqueId?: string,
45
+ ): Promise<TableItem> {
46
+ if (typeof data.token !== "string" || typeof data.userId !== "string") {
47
+ throw new Error("Token and userId are required for session creation");
48
+ }
49
+
50
+ const user = await this.userRepository.findById("user", [
51
+ {
52
+ field: "id",
53
+ operator: "eq",
54
+ value: data.userId,
55
+ connector: "AND",
56
+ },
57
+ ]);
58
+
59
+ if (!user) {
60
+ throw new Error("User not found");
61
+ }
62
+
63
+ const { sessions } = await user.$jazz.ensureLoaded({
64
+ resolve: {
65
+ sessions: true,
66
+ },
67
+ });
68
+
69
+ const session = this.getSchema("session").create(data, {
70
+ unique: data.token,
71
+ owner: this.owner,
72
+ });
73
+
74
+ sessions.$jazz.push(session);
75
+
76
+ return session;
77
+ }
78
+
79
+ /**
80
+ * Custom logic: sessions are stored inside the user object.
81
+ */
82
+ async findMany(
83
+ model: string,
84
+ where: CleanedWhere[] | undefined,
85
+ limit?: number,
86
+ sortBy?: { field: string; direction: "asc" | "desc" },
87
+ offset?: number,
88
+ ): Promise<TableItem[]> {
89
+ if (isWhereBySingleField("id", where)) {
90
+ return this.findById(model, where).then((node) => (node ? [node] : []));
91
+ }
92
+
93
+ if (isWhereBySingleField("token", where)) {
94
+ return this.findByUnique(model, where).then((node) =>
95
+ node ? [node] : [],
96
+ );
97
+ }
98
+
99
+ if (containWhereByField("userId", where)) {
100
+ const [userIdWhere, otherWhere] = extractWhereByField("userId", where);
101
+
102
+ const user = await this.userRepository.findById("user", [
103
+ {
104
+ field: "id",
105
+ operator: "eq",
106
+ value: userIdWhere!.value as string,
107
+ connector: "AND",
108
+ },
109
+ ]);
110
+
111
+ if (!user) {
112
+ console.warn("Trying to find user's sessions, but user not found");
113
+ return [];
114
+ }
115
+
116
+ const { sessions } = await user.$jazz.ensureLoaded({
117
+ resolve: {
118
+ sessions: {
119
+ $each: true,
120
+ },
121
+ },
122
+ });
123
+
124
+ return this.filterSortPaginateList(
125
+ sessions,
126
+ otherWhere,
127
+ limit,
128
+ sortBy,
129
+ offset,
130
+ );
131
+ }
132
+
133
+ throw new Error(
134
+ "Unable to find session with where: " + JSON.stringify(where),
135
+ );
136
+ }
137
+
138
+ /**
139
+ * Custom logic: sessions are stored inside the user object.
140
+ */
141
+ async deleteValue(model: string, where: CleanedWhere[]): Promise<number> {
142
+ const items = await this.findMany(model, where);
143
+ if (items.length === 0) {
144
+ return 0;
145
+ }
146
+
147
+ const userId = items[0]!.userId;
148
+
149
+ return this.deleteSession(userId, items);
150
+ }
151
+
152
+ private async deleteSession(
153
+ userId: string,
154
+ items: TableItem[],
155
+ ): Promise<number> {
156
+ const user = await this.userRepository.findById("user", [
157
+ {
158
+ field: "id",
159
+ operator: "eq",
160
+ value: userId,
161
+ connector: "AND",
162
+ },
163
+ ]);
164
+
165
+ if (!user) {
166
+ throw new Error("User not found");
167
+ }
168
+
169
+ const { sessions } = await user.$jazz.ensureLoaded({
170
+ resolve: {
171
+ sessions: true,
172
+ },
173
+ });
174
+
175
+ for (const toBeDeleted of items) {
176
+ // Get entries without trigger the shallow load
177
+ const index = [...sessions.entries()].findIndex(
178
+ ([_, value]) => value && value.$jazz.id === toBeDeleted.$jazz.id,
179
+ );
180
+
181
+ toBeDeleted.$jazz.set("_deleted", true);
182
+
183
+ if (index !== -1) {
184
+ sessions.$jazz.remove(index);
185
+ }
186
+ }
187
+
188
+ return items.length;
189
+ }
190
+ }