@nexpress/core 0.1.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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/dist/audit-54XLVCWD.js +14 -0
  4. package/dist/audit-54XLVCWD.js.map +1 -0
  5. package/dist/auth.d.ts +640 -0
  6. package/dist/auth.js +94 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/can-YLUHRJAB.js +19 -0
  9. package/dist/can-YLUHRJAB.js.map +1 -0
  10. package/dist/chunk-2G264RCD.js +68 -0
  11. package/dist/chunk-2G264RCD.js.map +1 -0
  12. package/dist/chunk-2YDGE7YX.js +92 -0
  13. package/dist/chunk-2YDGE7YX.js.map +1 -0
  14. package/dist/chunk-473S4TER.js +538 -0
  15. package/dist/chunk-473S4TER.js.map +1 -0
  16. package/dist/chunk-4ZLMEKFX.js +18 -0
  17. package/dist/chunk-4ZLMEKFX.js.map +1 -0
  18. package/dist/chunk-55FU6WED.js +179 -0
  19. package/dist/chunk-55FU6WED.js.map +1 -0
  20. package/dist/chunk-6YI5K2TI.js +1959 -0
  21. package/dist/chunk-6YI5K2TI.js.map +1 -0
  22. package/dist/chunk-BHK3AD3Q.js +41 -0
  23. package/dist/chunk-BHK3AD3Q.js.map +1 -0
  24. package/dist/chunk-CRUQBZUF.js +39 -0
  25. package/dist/chunk-CRUQBZUF.js.map +1 -0
  26. package/dist/chunk-CTSQ7BRI.js +175 -0
  27. package/dist/chunk-CTSQ7BRI.js.map +1 -0
  28. package/dist/chunk-DK2JBJH7.js +81 -0
  29. package/dist/chunk-DK2JBJH7.js.map +1 -0
  30. package/dist/chunk-DP2PREDU.js +597 -0
  31. package/dist/chunk-DP2PREDU.js.map +1 -0
  32. package/dist/chunk-EQ2Z3KMD.js +24 -0
  33. package/dist/chunk-EQ2Z3KMD.js.map +1 -0
  34. package/dist/chunk-FZ7O6DWI.js +305 -0
  35. package/dist/chunk-FZ7O6DWI.js.map +1 -0
  36. package/dist/chunk-ISLYFQWL.js +1270 -0
  37. package/dist/chunk-ISLYFQWL.js.map +1 -0
  38. package/dist/chunk-JJL74ZPK.js +68 -0
  39. package/dist/chunk-JJL74ZPK.js.map +1 -0
  40. package/dist/chunk-JKXAPSU4.js +24 -0
  41. package/dist/chunk-JKXAPSU4.js.map +1 -0
  42. package/dist/chunk-KU5M27ZC.js +24 -0
  43. package/dist/chunk-KU5M27ZC.js.map +1 -0
  44. package/dist/chunk-LSHHRDVR.js +34 -0
  45. package/dist/chunk-LSHHRDVR.js.map +1 -0
  46. package/dist/chunk-M43PGOQY.js +715 -0
  47. package/dist/chunk-M43PGOQY.js.map +1 -0
  48. package/dist/chunk-MEJAHXIO.js +150 -0
  49. package/dist/chunk-MEJAHXIO.js.map +1 -0
  50. package/dist/chunk-NUCGHWCF.js +101 -0
  51. package/dist/chunk-NUCGHWCF.js.map +1 -0
  52. package/dist/chunk-OK5HOCQI.js +845 -0
  53. package/dist/chunk-OK5HOCQI.js.map +1 -0
  54. package/dist/chunk-OROPGO65.js +13 -0
  55. package/dist/chunk-OROPGO65.js.map +1 -0
  56. package/dist/chunk-PPAS4SZR.js +176 -0
  57. package/dist/chunk-PPAS4SZR.js.map +1 -0
  58. package/dist/chunk-PPBWRKO2.js +171 -0
  59. package/dist/chunk-PPBWRKO2.js.map +1 -0
  60. package/dist/chunk-PZ5AY32C.js +10 -0
  61. package/dist/chunk-PZ5AY32C.js.map +1 -0
  62. package/dist/chunk-QO7LAQZH.js +321 -0
  63. package/dist/chunk-QO7LAQZH.js.map +1 -0
  64. package/dist/chunk-QVJ2HCAX.js +225 -0
  65. package/dist/chunk-QVJ2HCAX.js.map +1 -0
  66. package/dist/chunk-RIPHIRPP.js +68 -0
  67. package/dist/chunk-RIPHIRPP.js.map +1 -0
  68. package/dist/chunk-S27S42QY.js +134 -0
  69. package/dist/chunk-S27S42QY.js.map +1 -0
  70. package/dist/chunk-SBCVAC2Z.js +40 -0
  71. package/dist/chunk-SBCVAC2Z.js.map +1 -0
  72. package/dist/chunk-TFJ4MKPH.js +694 -0
  73. package/dist/chunk-TFJ4MKPH.js.map +1 -0
  74. package/dist/chunk-THX3SHYA.js +75 -0
  75. package/dist/chunk-THX3SHYA.js.map +1 -0
  76. package/dist/chunk-UGQSQO5B.js +222 -0
  77. package/dist/chunk-UGQSQO5B.js.map +1 -0
  78. package/dist/chunk-V2UNHGAP.js +26 -0
  79. package/dist/chunk-V2UNHGAP.js.map +1 -0
  80. package/dist/chunk-VGTPQXNQ.js +2790 -0
  81. package/dist/chunk-VGTPQXNQ.js.map +1 -0
  82. package/dist/chunk-VNIHXQ7W.js +194 -0
  83. package/dist/chunk-VNIHXQ7W.js.map +1 -0
  84. package/dist/chunk-WV272MPW.js +31 -0
  85. package/dist/chunk-WV272MPW.js.map +1 -0
  86. package/dist/chunk-X5KKBOUS.js +26 -0
  87. package/dist/chunk-X5KKBOUS.js.map +1 -0
  88. package/dist/chunk-XANPEOJC.js +17 -0
  89. package/dist/chunk-XANPEOJC.js.map +1 -0
  90. package/dist/chunk-XPVQIHAQ.js +83 -0
  91. package/dist/chunk-XPVQIHAQ.js.map +1 -0
  92. package/dist/chunk-ZCINJSS4.js +75 -0
  93. package/dist/chunk-ZCINJSS4.js.map +1 -0
  94. package/dist/community.d.ts +1425 -0
  95. package/dist/community.js +206 -0
  96. package/dist/community.js.map +1 -0
  97. package/dist/config-2GDU7PCK.js +32 -0
  98. package/dist/config-2GDU7PCK.js.map +1 -0
  99. package/dist/context-MNZ4QXPC.js +16 -0
  100. package/dist/context-MNZ4QXPC.js.map +1 -0
  101. package/dist/db-schema.d.ts +4 -0
  102. package/dist/db-schema.js +102 -0
  103. package/dist/db-schema.js.map +1 -0
  104. package/dist/db.d.ts +7 -0
  105. package/dist/db.js +117 -0
  106. package/dist/db.js.map +1 -0
  107. package/dist/digest-SY42GQSU.js +17 -0
  108. package/dist/digest-SY42GQSU.js.map +1 -0
  109. package/dist/errors-5OS3S2J3.js +22 -0
  110. package/dist/errors-5OS3S2J3.js.map +1 -0
  111. package/dist/host-OBOI4MJK.js +51 -0
  112. package/dist/host-OBOI4MJK.js.map +1 -0
  113. package/dist/i18n.d.ts +301 -0
  114. package/dist/i18n.js +68 -0
  115. package/dist/i18n.js.map +1 -0
  116. package/dist/index-B6-_vr_m.d.ts +590 -0
  117. package/dist/index-CY55LC0u.d.ts +4722 -0
  118. package/dist/index-CeiTvwbp.d.ts +168 -0
  119. package/dist/index-XwP1ET8b.d.ts +61 -0
  120. package/dist/index.d.ts +2037 -0
  121. package/dist/index.js +2205 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/job-log-VZXWQUDK.js +24 -0
  124. package/dist/job-log-VZXWQUDK.js.map +1 -0
  125. package/dist/jobs.d.ts +4 -0
  126. package/dist/jobs.js +76 -0
  127. package/dist/jobs.js.map +1 -0
  128. package/dist/logger-DqGaOU_j.d.ts +29 -0
  129. package/dist/logger-S7REWDNE.js +16 -0
  130. package/dist/logger-S7REWDNE.js.map +1 -0
  131. package/dist/media.d.ts +5 -0
  132. package/dist/media.js +41 -0
  133. package/dist/media.js.map +1 -0
  134. package/dist/mentions-2IHFVSHW.js +23 -0
  135. package/dist/mentions-2IHFVSHW.js.map +1 -0
  136. package/dist/mutes-EWAE5FZR.js +21 -0
  137. package/dist/mutes-EWAE5FZR.js.map +1 -0
  138. package/dist/notification-prefs-VPJDU7I6.js +21 -0
  139. package/dist/notification-prefs-VPJDU7I6.js.map +1 -0
  140. package/dist/observability.d.ts +156 -0
  141. package/dist/observability.js +32 -0
  142. package/dist/observability.js.map +1 -0
  143. package/dist/profanity-adapter-NU2JQSLX.js +12 -0
  144. package/dist/profanity-adapter-NU2JQSLX.js.map +1 -0
  145. package/dist/queue-XE5BC75T.js +14 -0
  146. package/dist/queue-XE5BC75T.js.map +1 -0
  147. package/dist/rate-limit.d.ts +99 -0
  148. package/dist/rate-limit.js +14 -0
  149. package/dist/rate-limit.js.map +1 -0
  150. package/dist/registry-XIXDEPVI.js +31 -0
  151. package/dist/registry-XIXDEPVI.js.map +1 -0
  152. package/dist/reputation-JRL2YQHM.js +11 -0
  153. package/dist/reputation-JRL2YQHM.js.map +1 -0
  154. package/dist/routes.d.ts +43 -0
  155. package/dist/routes.js +12 -0
  156. package/dist/routes.js.map +1 -0
  157. package/dist/scheduled-CIQM57HT.js +20 -0
  158. package/dist/scheduled-CIQM57HT.js.map +1 -0
  159. package/dist/seo.d.ts +410 -0
  160. package/dist/seo.js +44 -0
  161. package/dist/seo.js.map +1 -0
  162. package/dist/settings-FOBIESPB.js +17 -0
  163. package/dist/settings-FOBIESPB.js.map +1 -0
  164. package/dist/spam-adapter-XX3G737Z.js +12 -0
  165. package/dist/spam-adapter-XX3G737Z.js.map +1 -0
  166. package/dist/strings-VAE47B2C.js +29 -0
  167. package/dist/strings-VAE47B2C.js.map +1 -0
  168. package/dist/templates-IFVJMCJ6.js +12 -0
  169. package/dist/templates-IFVJMCJ6.js.map +1 -0
  170. package/dist/types-TlsbXS0T.d.ts +871 -0
  171. package/package.json +129 -0
