@happyvertical/smrt-profiles 0.37.2 → 0.37.4

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 (61) hide show
  1. package/dist/chunks/ApiKey-3DlNEc91.js +145 -0
  2. package/dist/chunks/ApiKey-3DlNEc91.js.map +1 -0
  3. package/dist/chunks/ApiKeyCollection-PwVs0qDb.js +83 -0
  4. package/dist/chunks/ApiKeyCollection-PwVs0qDb.js.map +1 -0
  5. package/dist/chunks/AuditLogCollection-CnIYU0aK.js +191 -0
  6. package/dist/chunks/AuditLogCollection-CnIYU0aK.js.map +1 -0
  7. package/dist/chunks/NostrIdentityCollection-CG6HGf6c.js +3267 -0
  8. package/dist/chunks/NostrIdentityCollection-CG6HGf6c.js.map +1 -0
  9. package/dist/chunks/OidcIdentityCollection-DiLneB7J.js +158 -0
  10. package/dist/chunks/OidcIdentityCollection-DiLneB7J.js.map +1 -0
  11. package/dist/chunks/ProfileAssetCollection-BY2Wbem3.js +93 -0
  12. package/dist/chunks/ProfileAssetCollection-BY2Wbem3.js.map +1 -0
  13. package/dist/chunks/ProfileCollection-DlhdOnDr.js +641 -0
  14. package/dist/chunks/ProfileCollection-DlhdOnDr.js.map +1 -0
  15. package/dist/chunks/ProfileMetadataCollection-CGcO4Iw_.js +105 -0
  16. package/dist/chunks/ProfileMetadataCollection-CGcO4Iw_.js.map +1 -0
  17. package/dist/chunks/ProfileMetafieldCollection-C8TX9HvN.js +142 -0
  18. package/dist/chunks/ProfileMetafieldCollection-C8TX9HvN.js.map +1 -0
  19. package/dist/chunks/ProfileRelationshipCollection-JnskbZTd.js +159 -0
  20. package/dist/chunks/ProfileRelationshipCollection-JnskbZTd.js.map +1 -0
  21. package/dist/chunks/ProfileRelationshipTermCollection-UopP7sja.js +105 -0
  22. package/dist/chunks/ProfileRelationshipTermCollection-UopP7sja.js.map +1 -0
  23. package/dist/chunks/ProfileRelationshipType-OMBn5cWR.js +98 -0
  24. package/dist/chunks/ProfileRelationshipType-OMBn5cWR.js.map +1 -0
  25. package/dist/chunks/ProfileRelationshipTypeCollection-CQu08kxF.js +54 -0
  26. package/dist/chunks/ProfileRelationshipTypeCollection-CQu08kxF.js.map +1 -0
  27. package/dist/index.js +690 -69
  28. package/dist/index.js.map +1 -1
  29. package/dist/manifest.json +2 -2
  30. package/dist/smrt-knowledge.json +9 -9
  31. package/dist/types.js +0 -2
  32. package/dist/utils.js +18 -36
  33. package/dist/utils.js.map +1 -1
  34. package/package.json +16 -16
  35. package/dist/chunks/ApiKey-B2LKEaP8.js +0 -143
  36. package/dist/chunks/ApiKey-B2LKEaP8.js.map +0 -1
  37. package/dist/chunks/ApiKeyCollection-B6Op817e.js +0 -91
  38. package/dist/chunks/ApiKeyCollection-B6Op817e.js.map +0 -1
  39. package/dist/chunks/AuditLogCollection-BYqCj0uE.js +0 -195
  40. package/dist/chunks/AuditLogCollection-BYqCj0uE.js.map +0 -1
  41. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js +0 -3065
  42. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js.map +0 -1
  43. package/dist/chunks/ProfileAssetCollection-CUM5QfuJ.js +0 -122
  44. package/dist/chunks/ProfileAssetCollection-CUM5QfuJ.js.map +0 -1
  45. package/dist/chunks/ProfileCollection-M2PK8bp-.js +0 -800
  46. package/dist/chunks/ProfileCollection-M2PK8bp-.js.map +0 -1
  47. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js +0 -120
  48. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js.map +0 -1
  49. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js +0 -184
  50. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js.map +0 -1
  51. package/dist/chunks/ProfileRelationshipCollection-_TsY7MHL.js +0 -189
  52. package/dist/chunks/ProfileRelationshipCollection-_TsY7MHL.js.map +0 -1
  53. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js +0 -117
  54. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js.map +0 -1
  55. package/dist/chunks/ProfileRelationshipType-BXBLldea.js +0 -103
  56. package/dist/chunks/ProfileRelationshipType-BXBLldea.js.map +0 -1
  57. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js +0 -48
  58. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js.map +0 -1
  59. package/dist/chunks/index-B9lXQy7N.js +0 -1029
  60. package/dist/chunks/index-B9lXQy7N.js.map +0 -1
  61. package/dist/types.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,70 +1,691 @@
