@anysoftinc/anydb-sdk 0.1.2 → 0.4.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 (48) hide show
  1. package/README.md +100 -163
  2. package/dist/anydb.datascript.core.js +336 -0
  3. package/dist/anydb.datascript.rules.js +29 -0
  4. package/dist/anydb.datascript.schema.js +35 -0
  5. package/dist/client.d.ts +46 -96
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +332 -305
  8. package/dist/cljs.core.js +38752 -0
  9. package/dist/cljs.reader.js +450 -0
  10. package/dist/cljs.tools.reader.edn.js +945 -0
  11. package/dist/cljs.tools.reader.impl.commons.js +205 -0
  12. package/dist/cljs.tools.reader.impl.errors.js +429 -0
  13. package/dist/cljs.tools.reader.impl.inspect.js +170 -0
  14. package/dist/cljs.tools.reader.impl.utils.js +413 -0
  15. package/dist/cljs.tools.reader.js +1815 -0
  16. package/dist/cljs.tools.reader.reader_types.js +826 -0
  17. package/dist/cljs_env.js +7672 -0
  18. package/dist/clojure.data.js +307 -0
  19. package/dist/clojure.edn.js +107 -0
  20. package/dist/clojure.set.js +394 -0
  21. package/dist/clojure.string.js +490 -0
  22. package/dist/clojure.walk.js +144 -0
  23. package/dist/datascript-backend.d.ts +26 -0
  24. package/dist/datascript-backend.d.ts.map +1 -0
  25. package/dist/datascript-backend.js +113 -0
  26. package/dist/datascript.built_ins.js +680 -0
  27. package/dist/datascript.conn.js +814 -0
  28. package/dist/datascript.core.js +1285 -0
  29. package/dist/datascript.db.js +4058 -0
  30. package/dist/datascript.impl.entity.js +588 -0
  31. package/dist/datascript.lru.js +213 -0
  32. package/dist/datascript.parser.js +8598 -0
  33. package/dist/datascript.pull_api.js +2287 -0
  34. package/dist/datascript.pull_parser.js +865 -0
  35. package/dist/datascript.query.js +2785 -0
  36. package/dist/datascript.serialize.js +352 -0
  37. package/dist/datascript.storage.js +50 -0
  38. package/dist/datascript.util.js +82 -0
  39. package/dist/extend_clj.core.js +134 -0
  40. package/dist/me.tonsky.persistent_sorted_set.arrays.js +54 -0
  41. package/dist/me.tonsky.persistent_sorted_set.js +2485 -0
  42. package/dist/nextauth-adapter.d.ts +7 -2
  43. package/dist/nextauth-adapter.d.ts.map +1 -1
  44. package/dist/nextauth-adapter.js +251 -149
  45. package/package.json +9 -5
  46. package/dist/query-builder.d.ts +0 -126
  47. package/dist/query-builder.d.ts.map +0 -1
  48. package/dist/query-builder.js +0 -207
@@ -1,5 +1,10 @@
1
1
  import type { Adapter } from "next-auth/adapters";
2
- import type { DatomicDatabase } from "./client";
2
+ import type { AnyDBClient } from "./client";
3
+ /**
4
+ * Creates NextAuth cookie configuration with proper security settings
5
+ * Uses __Host- prefix and secure cookies in production, regular cookies in development
6
+ */
7
+ export declare function createNextAuthCookies(cookieIdentifier: string): Record<string, any>;
3
8
  /**
4
9
  * Creates a NextAuth.js adapter for AnyDB/Datomic
5
10
  *
@@ -19,5 +24,5 @@ import type { DatomicDatabase } from "./client";
19
24
  * });
20
25
  * ```
21
26
  */
22
- export declare function AnyDBAdapter(db: DatomicDatabase): Adapter;
27
+ export declare function AnyDBAdapter(db: AnyDBClient): Adapter;
23
28
  //# sourceMappingURL=nextauth-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nextauth-adapter.d.ts","sourceRoot":"","sources":["../src/nextauth-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAkB,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAmHhD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,eAAe,GAAG,OAAO,CA4PzD"}
