@anysoftinc/anydb-sdk 0.1.2 → 0.3.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.
- package/README.md +91 -167
- package/dist/client.d.ts +45 -96
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +273 -305
- package/dist/datascript-backend.d.ts +26 -0
- package/dist/datascript-backend.d.ts.map +1 -0
- package/dist/datascript-backend.js +113 -0
- package/dist/nextauth-adapter.d.ts +7 -2
- package/dist/nextauth-adapter.d.ts.map +1 -1
- package/dist/nextauth-adapter.js +254 -149
- package/package.json +5 -2
- package/dist/query-builder.d.ts +0 -126
- package/dist/query-builder.d.ts.map +0 -1
- package/dist/query-builder.js +0 -207
package/dist/nextauth-adapter.js
CHANGED
|
@@ -1,100 +1,89 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { kw, sym, uuid as uuidEdn } from "./client";
|
|
2
|
+
/**
|
|
3
|
+
* Creates NextAuth cookie configuration with proper security settings
|
|
4
|
+
* Uses __Host- prefix and secure cookies in production, regular cookies in development
|
|
5
|
+
*/
|
|
6
|
+
export function createNextAuthCookies(cookieIdentifier) {
|
|
7
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
8
|
+
const prefix = isProduction ? "__Host-" : "";
|
|
9
|
+
const secure = isProduction;
|
|
10
|
+
return {
|
|
11
|
+
sessionToken: {
|
|
12
|
+
name: `${prefix}${cookieIdentifier}.session-token`,
|
|
13
|
+
options: {
|
|
14
|
+
httpOnly: true,
|
|
15
|
+
sameSite: "lax",
|
|
16
|
+
path: "/",
|
|
17
|
+
secure,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
callbackUrl: {
|
|
21
|
+
name: `${prefix}${cookieIdentifier}.callback-url`,
|
|
22
|
+
options: {
|
|
23
|
+
httpOnly: true,
|
|
24
|
+
sameSite: "lax",
|
|
25
|
+
path: "/",
|
|
26
|
+
secure,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
csrfToken: {
|
|
30
|
+
name: `${prefix}${cookieIdentifier}.csrf-token`,
|
|
31
|
+
options: {
|
|
32
|
+
httpOnly: true,
|
|
33
|
+
sameSite: "lax",
|
|
34
|
+
path: "/",
|
|
35
|
+
secure,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
2
40
|
// Datomic schema idents
|
|
3
41
|
const USER = {
|
|
4
|
-
id: "
|
|
5
|
-
name: "
|
|
6
|
-
email: "
|
|
7
|
-
emailVerified: "
|
|
8
|
-
image: "
|
|
42
|
+
id: "anydb.auth.user.v1/id", // uuid identity
|
|
43
|
+
name: "anydb.auth.user.v1/name",
|
|
44
|
+
email: "anydb.auth.user.v1/email",
|
|
45
|
+
emailVerified: "anydb.auth.user.v1/email-verified",
|
|
46
|
+
image: "anydb.auth.user.v1/image",
|
|
9
47
|
};
|
|
10
48
|
const ACCOUNT = {
|
|
11
|
-
id: "
|
|
12
|
-
userId: "
|
|
13
|
-
type: "
|
|
14
|
-
provider: "
|
|
15
|
-
providerAccountId: "
|
|
16
|
-
refreshToken: "
|
|
17
|
-
accessToken: "
|
|
18
|
-
expiresAt: "
|
|
19
|
-
tokenType: "
|
|
20
|
-
scope: "
|
|
21
|
-
idToken: "
|
|
22
|
-
sessionState: "
|
|
49
|
+
id: "anydb.auth.account.v1/id", // uuid identity
|
|
50
|
+
userId: "anydb.auth.account.v1/user-id", // uuid ref by value
|
|
51
|
+
type: "anydb.auth.account.v1/type",
|
|
52
|
+
provider: "anydb.auth.account.v1/provider",
|
|
53
|
+
providerAccountId: "anydb.auth.account.v1/provider-account-id",
|
|
54
|
+
refreshToken: "anydb.auth.account.v1/refresh-token",
|
|
55
|
+
accessToken: "anydb.auth.account.v1/access-token",
|
|
56
|
+
expiresAt: "anydb.auth.account.v1/expires-at",
|
|
57
|
+
tokenType: "anydb.auth.account.v1/token-type",
|
|
58
|
+
scope: "anydb.auth.account.v1/scope",
|
|
59
|
+
idToken: "anydb.auth.account.v1/id-token",
|
|
60
|
+
sessionState: "anydb.auth.account.v1/session-state",
|
|
23
61
|
};
|
|
24
62
|
const SESSION = {
|
|
25
|
-
id: "
|
|
26
|
-
sessionToken: "
|
|
27
|
-
userId: "
|
|
28
|
-
expires: "
|
|
63
|
+
id: "anydb.auth.session.v1/id", // uuid identity
|
|
64
|
+
sessionToken: "anydb.auth.session.v1/session-token",
|
|
65
|
+
userId: "anydb.auth.session.v1/user-id",
|
|
66
|
+
expires: "anydb.auth.session.v1/expires",
|
|
29
67
|
};
|
|
30
68
|
const VTOKEN = {
|
|
31
|
-
id: "
|
|
32
|
-
identifier: "
|
|
33
|
-
token: "
|
|
34
|
-
expires: "
|
|
69
|
+
id: "anydb.auth.vtoken.v1/id", // uuid identity
|
|
70
|
+
identifier: "anydb.auth.vtoken.v1/identifier",
|
|
71
|
+
token: "anydb.auth.vtoken.v1/token",
|
|
72
|
+
expires: "anydb.auth.vtoken.v1/expires",
|
|
35
73
|
};
|
|
36
|
-
async function ensureAuthSchema(db) {
|
|
37
|
-
const defs = [];
|
|
38
|
-
// Helper to conditionally add attribute definitions
|
|
39
|
-
const addAttr = async (ident, vt, card, opts = {}) => {
|
|
40
|
-
const exists = await db.querySymbolic({ find: [sym("?e")], where: [[sym("?e"), kw(":db/ident"), kw(ident)]] });
|
|
41
|
-
if (!exists || (Array.isArray(exists) && exists.length === 0)) {
|
|
42
|
-
defs.push(SchemaBuilder.attribute({ ":db/ident": ident, ":db/valueType": vt, ":db/cardinality": card, ...opts }));
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
// User attributes
|
|
46
|
-
await addAttr(USER.id, ":db.type/uuid", ":db.cardinality/one", { ":db/unique": ":db.unique/identity", ":db/index": true });
|
|
47
|
-
await addAttr(USER.name, ":db.type/string", ":db.cardinality/one");
|
|
48
|
-
// Make email an identity attribute so we can upsert users by email
|
|
49
|
-
await addAttr(USER.email, ":db.type/string", ":db.cardinality/one", { ":db/unique": ":db.unique/identity", ":db/index": true });
|
|
50
|
-
await addAttr(USER.emailVerified, ":db.type/instant", ":db.cardinality/one");
|
|
51
|
-
await addAttr(USER.image, ":db.type/string", ":db.cardinality/one");
|
|
52
|
-
// Account attributes
|
|
53
|
-
await addAttr(ACCOUNT.id, ":db.type/uuid", ":db.cardinality/one", { ":db/unique": ":db.unique/identity", ":db/index": true });
|
|
54
|
-
await addAttr(ACCOUNT.userId, ":db.type/uuid", ":db.cardinality/one", { ":db/index": true });
|
|
55
|
-
await addAttr(ACCOUNT.type, ":db.type/string", ":db.cardinality/one");
|
|
56
|
-
await addAttr(ACCOUNT.provider, ":db.type/string", ":db.cardinality/one", { ":db/index": true });
|
|
57
|
-
await addAttr(ACCOUNT.providerAccountId, ":db.type/string", ":db.cardinality/one", { ":db/index": true });
|
|
58
|
-
await addAttr(ACCOUNT.refreshToken, ":db.type/string", ":db.cardinality/one");
|
|
59
|
-
await addAttr(ACCOUNT.accessToken, ":db.type/string", ":db.cardinality/one");
|
|
60
|
-
await addAttr(ACCOUNT.expiresAt, ":db.type/long", ":db.cardinality/one");
|
|
61
|
-
await addAttr(ACCOUNT.tokenType, ":db.type/string", ":db.cardinality/one");
|
|
62
|
-
await addAttr(ACCOUNT.scope, ":db.type/string", ":db.cardinality/one");
|
|
63
|
-
await addAttr(ACCOUNT.idToken, ":db.type/string", ":db.cardinality/one");
|
|
64
|
-
await addAttr(ACCOUNT.sessionState, ":db.type/string", ":db.cardinality/one");
|
|
65
|
-
// Session attributes
|
|
66
|
-
await addAttr(SESSION.id, ":db.type/uuid", ":db.cardinality/one", { ":db/unique": ":db.unique/identity", ":db/index": true });
|
|
67
|
-
await addAttr(SESSION.userId, ":db.type/uuid", ":db.cardinality/one", { ":db/index": true });
|
|
68
|
-
await addAttr(SESSION.sessionToken, ":db.type/string", ":db.cardinality/one", { ":db/unique": ":db.unique/identity", ":db/index": true });
|
|
69
|
-
await addAttr(SESSION.expires, ":db.type/instant", ":db.cardinality/one");
|
|
70
|
-
// Verification token attributes
|
|
71
|
-
await addAttr(VTOKEN.id, ":db.type/uuid", ":db.cardinality/one", { ":db/unique": ":db.unique/identity", ":db/index": true });
|
|
72
|
-
await addAttr(VTOKEN.identifier, ":db.type/string", ":db.cardinality/one", { ":db/index": true });
|
|
73
|
-
await addAttr(VTOKEN.token, ":db.type/string", ":db.cardinality/one", { ":db/unique": ":db.unique/identity", ":db/index": true });
|
|
74
|
-
await addAttr(VTOKEN.expires, ":db.type/instant", ":db.cardinality/one");
|
|
75
|
-
// Install schema if there are new definitions
|
|
76
|
-
if (defs.length) {
|
|
77
|
-
try {
|
|
78
|
-
await db.transact(defs);
|
|
79
|
-
}
|
|
80
|
-
catch (e) {
|
|
81
|
-
// Schema attributes may already exist, which is fine
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
74
|
function toUser(m) {
|
|
86
75
|
const get = (k) => m && (m[k] ?? m[`:${k}`]);
|
|
87
76
|
// USER.id is ":auth.user.v1/id", so slice(1) gives "auth.user.v1/id"
|
|
88
|
-
const idKey = USER.id
|
|
77
|
+
const idKey = USER.id; // "auth.user.v1/id"
|
|
89
78
|
const idVal = get(idKey);
|
|
90
|
-
const id = typeof idVal ===
|
|
91
|
-
const emailVerified = get(USER.emailVerified
|
|
79
|
+
const id = typeof idVal === "string" ? idVal : idVal?.value ?? String(idVal);
|
|
80
|
+
const emailVerified = get(USER.emailVerified);
|
|
92
81
|
return {
|
|
93
82
|
id,
|
|
94
|
-
name: get(USER.name
|
|
95
|
-
email: get(USER.email
|
|
83
|
+
name: get(USER.name) || null,
|
|
84
|
+
email: get(USER.email) || null,
|
|
96
85
|
emailVerified: emailVerified ? new Date(emailVerified) : null,
|
|
97
|
-
image: get(USER.image
|
|
86
|
+
image: get(USER.image) || null,
|
|
98
87
|
};
|
|
99
88
|
}
|
|
100
89
|
/**
|
|
@@ -118,15 +107,44 @@ function toUser(m) {
|
|
|
118
107
|
*/
|
|
119
108
|
export function AnyDBAdapter(db) {
|
|
120
109
|
async function getUserByEmail(email) {
|
|
121
|
-
|
|
122
|
-
const q =
|
|
110
|
+
const norm = typeof email === "string" ? email.toLowerCase() : email;
|
|
111
|
+
const q = {
|
|
112
|
+
find: [
|
|
113
|
+
[
|
|
114
|
+
sym("pull"),
|
|
115
|
+
sym("?e"),
|
|
116
|
+
[
|
|
117
|
+
kw(USER.id),
|
|
118
|
+
kw(USER.name),
|
|
119
|
+
kw(USER.email),
|
|
120
|
+
kw(USER.emailVerified),
|
|
121
|
+
kw(USER.image),
|
|
122
|
+
],
|
|
123
|
+
],
|
|
124
|
+
],
|
|
125
|
+
where: [[sym("?e"), kw(USER.email), norm]],
|
|
126
|
+
};
|
|
123
127
|
const res = await db.query(q);
|
|
124
128
|
const rows = Array.isArray(res) ? res : [];
|
|
125
129
|
return rows.length ? toUser(rows[0][0]) : null;
|
|
126
130
|
}
|
|
127
131
|
async function getUser(id) {
|
|
128
|
-
|
|
129
|
-
|
|
132
|
+
const q = {
|
|
133
|
+
find: [
|
|
134
|
+
[
|
|
135
|
+
sym("pull"),
|
|
136
|
+
sym("?e"),
|
|
137
|
+
[
|
|
138
|
+
kw(USER.id),
|
|
139
|
+
kw(USER.name),
|
|
140
|
+
kw(USER.email),
|
|
141
|
+
kw(USER.emailVerified),
|
|
142
|
+
kw(USER.image),
|
|
143
|
+
],
|
|
144
|
+
],
|
|
145
|
+
],
|
|
146
|
+
where: [[sym("?e"), kw(USER.id), uuidEdn(id)]],
|
|
147
|
+
};
|
|
130
148
|
const res = await db.query(q);
|
|
131
149
|
const rows = Array.isArray(res) ? res : [];
|
|
132
150
|
if (!rows.length)
|
|
@@ -136,7 +154,6 @@ export function AnyDBAdapter(db) {
|
|
|
136
154
|
return {
|
|
137
155
|
// Users
|
|
138
156
|
async createUser(data) {
|
|
139
|
-
await ensureAuthSchema(db);
|
|
140
157
|
// Check if user already exists by email
|
|
141
158
|
if (data.email) {
|
|
142
159
|
const existing = await getUserByEmail(data.email);
|
|
@@ -144,47 +161,66 @@ export function AnyDBAdapter(db) {
|
|
|
144
161
|
return existing;
|
|
145
162
|
}
|
|
146
163
|
}
|
|
147
|
-
const id = globalThis.crypto?.randomUUID?.() ||
|
|
164
|
+
const id = globalThis.crypto?.randomUUID?.() ||
|
|
165
|
+
(await import("crypto")).randomUUID();
|
|
148
166
|
const tx = {
|
|
149
|
-
'db/id': DatomicUtils.tempId(),
|
|
150
167
|
[USER.id]: uuidEdn(id),
|
|
151
168
|
...(data.name ? { [USER.name]: data.name } : {}),
|
|
152
|
-
...(data.email ? { [USER.email]: data.email } : {}),
|
|
153
|
-
...(data.emailVerified
|
|
169
|
+
...(data.email ? { [USER.email]: String(data.email).toLowerCase() } : {}),
|
|
170
|
+
...(data.emailVerified
|
|
171
|
+
? { [USER.emailVerified]: data.emailVerified }
|
|
172
|
+
: {}),
|
|
154
173
|
...(data.image ? { [USER.image]: data.image } : {}),
|
|
155
174
|
};
|
|
156
175
|
await db.transact([tx]);
|
|
157
176
|
return {
|
|
158
177
|
id,
|
|
159
178
|
name: data.name ?? null,
|
|
160
|
-
email: data.email
|
|
179
|
+
email: (data.email ? String(data.email).toLowerCase() : null),
|
|
161
180
|
emailVerified: data.emailVerified ?? null,
|
|
162
|
-
image: data.image ?? null
|
|
181
|
+
image: data.image ?? null,
|
|
163
182
|
};
|
|
164
183
|
},
|
|
165
184
|
getUser,
|
|
166
185
|
getUserByEmail,
|
|
167
186
|
async getUserByAccount({ provider, providerAccountId }) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
[
|
|
187
|
+
const q = {
|
|
188
|
+
find: [
|
|
189
|
+
[
|
|
190
|
+
sym("pull"),
|
|
191
|
+
sym("?u"),
|
|
192
|
+
[
|
|
193
|
+
kw(USER.id),
|
|
194
|
+
kw(USER.name),
|
|
195
|
+
kw(USER.email),
|
|
196
|
+
kw(USER.emailVerified),
|
|
197
|
+
kw(USER.image),
|
|
198
|
+
],
|
|
199
|
+
],
|
|
200
|
+
],
|
|
201
|
+
where: [
|
|
202
|
+
[sym("?a"), kw(ACCOUNT.provider), provider],
|
|
203
|
+
[sym("?a"), kw(ACCOUNT.providerAccountId), providerAccountId],
|
|
204
|
+
[sym("?a"), kw(ACCOUNT.userId), sym("?uid")],
|
|
205
|
+
[sym("?u"), kw(USER.id), sym("?uid")],
|
|
206
|
+
],
|
|
207
|
+
};
|
|
174
208
|
const res = await db.query(q);
|
|
175
209
|
const rows = Array.isArray(res) ? res : [];
|
|
176
210
|
return rows.length ? toUser(rows[0][0]) : null;
|
|
177
211
|
},
|
|
178
212
|
async updateUser(data) {
|
|
179
|
-
await ensureAuthSchema(db);
|
|
180
213
|
if (!data.id)
|
|
181
214
|
throw new Error("updateUser requires id");
|
|
182
215
|
const tx = {
|
|
183
|
-
'db/id': DatomicUtils.tempId(),
|
|
184
216
|
[USER.id]: uuidEdn(data.id),
|
|
185
217
|
...(data.name !== undefined ? { [USER.name]: data.name } : {}),
|
|
186
|
-
...(data.email !== undefined
|
|
187
|
-
|
|
218
|
+
...(data.email !== undefined
|
|
219
|
+
? { [USER.email]: data.email ? String(data.email).toLowerCase() : null }
|
|
220
|
+
: {}),
|
|
221
|
+
...(data.emailVerified !== undefined
|
|
222
|
+
? { [USER.emailVerified]: data.emailVerified }
|
|
223
|
+
: {}),
|
|
188
224
|
...(data.image !== undefined ? { [USER.image]: data.image } : {}),
|
|
189
225
|
};
|
|
190
226
|
await db.transact([tx]);
|
|
@@ -192,58 +228,74 @@ export function AnyDBAdapter(db) {
|
|
|
192
228
|
return u;
|
|
193
229
|
},
|
|
194
230
|
async deleteUser(id) {
|
|
195
|
-
await
|
|
196
|
-
await db.transact([[":db/retractEntity", [USER.id], uuidEdn(id)]]);
|
|
231
|
+
await db.transact([["db/retractEntity", [USER.id], uuidEdn(id)]]);
|
|
197
232
|
return null;
|
|
198
233
|
},
|
|
199
234
|
// Accounts
|
|
200
235
|
async linkAccount(account) {
|
|
201
|
-
|
|
202
|
-
|
|
236
|
+
const id = globalThis.crypto?.randomUUID?.() ||
|
|
237
|
+
(await import("crypto")).randomUUID();
|
|
203
238
|
const tx = {
|
|
204
|
-
'db/id': DatomicUtils.tempId(),
|
|
205
239
|
[ACCOUNT.id]: uuidEdn(id),
|
|
206
240
|
[ACCOUNT.userId]: uuidEdn(account.userId),
|
|
207
241
|
[ACCOUNT.type]: account.type,
|
|
208
242
|
[ACCOUNT.provider]: account.provider,
|
|
209
243
|
[ACCOUNT.providerAccountId]: account.providerAccountId,
|
|
210
|
-
...(account.refresh_token
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
...(account.
|
|
244
|
+
...(account.refresh_token
|
|
245
|
+
? { [ACCOUNT.refreshToken]: account.refresh_token }
|
|
246
|
+
: {}),
|
|
247
|
+
...(account.access_token
|
|
248
|
+
? { [ACCOUNT.accessToken]: account.access_token }
|
|
249
|
+
: {}),
|
|
250
|
+
...(account.expires_at
|
|
251
|
+
? { [ACCOUNT.expiresAt]: account.expires_at }
|
|
252
|
+
: {}),
|
|
253
|
+
...(account.token_type
|
|
254
|
+
? { [ACCOUNT.tokenType]: account.token_type }
|
|
255
|
+
: {}),
|
|
214
256
|
...(account.scope ? { [ACCOUNT.scope]: account.scope } : {}),
|
|
215
257
|
...(account.id_token ? { [ACCOUNT.idToken]: account.id_token } : {}),
|
|
216
|
-
...(account.session_state
|
|
258
|
+
...(account.session_state
|
|
259
|
+
? { [ACCOUNT.sessionState]: account.session_state }
|
|
260
|
+
: {}),
|
|
217
261
|
};
|
|
218
262
|
await db.transact([tx]);
|
|
219
263
|
return account;
|
|
220
264
|
},
|
|
221
265
|
async unlinkAccount({ provider, providerAccountId }) {
|
|
222
|
-
|
|
223
|
-
|
|
266
|
+
const q = {
|
|
267
|
+
find: [sym("?id")],
|
|
268
|
+
where: [
|
|
269
|
+
[sym("?a"), kw(ACCOUNT.provider), provider],
|
|
270
|
+
[sym("?a"), kw(ACCOUNT.providerAccountId), providerAccountId],
|
|
271
|
+
[sym("?a"), kw(ACCOUNT.id), sym("?id")],
|
|
272
|
+
],
|
|
273
|
+
};
|
|
224
274
|
const res = await db.query(q);
|
|
225
275
|
const id = Array.isArray(res) && res[0]?.[0];
|
|
226
276
|
if (id) {
|
|
227
|
-
await db.transact([["
|
|
277
|
+
await db.transact([["db/retractEntity", [ACCOUNT.id], id]]);
|
|
228
278
|
}
|
|
229
279
|
},
|
|
230
280
|
// Sessions
|
|
231
281
|
async createSession(session) {
|
|
232
|
-
|
|
233
|
-
|
|
282
|
+
const id = globalThis.crypto?.randomUUID?.() ||
|
|
283
|
+
(await import("crypto")).randomUUID();
|
|
234
284
|
let expiresDate;
|
|
235
|
-
if (session.expires instanceof Date &&
|
|
285
|
+
if (session.expires instanceof Date &&
|
|
286
|
+
!isNaN(session.expires.getTime())) {
|
|
236
287
|
expiresDate = session.expires;
|
|
237
288
|
}
|
|
238
289
|
else if (session.expires) {
|
|
239
290
|
const d = new Date(session.expires);
|
|
240
|
-
expiresDate = isNaN(d.getTime())
|
|
291
|
+
expiresDate = isNaN(d.getTime())
|
|
292
|
+
? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
|
|
293
|
+
: d;
|
|
241
294
|
}
|
|
242
295
|
else {
|
|
243
296
|
expiresDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
244
297
|
}
|
|
245
298
|
const tx = {
|
|
246
|
-
'db/id': DatomicUtils.tempId(),
|
|
247
299
|
[SESSION.id]: uuidEdn(id),
|
|
248
300
|
[SESSION.userId]: uuidEdn(session.userId),
|
|
249
301
|
[SESSION.sessionToken]: session.sessionToken,
|
|
@@ -253,11 +305,36 @@ export function AnyDBAdapter(db) {
|
|
|
253
305
|
return { ...session, expires: expiresDate };
|
|
254
306
|
},
|
|
255
307
|
async getSessionAndUser(sessionToken) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
308
|
+
const q = {
|
|
309
|
+
find: [
|
|
310
|
+
[
|
|
311
|
+
sym("pull"),
|
|
312
|
+
sym("?s"),
|
|
313
|
+
[
|
|
314
|
+
kw(SESSION.id),
|
|
315
|
+
kw(SESSION.sessionToken),
|
|
316
|
+
kw(SESSION.userId),
|
|
317
|
+
kw(SESSION.expires),
|
|
318
|
+
],
|
|
319
|
+
],
|
|
320
|
+
[
|
|
321
|
+
sym("pull"),
|
|
322
|
+
sym("?u"),
|
|
323
|
+
[
|
|
324
|
+
kw(USER.id),
|
|
325
|
+
kw(USER.name),
|
|
326
|
+
kw(USER.email),
|
|
327
|
+
kw(USER.emailVerified),
|
|
328
|
+
kw(USER.image),
|
|
329
|
+
],
|
|
330
|
+
],
|
|
331
|
+
],
|
|
332
|
+
where: [
|
|
333
|
+
[sym("?s"), kw(SESSION.sessionToken), sessionToken],
|
|
334
|
+
[sym("?s"), kw(SESSION.userId), sym("?uid")],
|
|
335
|
+
[sym("?u"), kw(USER.id), sym("?uid")],
|
|
336
|
+
],
|
|
337
|
+
};
|
|
261
338
|
const res = await db.query(q);
|
|
262
339
|
const row = Array.isArray(res) ? res[0] : null;
|
|
263
340
|
if (!row)
|
|
@@ -265,49 +342,63 @@ export function AnyDBAdapter(db) {
|
|
|
265
342
|
const s = row[0];
|
|
266
343
|
const u = row[1];
|
|
267
344
|
const getS = (k) => s && (s[k] ?? s[`:${k}`]);
|
|
268
|
-
const expiresRaw = getS(SESSION.expires
|
|
345
|
+
const expiresRaw = getS(SESSION.expires);
|
|
269
346
|
const expires = expiresRaw instanceof Date ? expiresRaw : new Date(expiresRaw);
|
|
270
347
|
const sessionResult = {
|
|
271
|
-
sessionToken: getS(SESSION.sessionToken
|
|
272
|
-
userId: (getS(SESSION.userId
|
|
348
|
+
sessionToken: getS(SESSION.sessionToken),
|
|
349
|
+
userId: (getS(SESSION.userId)?.value ?? getS(SESSION.userId)),
|
|
273
350
|
expires,
|
|
274
351
|
};
|
|
275
352
|
return { session: sessionResult, user: toUser(u) };
|
|
276
353
|
},
|
|
277
354
|
async updateSession(partial) {
|
|
278
|
-
await ensureAuthSchema(db);
|
|
279
355
|
let expiresDate;
|
|
280
356
|
if (partial.expires instanceof Date) {
|
|
281
|
-
expiresDate = isNaN(partial.expires.getTime())
|
|
357
|
+
expiresDate = isNaN(partial.expires.getTime())
|
|
358
|
+
? undefined
|
|
359
|
+
: partial.expires;
|
|
282
360
|
}
|
|
283
361
|
else if (partial.expires) {
|
|
284
362
|
const d = new Date(partial.expires);
|
|
285
363
|
expiresDate = isNaN(d.getTime()) ? undefined : d;
|
|
286
364
|
}
|
|
365
|
+
if (!partial.sessionToken) {
|
|
366
|
+
throw new Error("updateSession requires sessionToken to upsert by identity");
|
|
367
|
+
}
|
|
287
368
|
const tx = {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
369
|
+
...(partial.sessionToken
|
|
370
|
+
? { [SESSION.sessionToken]: partial.sessionToken }
|
|
371
|
+
: {}),
|
|
372
|
+
...(partial.userId
|
|
373
|
+
? { [SESSION.userId]: uuidEdn(partial.userId) }
|
|
374
|
+
: {}),
|
|
291
375
|
...(expiresDate ? { [SESSION.expires]: expiresDate } : {}),
|
|
292
376
|
};
|
|
293
377
|
await db.transact([tx]);
|
|
294
|
-
return {
|
|
378
|
+
return {
|
|
379
|
+
...partial,
|
|
380
|
+
...(expiresDate ? { expires: expiresDate } : {}),
|
|
381
|
+
};
|
|
295
382
|
},
|
|
296
383
|
async deleteSession(sessionToken) {
|
|
297
|
-
|
|
298
|
-
|
|
384
|
+
const q = {
|
|
385
|
+
find: [sym("?id")],
|
|
386
|
+
where: [
|
|
387
|
+
[sym("?s"), kw(SESSION.sessionToken), sessionToken],
|
|
388
|
+
[sym("?s"), kw(SESSION.id), sym("?id")],
|
|
389
|
+
],
|
|
390
|
+
};
|
|
299
391
|
const res = await db.query(q);
|
|
300
392
|
const id = Array.isArray(res) && res[0]?.[0];
|
|
301
393
|
if (id) {
|
|
302
|
-
await db.transact([["
|
|
394
|
+
await db.transact([["db/retractEntity", [SESSION.id], id]]);
|
|
303
395
|
}
|
|
304
396
|
},
|
|
305
397
|
// Verification tokens (email sign-in, passwordless)
|
|
306
398
|
async createVerificationToken(token) {
|
|
307
|
-
|
|
308
|
-
|
|
399
|
+
const id = globalThis.crypto?.randomUUID?.() ||
|
|
400
|
+
(await import("crypto")).randomUUID();
|
|
309
401
|
const tx = {
|
|
310
|
-
'db/id': DatomicUtils.tempId(),
|
|
311
402
|
[VTOKEN.id]: uuidEdn(id),
|
|
312
403
|
[VTOKEN.identifier]: token.identifier,
|
|
313
404
|
[VTOKEN.token]: token.token,
|
|
@@ -317,23 +408,37 @@ export function AnyDBAdapter(db) {
|
|
|
317
408
|
return token;
|
|
318
409
|
},
|
|
319
410
|
async useVerificationToken(params) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
411
|
+
const q = {
|
|
412
|
+
find: [
|
|
413
|
+
[
|
|
414
|
+
sym("pull"),
|
|
415
|
+
sym("?e"),
|
|
416
|
+
[
|
|
417
|
+
kw(VTOKEN.id),
|
|
418
|
+
kw(VTOKEN.identifier),
|
|
419
|
+
kw(VTOKEN.token),
|
|
420
|
+
kw(VTOKEN.expires),
|
|
421
|
+
],
|
|
422
|
+
],
|
|
423
|
+
],
|
|
424
|
+
where: [
|
|
425
|
+
[sym("?e"), kw(VTOKEN.identifier), params.identifier],
|
|
426
|
+
[sym("?e"), kw(VTOKEN.token), params.token],
|
|
427
|
+
],
|
|
428
|
+
};
|
|
324
429
|
const res = await db.query(q);
|
|
325
430
|
const rows = Array.isArray(res) ? res : [];
|
|
326
431
|
if (!rows.length)
|
|
327
432
|
return null;
|
|
328
433
|
const m = rows[0][0];
|
|
329
434
|
const get = (k) => m && (m[k] ?? m[`:${k}`]);
|
|
330
|
-
const entityId = get(VTOKEN.id
|
|
435
|
+
const entityId = get(VTOKEN.id);
|
|
331
436
|
// Delete the token after using it
|
|
332
|
-
await db.transact([["
|
|
437
|
+
await db.transact([[kw("db/retractEntity"), entityId]]);
|
|
333
438
|
return {
|
|
334
|
-
identifier: get(VTOKEN.identifier
|
|
335
|
-
token: get(VTOKEN.token
|
|
336
|
-
expires: new Date(get(VTOKEN.expires
|
|
439
|
+
identifier: get(VTOKEN.identifier),
|
|
440
|
+
token: get(VTOKEN.token),
|
|
441
|
+
expires: new Date(get(VTOKEN.expires)),
|
|
337
442
|
};
|
|
338
443
|
},
|
|
339
444
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anysoftinc/anydb-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "AnyDB TypeScript SDK for querying and transacting with Datomic databases",
|
|
5
5
|
"main": "dist/client.js",
|
|
6
6
|
"types": "dist/client.d.ts",
|
|
@@ -23,12 +23,14 @@
|
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc",
|
|
26
|
+
"build:cljs": "npx shadow-cljs compile datascript",
|
|
26
27
|
"dev": "tsc --watch",
|
|
28
|
+
"dev:cljs": "npx shadow-cljs watch datascript",
|
|
27
29
|
"prepublishOnly": "npm run build",
|
|
28
30
|
"test": "jest",
|
|
29
31
|
"test:watch": "jest --watch",
|
|
30
32
|
"test:coverage": "jest --coverage",
|
|
31
|
-
"clean": "rm -rf dist"
|
|
33
|
+
"clean": "rm -rf dist dist-cljs target"
|
|
32
34
|
},
|
|
33
35
|
"dependencies": {
|
|
34
36
|
"edn-data": "^1.1.2"
|
|
@@ -46,6 +48,7 @@
|
|
|
46
48
|
"@types/node": "^20",
|
|
47
49
|
"jest": "^29.7.0",
|
|
48
50
|
"next-auth": "^4.24.11",
|
|
51
|
+
"shadow-cljs": "^2.25.10",
|
|
49
52
|
"ts-jest": "^29.1.4",
|
|
50
53
|
"typescript": "^5"
|
|
51
54
|
},
|