1
- import { B, M, a, O, b, c, P, d, e, f, g, h, i, p, r } from "./chunks/index-B9lXQy7N.js";
2
- import { a as a2, b as b2, P as P2, p as p2, s } from "./chunks/ProfileCollection-M2PK8bp-.js";
3
- import { a as a3, N, c as c2, b as b3, d as d2, f as f2, e as e2, g as g2, h as h2, i as i2, j, n, k, p as p3, l, s as s2, v, m } from "./chunks/NostrIdentityCollection-DadQBHWy.js";
4
- import { ApiKeyCollection } from "./chunks/ApiKeyCollection-B6Op817e.js";
5
- import { A, a as a4 } from "./chunks/AuditLogCollection-BYqCj0uE.js";
6
- import { P as P3, a as a5 } from "./chunks/ProfileAssetCollection-CUM5QfuJ.js";
7
- import { P as P4, a as a6 } from "./chunks/ProfileMetadataCollection-DEhmljMY.js";
8
- import { P as P5, a as a7 } from "./chunks/ProfileMetafieldCollection-DMKhSHXX.js";
9
- import { P as P6, a as a8 } from "./chunks/ProfileRelationshipCollection-_TsY7MHL.js";
10
- import { P as P7, a as a9 } from "./chunks/ProfileRelationshipTermCollection-CXem_qT-.js";
11
- import { ProfileRelationshipTypeCollection } from "./chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js";
12
- import { ApiKey } from "./chunks/ApiKey-B2LKEaP8.js";
13
- import { ProfileRelationshipType } from "./chunks/ProfileRelationshipType-BXBLldea.js";
14
- export {
15
- ApiKey,
16
- ApiKeyCollection,
17
- A as AuditLog,
18
- a4 as AuditLogCollection,
19
- B as Bot,
20
- M as MagicLinkToken,
21
- a as MagicLinkTokenCollection,
22
- a3 as NostrIdentity,
23
- N as NostrIdentityCollection,
24
- O as OidcIdentity,
25
- b as OidcIdentityCollection,
26
- c as Organization,
27
- P as Person,
28
- a2 as Profile,
29
- P3 as ProfileAsset,
30
- a5 as ProfileAssetCollection,
31
- b2 as ProfileCollection,
32
- P4 as ProfileMetadata,
33
- a6 as ProfileMetadataCollection,
34
- P5 as ProfileMetafield,
35
- a7 as ProfileMetafieldCollection,
36
- P6 as ProfileRelationship,
37
- a8 as ProfileRelationshipCollection,
38
- P7 as ProfileRelationshipTerm,
39
- a9 as ProfileRelationshipTermCollection,
40
- ProfileRelationshipType,
41
- ProfileRelationshipTypeCollection,
42
- P2 as ProfileType,
43
- d as ProfileTypeCollection,
44
- c2 as computeEventId,
45
- b3 as createAuthEvent,
46
- e as createMagicLinkService,
47
- f as createNip05Handler,
48
- g as createProfileFromNostr,
49
- h as createProfileFromOidc,
50
- d2 as decryptPrivkey,
51
- f2 as deriveEncryptionKey,
52
- e2 as encryptPrivkey,
53
- g2 as generateNostrKeypair,
54
- h2 as getPublicKey,
55
- i as isValidNip05Identifier,
56
- i2 as isValidPrivkey,
57
- j as isValidPubkey,
58
- n as npubToPubkey,
59
- k as nsecToPrivkey,
60
- p as parseNip05Identifier,
61
- p3 as privkeyToNsec,
62
- p2 as promptMessageOptions,
63
- l as pubkeyToNpub,
64
- r as resolveIdentity,
65
- s2 as signEvent,
66
- s as smrtProfilesGenerateBioPrompt,
67
- v as verifyAuthEvent,
68
- m as verifyNostrSignature
1
+ import { _ as pubkeyToNpub, a as computeEventId, b as verifyNostrSignature, c as deriveEncryptionKey, d as getPublicKey, f as isValidPrivkey, g as privkeyToNsec, h as nsecToPrivkey, l as encryptPrivkey, m as npubToPubkey, o as createAuthEvent, p as isValidPubkey, r as NostrIdentity, s as decryptPrivkey, t as NostrIdentityCollection, u as generateNostrKeypair, v as signEvent, x as __exportAll, y as verifyAuthEvent } from "./chunks/NostrIdentityCollection-CG6HGf6c.js";
2
+ import { i as ProfileType, o as promptMessageOptions, r as Profile, s as smrtProfilesGenerateBioPrompt, t as ProfileCollection } from "./chunks/ProfileCollection-DlhdOnDr.js";
3
+ import { t as ApiKey } from "./chunks/ApiKey-3DlNEc91.js";
4
+ import { r as OidcIdentity, t as OidcIdentityCollection } from "./chunks/OidcIdentityCollection-DiLneB7J.js";
5
+ import { t as ApiKeyCollection } from "./chunks/ApiKeyCollection-PwVs0qDb.js";
6
+ import { r as AuditLog, t as AuditLogCollection } from "./chunks/AuditLogCollection-CnIYU0aK.js";
7
+ import { r as ProfileAsset, t as ProfileAssetCollection } from "./chunks/ProfileAssetCollection-BY2Wbem3.js";
8
+ import { r as ProfileMetadata, t as ProfileMetadataCollection } from "./chunks/ProfileMetadataCollection-CGcO4Iw_.js";
9
+ import { r as ProfileMetafield, t as ProfileMetafieldCollection } from "./chunks/ProfileMetafieldCollection-C8TX9HvN.js";
10
+ import { r as ProfileRelationship, t as ProfileRelationshipCollection } from "./chunks/ProfileRelationshipCollection-JnskbZTd.js";
11
+ import { r as ProfileRelationshipTerm, t as ProfileRelationshipTermCollection } from "./chunks/ProfileRelationshipTermCollection-UopP7sja.js";
12
+ import { t as ProfileRelationshipType } from "./chunks/ProfileRelationshipType-OMBn5cWR.js";
13
+ import { t as ProfileRelationshipTypeCollection } from "./chunks/ProfileRelationshipTypeCollection-CQu08kxF.js";
14
+ import { ObjectRegistry, SmrtCollection, SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
15
+ import { createHash, randomBytes } from "node:crypto";
16
+ //#region src/__smrt-register__.ts
17
+ ObjectRegistry.registerPackageManifest(new URL("./manifest.json", "" + import.meta.url));
18
+ //#endregion
19
+ //#region src/models/MagicLinkToken.ts
20
+ var __defProp$1 = Object.defineProperty;
21
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
22
+ var __decorateClass$1 = (decorators, target, key, kind) => {
23
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
24
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
25
+ if (kind && result) __defProp$1(target, key, result);
26
+ return result;
69
27
  };
70
- //# sourceMappingURL=index.js.map
28
+ var DEFAULT_EXPIRATION_MINUTES = 15;
29
+ var MagicLinkToken = class extends SmrtObject {
30
+ nostrIdentityId;
31
+ /**
32
+ * SHA-256 hash of the token (never store plaintext)
33
+ */
34
+ tokenHash = "";
35
+ /**
36
+ * Email this token was sent to
37
+ */
38
+ email = "";
39
+ /**
40
+ * When this token expires
41
+ */
42
+ expiresAt = new Date(Date.now() + DEFAULT_EXPIRATION_MINUTES * 60 * 1e3);
43
+ /**
44
+ * When this token was used (null = unused)
45
+ */
46
+ usedAt = null;
47
+ /**
48
+ * IP address that requested the token (for rate limiting)
49
+ */
50
+ requestedFromIp = "";
51
+ /**
52
+ * When this token was created (for rate limiting)
53
+ */
54
+ createdAt = /* @__PURE__ */ new Date();
55
+ constructor(options = {}) {
56
+ super(options);
57
+ if (options.nostrIdentityId) this.nostrIdentityId = options.nostrIdentityId;
58
+ if (options.tokenHash) this.tokenHash = options.tokenHash;
59
+ if (options.email) this.email = options.email;
60
+ if (options.expiresAt) this.expiresAt = options.expiresAt;
61
+ if (options.usedAt !== void 0) this.usedAt = options.usedAt;
62
+ if (options.requestedFromIp) this.requestedFromIp = options.requestedFromIp;
63
+ if (options.createdAt) this.createdAt = options.createdAt;
64
+ }
65
+ /**
66
+ * Hash a token using SHA-256
67
+ */
68
+ static hashToken(token) {
69
+ return createHash("sha256").update(token).digest("hex");
70
+ }
71
+ /**
72
+ * Generate a new magic link token
73
+ * @param nostrIdentity - The identity this token is for
74
+ * @param email - The email address to send to
75
+ * @param options - Additional options
76
+ */
77
+ static async generate(nostrIdentity, email, options = {}) {
78
+ const token = randomBytes(32).toString("base64url");
79
+ const tokenHash = MagicLinkToken.hashToken(token);
80
+ const expiresInMinutes = options.expiresInMinutes ?? DEFAULT_EXPIRATION_MINUTES;
81
+ const expiresAt = new Date(Date.now() + expiresInMinutes * 60 * 1e3);
82
+ const magicLinkToken = new MagicLinkToken({
83
+ db: options.db ?? nostrIdentity.options?.db,
84
+ nostrIdentityId: nostrIdentity.id,
85
+ tokenHash,
86
+ email: email.toLowerCase(),
87
+ expiresAt,
88
+ requestedFromIp: options.requestedFromIp || ""
89
+ });
90
+ await magicLinkToken.initialize();
91
+ await magicLinkToken.save();
92
+ return {
93
+ token,
94
+ magicLinkToken
95
+ };
96
+ }
97
+ /**
98
+ * Verify a token and return the MagicLinkToken if valid
99
+ * @param token - The plaintext token to verify
100
+ * @param options - Database options
101
+ * @returns The MagicLinkToken if valid, null otherwise
102
+ */
103
+ static async verify(token, options = {}) {
104
+ const tokenHash = MagicLinkToken.hashToken(token);
105
+ const { MagicLinkTokenCollection } = await Promise.resolve().then(() => MagicLinkTokenCollection_exports);
106
+ const magicLinkToken = await (await MagicLinkTokenCollection.create(options)).findOne({ where: { tokenHash } });
107
+ if (!magicLinkToken) return null;
108
+ if (!magicLinkToken.isValid()) return null;
109
+ return magicLinkToken;
110
+ }
111
+ /**
112
+ * Check if this token is valid (not expired and not used)
113
+ */
114
+ isValid() {
115
+ if (this.usedAt !== null) return false;
116
+ if (/* @__PURE__ */ new Date() > this.expiresAt) return false;
117
+ return true;
118
+ }
119
+ /**
120
+ * Check if this token has expired
121
+ */
122
+ isExpired() {
123
+ return /* @__PURE__ */ new Date() > this.expiresAt;
124
+ }
125
+ /**
126
+ * Check if this token has been used
127
+ */
128
+ isUsed() {
129
+ return this.usedAt !== null;
130
+ }
131
+ /**
132
+ * Mark this token as used
133
+ */
134
+ async markUsed() {
135
+ this.usedAt = /* @__PURE__ */ new Date();
136
+ await this.save();
137
+ }
138
+ /**
139
+ * Get the NostrIdentity this token is for
140
+ */
141
+ async getNostrIdentity() {
142
+ return await this.getRelated("nostrIdentityId");
143
+ }
144
+ /**
145
+ * Get time remaining until expiration in seconds
146
+ */
147
+ getTimeRemainingSeconds() {
148
+ const now = /* @__PURE__ */ new Date();
149
+ const diff = this.expiresAt.getTime() - now.getTime();
150
+ return Math.max(0, Math.floor(diff / 1e3));
151
+ }
152
+ };
153
+ __decorateClass$1([foreignKey("NostrIdentity", { required: true })], MagicLinkToken.prototype, "nostrIdentityId", 2);
154
+ MagicLinkToken = __decorateClass$1([smrt({
155
+ tableName: "magic_link_tokens",
156
+ api: { exclude: ["*"] },
157
+ mcp: { exclude: ["*"] },
158
+ cli: { include: ["list"] }
159
+ })], MagicLinkToken);
160
+ //#endregion
161
+ //#region src/collections/MagicLinkTokenCollection.ts
162
+ var MagicLinkTokenCollection_exports = /* @__PURE__ */ __exportAll({ MagicLinkTokenCollection: () => MagicLinkTokenCollection });
163
+ var MagicLinkTokenCollection = class extends SmrtCollection {
164
+ static _itemClass = MagicLinkToken;
165
+ /**
166
+ * Find token by hash
167
+ */
168
+ async findByTokenHash(tokenHash) {
169
+ return await this.findOne({ where: { tokenHash } });
170
+ }
171
+ /**
172
+ * Find active (non-expired, non-used) tokens for an email
173
+ */
174
+ async findActiveForEmail(email) {
175
+ return (await this.list({ where: { email: email.toLowerCase() } })).filter((token) => token.isValid());
176
+ }
177
+ /**
178
+ * Find tokens for a NostrIdentity
179
+ */
180
+ async findByIdentity(nostrIdentityId) {
181
+ return await this.list({ where: { nostrIdentityId } });
182
+ }
183
+ /**
184
+ * Count tokens created for an email within a time window (for rate limiting)
185
+ * @param email - Email address
186
+ * @param withinMinutes - Time window in minutes
187
+ */
188
+ async countRecentByEmail(email, withinMinutes) {
189
+ const cutoff = /* @__PURE__ */ new Date(Date.now() - withinMinutes * 60 * 1e3);
190
+ return (await this.list({ where: { email: email.toLowerCase() } })).filter((token) => {
191
+ const createdAt = token.createdAt;
192
+ return createdAt && new Date(createdAt) > cutoff;
193
+ }).length;
194
+ }
195
+ /**
196
+ * Count tokens created from an IP within a time window (for rate limiting)
197
+ * @param ip - IP address
198
+ * @param withinMinutes - Time window in minutes
199
+ */
200
+ async countRecentByIp(ip, withinMinutes) {
201
+ const cutoff = /* @__PURE__ */ new Date(Date.now() - withinMinutes * 60 * 1e3);
202
+ return (await this.list({ where: { requestedFromIp: ip } })).filter((token) => {
203
+ const createdAt = token.createdAt;
204
+ return createdAt && new Date(createdAt) > cutoff;
205
+ }).length;
206
+ }
207
+ /**
208
+ * Verify a token and return it if valid
209
+ */
210
+ async verify(token) {
211
+ const tokenHash = MagicLinkToken.hashToken(token);
212
+ const magicLinkToken = await this.findByTokenHash(tokenHash);
213
+ if (!magicLinkToken) return null;
214
+ if (!magicLinkToken.isValid()) return null;
215
+ return magicLinkToken;
216
+ }
217
+ /**
218
+ * Clean up expired tokens by deleting them
219
+ * @returns Number of tokens deleted
220
+ */
221
+ async cleanupExpired() {
222
+ const allTokens = await this.list({});
223
+ let deleted = 0;
224
+ for (const token of allTokens) if (token.isExpired() || token.isUsed()) {
225
+ await token.delete();
226
+ deleted++;
227
+ }
228
+ return deleted;
229
+ }
230
+ /**
231
+ * Revoke all tokens for a NostrIdentity (e.g., when identity is deleted)
232
+ */
233
+ async revokeForIdentity(nostrIdentityId) {
234
+ const tokens = await this.findByIdentity(nostrIdentityId);
235
+ let revoked = 0;
236
+ for (const token of tokens) if (!token.isUsed()) {
237
+ await token.markUsed();
238
+ revoked++;
239
+ }
240
+ return revoked;
241
+ }
242
+ /**
243
+ * Check if rate limit is exceeded for email
244
+ * @param email - Email address
245
+ * @param maxPerHour - Maximum tokens allowed per hour (default: 5)
246
+ */
247
+ async isRateLimitedByEmail(email, maxPerHour = 5) {
248
+ return await this.countRecentByEmail(email, 60) >= maxPerHour;
249
+ }
250
+ /**
251
+ * Check if rate limit is exceeded for IP
252
+ * @param ip - IP address
253
+ * @param maxPerHour - Maximum tokens allowed per hour (default: 10)
254
+ */
255
+ async isRateLimitedByIp(ip, maxPerHour = 10) {
256
+ return await this.countRecentByIp(ip, 60) >= maxPerHour;
257
+ }
258
+ };
259
+ //#endregion
260
+ //#region src/auth/magicLinkService.ts
261
+ function createMagicLinkService(config) {
262
+ const { baseUrl, verifyPath = "/auth/verify", expiresInMinutes = 15, maxTokensPerEmailPerHour = 5, maxTokensPerIpPerHour = 10, masterSecret, sendEmail, db } = config;
263
+ return {
264
+ async initiate(email, options = {}) {
265
+ const normalizedEmail = email.toLowerCase().trim();
266
+ const { requestedFromIp, nip05Username } = options;
267
+ const identityCollection = await NostrIdentityCollection.create({ db });
268
+ const tokenCollection = await MagicLinkTokenCollection.create({ db });
269
+ if (await tokenCollection.isRateLimitedByEmail(normalizedEmail, maxTokensPerEmailPerHour)) return {
270
+ success: false,
271
+ created: false,
272
+ error: "Too many requests for this email. Please try again later.",
273
+ errorCode: "RATE_LIMITED_EMAIL"
274
+ };
275
+ if (requestedFromIp && await tokenCollection.isRateLimitedByIp(requestedFromIp, maxTokensPerIpPerHour)) return {
276
+ success: false,
277
+ created: false,
278
+ error: "Too many requests from this IP. Please try again later.",
279
+ errorCode: "RATE_LIMITED_IP"
280
+ };
281
+ let nostrIdentity = await identityCollection.findByEmail(normalizedEmail);
282
+ let profile = null;
283
+ let created = false;
284
+ if (!nostrIdentity) {
285
+ const { Person } = await Promise.resolve().then(() => ProfileTypes_exports);
286
+ const { ProfileTypeCollection } = await Promise.resolve().then(() => ProfileTypeCollection_exports);
287
+ let personType = await (await ProfileTypeCollection.create({ db })).getBySlug("person");
288
+ if (!personType) {
289
+ const { ProfileType } = await import("./chunks/ProfileCollection-DlhdOnDr.js").then((n) => n.a);
290
+ personType = new ProfileType({
291
+ db,
292
+ slug: "person",
293
+ name: "Person",
294
+ description: "Individual person profile"
295
+ });
296
+ await personType.initialize();
297
+ await personType.save();
298
+ }
299
+ profile = new Person({
300
+ db,
301
+ typeId: personType.id,
302
+ email: normalizedEmail,
303
+ name: normalizedEmail.split("@")[0]
304
+ });
305
+ await profile.initialize();
306
+ await profile.save();
307
+ const keypair = generateNostrKeypair();
308
+ const encrypted = encryptPrivkey(keypair.privkey, masterSecret);
309
+ const { NostrIdentity: NostrIdentityClass } = await import("./chunks/NostrIdentityCollection-CG6HGf6c.js").then((n) => n.i);
310
+ nostrIdentity = new NostrIdentityClass({
311
+ db,
312
+ profileId: profile.id,
313
+ pubkey: keypair.pubkey,
314
+ encryptedPrivkey: encrypted.ciphertext,
315
+ encryptionIv: encrypted.iv,
316
+ encryptionTag: encrypted.tag,
317
+ email: normalizedEmail,
318
+ nip05Username: nip05Username?.toLowerCase() || normalizedEmail.split("@")[0]
319
+ });
320
+ await nostrIdentity.initialize();
321
+ await nostrIdentity.save();
322
+ created = true;
323
+ } else profile = await nostrIdentity.getProfile();
324
+ const { token } = await MagicLinkToken.generate(nostrIdentity, normalizedEmail, {
325
+ expiresInMinutes,
326
+ requestedFromIp,
327
+ db
328
+ });
329
+ const magicLink = `${baseUrl}${verifyPath}?token=${encodeURIComponent(token)}`;
330
+ try {
331
+ await sendEmail(normalizedEmail, magicLink);
332
+ } catch (emailError) {
333
+ return {
334
+ success: false,
335
+ created,
336
+ nostrIdentity,
337
+ profile: profile || void 0,
338
+ error: "Failed to send email. Please try again.",
339
+ errorCode: "EMAIL_SEND_FAILED"
340
+ };
341
+ }
342
+ return {
343
+ success: true,
344
+ created,
345
+ nostrIdentity,
346
+ profile: profile || void 0
347
+ };
348
+ },
349
+ async verify(token) {
350
+ const magicLinkToken = await MagicLinkToken.verify(token, { db });
351
+ if (!magicLinkToken) {
352
+ const tokenHash = MagicLinkToken.hashToken(token);
353
+ const existingToken = await (await MagicLinkTokenCollection.create({ db })).findByTokenHash(tokenHash);
354
+ if (!existingToken) return {
355
+ success: false,
356
+ error: "Invalid or expired token.",
357
+ errorCode: "NOT_FOUND"
358
+ };
359
+ if (existingToken.isUsed()) return {
360
+ success: false,
361
+ error: "This link has already been used.",
362
+ errorCode: "USED_TOKEN"
363
+ };
364
+ if (existingToken.isExpired()) return {
365
+ success: false,
366
+ error: "This link has expired. Please request a new one.",
367
+ errorCode: "EXPIRED_TOKEN"
368
+ };
369
+ return {
370
+ success: false,
371
+ error: "Invalid token.",
372
+ errorCode: "INVALID_TOKEN"
373
+ };
374
+ }
375
+ await magicLinkToken.markUsed();
376
+ const nostrIdentity = await magicLinkToken.getNostrIdentity();
377
+ if (!nostrIdentity) return {
378
+ success: false,
379
+ error: "Identity not found.",
380
+ errorCode: "NOT_FOUND"
381
+ };
382
+ await nostrIdentity.markVerified();
383
+ const profile = await nostrIdentity.getProfile();
384
+ const keypair = nostrIdentity.getKeypair(masterSecret);
385
+ return {
386
+ success: true,
387
+ profile: profile || void 0,
388
+ nostrIdentity,
389
+ keypair
390
+ };
391
+ }
392
+ };
393
+ }
394
+ //#endregion
395
+ //#region src/auth/nip05Handler.ts
396
+ function createNip05Handler(config) {
397
+ const { db, defaultRelays = [], cacheTtlSeconds = 300 } = config;
398
+ return async function handleNip05Request(request) {
399
+ const { name } = request;
400
+ const response = await (await NostrIdentityCollection.create({ db })).getNip05Response(name || void 0);
401
+ if (defaultRelays.length > 0 && Object.keys(response.names).length > 0) {
402
+ response.relays = {};
403
+ for (const pubkey of Object.values(response.names)) response.relays[pubkey] = defaultRelays;
404
+ }
405
+ if (name && Object.keys(response.names).length === 0) return {
406
+ body: { names: {} },
407
+ headers: {
408
+ "Content-Type": "application/json",
409
+ "Access-Control-Allow-Origin": "*",
410
+ "Cache-Control": `public, max-age=${cacheTtlSeconds}`
411
+ },
412
+ status: 200
413
+ };
414
+ return {
415
+ body: response,
416
+ headers: {
417
+ "Content-Type": "application/json",
418
+ "Access-Control-Allow-Origin": "*",
419
+ "Cache-Control": `public, max-age=${cacheTtlSeconds}`
420
+ },
421
+ status: 200
422
+ };
423
+ };
424
+ }
425
+ function isValidNip05Identifier(identifier) {
426
+ const parts = identifier.split("@");
427
+ if (parts.length !== 2) return false;
428
+ const [localPart, domain] = parts;
429
+ if (!/^[a-z0-9_-]+$/i.test(localPart)) return false;
430
+ if (!/^[a-z0-9.-]+\.[a-z]{2,}$/i.test(domain)) return false;
431
+ return true;
432
+ }
433
+ function parseNip05Identifier(identifier) {
434
+ if (!isValidNip05Identifier(identifier)) return null;
435
+ const [localPart, domain] = identifier.split("@");
436
+ return {
437
+ localPart,
438
+ domain
439
+ };
440
+ }
441
+ //#endregion
442
+ //#region src/auth/resolveIdentity.ts
443
+ async function resolveIdentity(context) {
444
+ const options = { db: context.db };
445
+ if (context.apiKey) {
446
+ const apiKey = await ApiKey.verify(context.apiKey, options);
447
+ if (apiKey) {
448
+ const profile = await apiKey.getProfile();
449
+ if (profile) return {
450
+ profile,
451
+ source: "api_key",
452
+ apiKey
453
+ };
454
+ }
455
+ }
456
+ if (context.oidcSession?.sub && context.oidcSession?.iss) {
457
+ const oidcIdentity = await OidcIdentity.findBySubject(context.oidcSession.iss, context.oidcSession.sub, options);
458
+ if (oidcIdentity) {
459
+ await oidcIdentity.recordUsage();
460
+ const profile = await oidcIdentity.getProfile();
461
+ if (profile) return {
462
+ profile,
463
+ source: "oidc",
464
+ oidcIdentity
465
+ };
466
+ }
467
+ }
468
+ if (context.nostrAuth?.event && context.nostrAuth?.challenge) {
469
+ const { event, challenge } = context.nostrAuth;
470
+ if (verifyAuthEvent(event, challenge).valid) {
471
+ const nostrIdentity = await NostrIdentity.findByPubkey(event.pubkey, options);
472
+ if (nostrIdentity) {
473
+ await nostrIdentity.recordUsage();
474
+ const profile = await nostrIdentity.getProfile();
475
+ if (profile) return {
476
+ profile,
477
+ source: "nostr",
478
+ nostrIdentity
479
+ };
480
+ }
481
+ }
482
+ }
483
+ if (context.actor) {
484
+ const profile = await findProfileByExternalId("github", context.actor, options);
485
+ if (profile) return {
486
+ profile,
487
+ source: "actor"
488
+ };
489
+ }
490
+ return {
491
+ profile: null,
492
+ source: "none"
493
+ };
494
+ }
495
+ async function findProfileByExternalId(provider, externalId, options) {
496
+ const { OidcIdentityCollection } = await import("./chunks/OidcIdentityCollection-DiLneB7J.js").then((n) => n.n);
497
+ const identities = await (await OidcIdentityCollection.create(options)).findByProvider(provider);
498
+ for (const identity of identities) if (identity.subject === externalId || identity.email?.includes(externalId)) {
499
+ const profile = await identity.getProfile();
500
+ if (profile) return profile;
501
+ }
502
+ const { ProfileCollection } = await import("./chunks/ProfileCollection-DlhdOnDr.js").then((n) => n.n);
503
+ const profiles = await (await ProfileCollection.create(options)).list({
504
+ where: { email: `${externalId}@users.noreply.github.com` },
505
+ limit: 1
506
+ });
507
+ if (profiles.length > 0) return profiles[0];
508
+ return null;
509
+ }
510
+ async function createProfileFromOidc(claims, provider, options) {
511
+ const existingIdentity = await OidcIdentity.findBySubject(claims.iss, claims.sub, options);
512
+ if (existingIdentity) {
513
+ const profile2 = await existingIdentity.getProfile();
514
+ if (profile2) {
515
+ if (claims.email) existingIdentity.email = claims.email;
516
+ existingIdentity.lastUsedAt = /* @__PURE__ */ new Date();
517
+ await existingIdentity.save();
518
+ return {
519
+ profile: profile2,
520
+ oidcIdentity: existingIdentity,
521
+ created: false
522
+ };
523
+ }
524
+ }
525
+ if (claims.email && claims.email_verified) {
526
+ const { ProfileCollection } = await import("./chunks/ProfileCollection-DlhdOnDr.js").then((n) => n.n);
527
+ const existingProfile = await (await ProfileCollection.create(options)).findByEmail(claims.email);
528
+ if (existingProfile) return {
529
+ profile: existingProfile,
530
+ oidcIdentity: await OidcIdentity.findOrCreate(existingProfile, {
531
+ provider,
532
+ issuer: claims.iss,
533
+ subject: claims.sub,
534
+ email: claims.email
535
+ }, options),
536
+ created: false
537
+ };
538
+ }
539
+ const { Person } = await Promise.resolve().then(() => ProfileTypes_exports);
540
+ const { ProfileTypeCollection } = await Promise.resolve().then(() => ProfileTypeCollection_exports);
541
+ let personType = await (await ProfileTypeCollection.create(options)).getBySlug("person");
542
+ if (!personType) {
543
+ const { ProfileType } = await import("./chunks/ProfileCollection-DlhdOnDr.js").then((n) => n.a);
544
+ personType = new ProfileType({
545
+ ...options,
546
+ slug: "person",
547
+ name: "Person",
548
+ description: "Individual person profile"
549
+ });
550
+ await personType.initialize();
551
+ await personType.save();
552
+ }
553
+ const profile = new Person({
554
+ ...options,
555
+ typeId: personType.id,
556
+ email: claims.email || "",
557
+ name: claims.name || claims.preferred_username || claims.sub
558
+ });
559
+ await profile.initialize();
560
+ await profile.save();
561
+ return {
562
+ profile,
563
+ oidcIdentity: await OidcIdentity.findOrCreate(profile, {
564
+ provider,
565
+ issuer: claims.iss,
566
+ subject: claims.sub,
567
+ email: claims.email
568
+ }, options),
569
+ created: true
570
+ };
571
+ }
572
+ async function createProfileFromNostr(email, nostrData, options) {
573
+ const normalizedEmail = email.toLowerCase();
574
+ const existingIdentity = await NostrIdentity.findByEmail(normalizedEmail, options);
575
+ if (existingIdentity) {
576
+ const profile2 = await existingIdentity.getProfile();
577
+ if (profile2) return {
578
+ profile: profile2,
579
+ nostrIdentity: existingIdentity,
580
+ created: false
581
+ };
582
+ }
583
+ const { Person } = await Promise.resolve().then(() => ProfileTypes_exports);
584
+ const { ProfileTypeCollection } = await Promise.resolve().then(() => ProfileTypeCollection_exports);
585
+ let personType = await (await ProfileTypeCollection.create(options)).getBySlug("person");
586
+ if (!personType) {
587
+ const { ProfileType } = await import("./chunks/ProfileCollection-DlhdOnDr.js").then((n) => n.a);
588
+ personType = new ProfileType({
589
+ ...options,
590
+ slug: "person",
591
+ name: "Person",
592
+ description: "Individual person profile"
593
+ });
594
+ await personType.initialize();
595
+ await personType.save();
596
+ }
597
+ const profile = new Person({
598
+ ...options,
599
+ typeId: personType.id,
600
+ email: normalizedEmail,
601
+ name: normalizedEmail.split("@")[0]
602
+ });
603
+ await profile.initialize();
604
+ await profile.save();
605
+ const nostrIdentity = new NostrIdentity({
606
+ ...options,
607
+ profileId: profile.id,
608
+ pubkey: nostrData.pubkey,
609
+ encryptedPrivkey: nostrData.encryptedPrivkey,
610
+ encryptionIv: nostrData.encryptionIv,
611
+ encryptionTag: nostrData.encryptionTag,
612
+ email: normalizedEmail,
613
+ nip05Username: nostrData.nip05Username?.toLowerCase() || normalizedEmail.split("@")[0]
614
+ });
615
+ await nostrIdentity.initialize();
616
+ await nostrIdentity.save();
617
+ return {
618
+ profile,
619
+ nostrIdentity,
620
+ created: true
621
+ };
622
+ }
623
+ //#endregion
624
+ //#region src/collections/ProfileTypeCollection.ts
625
+ var ProfileTypeCollection_exports = /* @__PURE__ */ __exportAll({ ProfileTypeCollection: () => ProfileTypeCollection });
626
+ var ProfileTypeCollection = class extends SmrtCollection {
627
+ static _itemClass = ProfileType;
628
+ /**
629
+ * Get profile type by slug
630
+ *
631
+ * @param slug - The slug to search for
632
+ * @returns ProfileType instance or null
633
+ */
634
+ async getBySlug(slug) {
635
+ return await this.get({ slug });
636
+ }
637
+ /**
638
+ * Get or create a profile type by slug
639
+ *
640
+ * @param slug - The slug to search for
641
+ * @param defaults - Default values if creating
642
+ * @returns ProfileType instance
643
+ */
644
+ async getOrCreateBySlug(slug, defaults) {
645
+ const existing = await this.getBySlug(slug);
646
+ if (existing) return existing;
647
+ const profileType = await this.create({
648
+ slug,
649
+ ...defaults
650
+ });
651
+ await profileType.save();
652
+ return profileType;
653
+ }
654
+ };
655
+ //#endregion
656
+ //#region src/models/ProfileTypes.ts
657
+ var ProfileTypes_exports = /* @__PURE__ */ __exportAll({
658
+ Bot: () => Bot,
659
+ Organization: () => Organization,
660
+ Person: () => Person
661
+ });
662
+ var __defProp = Object.defineProperty;
663
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
664
+ var __decorateClass = (decorators, target, key, kind) => {
665
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
666
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
667
+ if (kind && result) __defProp(target, key, result);
668
+ return result;
669
+ };
670
+ var Person = class extends Profile {
671
+ constructor(options = {}) {
672
+ super(options);
673
+ }
674
+ };
675
+ Person = __decorateClass([smrt({ tableStrategy: "sti" })], Person);
676
+ var Organization = class extends Profile {
677
+ constructor(options = {}) {
678
+ super(options);
679
+ }
680
+ };
681
+ Organization = __decorateClass([smrt({ tableStrategy: "sti" })], Organization);
682
+ var Bot = class extends Profile {
683
+ constructor(options = {}) {
684
+ super(options);
685
+ }
686
+ };
687
+ Bot = __decorateClass([smrt({ tableStrategy: "sti" })], Bot);
688
+ //#endregion
689
+ export { ApiKey, ApiKeyCollection, AuditLog, AuditLogCollection, Bot, MagicLinkToken, MagicLinkTokenCollection, NostrIdentity, NostrIdentityCollection, OidcIdentity, OidcIdentityCollection, Organization, Person, Profile, ProfileAsset, ProfileAssetCollection, ProfileCollection, ProfileMetadata, ProfileMetadataCollection, ProfileMetafield, ProfileMetafieldCollection, ProfileRelationship, ProfileRelationshipCollection, ProfileRelationshipTerm, ProfileRelationshipTermCollection, ProfileRelationshipType, ProfileRelationshipTypeCollection, ProfileType, ProfileTypeCollection, computeEventId, createAuthEvent, createMagicLinkService, createNip05Handler, createProfileFromNostr, createProfileFromOidc, decryptPrivkey, deriveEncryptionKey, encryptPrivkey, generateNostrKeypair, getPublicKey, isValidNip05Identifier, isValidPrivkey, isValidPubkey, npubToPubkey, nsecToPrivkey, parseNip05Identifier, privkeyToNsec, promptMessageOptions, pubkeyToNpub, resolveIdentity, signEvent, smrtProfilesGenerateBioPrompt, verifyAuthEvent, verifyNostrSignature };
690
+
691
+ //# sourceMappingURL=index.js.map