1
+ {"version":3,"file":"nextauth-adapter.d.ts","sourceRoot":"","sources":["../src/nextauth-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAkB,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,UAAU,CAAC;AAI3D;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAkCrB;AA2DD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CA6WrD"}
@@ -1,100 +1,90 @@
1
- import { SchemaBuilder, DatomicUtils, kw, sym, uuid as uuidEdn } from "./client";
1
+ import { kw, sym, uuid as uuidEdn } from "./client";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ /**
4
+ * Creates NextAuth cookie configuration with proper security settings
5
+ * Uses __Host- prefix and secure cookies in production, regular cookies in development
6
+ */
7
+ export function createNextAuthCookies(cookieIdentifier) {
8
+ const isProduction = process.env.NODE_ENV === "production";
9
+ const prefix = isProduction ? "__Host-" : "";
10
+ const secure = isProduction;
11
+ return {
12
+ sessionToken: {
13
+ name: `${prefix}${cookieIdentifier}.session-token`,
14
+ options: {
15
+ httpOnly: true,
16
+ sameSite: "lax",
17
+ path: "/",
18
+ secure,
19
+ },
20
+ },
21
+ callbackUrl: {
22
+ name: `${prefix}${cookieIdentifier}.callback-url`,
23
+ options: {
24
+ httpOnly: true,
25
+ sameSite: "lax",
26
+ path: "/",
27
+ secure,
28
+ },
29
+ },
30
+ csrfToken: {
31
+ name: `${prefix}${cookieIdentifier}.csrf-token`,
32
+ options: {
33
+ httpOnly: true,
34
+ sameSite: "lax",
35
+ path: "/",
36
+ secure,
37
+ },
38
+ },
39
+ };
40
+ }
2
41
  // Datomic schema idents
3
42
  const USER = {
4
- id: ":auth.user.v1/id", // uuid identity
5
- name: ":auth.user.v1/name",
6
- email: ":auth.user.v1/email",
7
- emailVerified: ":auth.user.v1/email-verified",
8
- image: ":auth.user.v1/image",
43
+ id: "anydb.auth.user.v1/id", // uuid identity
44
+ name: "anydb.auth.user.v1/name",
45
+ email: "anydb.auth.user.v1/email",
46
+ emailVerified: "anydb.auth.user.v1/email-verified",
47
+ image: "anydb.auth.user.v1/image",
9
48
  };
10
49
  const ACCOUNT = {
11
- id: ":auth.account.v1/id", // uuid identity
12
- userId: ":auth.account.v1/user-id", // uuid ref by value
13
- type: ":auth.account.v1/type",
14
- provider: ":auth.account.v1/provider",
15
- providerAccountId: ":auth.account.v1/provider-account-id",
16
- refreshToken: ":auth.account.v1/refresh-token",
17
- accessToken: ":auth.account.v1/access-token",
18
- expiresAt: ":auth.account.v1/expires-at",
19
- tokenType: ":auth.account.v1/token-type",
20
- scope: ":auth.account.v1/scope",
21
- idToken: ":auth.account.v1/id-token",
22
- sessionState: ":auth.account.v1/session-state",
50
+ id: "anydb.auth.account.v1/id", // uuid identity
51
+ userId: "anydb.auth.account.v1/user-id", // uuid ref by value
52
+ type: "anydb.auth.account.v1/type",
53
+ provider: "anydb.auth.account.v1/provider",
54
+ providerAccountId: "anydb.auth.account.v1/provider-account-id",
55
+ refreshToken: "anydb.auth.account.v1/refresh-token",
56
+ accessToken: "anydb.auth.account.v1/access-token",
57
+ expiresAt: "anydb.auth.account.v1/expires-at",
58
+ tokenType: "anydb.auth.account.v1/token-type",
59
+ scope: "anydb.auth.account.v1/scope",
60
+ idToken: "anydb.auth.account.v1/id-token",
61
+ sessionState: "anydb.auth.account.v1/session-state",
23
62
  };
24
63
  const SESSION = {
25
- id: ":auth.session.v1/id", // uuid identity
26
- sessionToken: ":auth.session.v1/session-token",
27
- userId: ":auth.session.v1/user-id",
28
- expires: ":auth.session.v1/expires",
64
+ id: "anydb.auth.session.v1/id", // uuid identity
65
+ sessionToken: "anydb.auth.session.v1/session-token",
66
+ userId: "anydb.auth.session.v1/user-id",
67
+ expires: "anydb.auth.session.v1/expires",
29
68
  };
30
69
  const VTOKEN = {
31
- id: ":auth.vtoken.v1/id", // uuid identity
32
- identifier: ":auth.vtoken.v1/identifier",
33
- token: ":auth.vtoken.v1/token",
34
- expires: ":auth.vtoken.v1/expires",
70
+ id: "anydb.auth.vtoken.v1/id", // uuid identity
71
+ identifier: "anydb.auth.vtoken.v1/identifier",
72
+ token: "anydb.auth.vtoken.v1/token",
73
+ expires: "anydb.auth.vtoken.v1/expires",
35
74
  };
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
75
  function toUser(m) {
86
76
  const get = (k) => m && (m[k] ?? m[`:${k}`]);
87
77
  // USER.id is ":auth.user.v1/id", so slice(1) gives "auth.user.v1/id"
88
- const idKey = USER.id.slice(1); // "auth.user.v1/id"
78
+ const idKey = USER.id; // "auth.user.v1/id"
89
79
  const idVal = get(idKey);
90
- const id = typeof idVal === 'string' ? idVal : idVal?.value ?? String(idVal);
91
- const emailVerified = get(USER.emailVerified.slice(1));
80
+ const id = typeof idVal === "string" ? idVal : idVal?.value ?? String(idVal);
81
+ const emailVerified = get(USER.emailVerified);
92
82
  return {
93
83
  id,
94
- name: get(USER.name.slice(1)) || null,
95
- email: get(USER.email.slice(1)) || null,
84
+ name: get(USER.name) || null,
85
+ email: get(USER.email) || null,
96
86
  emailVerified: emailVerified ? new Date(emailVerified) : null,
97
- image: get(USER.image.slice(1)) || null,
87
+ image: get(USER.image) || null,
98
88
  };
99
89
  }
100
90
  /**
@@ -118,15 +108,44 @@ function toUser(m) {
118
108
  */
119
109
  export function AnyDBAdapter(db) {
120
110
  async function getUserByEmail(email) {
121
- await ensureAuthSchema(db);
122
- const q = `[:find (pull ?e [${USER.id} ${USER.name} ${USER.email} ${USER.emailVerified} ${USER.image}]) :where [?e ${USER.email} "${email}"]]`;
111
+ const norm = typeof email === "string" ? email.toLowerCase() : email;
112
+ const q = {
113
+ find: [
114
+ [
115
+ sym("pull"),
116
+ sym("?e"),
117
+ [
118
+ kw(USER.id),
119
+ kw(USER.name),
120
+ kw(USER.email),
121
+ kw(USER.emailVerified),
122
+ kw(USER.image),
123
+ ],
124
+ ],
125
+ ],
126
+ where: [[sym("?e"), kw(USER.email), norm]],
127
+ };
123
128
  const res = await db.query(q);
124
129
  const rows = Array.isArray(res) ? res : [];
125
130
  return rows.length ? toUser(rows[0][0]) : null;
126
131
  }
127
132
  async function getUser(id) {
128
- await ensureAuthSchema(db);
129
- const q = `[:find (pull ?e [${USER.id} ${USER.name} ${USER.email} ${USER.emailVerified} ${USER.image}]) :where [?e ${USER.id} #uuid "${id}"]]`;
133
+ const q = {
134
+ find: [
135
+ [
136
+ sym("pull"),
137
+ sym("?e"),
138
+ [
139
+ kw(USER.id),
140
+ kw(USER.name),
141
+ kw(USER.email),
142
+ kw(USER.emailVerified),
143
+ kw(USER.image),
144
+ ],
145
+ ],
146
+ ],
147
+ where: [[sym("?e"), kw(USER.id), uuidEdn(id)]],
148
+ };
130
149
  const res = await db.query(q);
131
150
  const rows = Array.isArray(res) ? res : [];
132
151
  if (!rows.length)
@@ -136,7 +155,6 @@ export function AnyDBAdapter(db) {
136
155
  return {
137
156
  // Users
138
157
  async createUser(data) {
139
- await ensureAuthSchema(db);
140
158
  // Check if user already exists by email
141
159
  if (data.email) {
142
160
  const existing = await getUserByEmail(data.email);
@@ -144,47 +162,65 @@ export function AnyDBAdapter(db) {
144
162
  return existing;
145
163
  }
146
164
  }
147
- const id = globalThis.crypto?.randomUUID?.() || (await import("crypto")).randomUUID();
165
+ const id = uuidv4();
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 ? { [USER.emailVerified]: 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 ?? null,
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
- await ensureAuthSchema(db);
169
- const q = `[:find (pull ?u [${USER.id} ${USER.name} ${USER.email} ${USER.emailVerified} ${USER.image}])
170
- :where [?a ${ACCOUNT.provider} "${provider}"]
171
- [?a ${ACCOUNT.providerAccountId} "${providerAccountId}"]
172
- [?a ${ACCOUNT.userId} ?uid]
173
- [?u ${USER.id} ?uid]]`;
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 ? { [USER.email]: data.email } : {}),
187
- ...(data.emailVerified !== undefined ? { [USER.emailVerified]: data.emailVerified } : {}),
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,72 @@ export function AnyDBAdapter(db) {
192
228
  return u;
193
229
  },
194
230
  async deleteUser(id) {
195
- await ensureAuthSchema(db);
196
- await db.transact([[":db/retractEntity", [USER.id], uuidEdn(id)]]);
231
+ await db.transact([[kw("db/retractEntity"), [USER.id], uuidEdn(id)]]);
197
232
  return null;
198
233
  },
199
234
  // Accounts
200
235
  async linkAccount(account) {
201
- await ensureAuthSchema(db);
202
- const id = globalThis.crypto?.randomUUID?.() || (await import("crypto")).randomUUID();
236
+ const id = uuidv4();
203
237
  const tx = {
204
- 'db/id': DatomicUtils.tempId(),
205
238
  [ACCOUNT.id]: uuidEdn(id),
206
239
  [ACCOUNT.userId]: uuidEdn(account.userId),
207
240
  [ACCOUNT.type]: account.type,
208
241
  [ACCOUNT.provider]: account.provider,
209
242
  [ACCOUNT.providerAccountId]: account.providerAccountId,
210
- ...(account.refresh_token ? { [ACCOUNT.refreshToken]: account.refresh_token } : {}),
211
- ...(account.access_token ? { [ACCOUNT.accessToken]: account.access_token } : {}),
212
- ...(account.expires_at ? { [ACCOUNT.expiresAt]: account.expires_at } : {}),
213
- ...(account.token_type ? { [ACCOUNT.tokenType]: account.token_type } : {}),
243
+ ...(account.refresh_token
244
+ ? { [ACCOUNT.refreshToken]: account.refresh_token }
245
+ : {}),
246
+ ...(account.access_token
247
+ ? { [ACCOUNT.accessToken]: account.access_token }
248
+ : {}),
249
+ ...(account.expires_at
250
+ ? { [ACCOUNT.expiresAt]: account.expires_at }
251
+ : {}),
252
+ ...(account.token_type
253
+ ? { [ACCOUNT.tokenType]: account.token_type }
254
+ : {}),
214
255
  ...(account.scope ? { [ACCOUNT.scope]: account.scope } : {}),
215
256
  ...(account.id_token ? { [ACCOUNT.idToken]: account.id_token } : {}),
216
- ...(account.session_state ? { [ACCOUNT.sessionState]: account.session_state } : {}),
257
+ ...(account.session_state
258
+ ? { [ACCOUNT.sessionState]: account.session_state }
259
+ : {}),
217
260
  };
218
261
  await db.transact([tx]);
219
262
  return account;
220
263
  },
221
264
  async unlinkAccount({ provider, providerAccountId }) {
222
- await ensureAuthSchema(db);
223
- const q = `[:find ?id :where [?a ${ACCOUNT.provider} "${provider}"] [?a ${ACCOUNT.providerAccountId} "${providerAccountId}"] [?a ${ACCOUNT.id} ?id]]`;
265
+ const q = {
266
+ find: [sym("?id")],
267
+ where: [
268
+ [sym("?a"), kw(ACCOUNT.provider), provider],
269
+ [sym("?a"), kw(ACCOUNT.providerAccountId), providerAccountId],
270
+ [sym("?a"), kw(ACCOUNT.id), sym("?id")],
271
+ ],
272
+ };
224
273
  const res = await db.query(q);
225
274
  const id = Array.isArray(res) && res[0]?.[0];
226
275
  if (id) {
227
- await db.transact([[":db/retractEntity", [ACCOUNT.id], id]]);
276
+ await db.transact([[kw("db/retractEntity"), [ACCOUNT.id], id]]);
228
277
  }
229
278
  },
230
279
  // Sessions
231
280
  async createSession(session) {
232
- await ensureAuthSchema(db);
233
- const id = globalThis.crypto?.randomUUID?.() || (await import("crypto")).randomUUID();
281
+ const id = uuidv4();
234
282
  let expiresDate;
235
- if (session.expires instanceof Date && !isNaN(session.expires.getTime())) {
283
+ if (session.expires instanceof Date &&
284
+ !isNaN(session.expires.getTime())) {
236
285
  expiresDate = session.expires;
237
286
  }
238
287
  else if (session.expires) {
239
288
  const d = new Date(session.expires);
240
- expiresDate = isNaN(d.getTime()) ? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) : d;
289
+ expiresDate = isNaN(d.getTime())
290
+ ? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
291
+ : d;
241
292
  }
242
293
  else {
243
294
  expiresDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
244
295
  }
245
296
  const tx = {
246
- 'db/id': DatomicUtils.tempId(),
247
297
  [SESSION.id]: uuidEdn(id),
248
298
  [SESSION.userId]: uuidEdn(session.userId),
249
299
  [SESSION.sessionToken]: session.sessionToken,
@@ -253,11 +303,36 @@ export function AnyDBAdapter(db) {
253
303
  return { ...session, expires: expiresDate };
254
304
  },
255
305
  async getSessionAndUser(sessionToken) {
256
- await ensureAuthSchema(db);
257
- const q = `[:find (pull ?s [${SESSION.id} ${SESSION.sessionToken} ${SESSION.userId} ${SESSION.expires}]) (pull ?u [${USER.id} ${USER.name} ${USER.email} ${USER.emailVerified} ${USER.image}])
258
- :where [?s ${SESSION.sessionToken} "${sessionToken}"]
259
- [?s ${SESSION.userId} ?uid]
260
- [?u ${USER.id} ?uid]]`;
306
+ const q = {
307
+ find: [
308
+ [
309
+ sym("pull"),
310
+ sym("?s"),
311
+ [
312
+ kw(SESSION.id),
313
+ kw(SESSION.sessionToken),
314
+ kw(SESSION.userId),
315
+ kw(SESSION.expires),
316
+ ],
317
+ ],
318
+ [
319
+ sym("pull"),
320
+ sym("?u"),
321
+ [
322
+ kw(USER.id),
323
+ kw(USER.name),
324
+ kw(USER.email),
325
+ kw(USER.emailVerified),
326
+ kw(USER.image),
327
+ ],
328
+ ],
329
+ ],
330
+ where: [
331
+ [sym("?s"), kw(SESSION.sessionToken), sessionToken],
332
+ [sym("?s"), kw(SESSION.userId), sym("?uid")],
333
+ [sym("?u"), kw(USER.id), sym("?uid")],
334
+ ],
335
+ };
261
336
  const res = await db.query(q);
262
337
  const row = Array.isArray(res) ? res[0] : null;
263
338
  if (!row)
@@ -265,49 +340,62 @@ export function AnyDBAdapter(db) {
265
340
  const s = row[0];
266
341
  const u = row[1];
267
342
  const getS = (k) => s && (s[k] ?? s[`:${k}`]);
268
- const expiresRaw = getS(SESSION.expires.slice(1));
343
+ const expiresRaw = getS(SESSION.expires);
269
344
  const expires = expiresRaw instanceof Date ? expiresRaw : new Date(expiresRaw);
270
345
  const sessionResult = {
271
- sessionToken: getS(SESSION.sessionToken.slice(1)),
272
- userId: (getS(SESSION.userId.slice(1))?.value ?? getS(SESSION.userId.slice(1))),
346
+ sessionToken: getS(SESSION.sessionToken),
347
+ userId: (getS(SESSION.userId)?.value ?? getS(SESSION.userId)),
273
348
  expires,
274
349
  };
275
350
  return { session: sessionResult, user: toUser(u) };
276
351
  },
277
352
  async updateSession(partial) {
278
- await ensureAuthSchema(db);
279
353
  let expiresDate;
280
354
  if (partial.expires instanceof Date) {
281
- expiresDate = isNaN(partial.expires.getTime()) ? undefined : partial.expires;
355
+ expiresDate = isNaN(partial.expires.getTime())
356
+ ? undefined
357
+ : partial.expires;
282
358
  }
283
359
  else if (partial.expires) {
284
360
  const d = new Date(partial.expires);
285
361
  expiresDate = isNaN(d.getTime()) ? undefined : d;
286
362
  }
363
+ if (!partial.sessionToken) {
364
+ throw new Error("updateSession requires sessionToken to upsert by identity");
365
+ }
287
366
  const tx = {
288
- 'db/id': DatomicUtils.tempId(),
289
- ...(partial.sessionToken ? { [SESSION.sessionToken]: partial.sessionToken } : {}),
290
- ...(partial.userId ? { [SESSION.userId]: uuidEdn(partial.userId) } : {}),
367
+ ...(partial.sessionToken
368
+ ? { [SESSION.sessionToken]: partial.sessionToken }
369
+ : {}),
370
+ ...(partial.userId
371
+ ? { [SESSION.userId]: uuidEdn(partial.userId) }
372
+ : {}),
291
373
  ...(expiresDate ? { [SESSION.expires]: expiresDate } : {}),
292
374
  };
293
375
  await db.transact([tx]);
294
- return { ...partial, ...(expiresDate ? { expires: expiresDate } : {}) };
376
+ return {
377
+ ...partial,
378
+ ...(expiresDate ? { expires: expiresDate } : {}),
379
+ };
295
380
  },
296
381
  async deleteSession(sessionToken) {
297
- await ensureAuthSchema(db);
298
- const q = `[:find ?id :where [?s ${SESSION.sessionToken} "${sessionToken}"] [?s ${SESSION.id} ?id]]`;
382
+ const q = {
383
+ find: [sym("?id")],
384
+ where: [
385
+ [sym("?s"), kw(SESSION.sessionToken), sessionToken],
386
+ [sym("?s"), kw(SESSION.id), sym("?id")],
387
+ ],
388
+ };
299
389
  const res = await db.query(q);
300
390
  const id = Array.isArray(res) && res[0]?.[0];
301
391
  if (id) {
302
- await db.transact([[":db/retractEntity", [SESSION.id], id]]);
392
+ await db.transact([[kw("db/retractEntity"), [SESSION.id], id]]);
303
393
  }
304
394
  },
305
395
  // Verification tokens (email sign-in, passwordless)
306
396
  async createVerificationToken(token) {
307
- await ensureAuthSchema(db);
308
- const id = globalThis.crypto?.randomUUID?.() || (await import("crypto")).randomUUID();
397
+ const id = uuidv4();
309
398
  const tx = {
310
- 'db/id': DatomicUtils.tempId(),
311
399
  [VTOKEN.id]: uuidEdn(id),
312
400
  [VTOKEN.identifier]: token.identifier,
313
401
  [VTOKEN.token]: token.token,
@@ -317,23 +405,37 @@ export function AnyDBAdapter(db) {
317
405
  return token;
318
406
  },
319
407
  async useVerificationToken(params) {
320
- await ensureAuthSchema(db);
321
- const q = `[:find (pull ?e [${VTOKEN.id} ${VTOKEN.identifier} ${VTOKEN.token} ${VTOKEN.expires}])
322
- :where [?e ${VTOKEN.identifier} "${params.identifier}"]
323
- [?e ${VTOKEN.token} "${params.token}"]]`;
408
+ const q = {
409
+ find: [
410
+ [
411
+ sym("pull"),
412
+ sym("?e"),
413
+ [
414
+ kw(VTOKEN.id),
415
+ kw(VTOKEN.identifier),
416
+ kw(VTOKEN.token),
417
+ kw(VTOKEN.expires),
418
+ ],
419
+ ],
420
+ ],
421
+ where: [
422
+ [sym("?e"), kw(VTOKEN.identifier), params.identifier],
423
+ [sym("?e"), kw(VTOKEN.token), params.token],
424
+ ],
425
+ };
324
426
  const res = await db.query(q);
325
427
  const rows = Array.isArray(res) ? res : [];
326
428
  if (!rows.length)
327
429
  return null;
328
430
  const m = rows[0][0];
329
431
  const get = (k) => m && (m[k] ?? m[`:${k}`]);
330
- const entityId = get(VTOKEN.id.slice(1));
432
+ const entityId = get(VTOKEN.id);
331
433
  // Delete the token after using it
332
- await db.transact([[":db/retractEntity", [VTOKEN.id], entityId]]);
434
+ await db.transact([[kw("db/retractEntity"), entityId]]);
333
435
  return {
334
- identifier: get(VTOKEN.identifier.slice(1)),
335
- token: get(VTOKEN.token.slice(1)),
336
- expires: new Date(get(VTOKEN.expires.slice(1))),
436
+ identifier: get(VTOKEN.identifier),
437
+ token: get(VTOKEN.token),
438
+ expires: new Date(get(VTOKEN.expires)),
337
439
  };
338
440
  },
339
441
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anysoftinc/anydb-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.4.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",
@@ -22,16 +22,19 @@
22
22
  }
23
23
  },
24
24
  "scripts": {
25
- "build": "tsc",
25
+ "build": "npm run build:cljs && tsc && node scripts/pack-cljs.js",
26
+ "build:cljs": "npx shadow-cljs compile datascript",
26
27
  "dev": "tsc --watch",
27
- "prepublishOnly": "npm run build",
28
+ "dev:cljs": "npx shadow-cljs watch datascript",
29
+ "prepublishOnly": "npm run build:cljs && tsc && node scripts/pack-cljs.js",
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
- "edn-data": "^1.1.2"
36
+ "edn-data": "^1.1.2",
37
+ "uuid": "^9.0.0"
35
38
  },
36
39
  "peerDependencies": {
37
40
  "next-auth": "^4.24.0 || ^5.0.0-beta"
@@ -46,6 +49,7 @@
46
49
  "@types/node": "^20",
47
50
  "jest": "^29.7.0",
48
51
  "next-auth": "^4.24.11",
52
+ "shadow-cljs": "^2.25.10",
49
53
  "ts-jest": "^29.1.4",
50
54
  "typescript": "^5"
51
55
  },