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.
- package/.svelte-kit/__package__/media/image.svelte +104 -98
- package/.svelte-kit/__package__/media/image.svelte.d.ts.map +1 -1
- package/.svelte-kit/__package__/tests/media/image.svelte.test.js +16 -2
- package/.turbo/turbo-build.log +48 -38
- package/CHANGELOG.md +20 -0
- package/dist/better-auth/database-adapter/index.d.ts +50 -0
- package/dist/better-auth/database-adapter/index.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/index.js +920 -0
- package/dist/better-auth/database-adapter/index.js.map +1 -0
- package/dist/better-auth/database-adapter/repository/account.d.ts +24 -0
- package/dist/better-auth/database-adapter/repository/account.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/repository/generic.d.ts +45 -0
- package/dist/better-auth/database-adapter/repository/generic.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/repository/index.d.ts +6 -0
- package/dist/better-auth/database-adapter/repository/index.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/repository/session.d.ts +29 -0
- package/dist/better-auth/database-adapter/repository/session.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/repository/user.d.ts +30 -0
- package/dist/better-auth/database-adapter/repository/user.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/repository/verification.d.ts +18 -0
- package/dist/better-auth/database-adapter/repository/verification.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/schema.d.ts +27 -0
- package/dist/better-auth/database-adapter/schema.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/index.test.d.ts +2 -0
- package/dist/better-auth/database-adapter/tests/index.test.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/repository/account.test.d.ts +2 -0
- package/dist/better-auth/database-adapter/tests/repository/account.test.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/repository/generic.test.d.ts +2 -0
- package/dist/better-auth/database-adapter/tests/repository/generic.test.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/repository/session.test.d.ts +2 -0
- package/dist/better-auth/database-adapter/tests/repository/session.test.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/repository/user.test.d.ts +2 -0
- package/dist/better-auth/database-adapter/tests/repository/user.test.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/repository/verification.test.d.ts +2 -0
- package/dist/better-auth/database-adapter/tests/repository/verification.test.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/sync-utils.d.ts +16 -0
- package/dist/better-auth/database-adapter/tests/sync-utils.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/tests/utils.test.d.ts +2 -0
- package/dist/better-auth/database-adapter/tests/utils.test.d.ts.map +1 -0
- package/dist/better-auth/database-adapter/utils.d.ts +16 -0
- package/dist/better-auth/database-adapter/utils.d.ts.map +1 -0
- package/dist/{chunk-GRN6OAUX.js → chunk-OTWWOZMB.js} +73 -4
- package/dist/chunk-OTWWOZMB.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/media/image.d.ts.map +1 -1
- package/dist/react-native-core/index.js +3 -1
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/media/image.d.ts.map +1 -1
- package/dist/svelte/media/image.svelte +104 -98
- package/dist/svelte/media/image.svelte.d.ts.map +1 -1
- package/dist/svelte/tests/media/image.svelte.test.js +16 -2
- package/dist/testing.js +1 -1
- package/dist/tools/implementation/refs.d.ts +1 -1
- package/dist/tools/implementation/refs.d.ts.map +1 -1
- package/dist/tools/subscribe/CoValueCoreSubscription.d.ts +4 -0
- package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
- package/dist/tools/subscribe/SubscriptionScope.d.ts +7 -0
- package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
- package/dist/tools/subscribe/index.d.ts.map +1 -1
- package/jazz-tools-0.18.6.tgz +0 -0
- package/package.json +10 -5
- package/src/better-auth/database-adapter/index.ts +228 -0
- package/src/better-auth/database-adapter/repository/account.ts +131 -0
- package/src/better-auth/database-adapter/repository/generic.ts +297 -0
- package/src/better-auth/database-adapter/repository/index.ts +5 -0
- package/src/better-auth/database-adapter/repository/session.ts +190 -0
- package/src/better-auth/database-adapter/repository/user.ts +158 -0
- package/src/better-auth/database-adapter/repository/verification.ts +37 -0
- package/src/better-auth/database-adapter/schema.ts +222 -0
- package/src/better-auth/database-adapter/tests/index.test.ts +690 -0
- package/src/better-auth/database-adapter/tests/repository/account.test.ts +149 -0
- package/src/better-auth/database-adapter/tests/repository/generic.test.ts +183 -0
- package/src/better-auth/database-adapter/tests/repository/session.test.ts +419 -0
- package/src/better-auth/database-adapter/tests/repository/user.test.ts +673 -0
- package/src/better-auth/database-adapter/tests/repository/verification.test.ts +101 -0
- package/src/better-auth/database-adapter/tests/sync-utils.ts +127 -0
- package/src/better-auth/database-adapter/tests/utils.test.ts +787 -0
- package/src/better-auth/database-adapter/utils.ts +178 -0
- package/src/react/media/image.tsx +2 -0
- package/src/react/tests/media/image.test.tsx +20 -2
- package/src/react-native-core/media/image.tsx +4 -1
- package/src/svelte/media/image.svelte +104 -98
- package/src/svelte/tests/media/image.svelte.test.ts +18 -2
- package/src/tools/implementation/refs.ts +27 -3
- package/src/tools/subscribe/CoValueCoreSubscription.ts +14 -0
- package/src/tools/subscribe/SubscriptionScope.ts +62 -1
- package/src/tools/subscribe/index.ts +8 -0
- package/tsup.config.ts +7 -0
- package/dist/chunk-GRN6OAUX.js.map +0 -1
@@ -0,0 +1,920 @@
|
|
1
|
+
// src/better-auth/database-adapter/index.ts
|
2
|
+
import { createAdapter } from "better-auth/adapters";
|
3
|
+
import { startWorker } from "jazz-tools/worker";
|
4
|
+
|
5
|
+
// src/better-auth/database-adapter/utils.ts
|
6
|
+
function filterListByWhere(data, where) {
|
7
|
+
if (!Array.isArray(data)) {
|
8
|
+
throw new Error("Expected data to be an array");
|
9
|
+
}
|
10
|
+
if (where === void 0) {
|
11
|
+
return data;
|
12
|
+
}
|
13
|
+
if (!Array.isArray(where)) {
|
14
|
+
throw new Error("Expected where to be an array");
|
15
|
+
}
|
16
|
+
function evaluateCondition(item, condition) {
|
17
|
+
const { field, operator, value } = condition;
|
18
|
+
const itemValue = field === "id" ? item.$jazz.id : item[field];
|
19
|
+
switch (operator) {
|
20
|
+
case "eq":
|
21
|
+
return itemValue === value;
|
22
|
+
case "ne":
|
23
|
+
return itemValue !== value;
|
24
|
+
case "lt":
|
25
|
+
return value !== null && itemValue < value;
|
26
|
+
case "lte":
|
27
|
+
return value !== null && itemValue <= value;
|
28
|
+
case "gt":
|
29
|
+
return value !== null && itemValue > value;
|
30
|
+
case "gte":
|
31
|
+
return value !== null && itemValue >= value;
|
32
|
+
case "in":
|
33
|
+
return Array.isArray(value) ? value.includes(itemValue) : false;
|
34
|
+
case "contains":
|
35
|
+
return typeof itemValue === "string" && typeof value === "string" ? itemValue.includes(value) : false;
|
36
|
+
case "starts_with":
|
37
|
+
return typeof itemValue === "string" && typeof value === "string" ? itemValue.startsWith(value) : false;
|
38
|
+
case "ends_with":
|
39
|
+
return typeof itemValue === "string" && typeof value === "string" ? itemValue.endsWith(value) : false;
|
40
|
+
default:
|
41
|
+
throw new Error(`Unsupported operator: ${operator}`);
|
42
|
+
}
|
43
|
+
}
|
44
|
+
return data.filter((item) => {
|
45
|
+
let result = true;
|
46
|
+
for (let i = 0; i < where.length; i++) {
|
47
|
+
const condition = where[i];
|
48
|
+
const matches = evaluateCondition(item, condition);
|
49
|
+
if (i === 0) {
|
50
|
+
result = matches;
|
51
|
+
} else {
|
52
|
+
const connector = condition.connector || "AND";
|
53
|
+
if (connector === "AND") {
|
54
|
+
result = result && matches;
|
55
|
+
} else if (connector === "OR") {
|
56
|
+
result = result || matches;
|
57
|
+
} else {
|
58
|
+
throw new Error(`Unsupported connector: ${connector}`);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
return result;
|
63
|
+
});
|
64
|
+
}
|
65
|
+
function sortListByField(data, sort) {
|
66
|
+
if (!sort) {
|
67
|
+
return data;
|
68
|
+
}
|
69
|
+
const { field, direction } = sort;
|
70
|
+
data.sort((a, b) => {
|
71
|
+
if (a === null || b === null) {
|
72
|
+
return 0;
|
73
|
+
}
|
74
|
+
if (typeof a[field] === "string" && typeof b[field] === "string") {
|
75
|
+
return direction === "asc" ? a[field].localeCompare(b[field]) : b[field].localeCompare(a[field]);
|
76
|
+
}
|
77
|
+
return direction === "asc" ? a[field] - b[field] : b[field] - a[field];
|
78
|
+
});
|
79
|
+
return data;
|
80
|
+
}
|
81
|
+
function paginateList(data, limit, offset) {
|
82
|
+
if (offset === void 0 && limit === void 0) {
|
83
|
+
return data;
|
84
|
+
}
|
85
|
+
if (limit === 0) {
|
86
|
+
return [];
|
87
|
+
}
|
88
|
+
let start = offset ?? 0;
|
89
|
+
if (start < 0) {
|
90
|
+
start = 0;
|
91
|
+
}
|
92
|
+
const end = limit ? start + limit : void 0;
|
93
|
+
return data.slice(start, end);
|
94
|
+
}
|
95
|
+
function isWhereByField(field, where) {
|
96
|
+
return where.field === field && where.operator === "eq" && where.connector === "AND";
|
97
|
+
}
|
98
|
+
function isWhereBySingleField(field, where) {
|
99
|
+
if (where === void 0 || where.length !== 1) {
|
100
|
+
return false;
|
101
|
+
}
|
102
|
+
const [cond] = where;
|
103
|
+
if (!cond) {
|
104
|
+
return false;
|
105
|
+
}
|
106
|
+
return isWhereByField(field, cond);
|
107
|
+
}
|
108
|
+
function containWhereByField(field, where) {
|
109
|
+
if (where === void 0) {
|
110
|
+
return false;
|
111
|
+
}
|
112
|
+
return where.some((cond) => isWhereByField(field, cond));
|
113
|
+
}
|
114
|
+
function extractWhereByField(field, where) {
|
115
|
+
if (where === void 0) {
|
116
|
+
return [void 0, []];
|
117
|
+
}
|
118
|
+
return [
|
119
|
+
where.find((cond) => isWhereByField(field, cond)),
|
120
|
+
where.filter((cond) => !isWhereByField(field, cond))
|
121
|
+
];
|
122
|
+
}
|
123
|
+
|
124
|
+
// src/better-auth/database-adapter/repository/generic.ts
|
125
|
+
var JazzRepository = class {
|
126
|
+
constructor(databaseSchema, databaseRoot, worker, betterAuthSchema = {}, ensureSync = false) {
|
127
|
+
this.coValuesTracker = void 0;
|
128
|
+
this.databaseSchema = databaseSchema;
|
129
|
+
this.databaseRoot = databaseRoot;
|
130
|
+
this.worker = worker;
|
131
|
+
this.owner = databaseRoot.group;
|
132
|
+
this.betterAuthSchema = betterAuthSchema;
|
133
|
+
if (ensureSync)
|
134
|
+
this.coValuesTracker = worker.$jazz.raw.core.node.syncManager.trackDirtyCoValues();
|
135
|
+
}
|
136
|
+
ensureSync() {
|
137
|
+
if (!this.coValuesTracker)
|
138
|
+
throw new Error("Repository wasn't initialized with ensureSync option");
|
139
|
+
return Promise.all(
|
140
|
+
Array.from(
|
141
|
+
this.coValuesTracker.done(),
|
142
|
+
(id) => this.worker.$jazz.raw.core.node.syncManager.waitForSync(id)
|
143
|
+
)
|
144
|
+
);
|
145
|
+
}
|
146
|
+
async create(model, data, uniqueId) {
|
147
|
+
const schema = this.getSchema(model);
|
148
|
+
const resolved = await this.databaseRoot.$jazz.ensureLoaded({
|
149
|
+
resolve: {
|
150
|
+
tables: {
|
151
|
+
[model]: true
|
152
|
+
}
|
153
|
+
}
|
154
|
+
});
|
155
|
+
const list = resolved.tables?.[model];
|
156
|
+
if (!uniqueId) {
|
157
|
+
const node2 = schema.create(data, {
|
158
|
+
owner: list.$jazz.owner
|
159
|
+
});
|
160
|
+
list.$jazz.push(node2);
|
161
|
+
return node2;
|
162
|
+
}
|
163
|
+
const existingNode = await schema.loadUnique(
|
164
|
+
uniqueId,
|
165
|
+
list.$jazz.owner.$jazz.id,
|
166
|
+
{
|
167
|
+
loadAs: this.worker
|
168
|
+
}
|
169
|
+
);
|
170
|
+
if (existingNode && existingNode.$jazz?.raw.get("_deleted") !== true) {
|
171
|
+
throw new Error("Entity already exists");
|
172
|
+
}
|
173
|
+
const node = await schema.upsertUnique({
|
174
|
+
value: {
|
175
|
+
...data,
|
176
|
+
_deleted: false
|
177
|
+
},
|
178
|
+
owner: list.$jazz.owner,
|
179
|
+
unique: uniqueId
|
180
|
+
});
|
181
|
+
if (!node) {
|
182
|
+
throw new Error("Unable to create entity");
|
183
|
+
}
|
184
|
+
list.$jazz.push(node);
|
185
|
+
return node;
|
186
|
+
}
|
187
|
+
async findOne(model, where) {
|
188
|
+
return this.findMany(model, where, 1).then((users) => users?.at(0) ?? null);
|
189
|
+
}
|
190
|
+
async findById(model, where) {
|
191
|
+
const id = where[0].value;
|
192
|
+
if (!id.startsWith("co_")) {
|
193
|
+
return null;
|
194
|
+
}
|
195
|
+
const node = await this.getSchema(model).load(id, { loadAs: this.worker });
|
196
|
+
if (!node) {
|
197
|
+
return null;
|
198
|
+
}
|
199
|
+
if (node.$jazz.raw.get("_deleted")) {
|
200
|
+
return null;
|
201
|
+
}
|
202
|
+
return node;
|
203
|
+
}
|
204
|
+
async findByUnique(model, where) {
|
205
|
+
const value = where[0].value;
|
206
|
+
const node = await this.getSchema(model).loadUnique(
|
207
|
+
value,
|
208
|
+
this.owner.$jazz.id,
|
209
|
+
{
|
210
|
+
loadAs: this.worker
|
211
|
+
}
|
212
|
+
);
|
213
|
+
if (!node) {
|
214
|
+
return null;
|
215
|
+
}
|
216
|
+
if (node.$jazz.raw.get("_deleted")) {
|
217
|
+
return null;
|
218
|
+
}
|
219
|
+
return node;
|
220
|
+
}
|
221
|
+
async findMany(model, where, limit, sortBy, offset) {
|
222
|
+
this.getSchema(model);
|
223
|
+
if (isWhereBySingleField("id", where)) {
|
224
|
+
return this.findById(model, where).then((node) => node ? [node] : []);
|
225
|
+
}
|
226
|
+
const resolvedRoot = await this.databaseRoot.$jazz.ensureLoaded({
|
227
|
+
resolve: {
|
228
|
+
tables: {
|
229
|
+
[model]: {
|
230
|
+
$each: true
|
231
|
+
}
|
232
|
+
}
|
233
|
+
}
|
234
|
+
});
|
235
|
+
const list = resolvedRoot.tables?.[model];
|
236
|
+
if (!list) {
|
237
|
+
return [];
|
238
|
+
}
|
239
|
+
return this.filterSortPaginateList(list, where, limit, sortBy, offset);
|
240
|
+
}
|
241
|
+
async update(model, where, update) {
|
242
|
+
const nodes = await this.findMany(model, where);
|
243
|
+
if (nodes.length === 0) {
|
244
|
+
return [];
|
245
|
+
}
|
246
|
+
for (const node of nodes) {
|
247
|
+
for (const [key, value] of Object.entries(
|
248
|
+
update
|
249
|
+
)) {
|
250
|
+
node.$jazz.set(key, value);
|
251
|
+
}
|
252
|
+
}
|
253
|
+
return nodes;
|
254
|
+
}
|
255
|
+
async deleteValue(model, where) {
|
256
|
+
const items = await this.findMany(model, where);
|
257
|
+
if (items.length === 0) {
|
258
|
+
return 0;
|
259
|
+
}
|
260
|
+
const resolved = await this.databaseRoot.$jazz.ensureLoaded({
|
261
|
+
resolve: {
|
262
|
+
tables: {
|
263
|
+
[model]: {
|
264
|
+
$each: true
|
265
|
+
}
|
266
|
+
}
|
267
|
+
}
|
268
|
+
});
|
269
|
+
if (!resolved) {
|
270
|
+
throw new Error("Unable to load values");
|
271
|
+
}
|
272
|
+
const list = resolved?.tables?.[model];
|
273
|
+
for (const toBeDeleted of items) {
|
274
|
+
const index = [...list.entries()].findIndex(
|
275
|
+
([_, value]) => value && value.$jazz.id === toBeDeleted.$jazz.id
|
276
|
+
);
|
277
|
+
toBeDeleted.$jazz.set("_deleted", true);
|
278
|
+
if (index !== -1) {
|
279
|
+
list.$jazz.remove(index);
|
280
|
+
}
|
281
|
+
}
|
282
|
+
return items.length;
|
283
|
+
}
|
284
|
+
async count(model, where) {
|
285
|
+
return this.findMany(model, where).then((values) => values.length);
|
286
|
+
}
|
287
|
+
getSchema(model) {
|
288
|
+
const schema = this.databaseSchema.shape.tables.shape[model]?.element;
|
289
|
+
if (!schema) {
|
290
|
+
throw new Error(`Schema for model "${model}" not found`);
|
291
|
+
}
|
292
|
+
return schema;
|
293
|
+
}
|
294
|
+
filterSortPaginateList(list, where, limit, sortBy, offset) {
|
295
|
+
return [
|
296
|
+
list.filter(
|
297
|
+
(item) => item !== null && item.$jazz.raw.get("_deleted") !== true
|
298
|
+
)
|
299
|
+
].map((list2) => filterListByWhere(list2, where)).map((list2) => sortListByField(list2, sortBy)).map((list2) => paginateList(list2, limit, offset)).at(0);
|
300
|
+
}
|
301
|
+
};
|
302
|
+
|
303
|
+
// src/better-auth/database-adapter/repository/user.ts
|
304
|
+
import { co, z } from "jazz-tools";
|
305
|
+
var EmailIndex = co.map({ user: z.string().nullable() });
|
306
|
+
var UserRepository = class extends JazzRepository {
|
307
|
+
/**
|
308
|
+
* Custom logic:
|
309
|
+
* - sessions are stored inside the user object
|
310
|
+
* - keep sync email index
|
311
|
+
*/
|
312
|
+
async create(model, data, uniqueId) {
|
313
|
+
const SessionListSchema = this.databaseSchema.shape.tables.shape.session;
|
314
|
+
if (!SessionListSchema) {
|
315
|
+
throw new Error("Session list schema not found");
|
316
|
+
}
|
317
|
+
const userEmail = data[this.getEmailProperty()];
|
318
|
+
const emailIndex = await this.loadEmailIndex(userEmail);
|
319
|
+
if (emailIndex?.user) {
|
320
|
+
throw new Error("Email already exists");
|
321
|
+
}
|
322
|
+
const user = await super.create(model, data, uniqueId);
|
323
|
+
await this.updateEmailIndex(userEmail, user.$jazz.id);
|
324
|
+
user.$jazz.set(
|
325
|
+
"sessions",
|
326
|
+
co.list(SessionListSchema).create([], user.$jazz.owner)
|
327
|
+
);
|
328
|
+
return user;
|
329
|
+
}
|
330
|
+
/**
|
331
|
+
* Custom logic:
|
332
|
+
* - if the email is in the where clause, find by email
|
333
|
+
*/
|
334
|
+
async findMany(model, where, limit, sortBy, offset) {
|
335
|
+
if (isWhereBySingleField("email", where)) {
|
336
|
+
return this.findByEmail(where[0].value);
|
337
|
+
}
|
338
|
+
return super.findMany(model, where, limit, sortBy, offset);
|
339
|
+
}
|
340
|
+
getEmailProperty() {
|
341
|
+
return this.betterAuthSchema.user?.fields.email?.fieldName || "email";
|
342
|
+
}
|
343
|
+
async findByEmail(email) {
|
344
|
+
const emailIndex = await this.loadEmailIndex(email);
|
345
|
+
const user = emailIndex?.user;
|
346
|
+
if (!user) {
|
347
|
+
return [];
|
348
|
+
}
|
349
|
+
return this.findById("user", [
|
350
|
+
{ field: "id", operator: "eq", value: user, connector: "AND" }
|
351
|
+
]).then((user2) => user2 ? [user2] : []);
|
352
|
+
}
|
353
|
+
/**
|
354
|
+
* Custom logic:
|
355
|
+
* - if the email is changed, update the email index
|
356
|
+
*/
|
357
|
+
async update(model, where, update) {
|
358
|
+
const nodes = await this.findMany(model, where);
|
359
|
+
if (nodes.length === 0) {
|
360
|
+
return [];
|
361
|
+
}
|
362
|
+
const newEmail = update[this.getEmailProperty()];
|
363
|
+
for (const node of nodes) {
|
364
|
+
const oldEmail = node.$jazz.raw.get(this.getEmailProperty());
|
365
|
+
for (const [key, value] of Object.entries(
|
366
|
+
update
|
367
|
+
)) {
|
368
|
+
node.$jazz.set(key, value);
|
369
|
+
}
|
370
|
+
if (oldEmail !== newEmail && oldEmail !== void 0 && newEmail !== void 0) {
|
371
|
+
await this.updateEmailIndex(oldEmail, null);
|
372
|
+
await this.updateEmailIndex(newEmail, node.$jazz.id);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
return nodes;
|
376
|
+
}
|
377
|
+
async deleteValue(model, where) {
|
378
|
+
const nodes = await this.findMany(model, where);
|
379
|
+
const deleted = await super.deleteValue(model, where);
|
380
|
+
for (const node of nodes) {
|
381
|
+
const email = node.$jazz.raw.get(this.getEmailProperty());
|
382
|
+
if (email) {
|
383
|
+
await this.updateEmailIndex(email, null);
|
384
|
+
}
|
385
|
+
}
|
386
|
+
return deleted;
|
387
|
+
}
|
388
|
+
async loadEmailIndex(email) {
|
389
|
+
const emailIndex = await EmailIndex.loadUnique(email, this.owner.$jazz.id, {
|
390
|
+
loadAs: this.worker
|
391
|
+
});
|
392
|
+
return emailIndex;
|
393
|
+
}
|
394
|
+
async updateEmailIndex(email, userId) {
|
395
|
+
await EmailIndex.upsertUnique({
|
396
|
+
value: {
|
397
|
+
user: userId
|
398
|
+
},
|
399
|
+
unique: email,
|
400
|
+
owner: this.owner
|
401
|
+
});
|
402
|
+
}
|
403
|
+
};
|
404
|
+
|
405
|
+
// src/better-auth/database-adapter/repository/session.ts
|
406
|
+
var SessionRepository = class extends JazzRepository {
|
407
|
+
constructor(databaseSchema, databaseRoot, worker, betterAuthSchema = {}, ensureSync = false) {
|
408
|
+
super(databaseSchema, databaseRoot, worker, betterAuthSchema, ensureSync);
|
409
|
+
this.userRepository = new UserRepository(
|
410
|
+
databaseSchema,
|
411
|
+
databaseRoot,
|
412
|
+
worker,
|
413
|
+
betterAuthSchema
|
414
|
+
);
|
415
|
+
}
|
416
|
+
/**
|
417
|
+
* Custom logic: sessions are stored inside the user object
|
418
|
+
*/
|
419
|
+
async create(model, data, uniqueId) {
|
420
|
+
if (typeof data.token !== "string" || typeof data.userId !== "string") {
|
421
|
+
throw new Error("Token and userId are required for session creation");
|
422
|
+
}
|
423
|
+
const user = await this.userRepository.findById("user", [
|
424
|
+
{
|
425
|
+
field: "id",
|
426
|
+
operator: "eq",
|
427
|
+
value: data.userId,
|
428
|
+
connector: "AND"
|
429
|
+
}
|
430
|
+
]);
|
431
|
+
if (!user) {
|
432
|
+
throw new Error("User not found");
|
433
|
+
}
|
434
|
+
const { sessions } = await user.$jazz.ensureLoaded({
|
435
|
+
resolve: {
|
436
|
+
sessions: true
|
437
|
+
}
|
438
|
+
});
|
439
|
+
const session = this.getSchema("session").create(data, {
|
440
|
+
unique: data.token,
|
441
|
+
owner: this.owner
|
442
|
+
});
|
443
|
+
sessions.$jazz.push(session);
|
444
|
+
return session;
|
445
|
+
}
|
446
|
+
/**
|
447
|
+
* Custom logic: sessions are stored inside the user object.
|
448
|
+
*/
|
449
|
+
async findMany(model, where, limit, sortBy, offset) {
|
450
|
+
if (isWhereBySingleField("id", where)) {
|
451
|
+
return this.findById(model, where).then((node) => node ? [node] : []);
|
452
|
+
}
|
453
|
+
if (isWhereBySingleField("token", where)) {
|
454
|
+
return this.findByUnique(model, where).then(
|
455
|
+
(node) => node ? [node] : []
|
456
|
+
);
|
457
|
+
}
|
458
|
+
if (containWhereByField("userId", where)) {
|
459
|
+
const [userIdWhere, otherWhere] = extractWhereByField("userId", where);
|
460
|
+
const user = await this.userRepository.findById("user", [
|
461
|
+
{
|
462
|
+
field: "id",
|
463
|
+
operator: "eq",
|
464
|
+
value: userIdWhere.value,
|
465
|
+
connector: "AND"
|
466
|
+
}
|
467
|
+
]);
|
468
|
+
if (!user) {
|
469
|
+
console.warn("Trying to find user's sessions, but user not found");
|
470
|
+
return [];
|
471
|
+
}
|
472
|
+
const { sessions } = await user.$jazz.ensureLoaded({
|
473
|
+
resolve: {
|
474
|
+
sessions: {
|
475
|
+
$each: true
|
476
|
+
}
|
477
|
+
}
|
478
|
+
});
|
479
|
+
return this.filterSortPaginateList(
|
480
|
+
sessions,
|
481
|
+
otherWhere,
|
482
|
+
limit,
|
483
|
+
sortBy,
|
484
|
+
offset
|
485
|
+
);
|
486
|
+
}
|
487
|
+
throw new Error(
|
488
|
+
"Unable to find session with where: " + JSON.stringify(where)
|
489
|
+
);
|
490
|
+
}
|
491
|
+
/**
|
492
|
+
* Custom logic: sessions are stored inside the user object.
|
493
|
+
*/
|
494
|
+
async deleteValue(model, where) {
|
495
|
+
const items = await this.findMany(model, where);
|
496
|
+
if (items.length === 0) {
|
497
|
+
return 0;
|
498
|
+
}
|
499
|
+
const userId = items[0].userId;
|
500
|
+
return this.deleteSession(userId, items);
|
501
|
+
}
|
502
|
+
async deleteSession(userId, items) {
|
503
|
+
const user = await this.userRepository.findById("user", [
|
504
|
+
{
|
505
|
+
field: "id",
|
506
|
+
operator: "eq",
|
507
|
+
value: userId,
|
508
|
+
connector: "AND"
|
509
|
+
}
|
510
|
+
]);
|
511
|
+
if (!user) {
|
512
|
+
throw new Error("User not found");
|
513
|
+
}
|
514
|
+
const { sessions } = await user.$jazz.ensureLoaded({
|
515
|
+
resolve: {
|
516
|
+
sessions: true
|
517
|
+
}
|
518
|
+
});
|
519
|
+
for (const toBeDeleted of items) {
|
520
|
+
const index = [...sessions.entries()].findIndex(
|
521
|
+
([_, value]) => value && value.$jazz.id === toBeDeleted.$jazz.id
|
522
|
+
);
|
523
|
+
toBeDeleted.$jazz.set("_deleted", true);
|
524
|
+
if (index !== -1) {
|
525
|
+
sessions.$jazz.remove(index);
|
526
|
+
}
|
527
|
+
}
|
528
|
+
return items.length;
|
529
|
+
}
|
530
|
+
};
|
531
|
+
|
532
|
+
// src/better-auth/database-adapter/repository/verification.ts
|
533
|
+
var VerificationRepository = class extends JazzRepository {
|
534
|
+
/**
|
535
|
+
* Custom logic: property identifier is used as uniqueId
|
536
|
+
*/
|
537
|
+
async create(model, data, uniqueId) {
|
538
|
+
return super.create(model, data, data["identifier"]);
|
539
|
+
}
|
540
|
+
/**
|
541
|
+
* Custom logic: property identifier is used as uniqueId
|
542
|
+
* If we look for identifier, we use findByUnique instead of findMany
|
543
|
+
*/
|
544
|
+
async findMany(model, where, limit, sortBy, offset) {
|
545
|
+
if (isWhereBySingleField("identifier", where)) {
|
546
|
+
return this.findByUnique(model, where).then(
|
547
|
+
(node) => node ? [node] : []
|
548
|
+
);
|
549
|
+
}
|
550
|
+
return super.findMany(model, where, limit, sortBy, offset);
|
551
|
+
}
|
552
|
+
};
|
553
|
+
|
554
|
+
// src/better-auth/database-adapter/repository/account.ts
|
555
|
+
import { co as co2, z as z2 } from "jazz-tools";
|
556
|
+
var AccountIdIndex = co2.list(z2.string());
|
557
|
+
var AccountRepository = class extends JazzRepository {
|
558
|
+
/**
|
559
|
+
* Custom logic:
|
560
|
+
* - keep sync accountId index
|
561
|
+
*/
|
562
|
+
async create(model, data, uniqueId) {
|
563
|
+
const account = await super.create(model, data, uniqueId);
|
564
|
+
await this.updateAccountIdIndex(
|
565
|
+
account[this.getAccountIdProperty()],
|
566
|
+
account.$jazz.id
|
567
|
+
);
|
568
|
+
return account;
|
569
|
+
}
|
570
|
+
/**
|
571
|
+
* Custom logic:
|
572
|
+
* - if the accountId is in the where clause, get the ids from the index
|
573
|
+
*/
|
574
|
+
async findMany(model, where, limit, sortBy, offset) {
|
575
|
+
if (isWhereBySingleField(this.getAccountIdProperty(), where)) {
|
576
|
+
const accountIdIndex = await this.getAccountIdIndex(where[0].value);
|
577
|
+
const ids = accountIdIndex ?? [];
|
578
|
+
if (ids.length === 0) {
|
579
|
+
return [];
|
580
|
+
}
|
581
|
+
const results = await Promise.all(
|
582
|
+
ids.map(
|
583
|
+
(id) => super.findById(model, [
|
584
|
+
{ field: "id", operator: "eq", value: id, connector: "AND" }
|
585
|
+
])
|
586
|
+
)
|
587
|
+
);
|
588
|
+
return results.filter((value) => value !== null);
|
589
|
+
}
|
590
|
+
return super.findMany(model, where, limit, sortBy, offset);
|
591
|
+
}
|
592
|
+
async deleteValue(model, where) {
|
593
|
+
const nodes = await this.findMany(model, where);
|
594
|
+
const deleted = await super.deleteValue(model, where);
|
595
|
+
for (const node of nodes) {
|
596
|
+
const accountId = node.$jazz.raw.get(this.getAccountIdProperty());
|
597
|
+
if (accountId) {
|
598
|
+
await this.deleteAccountIdIndex(accountId, node.$jazz.id);
|
599
|
+
}
|
600
|
+
}
|
601
|
+
return deleted;
|
602
|
+
}
|
603
|
+
async getAccountIdIndex(accountId) {
|
604
|
+
const indexUnique = this.getAccountIdProperty() + "_" + accountId;
|
605
|
+
const accountIdIndex = await AccountIdIndex.loadUnique(
|
606
|
+
indexUnique,
|
607
|
+
this.owner.$jazz.id,
|
608
|
+
{
|
609
|
+
loadAs: this.worker
|
610
|
+
}
|
611
|
+
);
|
612
|
+
return accountIdIndex;
|
613
|
+
}
|
614
|
+
async updateAccountIdIndex(accountId, entityId) {
|
615
|
+
const accountIdIndex = await this.getAccountIdIndex(accountId);
|
616
|
+
const ids = accountIdIndex ?? [];
|
617
|
+
await AccountIdIndex.upsertUnique({
|
618
|
+
value: [...ids, entityId],
|
619
|
+
unique: this.getAccountIdProperty() + "_" + accountId,
|
620
|
+
owner: this.owner
|
621
|
+
});
|
622
|
+
}
|
623
|
+
async deleteAccountIdIndex(accountId, entityId) {
|
624
|
+
const accountIdIndex = await this.getAccountIdIndex(accountId);
|
625
|
+
const ids = accountIdIndex ?? [];
|
626
|
+
await AccountIdIndex.upsertUnique({
|
627
|
+
value: ids.filter((id) => id !== entityId),
|
628
|
+
unique: this.getAccountIdProperty() + "_" + accountId,
|
629
|
+
owner: this.owner
|
630
|
+
});
|
631
|
+
}
|
632
|
+
getAccountIdProperty() {
|
633
|
+
return this.betterAuthSchema.account?.fields.accountId?.fieldName || "accountId";
|
634
|
+
}
|
635
|
+
};
|
636
|
+
|
637
|
+
// src/better-auth/database-adapter/schema.ts
|
638
|
+
import { Group, co as co3, z as z3 } from "jazz-tools";
|
639
|
+
var DATABASE_ROOT_ID = "better-auth-root";
|
640
|
+
function createJazzSchema(schema) {
|
641
|
+
const tablesSchema = generateSchemaFromBetterAuthSchema(schema);
|
642
|
+
const DatabaseRoot = co3.map({
|
643
|
+
group: Group,
|
644
|
+
tables: co3.map(tablesSchema)
|
645
|
+
});
|
646
|
+
const WorkerAccount = co3.account({
|
647
|
+
profile: co3.profile(),
|
648
|
+
root: co3.map({})
|
649
|
+
}).withMigration(async (account) => {
|
650
|
+
const dbRoot = await DatabaseRoot.loadUnique(
|
651
|
+
DATABASE_ROOT_ID,
|
652
|
+
account.$jazz.id,
|
653
|
+
{
|
654
|
+
resolve: {
|
655
|
+
group: true,
|
656
|
+
tables: true
|
657
|
+
},
|
658
|
+
loadAs: account
|
659
|
+
}
|
660
|
+
);
|
661
|
+
if (!dbRoot) {
|
662
|
+
const adminGroup = Group.create({ owner: account });
|
663
|
+
await DatabaseRoot.upsertUnique({
|
664
|
+
value: {
|
665
|
+
group: adminGroup,
|
666
|
+
// create empty tables for each model
|
667
|
+
tables: co3.map(tablesSchema).create(
|
668
|
+
Object.fromEntries(
|
669
|
+
Object.entries(tablesSchema).map(([key, value]) => [
|
670
|
+
key,
|
671
|
+
value.create([], adminGroup)
|
672
|
+
])
|
673
|
+
),
|
674
|
+
adminGroup
|
675
|
+
)
|
676
|
+
},
|
677
|
+
unique: DATABASE_ROOT_ID,
|
678
|
+
owner: account
|
679
|
+
});
|
680
|
+
} else {
|
681
|
+
for (const [key, value] of Object.entries(
|
682
|
+
DatabaseRoot.shape.tables.shape
|
683
|
+
)) {
|
684
|
+
if (dbRoot.tables[key] === void 0) {
|
685
|
+
dbRoot.tables.$jazz.set(key, value.create([], dbRoot.group));
|
686
|
+
}
|
687
|
+
}
|
688
|
+
}
|
689
|
+
});
|
690
|
+
return {
|
691
|
+
WorkerAccount,
|
692
|
+
DatabaseRoot,
|
693
|
+
betterAuthSchema: schema,
|
694
|
+
async loadDatabase(account, options) {
|
695
|
+
if (options?.resolve === false || typeof options?.resolve === "object" && options?.resolve.group !== true) {
|
696
|
+
throw new Error("Group is required to load the database");
|
697
|
+
}
|
698
|
+
const db = await DatabaseRoot.loadUnique(
|
699
|
+
DATABASE_ROOT_ID,
|
700
|
+
account.$jazz.id,
|
701
|
+
{
|
702
|
+
resolve: {
|
703
|
+
group: true,
|
704
|
+
tables: true
|
705
|
+
},
|
706
|
+
loadAs: account,
|
707
|
+
...options
|
708
|
+
}
|
709
|
+
);
|
710
|
+
if (!db) {
|
711
|
+
throw new Error("Database not found");
|
712
|
+
}
|
713
|
+
return db;
|
714
|
+
}
|
715
|
+
};
|
716
|
+
}
|
717
|
+
function generateSchemaFromBetterAuthSchema(schema) {
|
718
|
+
const tablesSchema = {};
|
719
|
+
for (const [key, value] of Object.entries(schema)) {
|
720
|
+
const modelShape = {
|
721
|
+
_deleted: z3.boolean()
|
722
|
+
};
|
723
|
+
for (const [fieldName, field] of Object.entries(value.fields)) {
|
724
|
+
modelShape[field.fieldName || fieldName] = convertFieldToCoValue(field);
|
725
|
+
}
|
726
|
+
const coMap = co3.map(modelShape);
|
727
|
+
tablesSchema[key] = co3.list(coMap);
|
728
|
+
}
|
729
|
+
if (tablesSchema["user"] && tablesSchema["session"]) {
|
730
|
+
tablesSchema["user"] = co3.list(
|
731
|
+
co3.map({
|
732
|
+
...tablesSchema["user"].element.shape,
|
733
|
+
sessions: tablesSchema["session"]
|
734
|
+
}).withMigration((user) => {
|
735
|
+
if (user.sessions === void 0) {
|
736
|
+
user.$jazz.set(
|
737
|
+
"sessions",
|
738
|
+
tablesSchema["session"].create([], user.$jazz.owner)
|
739
|
+
);
|
740
|
+
}
|
741
|
+
})
|
742
|
+
);
|
743
|
+
} else {
|
744
|
+
throw new Error(
|
745
|
+
"Cannot find user and session tables, sessions will not be persisted"
|
746
|
+
);
|
747
|
+
}
|
748
|
+
return tablesSchema;
|
749
|
+
}
|
750
|
+
function convertFieldToCoValue(field) {
|
751
|
+
let zodType;
|
752
|
+
switch (field.type) {
|
753
|
+
case "string":
|
754
|
+
zodType = z3.string();
|
755
|
+
break;
|
756
|
+
case "number":
|
757
|
+
zodType = z3.number();
|
758
|
+
break;
|
759
|
+
case "boolean":
|
760
|
+
zodType = z3.boolean();
|
761
|
+
break;
|
762
|
+
case "date":
|
763
|
+
zodType = z3.date();
|
764
|
+
break;
|
765
|
+
default:
|
766
|
+
throw new Error(`Unsupported field type: ${field.type}`);
|
767
|
+
}
|
768
|
+
if (field.required === false) {
|
769
|
+
zodType = zodType.optional();
|
770
|
+
}
|
771
|
+
return zodType;
|
772
|
+
}
|
773
|
+
function tableItem2Record(tableItem) {
|
774
|
+
if (!tableItem) {
|
775
|
+
return tableItem;
|
776
|
+
}
|
777
|
+
const { $jazz, ...rest } = tableItem;
|
778
|
+
return {
|
779
|
+
...rest,
|
780
|
+
id: $jazz.id
|
781
|
+
};
|
782
|
+
}
|
783
|
+
|
784
|
+
// src/better-auth/database-adapter/index.ts
|
785
|
+
var JazzBetterAuthDatabaseAdapter = (config) => createAdapter({
|
786
|
+
config: {
|
787
|
+
adapterId: "jazz-tools-adapter",
|
788
|
+
// A unique identifier for the adapter.
|
789
|
+
adapterName: "Jazz Tools Adapter",
|
790
|
+
// The name of the adapter.
|
791
|
+
debugLogs: config.debugLogs ?? false,
|
792
|
+
// Whether to enable debug logs.
|
793
|
+
supportsJSON: true,
|
794
|
+
// Whether the database supports JSON. (Default: false)
|
795
|
+
supportsDates: true,
|
796
|
+
// Whether the database supports dates. (Default: true)
|
797
|
+
supportsBooleans: true,
|
798
|
+
// Whether the database supports booleans. (Default: true)
|
799
|
+
supportsNumericIds: false,
|
800
|
+
// Whether the database supports auto-incrementing numeric IDs. (Default: true)
|
801
|
+
disableIdGeneration: true
|
802
|
+
},
|
803
|
+
adapter: ({ schema }) => {
|
804
|
+
const JazzSchema = createJazzSchema(schema);
|
805
|
+
let worker = void 0;
|
806
|
+
async function getWorker() {
|
807
|
+
if (worker) {
|
808
|
+
return worker;
|
809
|
+
}
|
810
|
+
const result = await startWorker({
|
811
|
+
AccountSchema: JazzSchema.WorkerAccount,
|
812
|
+
syncServer: config.syncServer,
|
813
|
+
accountID: config.accountID,
|
814
|
+
accountSecret: config.accountSecret,
|
815
|
+
skipInboxLoad: true,
|
816
|
+
asActiveAccount: false
|
817
|
+
});
|
818
|
+
worker = result.worker;
|
819
|
+
return worker;
|
820
|
+
}
|
821
|
+
async function initRepository(model, ensureSync = false) {
|
822
|
+
let Repository = void 0;
|
823
|
+
switch (model) {
|
824
|
+
case "user":
|
825
|
+
Repository = UserRepository;
|
826
|
+
break;
|
827
|
+
case "session":
|
828
|
+
Repository = SessionRepository;
|
829
|
+
break;
|
830
|
+
case "verification":
|
831
|
+
Repository = VerificationRepository;
|
832
|
+
break;
|
833
|
+
case "account":
|
834
|
+
Repository = AccountRepository;
|
835
|
+
break;
|
836
|
+
}
|
837
|
+
if (!Repository) {
|
838
|
+
Repository = JazzRepository;
|
839
|
+
}
|
840
|
+
const worker2 = await getWorker();
|
841
|
+
const database = await JazzSchema.loadDatabase(worker2);
|
842
|
+
const repository = new Repository(
|
843
|
+
JazzSchema.DatabaseRoot,
|
844
|
+
database,
|
845
|
+
worker2,
|
846
|
+
schema,
|
847
|
+
ensureSync
|
848
|
+
);
|
849
|
+
return repository;
|
850
|
+
}
|
851
|
+
return {
|
852
|
+
create: async ({ data, model, select }) => {
|
853
|
+
const repository = await initRepository(model, true);
|
854
|
+
const created = await repository.create(model, data);
|
855
|
+
await repository.ensureSync();
|
856
|
+
return tableItem2Record(created);
|
857
|
+
},
|
858
|
+
update: async ({ model, where, update }) => {
|
859
|
+
const repository = await initRepository(model, true);
|
860
|
+
const updated = await repository.update(
|
861
|
+
model,
|
862
|
+
where,
|
863
|
+
update
|
864
|
+
);
|
865
|
+
if (updated.length === 0) {
|
866
|
+
return null;
|
867
|
+
}
|
868
|
+
await repository.ensureSync();
|
869
|
+
return tableItem2Record(updated[0]);
|
870
|
+
},
|
871
|
+
updateMany: async ({ model, where, update }) => {
|
872
|
+
const repository = await initRepository(model, true);
|
873
|
+
const updated = await repository.update(model, where, update);
|
874
|
+
await repository.ensureSync();
|
875
|
+
return updated.length;
|
876
|
+
},
|
877
|
+
delete: async ({ model, where }) => {
|
878
|
+
const repository = await initRepository(model, true);
|
879
|
+
await repository.deleteValue(model, where);
|
880
|
+
await repository.ensureSync();
|
881
|
+
},
|
882
|
+
findOne: async ({ model, where }) => {
|
883
|
+
const repository = await initRepository(model);
|
884
|
+
const item = await repository.findOne(model, where);
|
885
|
+
return tableItem2Record(item);
|
886
|
+
},
|
887
|
+
findMany: async ({
|
888
|
+
model,
|
889
|
+
where,
|
890
|
+
limit,
|
891
|
+
sortBy,
|
892
|
+
offset
|
893
|
+
}) => {
|
894
|
+
const repository = await initRepository(model);
|
895
|
+
const items = await repository.findMany(
|
896
|
+
model,
|
897
|
+
where,
|
898
|
+
limit,
|
899
|
+
sortBy,
|
900
|
+
offset
|
901
|
+
);
|
902
|
+
return items.map(tableItem2Record);
|
903
|
+
},
|
904
|
+
deleteMany: async ({ model, where }) => {
|
905
|
+
const repository = await initRepository(model, true);
|
906
|
+
const deleted = await repository.deleteValue(model, where);
|
907
|
+
await repository.ensureSync();
|
908
|
+
return deleted;
|
909
|
+
},
|
910
|
+
count: async ({ model, where }) => {
|
911
|
+
const repository = await initRepository(model);
|
912
|
+
return repository.count(model, where);
|
913
|
+
}
|
914
|
+
};
|
915
|
+
}
|
916
|
+
});
|
917
|
+
export {
|
918
|
+
JazzBetterAuthDatabaseAdapter
|
919
|
+
};
|
920
|
+
//# sourceMappingURL=index.js.map
|