@@ -0,0 +1,845 @@
1
+ import {
2
+ recordAuditEvent
3
+ } from "./chunk-RIPHIRPP.js";
4
+ import {
5
+ getCommunitySettings
6
+ } from "./chunk-PPBWRKO2.js";
7
+ import {
8
+ NpAuthError,
9
+ NpForbiddenError,
10
+ NpNotFoundError,
11
+ NpValidationError
12
+ } from "./chunk-ZCINJSS4.js";
13
+ import {
14
+ readEnvPositiveInt
15
+ } from "./chunk-OROPGO65.js";
16
+ import {
17
+ getDb
18
+ } from "./chunk-XANPEOJC.js";
19
+ import {
20
+ npMemberIdentities,
21
+ npMemberSessions,
22
+ npMembers,
23
+ npSessions,
24
+ npUserOAuthIdentities,
25
+ npUsers
26
+ } from "./chunk-M43PGOQY.js";
27
+
28
+ // src/config/access.ts
29
+ var authenticated = ({ user }) => !!user;
30
+ var isAdmin = ({ user }) => user?.role === "admin";
31
+ var isEditorOrAbove = ({ user }) => !!user && (user.role === "admin" || user.role === "editor");
32
+ var isOwnerOrAdmin = ({ user, doc }) => user?.role === "admin" || doc?.createdBy === user?.id;
33
+
34
+ // src/auth/token.ts
35
+ import { randomBytes } from "crypto";
36
+ import { jwtVerify, SignJWT, errors as joseErrors } from "jose";
37
+ var textEncoder = new TextEncoder();
38
+ async function signToken(user, secret, expirationSeconds = 7200, tokenUse = "access") {
39
+ const secretKey = textEncoder.encode(secret);
40
+ return new SignJWT({
41
+ sub: user.id,
42
+ role: user.role,
43
+ ver: user.tokenVersion,
44
+ use: tokenUse
45
+ }).setProtectedHeader({ alg: "HS256" }).setJti(randomBytes(16).toString("base64url")).setIssuedAt().setExpirationTime(Math.floor(Date.now() / 1e3) + expirationSeconds).sign(secretKey);
46
+ }
47
+ async function verifyToken(token, secret, expectedUse) {
48
+ const secretKey = textEncoder.encode(secret);
49
+ const { payload } = await jwtVerify(token, secretKey);
50
+ const typed = payload;
51
+ if (typed.use !== "access" && typed.use !== "refresh") {
52
+ throw new NpAuthError("Staff token missing `use` claim");
53
+ }
54
+ const use = typed.use;
55
+ if (expectedUse && use !== expectedUse) {
56
+ throw new NpAuthError(
57
+ `Staff token use mismatch: expected ${expectedUse}, got ${use}`
58
+ );
59
+ }
60
+ return { ...typed, use };
61
+ }
62
+ function isTokenVerificationError(err) {
63
+ if (err instanceof NpAuthError) return true;
64
+ if (err instanceof joseErrors.JOSEError) return true;
65
+ return false;
66
+ }
67
+
68
+ // src/auth/password.ts
69
+ import { hash, verify } from "@node-rs/argon2";
70
+ var ARGON2_OPTIONS = {
71
+ memoryCost: 19456,
72
+ timeCost: 2,
73
+ outputLen: 32,
74
+ parallelism: 1
75
+ };
76
+ var TEST_ARGON2_OPTIONS = {
77
+ memoryCost: 8,
78
+ timeCost: 1,
79
+ outputLen: 32,
80
+ parallelism: 1
81
+ };
82
+ function hashPassword(password) {
83
+ return hash(
84
+ password,
85
+ process.env.NP_TEST_FAST_HASH === "1" ? TEST_ARGON2_OPTIONS : ARGON2_OPTIONS
86
+ );
87
+ }
88
+ function verifyPassword(passwordHash, password) {
89
+ return verify(passwordHash, password);
90
+ }
91
+
92
+ // src/auth/csrf.ts
93
+ var SAFE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
94
+ function verifyCsrf(method, cookieToken, headerToken) {
95
+ if (SAFE_METHODS.has(method.toUpperCase())) {
96
+ return true;
97
+ }
98
+ return Boolean(cookieToken && headerToken && cookieToken === headerToken);
99
+ }
100
+
101
+ // src/auth/oauth-providers.ts
102
+ var providers = /* @__PURE__ */ new Map();
103
+ function registerOAuthProvider(provider) {
104
+ if (!provider.id || typeof provider.id !== "string") {
105
+ throw new Error("OAuth provider must have a non-empty string id");
106
+ }
107
+ if (typeof provider.authorize !== "function" || typeof provider.exchange !== "function") {
108
+ throw new Error(
109
+ `OAuth provider "${provider.id}" must implement authorize() and exchange()`
110
+ );
111
+ }
112
+ providers.set(provider.id, provider);
113
+ }
114
+ function getOAuthProvider(id) {
115
+ return providers.get(id);
116
+ }
117
+ function listOAuthProviders() {
118
+ return Array.from(providers.values());
119
+ }
120
+ function resetOAuthProviders() {
121
+ providers.clear();
122
+ }
123
+
124
+ // src/auth/oauth-resolve.ts
125
+ import { eq, and, sql } from "drizzle-orm";
126
+ var SYNTHETIC_EMAIL_SUFFIX = ".oauth.local";
127
+ function syntheticEmail(provider, providerUserId) {
128
+ return `${providerUserId}@${provider}${SYNTHETIC_EMAIL_SUFFIX}`;
129
+ }
130
+ function deriveName(profile, fallbackEmail) {
131
+ if (profile.name && profile.name.trim().length > 0) return profile.name.trim();
132
+ const localPart = fallbackEmail.split("@")[0];
133
+ return localPart && localPart.length > 0 ? localPart : "Member";
134
+ }
135
+ async function resolveOAuthLogin(input) {
136
+ const db = getDb();
137
+ const provider = input.provider;
138
+ const profile = input.profile;
139
+ const role = input.defaultRole ?? "viewer";
140
+ const [existingLink] = await db.select({
141
+ userId: npUserOAuthIdentities.userId,
142
+ identityId: npUserOAuthIdentities.id
143
+ }).from(npUserOAuthIdentities).where(
144
+ and(
145
+ eq(npUserOAuthIdentities.provider, provider),
146
+ eq(npUserOAuthIdentities.providerUserId, profile.providerUserId)
147
+ )
148
+ ).limit(1);
149
+ if (existingLink) {
150
+ const metadata = mergeMetadata(profile);
151
+ await db.update(npUserOAuthIdentities).set({ metadata, updatedAt: /* @__PURE__ */ new Date() }).where(eq(npUserOAuthIdentities.id, existingLink.identityId));
152
+ const user = await loadUser(db, existingLink.userId);
153
+ return { user, created: false, linked: false };
154
+ }
155
+ if (profile.email) {
156
+ const normalizedEmail = profile.email.trim().toLowerCase();
157
+ const [existingUser] = await db.select({
158
+ id: npUsers.id,
159
+ email: npUsers.email,
160
+ name: npUsers.name,
161
+ role: npUsers.role,
162
+ tokenVersion: npUsers.tokenVersion
163
+ }).from(npUsers).where(eq(sql`lower(${npUsers.email})`, normalizedEmail)).limit(1);
164
+ if (existingUser) {
165
+ await db.insert(npUserOAuthIdentities).values({
166
+ userId: existingUser.id,
167
+ provider,
168
+ providerUserId: profile.providerUserId,
169
+ metadata: mergeMetadata(profile)
170
+ });
171
+ return { user: existingUser, created: false, linked: true };
172
+ }
173
+ }
174
+ const email = profile.email && profile.email.trim().length > 0 ? profile.email.trim().toLowerCase() : syntheticEmail(provider, profile.providerUserId);
175
+ const name = deriveName(profile, email);
176
+ const placeholderPassword = await hashPassword(
177
+ crypto.randomUUID() + crypto.randomUUID()
178
+ );
179
+ const [created] = await db.insert(npUsers).values({
180
+ email,
181
+ name,
182
+ password: placeholderPassword,
183
+ role
184
+ }).returning({
185
+ id: npUsers.id,
186
+ email: npUsers.email,
187
+ name: npUsers.name,
188
+ role: npUsers.role,
189
+ tokenVersion: npUsers.tokenVersion
190
+ });
191
+ await db.insert(npUserOAuthIdentities).values({
192
+ userId: created.id,
193
+ provider,
194
+ providerUserId: profile.providerUserId,
195
+ metadata: mergeMetadata(profile)
196
+ });
197
+ return { user: created, created: true, linked: true };
198
+ }
199
+ function mergeMetadata(profile) {
200
+ const base = {};
201
+ if (profile.avatarUrl) base.avatarUrl = profile.avatarUrl;
202
+ if (profile.email) base.email = profile.email;
203
+ if (profile.name) base.name = profile.name;
204
+ if (profile.metadata) Object.assign(base, profile.metadata);
205
+ return base;
206
+ }
207
+ async function loadUser(db, userId) {
208
+ const [row] = await db.select({
209
+ id: npUsers.id,
210
+ email: npUsers.email,
211
+ name: npUsers.name,
212
+ role: npUsers.role,
213
+ tokenVersion: npUsers.tokenVersion
214
+ }).from(npUsers).where(eq(npUsers.id, userId)).limit(1);
215
+ if (!row) {
216
+ throw new Error(`User ${userId} referenced by oauth identity is missing`);
217
+ }
218
+ return row;
219
+ }
220
+
221
+ // src/auth/oauth-resolve-member.ts
222
+ import { and as and2, eq as eq2, sql as sql2 } from "drizzle-orm";
223
+ var SYNTHETIC_EMAIL_SUFFIX2 = ".oauth.local";
224
+ var HANDLE_FALLBACK = "user";
225
+ var HANDLE_RANDOM_SUFFIX_BYTES = 4;
226
+ function syntheticEmail2(provider, providerUserId) {
227
+ return `${providerUserId}@${provider}${SYNTHETIC_EMAIL_SUFFIX2}`;
228
+ }
229
+ function generateHandle(profile, fallbackEmail) {
230
+ const seed = profile.metadata && typeof profile.metadata.login === "string" && profile.metadata.login || profile.name || fallbackEmail.split("@")[0] || HANDLE_FALLBACK;
231
+ const sanitized = String(seed).toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^[-_]+/, "").slice(0, 20);
232
+ const base = sanitized.length >= 3 ? sanitized : HANDLE_FALLBACK;
233
+ const suffix = Math.random().toString(36).slice(2, 2 + HANDLE_RANDOM_SUFFIX_BYTES);
234
+ return `${base}-${suffix}`.slice(0, 30);
235
+ }
236
+ function deriveDisplayName(profile, fallbackEmail) {
237
+ if (profile.name && profile.name.trim().length > 0) return profile.name.trim();
238
+ const localPart = fallbackEmail.split("@")[0];
239
+ return localPart && localPart.length > 0 ? localPart : "Member";
240
+ }
241
+ function mergeMetadata2(profile) {
242
+ const base = {};
243
+ if (profile.avatarUrl) base.avatarUrl = profile.avatarUrl;
244
+ if (profile.email) base.email = profile.email;
245
+ if (profile.name) base.name = profile.name;
246
+ if (profile.metadata) Object.assign(base, profile.metadata);
247
+ return base;
248
+ }
249
+ async function loadMember(db, memberId) {
250
+ const [row] = await db.select({
251
+ id: npMembers.id,
252
+ email: npMembers.email,
253
+ handle: npMembers.handle,
254
+ displayName: npMembers.displayName,
255
+ status: npMembers.status,
256
+ tokenVersion: npMembers.tokenVersion
257
+ }).from(npMembers).where(eq2(npMembers.id, memberId)).limit(1);
258
+ if (!row) {
259
+ throw new Error(`Member ${memberId} referenced by oauth identity is missing`);
260
+ }
261
+ return row;
262
+ }
263
+ async function resolveMemberOAuthLogin(input) {
264
+ const db = getDb();
265
+ const { provider, profile } = input;
266
+ const [existingLink] = await db.select({ memberId: npMemberIdentities.memberId, identityId: npMemberIdentities.id }).from(npMemberIdentities).where(
267
+ and2(
268
+ eq2(npMemberIdentities.provider, provider),
269
+ eq2(npMemberIdentities.subject, profile.providerUserId)
270
+ )
271
+ ).limit(1);
272
+ if (existingLink) {
273
+ await db.update(npMemberIdentities).set({ metadata: mergeMetadata2(profile), updatedAt: /* @__PURE__ */ new Date() }).where(eq2(npMemberIdentities.id, existingLink.identityId));
274
+ const member = await loadMember(db, existingLink.memberId);
275
+ return { member, created: false, linked: false };
276
+ }
277
+ if (profile.email) {
278
+ const normalizedEmail = profile.email.trim().toLowerCase();
279
+ const [existingMember] = await db.select({
280
+ id: npMembers.id,
281
+ email: npMembers.email,
282
+ handle: npMembers.handle,
283
+ displayName: npMembers.displayName,
284
+ status: npMembers.status,
285
+ tokenVersion: npMembers.tokenVersion
286
+ }).from(npMembers).where(eq2(sql2`lower(${npMembers.email})`, normalizedEmail)).limit(1);
287
+ if (existingMember) {
288
+ if (existingMember.status !== "active") {
289
+ return { member: existingMember, created: false, linked: false };
290
+ }
291
+ await db.insert(npMemberIdentities).values({
292
+ memberId: existingMember.id,
293
+ provider,
294
+ subject: profile.providerUserId,
295
+ email: profile.email,
296
+ metadata: mergeMetadata2(profile)
297
+ });
298
+ return { member: existingMember, created: false, linked: true };
299
+ }
300
+ }
301
+ const settings = await getCommunitySettings();
302
+ if (!settings.registrationEnabled) {
303
+ throw new NpForbiddenError("members", "register");
304
+ }
305
+ const email = profile.email && profile.email.trim().length > 0 ? profile.email.trim().toLowerCase() : syntheticEmail2(provider, profile.providerUserId);
306
+ const displayName = deriveDisplayName(profile, email);
307
+ const handle = generateHandle(profile, email);
308
+ const placeholderPassword = await hashPassword(
309
+ crypto.randomUUID() + crypto.randomUUID()
310
+ );
311
+ const [created] = await db.insert(npMembers).values({
312
+ email,
313
+ handle,
314
+ displayName,
315
+ password: placeholderPassword,
316
+ // OAuth verifies the address out-of-band (the provider showed the
317
+ // user a real login screen for it), so skip the email-verify
318
+ // dance that password registration goes through.
319
+ emailVerified: true,
320
+ status: "active"
321
+ }).returning({
322
+ id: npMembers.id,
323
+ email: npMembers.email,
324
+ handle: npMembers.handle,
325
+ displayName: npMembers.displayName,
326
+ status: npMembers.status,
327
+ tokenVersion: npMembers.tokenVersion
328
+ });
329
+ await db.insert(npMemberIdentities).values({
330
+ memberId: created.id,
331
+ provider,
332
+ subject: profile.providerUserId,
333
+ email: profile.email ?? null,
334
+ metadata: mergeMetadata2(profile)
335
+ });
336
+ return { member: created, created: true, linked: true };
337
+ }
338
+
339
+ // src/auth/oauth-state.ts
340
+ import { createHmac, randomBytes as randomBytes2, timingSafeEqual } from "crypto";
341
+ var STATE_TTL_SECONDS = readEnvPositiveInt("NP_OAUTH_STATE_TTL_SECONDS", 600);
342
+ var CODE_VERIFIER_BYTES = 32;
343
+ function b64url(input) {
344
+ return Buffer.from(input).toString("base64url");
345
+ }
346
+ function sign(payload, secret) {
347
+ return createHmac("sha256", secret).update(payload).digest("base64url");
348
+ }
349
+ function issueOAuthState(providerId, secret) {
350
+ const nonce = randomBytes2(16).toString("base64url");
351
+ const codeVerifier = randomBytes2(CODE_VERIFIER_BYTES).toString("base64url");
352
+ const expSeconds = Math.floor(Date.now() / 1e3) + STATE_TTL_SECONDS;
353
+ const payload = { providerId, nonce, expSeconds, codeVerifier };
354
+ const encoded = b64url(JSON.stringify(payload));
355
+ const sig = sign(encoded, secret);
356
+ return { token: `${encoded}.${sig}`, codeVerifier };
357
+ }
358
+ function verifyOAuthState(token, expectedProviderId, secret) {
359
+ if (typeof token !== "string" || !token.includes(".")) {
360
+ return { ok: false, reason: "format" };
361
+ }
362
+ const [encoded, sig] = token.split(".");
363
+ if (!encoded || !sig) {
364
+ return { ok: false, reason: "format" };
365
+ }
366
+ const expectedSig = sign(encoded, secret);
367
+ const sigBuf = Buffer.from(sig);
368
+ const expectedBuf = Buffer.from(expectedSig);
369
+ if (sigBuf.length !== expectedBuf.length || !timingSafeEqual(sigBuf, expectedBuf)) {
370
+ return { ok: false, reason: "signature" };
371
+ }
372
+ let payload;
373
+ try {
374
+ payload = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
375
+ } catch {
376
+ return { ok: false, reason: "format" };
377
+ }
378
+ if (!payload || typeof payload.providerId !== "string" || typeof payload.nonce !== "string" || typeof payload.expSeconds !== "number" || typeof payload.codeVerifier !== "string" || payload.codeVerifier.length === 0) {
379
+ return { ok: false, reason: "format" };
380
+ }
381
+ if (payload.providerId !== expectedProviderId) {
382
+ return { ok: false, reason: "signature" };
383
+ }
384
+ if (payload.expSeconds <= Math.floor(Date.now() / 1e3)) {
385
+ return { ok: false, reason: "expired" };
386
+ }
387
+ return { ok: true, payload };
388
+ }
389
+
390
+ // src/auth/oauth-arctic.ts
391
+ function fromArctic(factory, opts) {
392
+ const usePkce = opts.pkce !== false;
393
+ const scopes = opts.scopes ?? [];
394
+ return {
395
+ id: opts.id,
396
+ label: opts.label,
397
+ authorize({ state, redirectUri, codeVerifier }) {
398
+ const arctic = factory(redirectUri);
399
+ const url = usePkce ? arctic.createAuthorizationURL(state, codeVerifier, scopes) : arctic.createAuthorizationURL(state, scopes);
400
+ return url.toString();
401
+ },
402
+ async exchange({ code, redirectUri, codeVerifier }) {
403
+ const arctic = factory(redirectUri);
404
+ const tokens = usePkce ? await arctic.validateAuthorizationCode(code, codeVerifier) : await arctic.validateAuthorizationCode(code);
405
+ return opts.fetchProfile(tokens.accessToken(), tokens);
406
+ }
407
+ };
408
+ }
409
+
410
+ // src/auth/session.ts
411
+ import { webcrypto } from "crypto";
412
+ import { eq as eq3, sql as sql3 } from "drizzle-orm";
413
+ async function sha256(input) {
414
+ const digest = await webcrypto.subtle.digest(
415
+ "SHA-256",
416
+ new TextEncoder().encode(input)
417
+ );
418
+ return Array.from(
419
+ new Uint8Array(digest),
420
+ (byte) => byte.toString(16).padStart(2, "0")
421
+ ).join("");
422
+ }
423
+ async function verifyTokenFull(token, secret, db, expectedUse = "access") {
424
+ const payload = await verifyToken(token, secret, expectedUse);
425
+ const [user] = await db.select({
426
+ id: npUsers.id,
427
+ email: npUsers.email,
428
+ name: npUsers.name,
429
+ role: npUsers.role,
430
+ tokenVersion: npUsers.tokenVersion
431
+ }).from(npUsers).where(eq3(npUsers.id, payload.sub)).limit(1);
432
+ if (!user || user.tokenVersion !== payload.ver) {
433
+ return null;
434
+ }
435
+ return user;
436
+ }
437
+ async function invalidateAllSessions(userId, db) {
438
+ await db.transaction(async (tx) => {
439
+ await tx.update(npUsers).set({
440
+ tokenVersion: sql3`${npUsers.tokenVersion} + 1`
441
+ }).where(eq3(npUsers.id, userId));
442
+ await tx.delete(npSessions).where(eq3(npSessions.userId, userId));
443
+ });
444
+ }
445
+
446
+ // src/auth/identities-admin.ts
447
+ import { and as and3, desc, eq as eq4 } from "drizzle-orm";
448
+ async function assertUserExists(userId) {
449
+ const db = getDb();
450
+ const [row] = await db.select({ id: npUsers.id }).from(npUsers).where(eq4(npUsers.id, userId)).limit(1);
451
+ if (!row) throw new NpNotFoundError("user", userId);
452
+ }
453
+ async function assertMemberExists(memberId) {
454
+ const db = getDb();
455
+ const [row] = await db.select({ id: npMembers.id }).from(npMembers).where(eq4(npMembers.id, memberId)).limit(1);
456
+ if (!row) throw new NpNotFoundError("member", memberId);
457
+ }
458
+ async function listUserIdentities(userId) {
459
+ await assertUserExists(userId);
460
+ const db = getDb();
461
+ const rows = await db.select().from(npUserOAuthIdentities).where(eq4(npUserOAuthIdentities.userId, userId)).orderBy(desc(npUserOAuthIdentities.createdAt));
462
+ return rows;
463
+ }
464
+ async function listMemberIdentities(memberId) {
465
+ await assertMemberExists(memberId);
466
+ const db = getDb();
467
+ const rows = await db.select().from(npMemberIdentities).where(eq4(npMemberIdentities.memberId, memberId)).orderBy(desc(npMemberIdentities.createdAt));
468
+ return rows;
469
+ }
470
+ async function revokeUserIdentity(userId, identityId, actor) {
471
+ const db = getDb();
472
+ const [existing] = await db.select().from(npUserOAuthIdentities).where(
473
+ and3(
474
+ eq4(npUserOAuthIdentities.id, identityId),
475
+ eq4(npUserOAuthIdentities.userId, userId)
476
+ )
477
+ ).limit(1);
478
+ if (!existing) {
479
+ throw new NpNotFoundError("identity", identityId);
480
+ }
481
+ const deleted = await db.delete(npUserOAuthIdentities).where(eq4(npUserOAuthIdentities.id, identityId)).returning({ id: npUserOAuthIdentities.id });
482
+ if (deleted.length === 0) return;
483
+ await recordAuditEvent({
484
+ actor: { kind: "staff", userId: actor.staffUserId },
485
+ action: "user.identity.revoke",
486
+ targetType: "user",
487
+ targetId: userId,
488
+ payload: {
489
+ identityId,
490
+ provider: existing.provider,
491
+ providerUserId: existing.providerUserId
492
+ }
493
+ });
494
+ }
495
+ async function revokeMemberIdentity(memberId, identityId, actor) {
496
+ const db = getDb();
497
+ const [existing] = await db.select().from(npMemberIdentities).where(
498
+ and3(
499
+ eq4(npMemberIdentities.id, identityId),
500
+ eq4(npMemberIdentities.memberId, memberId)
501
+ )
502
+ ).limit(1);
503
+ if (!existing) throw new NpNotFoundError("identity", identityId);
504
+ const deleted = await db.delete(npMemberIdentities).where(eq4(npMemberIdentities.id, identityId)).returning({ id: npMemberIdentities.id });
505
+ if (deleted.length === 0) return;
506
+ await recordAuditEvent({
507
+ actor: { kind: "staff", userId: actor.staffUserId },
508
+ action: "member.identity.revoke",
509
+ targetType: "member",
510
+ targetId: memberId,
511
+ payload: {
512
+ identityId,
513
+ provider: existing.provider,
514
+ subject: existing.subject
515
+ }
516
+ });
517
+ }
518
+
519
+ // src/auth/reset-token.ts
520
+ import { randomBytes as randomBytes3 } from "crypto";
521
+ import { and as and4, eq as eq5, gt, isNotNull, sql as sql4 } from "drizzle-orm";
522
+ var MIN_PASSWORD_LENGTH = 8;
523
+ function generateRawToken() {
524
+ return randomBytes3(32).toString("hex");
525
+ }
526
+ async function createPasswordResetToken(db, options) {
527
+ const token = generateRawToken();
528
+ const tokenHash = await sha256(token);
529
+ const expiresAt = new Date(Date.now() + options.ttlMs);
530
+ await db.update(npUsers).set({
531
+ passwordResetTokenHash: tokenHash,
532
+ passwordResetExpiresAt: expiresAt,
533
+ passwordResetPurpose: options.purpose,
534
+ updatedAt: /* @__PURE__ */ new Date()
535
+ }).where(eq5(npUsers.id, options.userId));
536
+ return { token, expiresAt, purpose: options.purpose };
537
+ }
538
+ async function requestPasswordReset(db, email, ttlMs) {
539
+ const normalizedEmail = email.trim().toLowerCase();
540
+ const [user] = await db.select({
541
+ id: npUsers.id,
542
+ email: npUsers.email,
543
+ name: npUsers.name
544
+ }).from(npUsers).where(eq5(npUsers.email, normalizedEmail)).limit(1);
545
+ if (!user) {
546
+ return { userId: null, name: null, email: null, issued: null };
547
+ }
548
+ const issued = await createPasswordResetToken(db, {
549
+ userId: user.id,
550
+ purpose: "reset",
551
+ ttlMs
552
+ });
553
+ return { userId: user.id, name: user.name, email: user.email, issued };
554
+ }
555
+ async function consumePasswordResetToken(db, options) {
556
+ if (!options.token || typeof options.token !== "string") {
557
+ throw new NpValidationError("Invalid input", [
558
+ { field: "token", message: "Reset token is required." }
559
+ ]);
560
+ }
561
+ if (!options.newPassword || options.newPassword.length < MIN_PASSWORD_LENGTH) {
562
+ throw new NpValidationError("Invalid input", [
563
+ {
564
+ field: "password",
565
+ message: `Password must be at least ${MIN_PASSWORD_LENGTH} characters.`
566
+ }
567
+ ]);
568
+ }
569
+ const tokenHash = await sha256(options.token);
570
+ const now = /* @__PURE__ */ new Date();
571
+ const [user] = await db.select({
572
+ id: npUsers.id,
573
+ email: npUsers.email,
574
+ purpose: npUsers.passwordResetPurpose
575
+ }).from(npUsers).where(
576
+ and4(
577
+ eq5(npUsers.passwordResetTokenHash, tokenHash),
578
+ isNotNull(npUsers.passwordResetExpiresAt),
579
+ gt(npUsers.passwordResetExpiresAt, now)
580
+ )
581
+ ).limit(1);
582
+ if (!user) {
583
+ throw new NpValidationError("Invalid input", [
584
+ { field: "token", message: "Reset link is invalid or has expired." }
585
+ ]);
586
+ }
587
+ const newPasswordHash = await hashPassword(options.newPassword);
588
+ await db.transaction(async (tx) => {
589
+ await tx.update(npUsers).set({
590
+ password: newPasswordHash,
591
+ passwordResetTokenHash: null,
592
+ passwordResetExpiresAt: null,
593
+ passwordResetPurpose: null,
594
+ loginAttempts: 0,
595
+ lockUntil: null,
596
+ tokenVersion: sql4`${npUsers.tokenVersion} + 1`,
597
+ updatedAt: /* @__PURE__ */ new Date()
598
+ }).where(eq5(npUsers.id, user.id));
599
+ await tx.delete(npSessions).where(eq5(npSessions.userId, user.id));
600
+ });
601
+ return {
602
+ userId: user.id,
603
+ email: user.email,
604
+ purpose: user.purpose ?? "reset"
605
+ };
606
+ }
607
+
608
+ // src/auth/member-token.ts
609
+ import { randomBytes as randomBytes4 } from "crypto";
610
+ import { jwtVerify as jwtVerify2, SignJWT as SignJWT2 } from "jose";
611
+ var textEncoder2 = new TextEncoder();
612
+ var MEMBER_AUDIENCE = "member";
613
+ async function signMemberToken(member, secret, expirationSeconds = 7200, tokenUse = "access") {
614
+ const secretKey = textEncoder2.encode(secret);
615
+ return new SignJWT2({ sub: member.id, ver: member.tokenVersion, use: tokenUse }).setProtectedHeader({ alg: "HS256" }).setAudience(MEMBER_AUDIENCE).setJti(randomBytes4(16).toString("base64url")).setIssuedAt().setExpirationTime(Math.floor(Date.now() / 1e3) + expirationSeconds).sign(secretKey);
616
+ }
617
+ async function verifyMemberToken(token, secret, expectedUse) {
618
+ const secretKey = textEncoder2.encode(secret);
619
+ const { payload } = await jwtVerify2(token, secretKey, { audience: MEMBER_AUDIENCE });
620
+ const typed = payload;
621
+ if (typed.use !== "access" && typed.use !== "refresh") {
622
+ throw new NpAuthError("Member token missing `use` claim");
623
+ }
624
+ const use = typed.use;
625
+ if (expectedUse && use !== expectedUse) {
626
+ throw new NpAuthError(
627
+ `Member token use mismatch: expected ${expectedUse}, got ${use}`
628
+ );
629
+ }
630
+ return { ...typed, aud: MEMBER_AUDIENCE, use };
631
+ }
632
+
633
+ // src/auth/member-session.ts
634
+ import { and as and5, eq as eq6, gt as gt2, sql as sql5 } from "drizzle-orm";
635
+ async function getMemberFromTokenPayload(db, payload, accessToken) {
636
+ const [row] = await db.select({
637
+ id: npMembers.id,
638
+ email: npMembers.email,
639
+ handle: npMembers.handle,
640
+ displayName: npMembers.displayName,
641
+ status: npMembers.status,
642
+ tokenVersion: npMembers.tokenVersion
643
+ }).from(npMembers).where(eq6(npMembers.id, payload.sub)).limit(1);
644
+ if (!row) return null;
645
+ if (row.tokenVersion !== payload.ver) return null;
646
+ if (accessToken) {
647
+ const tokenHash = await sha256(accessToken);
648
+ const now = /* @__PURE__ */ new Date();
649
+ const [session] = await db.select({ id: npMemberSessions.id }).from(npMemberSessions).where(
650
+ and5(
651
+ eq6(npMemberSessions.memberId, row.id),
652
+ eq6(npMemberSessions.tokenHash, tokenHash),
653
+ gt2(npMemberSessions.expiresAt, now)
654
+ )
655
+ ).limit(1);
656
+ if (!session) return null;
657
+ }
658
+ return row;
659
+ }
660
+ async function invalidateAllMemberSessions(db, memberId) {
661
+ await db.transaction(async (tx) => {
662
+ await tx.update(npMembers).set({
663
+ tokenVersion: sql5`${npMembers.tokenVersion} + 1`,
664
+ updatedAt: /* @__PURE__ */ new Date()
665
+ }).where(eq6(npMembers.id, memberId));
666
+ await tx.delete(npMemberSessions).where(eq6(npMemberSessions.memberId, memberId));
667
+ });
668
+ }
669
+
670
+ // src/auth/member-credentials.ts
671
+ import { randomBytes as randomBytes5 } from "crypto";
672
+ import { and as and6, eq as eq7, gt as gt3, isNotNull as isNotNull2, sql as sql6 } from "drizzle-orm";
673
+ var MIN_PASSWORD_LENGTH2 = 8;
674
+ function generateRawToken2() {
675
+ return randomBytes5(32).toString("hex");
676
+ }
677
+ async function createMemberEmailVerifyToken(db, memberId, ttlMs) {
678
+ const token = generateRawToken2();
679
+ const tokenHash = await sha256(token);
680
+ const expiresAt = new Date(Date.now() + ttlMs);
681
+ await db.update(npMembers).set({
682
+ emailVerifyTokenHash: tokenHash,
683
+ emailVerifyExpiresAt: expiresAt,
684
+ updatedAt: /* @__PURE__ */ new Date()
685
+ }).where(eq7(npMembers.id, memberId));
686
+ return { token, expiresAt };
687
+ }
688
+ async function consumeMemberEmailVerifyToken(db, token) {
689
+ if (!token || typeof token !== "string") {
690
+ throw new NpValidationError("Invalid input", [
691
+ { field: "token", message: "Verification token is required." }
692
+ ]);
693
+ }
694
+ const tokenHash = await sha256(token);
695
+ const now = /* @__PURE__ */ new Date();
696
+ const [member] = await db.select({
697
+ id: npMembers.id,
698
+ email: npMembers.email,
699
+ handle: npMembers.handle,
700
+ displayName: npMembers.displayName
701
+ }).from(npMembers).where(
702
+ and6(
703
+ eq7(npMembers.emailVerifyTokenHash, tokenHash),
704
+ isNotNull2(npMembers.emailVerifyExpiresAt),
705
+ gt3(npMembers.emailVerifyExpiresAt, now)
706
+ )
707
+ ).limit(1);
708
+ if (!member) {
709
+ throw new NpValidationError("Invalid input", [
710
+ { field: "token", message: "Verification link is invalid or has expired." }
711
+ ]);
712
+ }
713
+ await db.update(npMembers).set({
714
+ emailVerified: true,
715
+ // Pending → active on first verify so login can succeed afterwards.
716
+ // Suspended/deleted members stay where they are; the mod UI flips
717
+ // those statuses, never the verify endpoint.
718
+ status: sql6`case when ${npMembers.status} = 'pending' then 'active' else ${npMembers.status} end`,
719
+ emailVerifyTokenHash: null,
720
+ emailVerifyExpiresAt: null,
721
+ updatedAt: now
722
+ }).where(eq7(npMembers.id, member.id));
723
+ return {
724
+ memberId: member.id,
725
+ email: member.email,
726
+ handle: member.handle,
727
+ displayName: member.displayName
728
+ };
729
+ }
730
+ async function requestMemberPasswordReset(db, email, ttlMs) {
731
+ const normalizedEmail = email.trim().toLowerCase();
732
+ const [member] = await db.select({
733
+ id: npMembers.id,
734
+ email: npMembers.email,
735
+ displayName: npMembers.displayName,
736
+ status: npMembers.status
737
+ }).from(npMembers).where(eq7(npMembers.email, normalizedEmail)).limit(1);
738
+ if (!member || member.status === "deleted") {
739
+ return { memberId: null, displayName: null, email: null, issued: null };
740
+ }
741
+ const token = generateRawToken2();
742
+ const tokenHash = await sha256(token);
743
+ const expiresAt = new Date(Date.now() + ttlMs);
744
+ await db.update(npMembers).set({
745
+ passwordResetTokenHash: tokenHash,
746
+ passwordResetExpiresAt: expiresAt,
747
+ updatedAt: /* @__PURE__ */ new Date()
748
+ }).where(eq7(npMembers.id, member.id));
749
+ return {
750
+ memberId: member.id,
751
+ displayName: member.displayName,
752
+ email: member.email,
753
+ issued: { token, expiresAt }
754
+ };
755
+ }
756
+ async function consumeMemberPasswordReset(db, token, newPassword) {
757
+ if (!token || typeof token !== "string") {
758
+ throw new NpValidationError("Invalid input", [
759
+ { field: "token", message: "Reset token is required." }
760
+ ]);
761
+ }
762
+ if (!newPassword || newPassword.length < MIN_PASSWORD_LENGTH2) {
763
+ throw new NpValidationError("Invalid input", [
764
+ {
765
+ field: "password",
766
+ message: `Password must be at least ${MIN_PASSWORD_LENGTH2} characters.`
767
+ }
768
+ ]);
769
+ }
770
+ const tokenHash = await sha256(token);
771
+ const now = /* @__PURE__ */ new Date();
772
+ const [member] = await db.select({ id: npMembers.id, email: npMembers.email }).from(npMembers).where(
773
+ and6(
774
+ eq7(npMembers.passwordResetTokenHash, tokenHash),
775
+ isNotNull2(npMembers.passwordResetExpiresAt),
776
+ gt3(npMembers.passwordResetExpiresAt, now)
777
+ )
778
+ ).limit(1);
779
+ if (!member) {
780
+ throw new NpValidationError("Invalid input", [
781
+ { field: "token", message: "Reset link is invalid or has expired." }
782
+ ]);
783
+ }
784
+ const newPasswordHash = await hashPassword(newPassword);
785
+ await db.transaction(async (tx) => {
786
+ await tx.update(npMembers).set({
787
+ password: newPasswordHash,
788
+ passwordResetTokenHash: null,
789
+ passwordResetExpiresAt: null,
790
+ loginAttempts: 0,
791
+ lockUntil: null,
792
+ // Bump tokenVersion in-place so existing JWTs are invalidated. Also
793
+ // mark email as verified — completing a reset on an unverified
794
+ // account is itself proof of email ownership.
795
+ tokenVersion: sql6`${npMembers.tokenVersion} + 1`,
796
+ emailVerified: true,
797
+ status: sql6`case when ${npMembers.status} = 'pending' then 'active' else ${npMembers.status} end`,
798
+ updatedAt: /* @__PURE__ */ new Date()
799
+ }).where(eq7(npMembers.id, member.id));
800
+ await tx.delete(npMemberSessions).where(eq7(npMemberSessions.memberId, member.id));
801
+ });
802
+ return { memberId: member.id, email: member.email };
803
+ }
804
+
805
+ export {
806
+ authenticated,
807
+ isAdmin,
808
+ isEditorOrAbove,
809
+ isOwnerOrAdmin,
810
+ signToken,
811
+ verifyToken,
812
+ isTokenVerificationError,
813
+ ARGON2_OPTIONS,
814
+ hashPassword,
815
+ verifyPassword,
816
+ verifyCsrf,
817
+ registerOAuthProvider,
818
+ getOAuthProvider,
819
+ listOAuthProviders,
820
+ resetOAuthProviders,
821
+ resolveOAuthLogin,
822
+ resolveMemberOAuthLogin,
823
+ issueOAuthState,
824
+ verifyOAuthState,
825
+ fromArctic,
826
+ sha256,
827
+ verifyTokenFull,
828
+ invalidateAllSessions,
829
+ listUserIdentities,
830
+ listMemberIdentities,
831
+ revokeUserIdentity,
832
+ revokeMemberIdentity,
833
+ createPasswordResetToken,
834
+ requestPasswordReset,
835
+ consumePasswordResetToken,
836
+ signMemberToken,
837
+ verifyMemberToken,
838
+ getMemberFromTokenPayload,
839
+ invalidateAllMemberSessions,
840
+ createMemberEmailVerifyToken,
841
+ consumeMemberEmailVerifyToken,
842
+ requestMemberPasswordReset,
843
+ consumeMemberPasswordReset
844
+ };
845
+ //# sourceMappingURL=chunk-OK5HOCQI.js.map