@gello/auth 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +14 -0
- package/index.d.ts.map +1 -0
- package/index.js +2266 -0
- package/package.json +37 -27
- package/index.cjs +0 -3074
- package/src/index.d.ts +0 -14
- package/src/index.d.ts.map +0 -1
package/index.cjs
DELETED
|
@@ -1,3074 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// libs/publishable/auth/src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
AbilitiesLive: () => AbilitiesLive,
|
|
34
|
-
AbilitiesTag: () => AbilitiesTag,
|
|
35
|
-
AbstractProvider: () => AbstractProvider,
|
|
36
|
-
Auth: () => Auth,
|
|
37
|
-
AuthError: () => AuthError,
|
|
38
|
-
AuthGuard: () => AuthGuard,
|
|
39
|
-
AuthServiceLive: () => AuthServiceLive,
|
|
40
|
-
AuthTag: () => AuthTag,
|
|
41
|
-
AuthenticatedUserTag: () => AuthenticatedUserTag,
|
|
42
|
-
AuthenticationError: () => AuthenticationError,
|
|
43
|
-
AuthorizationError: () => AuthorizationError,
|
|
44
|
-
CommonAbilities: () => CommonAbilities,
|
|
45
|
-
CsrfMismatchError: () => CsrfMismatchError,
|
|
46
|
-
CsrfToken: () => CsrfToken,
|
|
47
|
-
CsrfTokenTag: () => CsrfTokenTag,
|
|
48
|
-
CurrentSessionTag: () => CurrentSessionTag,
|
|
49
|
-
ForbiddenError: () => ForbiddenError,
|
|
50
|
-
FullTestAuthLayer: () => FullTestAuthLayer,
|
|
51
|
-
GithubProvider: () => GithubProvider,
|
|
52
|
-
GoogleProvider: () => GoogleProvider,
|
|
53
|
-
HashedToken: () => HashedToken,
|
|
54
|
-
HashingError: () => HashingError,
|
|
55
|
-
InsufficientScopeError: () => InsufficientScopeError,
|
|
56
|
-
InvalidPasswordError: () => InvalidPasswordError,
|
|
57
|
-
JwtError: () => JwtError,
|
|
58
|
-
JwtServiceLive: () => JwtServiceLive,
|
|
59
|
-
JwtServiceTag: () => JwtServiceTag,
|
|
60
|
-
MemorySessionStoreLive: () => MemorySessionStoreLive,
|
|
61
|
-
MockPasswordHasherLive: () => MockPasswordHasherLive,
|
|
62
|
-
MockTokenHasherLive: () => MockTokenHasherLive,
|
|
63
|
-
MockTokenStoreLive: () => MockTokenStoreLive,
|
|
64
|
-
MockUserProviderLive: () => MockUserProviderLive,
|
|
65
|
-
NewLogin: () => NewLogin,
|
|
66
|
-
NewLoginMailable: () => NewLoginMailable,
|
|
67
|
-
OAuthConfigError: () => OAuthConfigError,
|
|
68
|
-
OAuthError: () => OAuthError,
|
|
69
|
-
OAuthProviderNotFoundError: () => OAuthProviderNotFoundError,
|
|
70
|
-
OAuthStateMismatchError: () => OAuthStateMismatchError,
|
|
71
|
-
OAuthTokenError: () => OAuthTokenError,
|
|
72
|
-
OAuthUserError: () => OAuthUserError,
|
|
73
|
-
PasswordChanged: () => PasswordChanged,
|
|
74
|
-
PasswordHasherTag: () => PasswordHasherTag,
|
|
75
|
-
PersonalAccessToken: () => PersonalAccessToken,
|
|
76
|
-
PlainTextToken: () => PlainTextToken,
|
|
77
|
-
RateLimitError: () => RateLimitError,
|
|
78
|
-
ResetPassword: () => ResetPassword,
|
|
79
|
-
ResetPasswordMailable: () => ResetPasswordMailable,
|
|
80
|
-
Session: () => Session,
|
|
81
|
-
SessionError: () => SessionError,
|
|
82
|
-
SessionExpiredError: () => SessionExpiredError,
|
|
83
|
-
SessionId: () => SessionId,
|
|
84
|
-
SessionNotFoundError: () => SessionNotFoundError,
|
|
85
|
-
SessionStoreTag: () => SessionStoreTag,
|
|
86
|
-
Social: () => Social,
|
|
87
|
-
SocialLive: () => SocialLive,
|
|
88
|
-
SocialTag: () => SocialTag,
|
|
89
|
-
SocialToken: () => SocialToken,
|
|
90
|
-
SocialUser: () => SocialUser,
|
|
91
|
-
TestAuthLayer: () => TestAuthLayer,
|
|
92
|
-
TestSessionLayer: () => TestSessionLayer,
|
|
93
|
-
TokenExpiredError: () => TokenExpiredError,
|
|
94
|
-
TokenHasherTag: () => TokenHasherTag,
|
|
95
|
-
TokenId: () => TokenId,
|
|
96
|
-
TokenNotFoundError: () => TokenNotFoundError,
|
|
97
|
-
TokenServiceLive: () => TokenServiceLive,
|
|
98
|
-
TokenServiceTag: () => TokenServiceTag,
|
|
99
|
-
TokenStoreTag: () => TokenStoreTag,
|
|
100
|
-
UserId: () => UserId,
|
|
101
|
-
UserNotFoundError: () => UserNotFoundError,
|
|
102
|
-
UserProviderTag: () => UserProviderTag,
|
|
103
|
-
VerifyEmail: () => VerifyEmail,
|
|
104
|
-
VerifyEmailMailable: () => VerifyEmailMailable,
|
|
105
|
-
WelcomeEmail: () => WelcomeEmail,
|
|
106
|
-
WelcomeEmailMailable: () => WelcomeEmailMailable,
|
|
107
|
-
actingAs: () => actingAs,
|
|
108
|
-
actingAsLayer: () => actingAsLayer,
|
|
109
|
-
assertAuthenticated: () => assertAuthenticated,
|
|
110
|
-
assertCan: () => assertCan,
|
|
111
|
-
assertCannot: () => assertCannot,
|
|
112
|
-
assertGuest: () => assertGuest,
|
|
113
|
-
assertHasScope: () => assertHasScope,
|
|
114
|
-
assertLacksScope: () => assertLacksScope,
|
|
115
|
-
authenticate: () => authenticate,
|
|
116
|
-
authorize: () => authorize,
|
|
117
|
-
authorizeAction: () => authorizeMiddleware,
|
|
118
|
-
authorizeAll: () => authorizeAll,
|
|
119
|
-
authorizeAny: () => authorizeAny,
|
|
120
|
-
authorizeMiddleware: () => authorizeMiddleware,
|
|
121
|
-
can: () => can,
|
|
122
|
-
cannot: () => cannot,
|
|
123
|
-
createAbilities: () => createAbilities,
|
|
124
|
-
createAuthenticatedUser: () => createAuthenticatedUser,
|
|
125
|
-
createGithubProvider: () => createGithubProvider,
|
|
126
|
-
createGoogleProvider: () => createGoogleProvider,
|
|
127
|
-
createMockUser: () => createMockUser,
|
|
128
|
-
csrf: () => csrf,
|
|
129
|
-
currentUser: () => currentUser,
|
|
130
|
-
defaultCsrfConfig: () => defaultCsrfConfig,
|
|
131
|
-
defaultSessionConfig: () => defaultSessionConfig,
|
|
132
|
-
defineAbilitiesFor: () => defineAbilitiesFor,
|
|
133
|
-
endSession: () => endSession,
|
|
134
|
-
generateCsrfCookie: () => generateCsrfCookie,
|
|
135
|
-
getSessionData: () => getSessionData,
|
|
136
|
-
getSubjectType: () => getSubjectType,
|
|
137
|
-
hasApiTokens: () => hasApiTokens,
|
|
138
|
-
hasScope: () => hasScope,
|
|
139
|
-
isAuthenticated: () => isAuthenticated,
|
|
140
|
-
makeJwtService: () => makeJwtService,
|
|
141
|
-
makeMemorySessionStore: () => makeMemorySessionStore,
|
|
142
|
-
makeMockPasswordHasher: () => makeMockPasswordHasher,
|
|
143
|
-
makeMockTokenHasher: () => makeMockTokenHasher,
|
|
144
|
-
makeMockTokenStore: () => makeMockTokenStore,
|
|
145
|
-
makeMockUserProvider: () => makeMockUserProvider,
|
|
146
|
-
makeSocialService: () => makeSocialService,
|
|
147
|
-
matchAction: () => matchAction,
|
|
148
|
-
matchConditions: () => matchConditions,
|
|
149
|
-
matchSubject: () => matchSubject,
|
|
150
|
-
newLogin: () => newLogin,
|
|
151
|
-
requireAdmin: () => requireAdmin,
|
|
152
|
-
requireAny: () => requireAny,
|
|
153
|
-
requireScope: () => requireScope,
|
|
154
|
-
resetPassword: () => resetPassword,
|
|
155
|
-
session: () => session,
|
|
156
|
-
setSessionData: () => setSessionData,
|
|
157
|
-
startSession: () => startSession,
|
|
158
|
-
tokenScope: () => tokenScope,
|
|
159
|
-
tokenScopes: () => tokenScopes,
|
|
160
|
-
verifyCsrfToken: () => verifyCsrfToken,
|
|
161
|
-
verifyEmail: () => verifyEmail,
|
|
162
|
-
welcomeEmail: () => welcomeEmail,
|
|
163
|
-
withApiTokens: () => withApiTokens
|
|
164
|
-
});
|
|
165
|
-
module.exports = __toCommonJS(index_exports);
|
|
166
|
-
|
|
167
|
-
// libs/auth/core/src/lib/domain/types.ts
|
|
168
|
-
var import_effect = require("effect");
|
|
169
|
-
var TokenId = import_effect.Brand.nominal();
|
|
170
|
-
var PlainTextToken = import_effect.Brand.nominal();
|
|
171
|
-
var HashedToken = import_effect.Brand.nominal();
|
|
172
|
-
var UserId = import_effect.Brand.nominal();
|
|
173
|
-
var PersonalAccessToken = {
|
|
174
|
-
/**
|
|
175
|
-
* Generate a new TokenId
|
|
176
|
-
*/
|
|
177
|
-
generateId: () => TokenId(crypto.randomUUID()),
|
|
178
|
-
/**
|
|
179
|
-
* Generate a random plain text token
|
|
180
|
-
*/
|
|
181
|
-
generatePlainText: () => {
|
|
182
|
-
const bytes = new Uint8Array(32);
|
|
183
|
-
crypto.getRandomValues(bytes);
|
|
184
|
-
const token = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
185
|
-
return PlainTextToken(token);
|
|
186
|
-
},
|
|
187
|
-
/**
|
|
188
|
-
* Check if token has a specific scope
|
|
189
|
-
*/
|
|
190
|
-
hasScope: (token, scope) => {
|
|
191
|
-
if (token.scopes.includes("*")) return true;
|
|
192
|
-
return token.scopes.includes(scope);
|
|
193
|
-
},
|
|
194
|
-
/**
|
|
195
|
-
* Check if token has all specified scopes
|
|
196
|
-
*/
|
|
197
|
-
hasAllScopes: (token, scopes) => {
|
|
198
|
-
return scopes.every((scope) => PersonalAccessToken.hasScope(token, scope));
|
|
199
|
-
},
|
|
200
|
-
/**
|
|
201
|
-
* Check if token has any of the specified scopes
|
|
202
|
-
*/
|
|
203
|
-
hasAnyScope: (token, scopes) => {
|
|
204
|
-
return scopes.some((scope) => PersonalAccessToken.hasScope(token, scope));
|
|
205
|
-
},
|
|
206
|
-
/**
|
|
207
|
-
* Check if token is expired
|
|
208
|
-
*/
|
|
209
|
-
isExpired: (token) => {
|
|
210
|
-
if (!token.expiresAt) return false;
|
|
211
|
-
return /* @__PURE__ */ new Date() > token.expiresAt;
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
var AuthGuard = {
|
|
215
|
-
authenticated: (user) => ({
|
|
216
|
-
_tag: "Authenticated",
|
|
217
|
-
user
|
|
218
|
-
}),
|
|
219
|
-
guest: () => ({
|
|
220
|
-
_tag: "Guest"
|
|
221
|
-
}),
|
|
222
|
-
isAuthenticated: (guard) => guard._tag === "Authenticated",
|
|
223
|
-
isGuest: (guard) => guard._tag === "Guest"
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
// libs/auth/core/src/lib/domain/errors.ts
|
|
227
|
-
var import_effect2 = require("effect");
|
|
228
|
-
var AuthError = class extends import_effect2.Data.TaggedError("AuthError") {
|
|
229
|
-
};
|
|
230
|
-
var AuthenticationError = class extends import_effect2.Data.TaggedError("AuthenticationError") {
|
|
231
|
-
};
|
|
232
|
-
var TokenNotFoundError = class extends import_effect2.Data.TaggedError("TokenNotFoundError") {
|
|
233
|
-
};
|
|
234
|
-
var TokenExpiredError = class extends import_effect2.Data.TaggedError("TokenExpiredError") {
|
|
235
|
-
};
|
|
236
|
-
var InsufficientScopeError = class extends import_effect2.Data.TaggedError("InsufficientScopeError") {
|
|
237
|
-
};
|
|
238
|
-
var UserNotFoundError = class extends import_effect2.Data.TaggedError("UserNotFoundError") {
|
|
239
|
-
};
|
|
240
|
-
var HashingError = class extends import_effect2.Data.TaggedError("HashingError") {
|
|
241
|
-
};
|
|
242
|
-
var InvalidPasswordError = class extends import_effect2.Data.TaggedError("InvalidPasswordError") {
|
|
243
|
-
};
|
|
244
|
-
var RateLimitError = class extends import_effect2.Data.TaggedError("RateLimitError") {
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// libs/auth/core/src/lib/ports/TokenStore.ts
|
|
248
|
-
var import_effect3 = require("effect");
|
|
249
|
-
var TokenStoreTag = class extends import_effect3.Context.Tag("@gello/auth/TokenStore")() {
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
// libs/auth/core/src/lib/ports/PasswordHasher.ts
|
|
253
|
-
var import_effect4 = require("effect");
|
|
254
|
-
var PasswordHasherTag = class extends import_effect4.Context.Tag("@gello/auth/PasswordHasher")() {
|
|
255
|
-
};
|
|
256
|
-
var TokenHasherTag = class extends import_effect4.Context.Tag("@gello/auth/TokenHasher")() {
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// libs/auth/core/src/lib/ports/Authenticatable.ts
|
|
260
|
-
var import_effect5 = require("effect");
|
|
261
|
-
var UserProviderTag = class extends import_effect5.Context.Tag("@gello/auth/UserProvider")() {
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
// libs/auth/core/src/lib/services/TokenService.ts
|
|
265
|
-
var import_effect6 = require("effect");
|
|
266
|
-
var TokenServiceTag = class extends import_effect6.Context.Tag("@gello/auth/TokenService")() {
|
|
267
|
-
};
|
|
268
|
-
var TokenServiceLive = import_effect6.Layer.effect(
|
|
269
|
-
TokenServiceTag,
|
|
270
|
-
import_effect6.Effect.gen(function* () {
|
|
271
|
-
const store = yield* TokenStoreTag;
|
|
272
|
-
const hasher = yield* TokenHasherTag;
|
|
273
|
-
const service = {
|
|
274
|
-
createToken: (userId, name, scopes = ["*"], expiresAt) => import_effect6.Effect.gen(function* () {
|
|
275
|
-
const plainTextToken = PersonalAccessToken.generatePlainText();
|
|
276
|
-
const hashedToken = HashedToken(hasher.hash(plainTextToken));
|
|
277
|
-
const token = {
|
|
278
|
-
id: PersonalAccessToken.generateId(),
|
|
279
|
-
userId,
|
|
280
|
-
name,
|
|
281
|
-
token: hashedToken,
|
|
282
|
-
scopes,
|
|
283
|
-
expiresAt,
|
|
284
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
285
|
-
};
|
|
286
|
-
const savedToken = yield* store.create(token);
|
|
287
|
-
return {
|
|
288
|
-
accessToken: savedToken,
|
|
289
|
-
plainTextToken
|
|
290
|
-
};
|
|
291
|
-
}),
|
|
292
|
-
verifyToken: (plainTextToken) => import_effect6.Effect.gen(function* () {
|
|
293
|
-
const hashedToken = HashedToken(hasher.hash(plainTextToken));
|
|
294
|
-
const maybeToken = yield* store.findByToken(hashedToken);
|
|
295
|
-
if (import_effect6.Option.isNone(maybeToken)) {
|
|
296
|
-
return yield* import_effect6.Effect.fail(
|
|
297
|
-
new AuthenticationError({
|
|
298
|
-
message: "Invalid token",
|
|
299
|
-
reason: "invalid_token"
|
|
300
|
-
})
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
const token = maybeToken.value;
|
|
304
|
-
if (PersonalAccessToken.isExpired(token)) {
|
|
305
|
-
return yield* import_effect6.Effect.fail(
|
|
306
|
-
new TokenExpiredError({
|
|
307
|
-
message: "Token has expired",
|
|
308
|
-
tokenId: token.id,
|
|
309
|
-
expiredAt: token.expiresAt
|
|
310
|
-
})
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
yield* store.updateLastUsed(token.id, /* @__PURE__ */ new Date()).pipe(import_effect6.Effect.catchAll(() => import_effect6.Effect.void));
|
|
314
|
-
return token;
|
|
315
|
-
}),
|
|
316
|
-
getUserTokens: (userId) => store.findByUser(userId),
|
|
317
|
-
revokeToken: (tokenId) => store.delete(tokenId),
|
|
318
|
-
revokeAllTokens: (userId) => store.deleteByUser(userId),
|
|
319
|
-
pruneExpiredTokens: () => store.deleteExpired()
|
|
320
|
-
};
|
|
321
|
-
return service;
|
|
322
|
-
})
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
// libs/auth/core/src/lib/services/Auth.ts
|
|
326
|
-
var import_effect7 = require("effect");
|
|
327
|
-
var AuthTag = class extends import_effect7.Context.Tag("@gello/auth/Auth")() {
|
|
328
|
-
};
|
|
329
|
-
var AuthServiceLive = import_effect7.Layer.effect(
|
|
330
|
-
AuthTag,
|
|
331
|
-
import_effect7.Effect.gen(function* () {
|
|
332
|
-
const userProvider = yield* UserProviderTag;
|
|
333
|
-
const passwordHasher = yield* PasswordHasherTag;
|
|
334
|
-
const tokenService = yield* TokenServiceTag;
|
|
335
|
-
const service = {
|
|
336
|
-
attempt: (email, password) => import_effect7.Effect.gen(function* () {
|
|
337
|
-
const maybeUser = yield* userProvider.findByEmail(email);
|
|
338
|
-
if (import_effect7.Option.isNone(maybeUser)) {
|
|
339
|
-
return yield* import_effect7.Effect.fail(
|
|
340
|
-
new AuthenticationError({
|
|
341
|
-
message: "Invalid credentials",
|
|
342
|
-
reason: "invalid_credentials"
|
|
343
|
-
})
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
const user = maybeUser.value;
|
|
347
|
-
const isValid = yield* passwordHasher.verify(password, user.password).pipe(
|
|
348
|
-
import_effect7.Effect.mapError(
|
|
349
|
-
() => new AuthenticationError({
|
|
350
|
-
message: "Invalid credentials",
|
|
351
|
-
reason: "invalid_credentials"
|
|
352
|
-
})
|
|
353
|
-
)
|
|
354
|
-
);
|
|
355
|
-
if (!isValid) {
|
|
356
|
-
return yield* import_effect7.Effect.fail(
|
|
357
|
-
new AuthenticationError({
|
|
358
|
-
message: "Invalid credentials",
|
|
359
|
-
reason: "invalid_credentials"
|
|
360
|
-
})
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
return user;
|
|
364
|
-
}),
|
|
365
|
-
authenticateWithToken: (token) => import_effect7.Effect.gen(function* () {
|
|
366
|
-
const accessToken = yield* tokenService.verifyToken(token).pipe(
|
|
367
|
-
import_effect7.Effect.mapError(
|
|
368
|
-
(e) => new AuthenticationError({
|
|
369
|
-
message: e.message,
|
|
370
|
-
reason: e._tag === "TokenExpiredError" ? "expired_token" : "invalid_token"
|
|
371
|
-
})
|
|
372
|
-
)
|
|
373
|
-
);
|
|
374
|
-
const maybeUser = yield* userProvider.findById(accessToken.userId);
|
|
375
|
-
if (import_effect7.Option.isNone(maybeUser)) {
|
|
376
|
-
return yield* import_effect7.Effect.fail(
|
|
377
|
-
new AuthenticationError({
|
|
378
|
-
message: "User not found",
|
|
379
|
-
reason: "invalid_token"
|
|
380
|
-
})
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
return {
|
|
384
|
-
id: accessToken.userId,
|
|
385
|
-
token: accessToken
|
|
386
|
-
};
|
|
387
|
-
}),
|
|
388
|
-
getUser: (userId) => import_effect7.Effect.gen(function* () {
|
|
389
|
-
const maybeUser = yield* userProvider.findById(userId);
|
|
390
|
-
if (import_effect7.Option.isNone(maybeUser)) {
|
|
391
|
-
return yield* import_effect7.Effect.fail(
|
|
392
|
-
new UserNotFoundError({
|
|
393
|
-
message: "User not found",
|
|
394
|
-
userId
|
|
395
|
-
})
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
return maybeUser.value;
|
|
399
|
-
}),
|
|
400
|
-
createToken: (userId, name, scopes, expiresAt) => tokenService.createToken(userId, name, scopes, expiresAt),
|
|
401
|
-
getTokens: (userId) => tokenService.getUserTokens(userId),
|
|
402
|
-
revokeToken: (tokenId) => tokenService.revokeToken(tokenId).pipe(
|
|
403
|
-
import_effect7.Effect.catchAll(() => import_effect7.Effect.void)
|
|
404
|
-
),
|
|
405
|
-
revokeAllTokens: (userId) => tokenService.revokeAllTokens(userId),
|
|
406
|
-
hashPassword: (password) => passwordHasher.hash(password).pipe(
|
|
407
|
-
import_effect7.Effect.catchAll(() => import_effect7.Effect.succeed(""))
|
|
408
|
-
),
|
|
409
|
-
needsRehash: (hash) => passwordHasher.needsRehash(hash)
|
|
410
|
-
};
|
|
411
|
-
return service;
|
|
412
|
-
})
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
// libs/auth/core/src/lib/mixins/HasApiTokens.ts
|
|
416
|
-
var import_effect8 = require("effect");
|
|
417
|
-
function withApiTokens(user) {
|
|
418
|
-
return {
|
|
419
|
-
...user,
|
|
420
|
-
createToken(name, scopes, expiresAt) {
|
|
421
|
-
return import_effect8.Effect.gen(function* () {
|
|
422
|
-
const tokenService = yield* TokenServiceTag;
|
|
423
|
-
return yield* tokenService.createToken(user.id, name, scopes, expiresAt);
|
|
424
|
-
});
|
|
425
|
-
},
|
|
426
|
-
tokens() {
|
|
427
|
-
return import_effect8.Effect.gen(function* () {
|
|
428
|
-
const tokenService = yield* TokenServiceTag;
|
|
429
|
-
return yield* tokenService.getUserTokens(user.id);
|
|
430
|
-
});
|
|
431
|
-
},
|
|
432
|
-
revokeToken(tokenId) {
|
|
433
|
-
return import_effect8.Effect.gen(function* () {
|
|
434
|
-
const tokenService = yield* TokenServiceTag;
|
|
435
|
-
return yield* tokenService.revokeToken(tokenId).pipe(
|
|
436
|
-
import_effect8.Effect.catchAll(() => import_effect8.Effect.void)
|
|
437
|
-
);
|
|
438
|
-
});
|
|
439
|
-
},
|
|
440
|
-
revokeAllTokens() {
|
|
441
|
-
return import_effect8.Effect.gen(function* () {
|
|
442
|
-
const tokenService = yield* TokenServiceTag;
|
|
443
|
-
return yield* tokenService.revokeAllTokens(user.id);
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
function hasApiTokens(obj) {
|
|
449
|
-
return typeof obj === "object" && obj !== null && "id" in obj && "createToken" in obj && "tokens" in obj;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// libs/auth/core/src/lib/middleware/authenticate.ts
|
|
453
|
-
var import_effect9 = require("effect");
|
|
454
|
-
var import_platform = require("@effect/platform");
|
|
455
|
-
var AuthenticatedUserTag = class extends import_effect9.Context.Tag("@gello/auth/AuthenticatedUser")() {
|
|
456
|
-
};
|
|
457
|
-
var extractBearerToken = (header) => {
|
|
458
|
-
if (!header) return null;
|
|
459
|
-
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
460
|
-
return match ? match[1] : null;
|
|
461
|
-
};
|
|
462
|
-
var authenticate = (options = {}) => ({
|
|
463
|
-
name: "authenticate",
|
|
464
|
-
apply: (effect) => import_effect9.Effect.gen(function* () {
|
|
465
|
-
const request = yield* import_platform.HttpServerRequest.HttpServerRequest;
|
|
466
|
-
const auth = yield* AuthTag;
|
|
467
|
-
const authHeader = request.headers["authorization"];
|
|
468
|
-
const token = extractBearerToken(authHeader);
|
|
469
|
-
if (!token) {
|
|
470
|
-
if (options.optional) {
|
|
471
|
-
return yield* effect;
|
|
472
|
-
}
|
|
473
|
-
return import_platform.HttpServerResponse.json(
|
|
474
|
-
{ error: "Authentication required" },
|
|
475
|
-
{ status: 401 }
|
|
476
|
-
);
|
|
477
|
-
}
|
|
478
|
-
const authResult = yield* auth.authenticateWithToken(token).pipe(
|
|
479
|
-
import_effect9.Effect.either
|
|
480
|
-
);
|
|
481
|
-
if (authResult._tag === "Left") {
|
|
482
|
-
if (options.optional) {
|
|
483
|
-
return yield* effect;
|
|
484
|
-
}
|
|
485
|
-
const error = authResult.left;
|
|
486
|
-
return import_platform.HttpServerResponse.json(
|
|
487
|
-
{ error: error.message },
|
|
488
|
-
{ status: 401 }
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
const user = authResult.right;
|
|
492
|
-
return yield* effect.pipe(
|
|
493
|
-
import_effect9.Effect.provideService(AuthenticatedUserTag, user)
|
|
494
|
-
);
|
|
495
|
-
})
|
|
496
|
-
});
|
|
497
|
-
authenticate.optional = () => {
|
|
498
|
-
const base = authenticate({ optional: true });
|
|
499
|
-
return {
|
|
500
|
-
...base,
|
|
501
|
-
name: "authenticate.optional"
|
|
502
|
-
};
|
|
503
|
-
};
|
|
504
|
-
var currentUser = () => import_effect9.Effect.flatMap(
|
|
505
|
-
import_effect9.Effect.either(AuthenticatedUserTag),
|
|
506
|
-
(result) => result._tag === "Right" ? import_effect9.Effect.succeed(result.right) : import_effect9.Effect.fail(
|
|
507
|
-
new AuthenticationError({
|
|
508
|
-
message: "Not authenticated",
|
|
509
|
-
reason: "missing_token"
|
|
510
|
-
})
|
|
511
|
-
)
|
|
512
|
-
);
|
|
513
|
-
var isAuthenticated = () => import_effect9.Effect.map(import_effect9.Effect.either(AuthenticatedUserTag), (result) => result._tag === "Right");
|
|
514
|
-
|
|
515
|
-
// libs/auth/core/src/lib/middleware/scopes.ts
|
|
516
|
-
var import_effect10 = require("effect");
|
|
517
|
-
var import_platform2 = require("@effect/platform");
|
|
518
|
-
var tokenScopes = (...requiredScopes) => ({
|
|
519
|
-
name: "tokenScopes",
|
|
520
|
-
apply: (effect) => import_effect10.Effect.gen(function* () {
|
|
521
|
-
const user = yield* AuthenticatedUserTag;
|
|
522
|
-
if (!user.token) {
|
|
523
|
-
return yield* effect;
|
|
524
|
-
}
|
|
525
|
-
const hasAll = PersonalAccessToken.hasAllScopes(user.token, requiredScopes);
|
|
526
|
-
if (!hasAll) {
|
|
527
|
-
return import_platform2.HttpServerResponse.json(
|
|
528
|
-
{
|
|
529
|
-
error: "Insufficient scope",
|
|
530
|
-
required: requiredScopes,
|
|
531
|
-
provided: user.token.scopes
|
|
532
|
-
},
|
|
533
|
-
{ status: 403 }
|
|
534
|
-
);
|
|
535
|
-
}
|
|
536
|
-
return yield* effect;
|
|
537
|
-
})
|
|
538
|
-
});
|
|
539
|
-
var tokenScope = (...allowedScopes) => ({
|
|
540
|
-
name: "tokenScope",
|
|
541
|
-
apply: (effect) => import_effect10.Effect.gen(function* () {
|
|
542
|
-
const user = yield* AuthenticatedUserTag;
|
|
543
|
-
if (!user.token) {
|
|
544
|
-
return yield* effect;
|
|
545
|
-
}
|
|
546
|
-
const hasAny = PersonalAccessToken.hasAnyScope(user.token, allowedScopes);
|
|
547
|
-
if (!hasAny) {
|
|
548
|
-
return import_platform2.HttpServerResponse.json(
|
|
549
|
-
{
|
|
550
|
-
error: "Insufficient scope",
|
|
551
|
-
required: `One of: ${allowedScopes.join(", ")}`,
|
|
552
|
-
provided: user.token.scopes
|
|
553
|
-
},
|
|
554
|
-
{ status: 403 }
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
return yield* effect;
|
|
558
|
-
})
|
|
559
|
-
});
|
|
560
|
-
var hasScope = (scope) => import_effect10.Effect.gen(function* () {
|
|
561
|
-
const user = yield* AuthenticatedUserTag;
|
|
562
|
-
if (!user.token) return true;
|
|
563
|
-
return PersonalAccessToken.hasScope(user.token, scope);
|
|
564
|
-
});
|
|
565
|
-
var requireScope = (scope) => import_effect10.Effect.gen(function* () {
|
|
566
|
-
const user = yield* AuthenticatedUserTag;
|
|
567
|
-
if (!user.token) return;
|
|
568
|
-
if (!PersonalAccessToken.hasScope(user.token, scope)) {
|
|
569
|
-
return yield* import_effect10.Effect.fail(
|
|
570
|
-
new InsufficientScopeError({
|
|
571
|
-
message: `Token lacks required scope: ${scope}`,
|
|
572
|
-
required: [scope],
|
|
573
|
-
provided: user.token.scopes
|
|
574
|
-
})
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
// libs/auth/session/src/lib/domain/types.ts
|
|
580
|
-
var import_effect11 = require("effect");
|
|
581
|
-
var SessionId = import_effect11.Brand.nominal();
|
|
582
|
-
var CsrfToken = import_effect11.Brand.nominal();
|
|
583
|
-
var Session = {
|
|
584
|
-
/**
|
|
585
|
-
* Generate a new SessionId
|
|
586
|
-
*/
|
|
587
|
-
generateId: () => SessionId(crypto.randomUUID()),
|
|
588
|
-
/**
|
|
589
|
-
* Generate a CSRF token
|
|
590
|
-
*/
|
|
591
|
-
generateCsrfToken: () => {
|
|
592
|
-
const bytes = new Uint8Array(32);
|
|
593
|
-
crypto.getRandomValues(bytes);
|
|
594
|
-
return CsrfToken(
|
|
595
|
-
Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("")
|
|
596
|
-
);
|
|
597
|
-
},
|
|
598
|
-
/**
|
|
599
|
-
* Create a new session
|
|
600
|
-
*/
|
|
601
|
-
make: (userId, options) => {
|
|
602
|
-
const now = /* @__PURE__ */ new Date();
|
|
603
|
-
const lifetime = options?.lifetime ?? import_effect11.Duration.hours(2);
|
|
604
|
-
const expiresAt = new Date(now.getTime() + import_effect11.Duration.toMillis(lifetime));
|
|
605
|
-
return {
|
|
606
|
-
id: Session.generateId(),
|
|
607
|
-
userId,
|
|
608
|
-
data: options?.data ?? {},
|
|
609
|
-
ipAddress: options?.ipAddress,
|
|
610
|
-
userAgent: options?.userAgent,
|
|
611
|
-
lastActivity: now,
|
|
612
|
-
createdAt: now,
|
|
613
|
-
expiresAt
|
|
614
|
-
};
|
|
615
|
-
},
|
|
616
|
-
/**
|
|
617
|
-
* Check if session is expired
|
|
618
|
-
*/
|
|
619
|
-
isExpired: (session2) => {
|
|
620
|
-
return /* @__PURE__ */ new Date() > session2.expiresAt;
|
|
621
|
-
},
|
|
622
|
-
/**
|
|
623
|
-
* Extend session expiration
|
|
624
|
-
*/
|
|
625
|
-
touch: (session2, lifetime) => {
|
|
626
|
-
const now = /* @__PURE__ */ new Date();
|
|
627
|
-
return {
|
|
628
|
-
...session2,
|
|
629
|
-
lastActivity: now,
|
|
630
|
-
expiresAt: new Date(now.getTime() + import_effect11.Duration.toMillis(lifetime))
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
};
|
|
634
|
-
var defaultSessionConfig = {
|
|
635
|
-
cookieName: "gello_session",
|
|
636
|
-
lifetime: import_effect11.Duration.hours(2),
|
|
637
|
-
path: "/",
|
|
638
|
-
secure: true,
|
|
639
|
-
httpOnly: true,
|
|
640
|
-
sameSite: "lax"
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
// libs/auth/session/src/lib/domain/errors.ts
|
|
644
|
-
var import_effect12 = require("effect");
|
|
645
|
-
var SessionError = class extends import_effect12.Data.TaggedError("SessionError") {
|
|
646
|
-
};
|
|
647
|
-
var SessionNotFoundError = class extends import_effect12.Data.TaggedError("SessionNotFoundError") {
|
|
648
|
-
};
|
|
649
|
-
var SessionExpiredError = class extends import_effect12.Data.TaggedError("SessionExpiredError") {
|
|
650
|
-
};
|
|
651
|
-
var CsrfMismatchError = class extends import_effect12.Data.TaggedError("CsrfMismatchError") {
|
|
652
|
-
};
|
|
653
|
-
var JwtError = class extends import_effect12.Data.TaggedError("JwtError") {
|
|
654
|
-
};
|
|
655
|
-
|
|
656
|
-
// libs/auth/session/src/lib/ports/SessionStore.ts
|
|
657
|
-
var import_effect13 = require("effect");
|
|
658
|
-
var SessionStoreTag = class extends import_effect13.Context.Tag("@gello/auth/SessionStore")() {
|
|
659
|
-
};
|
|
660
|
-
|
|
661
|
-
// libs/auth/session/src/lib/drivers/MemorySessionStore.ts
|
|
662
|
-
var import_effect14 = require("effect");
|
|
663
|
-
var makeMemorySessionStore = () => {
|
|
664
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
665
|
-
return {
|
|
666
|
-
get: (id) => import_effect14.Effect.sync(() => {
|
|
667
|
-
const session2 = sessions.get(id);
|
|
668
|
-
if (!session2) return import_effect14.Option.none();
|
|
669
|
-
if (/* @__PURE__ */ new Date() > session2.expiresAt) {
|
|
670
|
-
sessions.delete(id);
|
|
671
|
-
return import_effect14.Option.none();
|
|
672
|
-
}
|
|
673
|
-
return import_effect14.Option.some(session2);
|
|
674
|
-
}),
|
|
675
|
-
put: (session2) => import_effect14.Effect.sync(() => {
|
|
676
|
-
sessions.set(session2.id, session2);
|
|
677
|
-
}),
|
|
678
|
-
destroy: (id) => import_effect14.Effect.sync(() => {
|
|
679
|
-
if (!sessions.has(id)) {
|
|
680
|
-
throw new SessionNotFoundError({
|
|
681
|
-
message: "Session not found",
|
|
682
|
-
sessionId: id
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
sessions.delete(id);
|
|
686
|
-
}),
|
|
687
|
-
destroyByUser: (userId) => import_effect14.Effect.sync(() => {
|
|
688
|
-
let count = 0;
|
|
689
|
-
for (const [id, session2] of sessions) {
|
|
690
|
-
if (session2.userId === userId) {
|
|
691
|
-
sessions.delete(id);
|
|
692
|
-
count++;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
return count;
|
|
696
|
-
}),
|
|
697
|
-
touch: (id, lifetime) => import_effect14.Effect.sync(() => {
|
|
698
|
-
const session2 = sessions.get(id);
|
|
699
|
-
if (!session2) {
|
|
700
|
-
throw new SessionNotFoundError({
|
|
701
|
-
message: "Session not found",
|
|
702
|
-
sessionId: id
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
const now = /* @__PURE__ */ new Date();
|
|
706
|
-
sessions.set(id, {
|
|
707
|
-
...session2,
|
|
708
|
-
lastActivity: now,
|
|
709
|
-
expiresAt: new Date(now.getTime() + import_effect14.Duration.toMillis(lifetime))
|
|
710
|
-
});
|
|
711
|
-
}),
|
|
712
|
-
gc: (maxLifetime) => import_effect14.Effect.sync(() => {
|
|
713
|
-
const now = /* @__PURE__ */ new Date();
|
|
714
|
-
const cutoff = new Date(now.getTime() - import_effect14.Duration.toMillis(maxLifetime));
|
|
715
|
-
let count = 0;
|
|
716
|
-
for (const [id, session2] of sessions) {
|
|
717
|
-
if (session2.expiresAt < now || session2.lastActivity < cutoff) {
|
|
718
|
-
sessions.delete(id);
|
|
719
|
-
count++;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
return count;
|
|
723
|
-
})
|
|
724
|
-
};
|
|
725
|
-
};
|
|
726
|
-
var MemorySessionStoreLive = import_effect14.Layer.succeed(
|
|
727
|
-
SessionStoreTag,
|
|
728
|
-
makeMemorySessionStore()
|
|
729
|
-
);
|
|
730
|
-
|
|
731
|
-
// libs/auth/session/src/lib/services/JwtService.ts
|
|
732
|
-
var import_effect15 = require("effect");
|
|
733
|
-
var JwtServiceTag = class extends import_effect15.Context.Tag("@gello/auth/JwtService")() {
|
|
734
|
-
};
|
|
735
|
-
var base64urlEncode = (data) => {
|
|
736
|
-
const base64 = Buffer.from(data).toString("base64");
|
|
737
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
738
|
-
};
|
|
739
|
-
var base64urlDecode = (data) => {
|
|
740
|
-
const base64 = data.replace(/-/g, "+").replace(/_/g, "/");
|
|
741
|
-
const pad = base64.length % 4;
|
|
742
|
-
const padded = pad ? base64 + "=".repeat(4 - pad) : base64;
|
|
743
|
-
return Buffer.from(padded, "base64").toString("utf-8");
|
|
744
|
-
};
|
|
745
|
-
var hmacSha256 = async (data, secret) => {
|
|
746
|
-
const encoder = new TextEncoder();
|
|
747
|
-
const key = await crypto.subtle.importKey(
|
|
748
|
-
"raw",
|
|
749
|
-
encoder.encode(secret),
|
|
750
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
751
|
-
false,
|
|
752
|
-
["sign"]
|
|
753
|
-
);
|
|
754
|
-
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
|
|
755
|
-
const base64 = Buffer.from(signature).toString("base64");
|
|
756
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
757
|
-
};
|
|
758
|
-
var makeJwtService = (secret) => {
|
|
759
|
-
return {
|
|
760
|
-
sign: (payload, expiresInSeconds = 3600) => import_effect15.Effect.tryPromise({
|
|
761
|
-
try: async () => {
|
|
762
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
763
|
-
const fullPayload = {
|
|
764
|
-
...payload,
|
|
765
|
-
iat: now,
|
|
766
|
-
exp: now + expiresInSeconds
|
|
767
|
-
};
|
|
768
|
-
const header = base64urlEncode(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
|
769
|
-
const body = base64urlEncode(JSON.stringify(fullPayload));
|
|
770
|
-
const signature = await hmacSha256(`${header}.${body}`, secret);
|
|
771
|
-
return `${header}.${body}.${signature}`;
|
|
772
|
-
},
|
|
773
|
-
catch: (e) => new JwtError({
|
|
774
|
-
message: "Failed to sign JWT",
|
|
775
|
-
reason: "malformed",
|
|
776
|
-
cause: e
|
|
777
|
-
})
|
|
778
|
-
}),
|
|
779
|
-
verify: (token) => import_effect15.Effect.tryPromise({
|
|
780
|
-
try: async () => {
|
|
781
|
-
const parts = token.split(".");
|
|
782
|
-
if (parts.length !== 3) {
|
|
783
|
-
throw new JwtError({
|
|
784
|
-
message: "Invalid JWT format",
|
|
785
|
-
reason: "malformed"
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
const [header, body, signature] = parts;
|
|
789
|
-
const expectedSignature = await hmacSha256(`${header}.${body}`, secret);
|
|
790
|
-
if (signature !== expectedSignature) {
|
|
791
|
-
throw new JwtError({
|
|
792
|
-
message: "Invalid JWT signature",
|
|
793
|
-
reason: "invalid"
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
const payload = JSON.parse(base64urlDecode(body));
|
|
797
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
798
|
-
if (payload.exp && payload.exp < now) {
|
|
799
|
-
throw new JwtError({
|
|
800
|
-
message: "JWT has expired",
|
|
801
|
-
reason: "expired"
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
return payload;
|
|
805
|
-
},
|
|
806
|
-
catch: (e) => {
|
|
807
|
-
if (e instanceof JwtError) return e;
|
|
808
|
-
return new JwtError({
|
|
809
|
-
message: "Failed to verify JWT",
|
|
810
|
-
reason: "malformed",
|
|
811
|
-
cause: e
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
}),
|
|
815
|
-
decode: (token) => import_effect15.Effect.try({
|
|
816
|
-
try: () => {
|
|
817
|
-
const parts = token.split(".");
|
|
818
|
-
if (parts.length !== 3) {
|
|
819
|
-
throw new JwtError({
|
|
820
|
-
message: "Invalid JWT format",
|
|
821
|
-
reason: "malformed"
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
return JSON.parse(base64urlDecode(parts[1]));
|
|
825
|
-
},
|
|
826
|
-
catch: (e) => {
|
|
827
|
-
if (e instanceof JwtError) return e;
|
|
828
|
-
return new JwtError({
|
|
829
|
-
message: "Failed to decode JWT",
|
|
830
|
-
reason: "malformed",
|
|
831
|
-
cause: e
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
})
|
|
835
|
-
};
|
|
836
|
-
};
|
|
837
|
-
var JwtServiceLive = (secret) => import_effect15.Layer.succeed(JwtServiceTag, makeJwtService(secret));
|
|
838
|
-
|
|
839
|
-
// libs/auth/session/src/lib/middleware/session.ts
|
|
840
|
-
var import_effect16 = require("effect");
|
|
841
|
-
var import_platform3 = require("@effect/platform");
|
|
842
|
-
var CurrentSessionTag = class extends import_effect16.Context.Tag("@gello/auth/CurrentSession")() {
|
|
843
|
-
};
|
|
844
|
-
var parseCookies = (header) => {
|
|
845
|
-
const cookies = /* @__PURE__ */ new Map();
|
|
846
|
-
if (!header) return cookies;
|
|
847
|
-
header.split(";").forEach((cookie) => {
|
|
848
|
-
const [name, ...rest] = cookie.trim().split("=");
|
|
849
|
-
if (name) {
|
|
850
|
-
cookies.set(name, rest.join("="));
|
|
851
|
-
}
|
|
852
|
-
});
|
|
853
|
-
return cookies;
|
|
854
|
-
};
|
|
855
|
-
var session = (options = {}) => {
|
|
856
|
-
const config = { ...defaultSessionConfig, ...options };
|
|
857
|
-
return {
|
|
858
|
-
name: "session",
|
|
859
|
-
apply: (effect) => import_effect16.Effect.gen(function* () {
|
|
860
|
-
const request = yield* import_platform3.HttpServerRequest.HttpServerRequest;
|
|
861
|
-
const store = yield* SessionStoreTag;
|
|
862
|
-
const cookies = parseCookies(request.headers["cookie"]);
|
|
863
|
-
const sessionId = cookies.get(config.cookieName);
|
|
864
|
-
let currentSession = null;
|
|
865
|
-
if (sessionId) {
|
|
866
|
-
const maybeSession = yield* store.get(SessionId(sessionId));
|
|
867
|
-
if (import_effect16.Option.isSome(maybeSession)) {
|
|
868
|
-
const sess = maybeSession.value;
|
|
869
|
-
if (!Session.isExpired(sess)) {
|
|
870
|
-
yield* store.touch(sess.id, config.lifetime).pipe(
|
|
871
|
-
import_effect16.Effect.catchAll(() => import_effect16.Effect.void)
|
|
872
|
-
);
|
|
873
|
-
currentSession = Session.touch(sess, config.lifetime);
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
if (!currentSession && !options.optional) {
|
|
878
|
-
return import_platform3.HttpServerResponse.json(
|
|
879
|
-
{ error: "Session required" },
|
|
880
|
-
{ status: 401 }
|
|
881
|
-
);
|
|
882
|
-
}
|
|
883
|
-
if (currentSession) {
|
|
884
|
-
return yield* effect.pipe(
|
|
885
|
-
import_effect16.Effect.provideService(CurrentSessionTag, currentSession)
|
|
886
|
-
);
|
|
887
|
-
}
|
|
888
|
-
return yield* effect;
|
|
889
|
-
})
|
|
890
|
-
};
|
|
891
|
-
};
|
|
892
|
-
session.optional = () => {
|
|
893
|
-
const base = session({ optional: true });
|
|
894
|
-
return {
|
|
895
|
-
...base,
|
|
896
|
-
name: "session.optional"
|
|
897
|
-
};
|
|
898
|
-
};
|
|
899
|
-
var startSession = (user, config = defaultSessionConfig, data = {}) => import_effect16.Effect.gen(function* () {
|
|
900
|
-
const store = yield* SessionStoreTag;
|
|
901
|
-
const request = yield* import_platform3.HttpServerRequest.HttpServerRequest;
|
|
902
|
-
const newSession = Session.make(user.id, {
|
|
903
|
-
data,
|
|
904
|
-
ipAddress: request.headers["x-forwarded-for"] ?? request.headers["x-real-ip"],
|
|
905
|
-
userAgent: request.headers["user-agent"],
|
|
906
|
-
lifetime: config.lifetime
|
|
907
|
-
});
|
|
908
|
-
yield* store.put(newSession);
|
|
909
|
-
const cookieParts = [
|
|
910
|
-
`${config.cookieName}=${newSession.id}`,
|
|
911
|
-
`Path=${config.path}`,
|
|
912
|
-
`Max-Age=${Math.floor(import_effect16.Duration.toSeconds(config.lifetime))}`,
|
|
913
|
-
config.httpOnly ? "HttpOnly" : "",
|
|
914
|
-
config.secure ? "Secure" : "",
|
|
915
|
-
`SameSite=${config.sameSite}`,
|
|
916
|
-
config.domain ? `Domain=${config.domain}` : ""
|
|
917
|
-
].filter(Boolean);
|
|
918
|
-
return {
|
|
919
|
-
session: newSession,
|
|
920
|
-
cookie: cookieParts.join("; ")
|
|
921
|
-
};
|
|
922
|
-
});
|
|
923
|
-
var endSession = (config = defaultSessionConfig) => import_effect16.Effect.gen(function* () {
|
|
924
|
-
const session2 = yield* CurrentSessionTag;
|
|
925
|
-
const store = yield* SessionStoreTag;
|
|
926
|
-
yield* store.destroy(session2.id).pipe(import_effect16.Effect.catchAll(() => import_effect16.Effect.void));
|
|
927
|
-
const cookieParts = [
|
|
928
|
-
`${config.cookieName}=`,
|
|
929
|
-
`Path=${config.path}`,
|
|
930
|
-
`Max-Age=0`,
|
|
931
|
-
config.httpOnly ? "HttpOnly" : "",
|
|
932
|
-
config.secure ? "Secure" : "",
|
|
933
|
-
`SameSite=${config.sameSite}`
|
|
934
|
-
].filter(Boolean);
|
|
935
|
-
return cookieParts.join("; ");
|
|
936
|
-
});
|
|
937
|
-
var getSessionData = (key) => import_effect16.Effect.gen(function* () {
|
|
938
|
-
const session2 = yield* CurrentSessionTag;
|
|
939
|
-
return session2.data[key];
|
|
940
|
-
});
|
|
941
|
-
var setSessionData = (key, value) => import_effect16.Effect.gen(function* () {
|
|
942
|
-
const session2 = yield* CurrentSessionTag;
|
|
943
|
-
const store = yield* SessionStoreTag;
|
|
944
|
-
const updatedSession = {
|
|
945
|
-
...session2,
|
|
946
|
-
data: { ...session2.data, [key]: value }
|
|
947
|
-
};
|
|
948
|
-
yield* store.put(updatedSession);
|
|
949
|
-
});
|
|
950
|
-
|
|
951
|
-
// libs/auth/session/src/lib/middleware/csrf.ts
|
|
952
|
-
var import_effect17 = require("effect");
|
|
953
|
-
var import_platform4 = require("@effect/platform");
|
|
954
|
-
var CsrfTokenTag = class extends import_effect17.Context.Tag("@gello/auth/CsrfToken")() {
|
|
955
|
-
};
|
|
956
|
-
var defaultCsrfConfig = {
|
|
957
|
-
cookieName: "XSRF-TOKEN",
|
|
958
|
-
headerName: "X-XSRF-TOKEN",
|
|
959
|
-
protectedMethods: ["POST", "PUT", "PATCH", "DELETE"],
|
|
960
|
-
path: "/",
|
|
961
|
-
secure: true,
|
|
962
|
-
sameSite: "lax"
|
|
963
|
-
};
|
|
964
|
-
var parseCookies2 = (header) => {
|
|
965
|
-
const cookies = /* @__PURE__ */ new Map();
|
|
966
|
-
if (!header) return cookies;
|
|
967
|
-
header.split(";").forEach((cookie) => {
|
|
968
|
-
const [name, ...rest] = cookie.trim().split("=");
|
|
969
|
-
if (name) {
|
|
970
|
-
cookies.set(name, rest.join("="));
|
|
971
|
-
}
|
|
972
|
-
});
|
|
973
|
-
return cookies;
|
|
974
|
-
};
|
|
975
|
-
var csrf = (config = {}) => {
|
|
976
|
-
const cfg = { ...defaultCsrfConfig, ...config };
|
|
977
|
-
return {
|
|
978
|
-
name: "csrf",
|
|
979
|
-
apply: (effect) => import_effect17.Effect.gen(function* () {
|
|
980
|
-
const request = yield* import_platform4.HttpServerRequest.HttpServerRequest;
|
|
981
|
-
const method = request.method.toUpperCase();
|
|
982
|
-
if (!cfg.protectedMethods.includes(method)) {
|
|
983
|
-
return yield* effect;
|
|
984
|
-
}
|
|
985
|
-
const cookies = parseCookies2(request.headers["cookie"]);
|
|
986
|
-
const cookieToken = cookies.get(cfg.cookieName);
|
|
987
|
-
const headerToken = request.headers[cfg.headerName.toLowerCase()];
|
|
988
|
-
if (!cookieToken || !headerToken) {
|
|
989
|
-
return import_platform4.HttpServerResponse.json(
|
|
990
|
-
{ error: "CSRF token missing" },
|
|
991
|
-
{ status: 419 }
|
|
992
|
-
);
|
|
993
|
-
}
|
|
994
|
-
if (cookieToken !== headerToken) {
|
|
995
|
-
return import_platform4.HttpServerResponse.json(
|
|
996
|
-
{ error: "CSRF token mismatch" },
|
|
997
|
-
{ status: 419 }
|
|
998
|
-
);
|
|
999
|
-
}
|
|
1000
|
-
return yield* effect.pipe(
|
|
1001
|
-
import_effect17.Effect.provideService(CsrfTokenTag, CsrfToken(cookieToken))
|
|
1002
|
-
);
|
|
1003
|
-
})
|
|
1004
|
-
};
|
|
1005
|
-
};
|
|
1006
|
-
var generateCsrfCookie = (config = {}) => {
|
|
1007
|
-
const cfg = { ...defaultCsrfConfig, ...config };
|
|
1008
|
-
return import_effect17.Effect.sync(() => {
|
|
1009
|
-
const token = Session.generateCsrfToken();
|
|
1010
|
-
const cookieParts = [
|
|
1011
|
-
`${cfg.cookieName}=${token}`,
|
|
1012
|
-
`Path=${cfg.path}`,
|
|
1013
|
-
// Not HttpOnly so JS can read it
|
|
1014
|
-
cfg.secure ? "Secure" : "",
|
|
1015
|
-
`SameSite=${cfg.sameSite}`
|
|
1016
|
-
].filter(Boolean);
|
|
1017
|
-
return cookieParts.join("; ");
|
|
1018
|
-
});
|
|
1019
|
-
};
|
|
1020
|
-
var verifyCsrfToken = (expected, provided) => import_effect17.Effect.gen(function* () {
|
|
1021
|
-
if (expected !== provided) {
|
|
1022
|
-
return yield* import_effect17.Effect.fail(
|
|
1023
|
-
new CsrfMismatchError({
|
|
1024
|
-
message: "CSRF token mismatch"
|
|
1025
|
-
})
|
|
1026
|
-
);
|
|
1027
|
-
}
|
|
1028
|
-
});
|
|
1029
|
-
|
|
1030
|
-
// libs/auth/authorization/src/lib/domain/types.ts
|
|
1031
|
-
var getSubjectType = (subject) => {
|
|
1032
|
-
if (typeof subject === "string") return subject;
|
|
1033
|
-
return subject.constructor.name;
|
|
1034
|
-
};
|
|
1035
|
-
var matchAction = (ruleAction, action) => {
|
|
1036
|
-
const actions = Array.isArray(ruleAction) ? ruleAction : [ruleAction];
|
|
1037
|
-
return actions.includes(action) || actions.includes("manage");
|
|
1038
|
-
};
|
|
1039
|
-
var matchSubject = (ruleSubject, subject) => {
|
|
1040
|
-
const subjects = Array.isArray(ruleSubject) ? ruleSubject : [ruleSubject];
|
|
1041
|
-
const subjectType = getSubjectType(subject);
|
|
1042
|
-
return subjects.some((s) => {
|
|
1043
|
-
if (s === "all") return true;
|
|
1044
|
-
if (typeof s === "string") return s === subjectType;
|
|
1045
|
-
return s === subject;
|
|
1046
|
-
});
|
|
1047
|
-
};
|
|
1048
|
-
var matchConditions = (conditions, subject) => {
|
|
1049
|
-
if (!conditions) return true;
|
|
1050
|
-
if (typeof subject === "string") return true;
|
|
1051
|
-
return Object.entries(conditions).every(([key, value]) => {
|
|
1052
|
-
return subject[key] === value;
|
|
1053
|
-
});
|
|
1054
|
-
};
|
|
1055
|
-
var createAbilities = (rules) => {
|
|
1056
|
-
const can2 = (action, subject) => {
|
|
1057
|
-
const matchingRules = rules.filter(
|
|
1058
|
-
(rule) => matchAction(rule.action, action) && matchSubject(rule.subject, subject) && matchConditions(rule.conditions, subject)
|
|
1059
|
-
);
|
|
1060
|
-
if (matchingRules.length === 0) return false;
|
|
1061
|
-
const hasInverted = matchingRules.some((r) => r.inverted);
|
|
1062
|
-
const hasPositive = matchingRules.some((r) => !r.inverted);
|
|
1063
|
-
if (hasInverted) return false;
|
|
1064
|
-
return hasPositive;
|
|
1065
|
-
};
|
|
1066
|
-
const cannot2 = (action, subject) => !can2(action, subject);
|
|
1067
|
-
const relevantRuleFor = (action, subject) => {
|
|
1068
|
-
return rules.find(
|
|
1069
|
-
(rule) => matchAction(rule.action, action) && matchSubject(rule.subject, subject) && matchConditions(rule.conditions, subject)
|
|
1070
|
-
);
|
|
1071
|
-
};
|
|
1072
|
-
return {
|
|
1073
|
-
rules,
|
|
1074
|
-
can: can2,
|
|
1075
|
-
cannot: cannot2,
|
|
1076
|
-
relevantRuleFor
|
|
1077
|
-
};
|
|
1078
|
-
};
|
|
1079
|
-
|
|
1080
|
-
// libs/auth/authorization/src/lib/domain/errors.ts
|
|
1081
|
-
var import_effect18 = require("effect");
|
|
1082
|
-
var AuthorizationError = class extends import_effect18.Data.TaggedError("AuthorizationError") {
|
|
1083
|
-
};
|
|
1084
|
-
var ForbiddenError = class extends import_effect18.Data.TaggedError("ForbiddenError") {
|
|
1085
|
-
};
|
|
1086
|
-
|
|
1087
|
-
// libs/auth/authorization/src/lib/services/AbilityBuilder.ts
|
|
1088
|
-
function defineAbilitiesFor(user, define) {
|
|
1089
|
-
const rules = [];
|
|
1090
|
-
const context = {
|
|
1091
|
-
user,
|
|
1092
|
-
can: (action, subject, conditions) => {
|
|
1093
|
-
rules.push({
|
|
1094
|
-
action: Array.isArray(action) ? action : [action],
|
|
1095
|
-
subject: Array.isArray(subject) ? subject : [subject],
|
|
1096
|
-
conditions,
|
|
1097
|
-
inverted: false
|
|
1098
|
-
});
|
|
1099
|
-
},
|
|
1100
|
-
cannot: (action, subject, conditions, reason) => {
|
|
1101
|
-
rules.push({
|
|
1102
|
-
action: Array.isArray(action) ? action : [action],
|
|
1103
|
-
subject: Array.isArray(subject) ? subject : [subject],
|
|
1104
|
-
conditions,
|
|
1105
|
-
inverted: true,
|
|
1106
|
-
reason
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
};
|
|
1110
|
-
define(context);
|
|
1111
|
-
return createAbilities(rules);
|
|
1112
|
-
}
|
|
1113
|
-
var CommonAbilities = {
|
|
1114
|
-
/**
|
|
1115
|
-
* CRUD abilities for a resource
|
|
1116
|
-
*/
|
|
1117
|
-
crud: (subject) => ["create", "read", "update", "delete"],
|
|
1118
|
-
/**
|
|
1119
|
-
* Full management (includes all actions)
|
|
1120
|
-
*/
|
|
1121
|
-
manage: "manage",
|
|
1122
|
-
/**
|
|
1123
|
-
* All subjects wildcard
|
|
1124
|
-
*/
|
|
1125
|
-
all: "all"
|
|
1126
|
-
};
|
|
1127
|
-
|
|
1128
|
-
// libs/auth/authorization/src/lib/services/Abilities.ts
|
|
1129
|
-
var import_effect19 = require("effect");
|
|
1130
|
-
var AbilitiesTag = class extends import_effect19.Context.Tag("@gello/auth/Abilities")() {
|
|
1131
|
-
};
|
|
1132
|
-
var can = (action, subject) => import_effect19.Effect.gen(function* () {
|
|
1133
|
-
const abilities = yield* AbilitiesTag;
|
|
1134
|
-
return abilities.can(action, subject);
|
|
1135
|
-
});
|
|
1136
|
-
var cannot = (action, subject) => import_effect19.Effect.gen(function* () {
|
|
1137
|
-
const abilities = yield* AbilitiesTag;
|
|
1138
|
-
return abilities.cannot(action, subject);
|
|
1139
|
-
});
|
|
1140
|
-
var authorize = (action, subject) => import_effect19.Effect.gen(function* () {
|
|
1141
|
-
const abilities = yield* AbilitiesTag;
|
|
1142
|
-
const allowed = abilities.can(action, subject);
|
|
1143
|
-
if (!allowed) {
|
|
1144
|
-
const rule = abilities.relevantRuleFor(action, subject);
|
|
1145
|
-
return yield* import_effect19.Effect.fail(
|
|
1146
|
-
new ForbiddenError({
|
|
1147
|
-
message: rule?.reason ?? `You are not authorized to ${action} this ${getSubjectType(subject)}`,
|
|
1148
|
-
action,
|
|
1149
|
-
subject: getSubjectType(subject),
|
|
1150
|
-
rule
|
|
1151
|
-
})
|
|
1152
|
-
);
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1155
|
-
var authorizeAll = (actions, subject) => import_effect19.Effect.gen(function* () {
|
|
1156
|
-
for (const action of actions) {
|
|
1157
|
-
yield* authorize(action, subject);
|
|
1158
|
-
}
|
|
1159
|
-
});
|
|
1160
|
-
var authorizeAny = (actions, subject) => import_effect19.Effect.gen(function* () {
|
|
1161
|
-
const abilities = yield* AbilitiesTag;
|
|
1162
|
-
const hasAny = actions.some((action) => abilities.can(action, subject));
|
|
1163
|
-
if (!hasAny) {
|
|
1164
|
-
return yield* import_effect19.Effect.fail(
|
|
1165
|
-
new ForbiddenError({
|
|
1166
|
-
message: `You are not authorized to perform any of [${actions.join(", ")}] on this ${getSubjectType(subject)}`,
|
|
1167
|
-
action: actions[0],
|
|
1168
|
-
subject: getSubjectType(subject)
|
|
1169
|
-
})
|
|
1170
|
-
);
|
|
1171
|
-
}
|
|
1172
|
-
});
|
|
1173
|
-
var AbilitiesLive = (abilities) => import_effect19.Layer.succeed(AbilitiesTag, abilities);
|
|
1174
|
-
|
|
1175
|
-
// libs/auth/authorization/src/lib/middleware/authorize.ts
|
|
1176
|
-
var import_effect20 = require("effect");
|
|
1177
|
-
var import_platform5 = require("@effect/platform");
|
|
1178
|
-
var authorizeMiddleware = (action, getSubject, options = {}) => ({
|
|
1179
|
-
name: "authorize",
|
|
1180
|
-
apply: (effect) => import_effect20.Effect.gen(function* () {
|
|
1181
|
-
const abilities = yield* AbilitiesTag;
|
|
1182
|
-
const subject = getSubject ? yield* getSubject().pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed(action))) : action;
|
|
1183
|
-
const allowed = abilities.can(action, subject);
|
|
1184
|
-
if (!allowed) {
|
|
1185
|
-
const subjectType = getSubjectType(subject);
|
|
1186
|
-
const message = options.message ?? `You are not authorized to ${action} ${subjectType}`;
|
|
1187
|
-
const status = options.status ?? 403;
|
|
1188
|
-
return import_platform5.HttpServerResponse.json({ error: message }, { status });
|
|
1189
|
-
}
|
|
1190
|
-
return yield* effect;
|
|
1191
|
-
})
|
|
1192
|
-
});
|
|
1193
|
-
var requireAdmin = (options = {}) => {
|
|
1194
|
-
const base = authorizeMiddleware("manage", () => import_effect20.Effect.succeed("all"), options);
|
|
1195
|
-
return {
|
|
1196
|
-
...base,
|
|
1197
|
-
name: "requireAdmin"
|
|
1198
|
-
};
|
|
1199
|
-
};
|
|
1200
|
-
var requireAny = (actions, getSubject, options = {}) => ({
|
|
1201
|
-
name: "requireAny",
|
|
1202
|
-
apply: (effect) => import_effect20.Effect.gen(function* () {
|
|
1203
|
-
const abilities = yield* AbilitiesTag;
|
|
1204
|
-
const subject = getSubject ? yield* getSubject().pipe(import_effect20.Effect.catchAll(() => import_effect20.Effect.succeed(actions[0]))) : actions[0];
|
|
1205
|
-
const hasAny = actions.some((action) => abilities.can(action, subject));
|
|
1206
|
-
if (!hasAny) {
|
|
1207
|
-
const message = options.message ?? `You are not authorized to perform this action`;
|
|
1208
|
-
const status = options.status ?? 403;
|
|
1209
|
-
return import_platform5.HttpServerResponse.json({ error: message }, { status });
|
|
1210
|
-
}
|
|
1211
|
-
return yield* effect;
|
|
1212
|
-
})
|
|
1213
|
-
});
|
|
1214
|
-
|
|
1215
|
-
// libs/auth/social/src/lib/domain/SocialUser.ts
|
|
1216
|
-
var SocialUser = {
|
|
1217
|
-
make: (provider, data) => ({
|
|
1218
|
-
id: data.id,
|
|
1219
|
-
email: data.email ?? null,
|
|
1220
|
-
name: data.name ?? null,
|
|
1221
|
-
firstName: data.firstName ?? null,
|
|
1222
|
-
lastName: data.lastName ?? null,
|
|
1223
|
-
avatar: data.avatar ?? null,
|
|
1224
|
-
nickname: data.nickname ?? null,
|
|
1225
|
-
provider,
|
|
1226
|
-
raw: data.raw ?? {}
|
|
1227
|
-
})
|
|
1228
|
-
};
|
|
1229
|
-
|
|
1230
|
-
// libs/auth/social/src/lib/domain/SocialToken.ts
|
|
1231
|
-
var SocialToken = {
|
|
1232
|
-
make: (data) => ({
|
|
1233
|
-
accessToken: data.accessToken,
|
|
1234
|
-
refreshToken: data.refreshToken ?? null,
|
|
1235
|
-
expiresAt: data.expiresAt ?? (data.expiresIn ? new Date(Date.now() + data.expiresIn * 1e3) : null),
|
|
1236
|
-
tokenType: data.tokenType ?? "Bearer",
|
|
1237
|
-
scopes: typeof data.scope === "string" ? data.scope.split(" ") : data.scope ?? []
|
|
1238
|
-
}),
|
|
1239
|
-
/**
|
|
1240
|
-
* Check if token is expired
|
|
1241
|
-
*/
|
|
1242
|
-
isExpired: (token) => {
|
|
1243
|
-
if (!token.expiresAt) return false;
|
|
1244
|
-
return /* @__PURE__ */ new Date() > token.expiresAt;
|
|
1245
|
-
},
|
|
1246
|
-
/**
|
|
1247
|
-
* Check if token can be refreshed
|
|
1248
|
-
*/
|
|
1249
|
-
canRefresh: (token) => {
|
|
1250
|
-
return token.refreshToken !== null;
|
|
1251
|
-
}
|
|
1252
|
-
};
|
|
1253
|
-
|
|
1254
|
-
// libs/auth/social/src/lib/domain/errors.ts
|
|
1255
|
-
var import_effect21 = require("effect");
|
|
1256
|
-
var OAuthError = class extends import_effect21.Data.TaggedError("OAuthError") {
|
|
1257
|
-
};
|
|
1258
|
-
var OAuthConfigError = class extends import_effect21.Data.TaggedError("OAuthConfigError") {
|
|
1259
|
-
};
|
|
1260
|
-
var OAuthStateMismatchError = class extends import_effect21.Data.TaggedError("OAuthStateMismatchError") {
|
|
1261
|
-
};
|
|
1262
|
-
var OAuthTokenError = class extends import_effect21.Data.TaggedError("OAuthTokenError") {
|
|
1263
|
-
};
|
|
1264
|
-
var OAuthUserError = class extends import_effect21.Data.TaggedError("OAuthUserError") {
|
|
1265
|
-
};
|
|
1266
|
-
var OAuthProviderNotFoundError = class extends import_effect21.Data.TaggedError("OAuthProviderNotFoundError") {
|
|
1267
|
-
};
|
|
1268
|
-
|
|
1269
|
-
// libs/auth/social/src/lib/providers/AbstractProvider.ts
|
|
1270
|
-
var import_effect22 = require("effect");
|
|
1271
|
-
var AbstractProvider = class {
|
|
1272
|
-
config;
|
|
1273
|
-
constructor(config) {
|
|
1274
|
-
this.config = config;
|
|
1275
|
-
}
|
|
1276
|
-
/**
|
|
1277
|
-
* Build the authorization URL
|
|
1278
|
-
*/
|
|
1279
|
-
getAuthorizationUrl(scopes, state) {
|
|
1280
|
-
const allScopes = [.../* @__PURE__ */ new Set([...this.defaultScopes, ...scopes])];
|
|
1281
|
-
const params = new URLSearchParams({
|
|
1282
|
-
client_id: this.config.clientId,
|
|
1283
|
-
redirect_uri: this.config.redirectUri,
|
|
1284
|
-
response_type: "code",
|
|
1285
|
-
scope: allScopes.join(" "),
|
|
1286
|
-
state
|
|
1287
|
-
});
|
|
1288
|
-
return `${this.authorizationUrl}?${params.toString()}`;
|
|
1289
|
-
}
|
|
1290
|
-
/**
|
|
1291
|
-
* Exchange code for access token
|
|
1292
|
-
*/
|
|
1293
|
-
getAccessToken(code) {
|
|
1294
|
-
return import_effect22.Effect.tryPromise({
|
|
1295
|
-
try: async () => {
|
|
1296
|
-
const response = await fetch(this.tokenUrl, {
|
|
1297
|
-
method: "POST",
|
|
1298
|
-
headers: {
|
|
1299
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
1300
|
-
Accept: "application/json"
|
|
1301
|
-
},
|
|
1302
|
-
body: new URLSearchParams({
|
|
1303
|
-
client_id: this.config.clientId,
|
|
1304
|
-
client_secret: this.config.clientSecret,
|
|
1305
|
-
code,
|
|
1306
|
-
redirect_uri: this.config.redirectUri,
|
|
1307
|
-
grant_type: "authorization_code"
|
|
1308
|
-
})
|
|
1309
|
-
});
|
|
1310
|
-
if (!response.ok) {
|
|
1311
|
-
const error = await response.json().catch(() => ({}));
|
|
1312
|
-
throw new OAuthTokenError({
|
|
1313
|
-
message: "Failed to exchange code for token",
|
|
1314
|
-
provider: this.name,
|
|
1315
|
-
error: error["error"],
|
|
1316
|
-
errorDescription: error["error_description"]
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
const data = await response.json();
|
|
1320
|
-
return SocialToken.make({
|
|
1321
|
-
accessToken: data["access_token"],
|
|
1322
|
-
refreshToken: data["refresh_token"],
|
|
1323
|
-
expiresIn: data["expires_in"],
|
|
1324
|
-
tokenType: data["token_type"],
|
|
1325
|
-
scope: data["scope"]
|
|
1326
|
-
});
|
|
1327
|
-
},
|
|
1328
|
-
catch: (e) => {
|
|
1329
|
-
if (e instanceof OAuthTokenError) return e;
|
|
1330
|
-
return new OAuthTokenError({
|
|
1331
|
-
message: "Failed to exchange code for token",
|
|
1332
|
-
provider: this.name,
|
|
1333
|
-
error: String(e)
|
|
1334
|
-
});
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
/**
|
|
1339
|
-
* Refresh access token
|
|
1340
|
-
*/
|
|
1341
|
-
refreshToken(refreshToken) {
|
|
1342
|
-
return import_effect22.Effect.tryPromise({
|
|
1343
|
-
try: async () => {
|
|
1344
|
-
const response = await fetch(this.tokenUrl, {
|
|
1345
|
-
method: "POST",
|
|
1346
|
-
headers: {
|
|
1347
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
1348
|
-
Accept: "application/json"
|
|
1349
|
-
},
|
|
1350
|
-
body: new URLSearchParams({
|
|
1351
|
-
client_id: this.config.clientId,
|
|
1352
|
-
client_secret: this.config.clientSecret,
|
|
1353
|
-
refresh_token: refreshToken,
|
|
1354
|
-
grant_type: "refresh_token"
|
|
1355
|
-
})
|
|
1356
|
-
});
|
|
1357
|
-
if (!response.ok) {
|
|
1358
|
-
const error = await response.json().catch(() => ({}));
|
|
1359
|
-
throw new OAuthTokenError({
|
|
1360
|
-
message: "Failed to refresh token",
|
|
1361
|
-
provider: this.name,
|
|
1362
|
-
error: error["error"],
|
|
1363
|
-
errorDescription: error["error_description"]
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
const data = await response.json();
|
|
1367
|
-
return SocialToken.make({
|
|
1368
|
-
accessToken: data["access_token"],
|
|
1369
|
-
refreshToken: data["refresh_token"] ?? refreshToken,
|
|
1370
|
-
expiresIn: data["expires_in"],
|
|
1371
|
-
tokenType: data["token_type"],
|
|
1372
|
-
scope: data["scope"]
|
|
1373
|
-
});
|
|
1374
|
-
},
|
|
1375
|
-
catch: (e) => {
|
|
1376
|
-
if (e instanceof OAuthTokenError) return e;
|
|
1377
|
-
return new OAuthTokenError({
|
|
1378
|
-
message: "Failed to refresh token",
|
|
1379
|
-
provider: this.name,
|
|
1380
|
-
error: String(e)
|
|
1381
|
-
});
|
|
1382
|
-
}
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
/**
|
|
1386
|
-
* Get user information
|
|
1387
|
-
*/
|
|
1388
|
-
getUser(token) {
|
|
1389
|
-
return import_effect22.Effect.tryPromise({
|
|
1390
|
-
try: async () => {
|
|
1391
|
-
const response = await fetch(this.userInfoUrl, {
|
|
1392
|
-
headers: {
|
|
1393
|
-
Authorization: `${token.tokenType} ${token.accessToken}`,
|
|
1394
|
-
Accept: "application/json"
|
|
1395
|
-
}
|
|
1396
|
-
});
|
|
1397
|
-
if (!response.ok) {
|
|
1398
|
-
throw new OAuthUserError({
|
|
1399
|
-
message: "Failed to fetch user info",
|
|
1400
|
-
provider: this.name
|
|
1401
|
-
});
|
|
1402
|
-
}
|
|
1403
|
-
const data = await response.json();
|
|
1404
|
-
return this.parseUser(data);
|
|
1405
|
-
},
|
|
1406
|
-
catch: (e) => {
|
|
1407
|
-
if (e instanceof OAuthUserError) return e;
|
|
1408
|
-
return new OAuthUserError({
|
|
1409
|
-
message: "Failed to fetch user info",
|
|
1410
|
-
provider: this.name,
|
|
1411
|
-
cause: e
|
|
1412
|
-
});
|
|
1413
|
-
}
|
|
1414
|
-
});
|
|
1415
|
-
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Create a builder for fluent API
|
|
1418
|
-
*/
|
|
1419
|
-
builder() {
|
|
1420
|
-
let additionalScopes = [];
|
|
1421
|
-
let isStateless = false;
|
|
1422
|
-
let storedState = null;
|
|
1423
|
-
const provider = this;
|
|
1424
|
-
const builder = {
|
|
1425
|
-
scopes: (scopes) => {
|
|
1426
|
-
additionalScopes = scopes;
|
|
1427
|
-
return builder;
|
|
1428
|
-
},
|
|
1429
|
-
stateless: () => {
|
|
1430
|
-
isStateless = true;
|
|
1431
|
-
return builder;
|
|
1432
|
-
},
|
|
1433
|
-
redirect: () => import_effect22.Effect.sync(() => {
|
|
1434
|
-
storedState = crypto.randomUUID();
|
|
1435
|
-
return provider.getAuthorizationUrl(additionalScopes, storedState);
|
|
1436
|
-
}),
|
|
1437
|
-
user: (code, state) => import_effect22.Effect.gen(function* () {
|
|
1438
|
-
if (!isStateless && storedState && state !== storedState) {
|
|
1439
|
-
return yield* import_effect22.Effect.fail(
|
|
1440
|
-
new OAuthStateMismatchError({
|
|
1441
|
-
message: "OAuth state mismatch",
|
|
1442
|
-
provider: provider.name
|
|
1443
|
-
})
|
|
1444
|
-
);
|
|
1445
|
-
}
|
|
1446
|
-
const token = yield* provider.getAccessToken(code);
|
|
1447
|
-
const user = yield* provider.getUser(token);
|
|
1448
|
-
return { user, token };
|
|
1449
|
-
})
|
|
1450
|
-
};
|
|
1451
|
-
return builder;
|
|
1452
|
-
}
|
|
1453
|
-
};
|
|
1454
|
-
|
|
1455
|
-
// libs/auth/social/src/lib/providers/GithubProvider.ts
|
|
1456
|
-
var GithubProvider = class extends AbstractProvider {
|
|
1457
|
-
name = "github";
|
|
1458
|
-
get authorizationUrl() {
|
|
1459
|
-
return "https://github.com/login/oauth/authorize";
|
|
1460
|
-
}
|
|
1461
|
-
get tokenUrl() {
|
|
1462
|
-
return "https://github.com/login/oauth/access_token";
|
|
1463
|
-
}
|
|
1464
|
-
get userInfoUrl() {
|
|
1465
|
-
return "https://api.github.com/user";
|
|
1466
|
-
}
|
|
1467
|
-
get defaultScopes() {
|
|
1468
|
-
return ["user:email"];
|
|
1469
|
-
}
|
|
1470
|
-
parseUser(data) {
|
|
1471
|
-
return SocialUser.make("github", {
|
|
1472
|
-
id: String(data["id"]),
|
|
1473
|
-
email: data["email"],
|
|
1474
|
-
name: data["name"],
|
|
1475
|
-
nickname: data["login"],
|
|
1476
|
-
avatar: data["avatar_url"],
|
|
1477
|
-
raw: data
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
var createGithubProvider = (config) => {
|
|
1482
|
-
return new GithubProvider(config);
|
|
1483
|
-
};
|
|
1484
|
-
|
|
1485
|
-
// libs/auth/social/src/lib/providers/GoogleProvider.ts
|
|
1486
|
-
var GoogleProvider = class extends AbstractProvider {
|
|
1487
|
-
name = "google";
|
|
1488
|
-
get authorizationUrl() {
|
|
1489
|
-
return "https://accounts.google.com/o/oauth2/v2/auth";
|
|
1490
|
-
}
|
|
1491
|
-
get tokenUrl() {
|
|
1492
|
-
return "https://oauth2.googleapis.com/token";
|
|
1493
|
-
}
|
|
1494
|
-
get userInfoUrl() {
|
|
1495
|
-
return "https://www.googleapis.com/oauth2/v2/userinfo";
|
|
1496
|
-
}
|
|
1497
|
-
get defaultScopes() {
|
|
1498
|
-
return ["openid", "email", "profile"];
|
|
1499
|
-
}
|
|
1500
|
-
parseUser(data) {
|
|
1501
|
-
return SocialUser.make("google", {
|
|
1502
|
-
id: data["id"],
|
|
1503
|
-
email: data["email"],
|
|
1504
|
-
name: data["name"],
|
|
1505
|
-
firstName: data["given_name"],
|
|
1506
|
-
lastName: data["family_name"],
|
|
1507
|
-
avatar: data["picture"],
|
|
1508
|
-
raw: data
|
|
1509
|
-
});
|
|
1510
|
-
}
|
|
1511
|
-
/**
|
|
1512
|
-
* Override to add Google-specific parameters
|
|
1513
|
-
*/
|
|
1514
|
-
getAuthorizationUrl(scopes, state) {
|
|
1515
|
-
const allScopes = [.../* @__PURE__ */ new Set([...this.defaultScopes, ...scopes])];
|
|
1516
|
-
const params = new URLSearchParams({
|
|
1517
|
-
client_id: this.config.clientId,
|
|
1518
|
-
redirect_uri: this.config.redirectUri,
|
|
1519
|
-
response_type: "code",
|
|
1520
|
-
scope: allScopes.join(" "),
|
|
1521
|
-
state,
|
|
1522
|
-
access_type: "offline",
|
|
1523
|
-
// Get refresh token
|
|
1524
|
-
prompt: "consent"
|
|
1525
|
-
// Force consent screen
|
|
1526
|
-
});
|
|
1527
|
-
return `${this.authorizationUrl}?${params.toString()}`;
|
|
1528
|
-
}
|
|
1529
|
-
};
|
|
1530
|
-
var createGoogleProvider = (config) => {
|
|
1531
|
-
return new GoogleProvider(config);
|
|
1532
|
-
};
|
|
1533
|
-
|
|
1534
|
-
// libs/auth/social/src/lib/services/Social.ts
|
|
1535
|
-
var import_effect23 = require("effect");
|
|
1536
|
-
var SocialTag = class extends import_effect23.Context.Tag("@gello/auth/Social")() {
|
|
1537
|
-
};
|
|
1538
|
-
var builtInProviders = {
|
|
1539
|
-
github: (config) => new GithubProvider(config),
|
|
1540
|
-
google: (config) => new GoogleProvider(config)
|
|
1541
|
-
};
|
|
1542
|
-
var makeSocialService = (config, customProviders = {}) => {
|
|
1543
|
-
const allFactories = { ...builtInProviders, ...customProviders };
|
|
1544
|
-
const providerInstances = /* @__PURE__ */ new Map();
|
|
1545
|
-
const getProvider = (name) => {
|
|
1546
|
-
let provider = providerInstances.get(name);
|
|
1547
|
-
if (provider) return provider;
|
|
1548
|
-
const providerConfig = config.providers[name];
|
|
1549
|
-
if (!providerConfig) {
|
|
1550
|
-
throw new OAuthProviderNotFoundError({
|
|
1551
|
-
message: `OAuth provider "${name}" is not configured`,
|
|
1552
|
-
provider: name
|
|
1553
|
-
});
|
|
1554
|
-
}
|
|
1555
|
-
const factory = allFactories[name];
|
|
1556
|
-
if (!factory) {
|
|
1557
|
-
throw new OAuthProviderNotFoundError({
|
|
1558
|
-
message: `OAuth provider "${name}" is not supported`,
|
|
1559
|
-
provider: name
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
provider = factory(providerConfig);
|
|
1563
|
-
providerInstances.set(name, provider);
|
|
1564
|
-
return provider;
|
|
1565
|
-
};
|
|
1566
|
-
return {
|
|
1567
|
-
driver: (name) => {
|
|
1568
|
-
const provider = getProvider(name);
|
|
1569
|
-
if ("builder" in provider && typeof provider.builder === "function") {
|
|
1570
|
-
return provider.builder();
|
|
1571
|
-
}
|
|
1572
|
-
throw new Error(`Provider "${name}" does not support the builder pattern`);
|
|
1573
|
-
},
|
|
1574
|
-
hasProvider: (name) => {
|
|
1575
|
-
return name in config.providers && name in allFactories;
|
|
1576
|
-
},
|
|
1577
|
-
providers: () => {
|
|
1578
|
-
return Object.keys(config.providers).filter((name) => name in allFactories);
|
|
1579
|
-
}
|
|
1580
|
-
};
|
|
1581
|
-
};
|
|
1582
|
-
var SocialLive = (config, customProviders) => import_effect23.Layer.succeed(SocialTag, makeSocialService(config, customProviders));
|
|
1583
|
-
var Social = {
|
|
1584
|
-
driver: (name) => import_effect23.Effect.gen(function* () {
|
|
1585
|
-
const social = yield* SocialTag;
|
|
1586
|
-
return social.driver(name);
|
|
1587
|
-
}),
|
|
1588
|
-
hasProvider: (name) => import_effect23.Effect.gen(function* () {
|
|
1589
|
-
const social = yield* SocialTag;
|
|
1590
|
-
return social.hasProvider(name);
|
|
1591
|
-
}),
|
|
1592
|
-
providers: () => import_effect23.Effect.gen(function* () {
|
|
1593
|
-
const social = yield* SocialTag;
|
|
1594
|
-
return social.providers();
|
|
1595
|
-
})
|
|
1596
|
-
};
|
|
1597
|
-
|
|
1598
|
-
// libs/mail/templates/src/lib/theme/defaultTheme.ts
|
|
1599
|
-
var defaultTheme = {
|
|
1600
|
-
brand: {
|
|
1601
|
-
name: "My App",
|
|
1602
|
-
logoWidth: 120
|
|
1603
|
-
},
|
|
1604
|
-
colors: {
|
|
1605
|
-
primary: "#3b82f6",
|
|
1606
|
-
// Blue 500
|
|
1607
|
-
secondary: "#64748b",
|
|
1608
|
-
// Slate 500
|
|
1609
|
-
background: "#ffffff",
|
|
1610
|
-
surface: "#f8fafc",
|
|
1611
|
-
// Slate 50
|
|
1612
|
-
text: "#1e293b",
|
|
1613
|
-
// Slate 800
|
|
1614
|
-
muted: "#94a3b8",
|
|
1615
|
-
// Slate 400
|
|
1616
|
-
border: "#e2e8f0",
|
|
1617
|
-
// Slate 200
|
|
1618
|
-
success: "#22c55e",
|
|
1619
|
-
// Green 500
|
|
1620
|
-
warning: "#f59e0b",
|
|
1621
|
-
// Amber 500
|
|
1622
|
-
error: "#ef4444"
|
|
1623
|
-
// Red 500
|
|
1624
|
-
},
|
|
1625
|
-
fonts: {
|
|
1626
|
-
heading: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
|
|
1627
|
-
body: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
|
|
1628
|
-
mono: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace"
|
|
1629
|
-
},
|
|
1630
|
-
layout: {
|
|
1631
|
-
maxWidth: 600,
|
|
1632
|
-
padding: 24,
|
|
1633
|
-
borderRadius: 8
|
|
1634
|
-
}
|
|
1635
|
-
};
|
|
1636
|
-
|
|
1637
|
-
// libs/mail/templates/src/lib/theme/ThemeProvider.tsx
|
|
1638
|
-
var React = __toESM(require("react"), 1);
|
|
1639
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1640
|
-
var ThemeContext = React.createContext(defaultTheme);
|
|
1641
|
-
function useMailTheme() {
|
|
1642
|
-
return React.useContext(ThemeContext);
|
|
1643
|
-
}
|
|
1644
|
-
function ThemeProvider({ theme, children }) {
|
|
1645
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: theme, children });
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
// libs/mail/templates/src/lib/components/layout/BaseLayout.tsx
|
|
1649
|
-
var import_components = require("@react-email/components");
|
|
1650
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1651
|
-
function BaseLayout({ theme, preview, children }) {
|
|
1652
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeProvider, { theme, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_components.Html, { children: [
|
|
1653
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Head, {}),
|
|
1654
|
-
preview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { display: "none" }, children: preview }),
|
|
1655
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Body, { style: bodyStyle(theme), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_components.Container, { style: containerStyle(theme), children: [
|
|
1656
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Header, {}),
|
|
1657
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Section, { style: contentStyle(theme), children }),
|
|
1658
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Footer, {})
|
|
1659
|
-
] }) })
|
|
1660
|
-
] }) });
|
|
1661
|
-
}
|
|
1662
|
-
function Header() {
|
|
1663
|
-
const theme = useMailTheme();
|
|
1664
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Section, { style: headerStyle(theme), children: theme.brand.logo ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Link, { href: theme.brand.website, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1665
|
-
import_components.Img,
|
|
1666
|
-
{
|
|
1667
|
-
src: theme.brand.logo,
|
|
1668
|
-
width: theme.brand.logoWidth ?? 120,
|
|
1669
|
-
alt: theme.brand.name,
|
|
1670
|
-
style: { margin: "0 auto" }
|
|
1671
|
-
}
|
|
1672
|
-
) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Text, { style: brandTextStyle(theme), children: theme.brand.name }) });
|
|
1673
|
-
}
|
|
1674
|
-
function Footer() {
|
|
1675
|
-
const theme = useMailTheme();
|
|
1676
|
-
const footer = theme.footer;
|
|
1677
|
-
if (!footer) return null;
|
|
1678
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_components.Section, { style: footerStyle(theme), children: [
|
|
1679
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Hr, { style: dividerStyle(theme) }),
|
|
1680
|
-
footer.company && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Text, { style: footerTextStyle(theme), children: footer.company }),
|
|
1681
|
-
footer.address && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Text, { style: footerTextStyle(theme), children: footer.address }),
|
|
1682
|
-
footer.socialLinks && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_components.Section, { style: { textAlign: "center", marginTop: "16px" }, children: [
|
|
1683
|
-
footer.socialLinks.twitter && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Link, { href: footer.socialLinks.twitter, style: socialLinkStyle(theme), children: "Twitter" }),
|
|
1684
|
-
footer.socialLinks.facebook && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Link, { href: footer.socialLinks.facebook, style: socialLinkStyle(theme), children: "Facebook" }),
|
|
1685
|
-
footer.socialLinks.linkedin && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Link, { href: footer.socialLinks.linkedin, style: socialLinkStyle(theme), children: "LinkedIn" }),
|
|
1686
|
-
footer.socialLinks.github && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Link, { href: footer.socialLinks.github, style: socialLinkStyle(theme), children: "GitHub" })
|
|
1687
|
-
] }),
|
|
1688
|
-
footer.unsubscribeUrl && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Text, { style: unsubscribeStyle(theme), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components.Link, { href: footer.unsubscribeUrl, style: { color: theme.colors.muted }, children: "Unsubscribe" }) })
|
|
1689
|
-
] });
|
|
1690
|
-
}
|
|
1691
|
-
function bodyStyle(theme) {
|
|
1692
|
-
return {
|
|
1693
|
-
backgroundColor: theme.colors.surface,
|
|
1694
|
-
fontFamily: theme.fonts.body,
|
|
1695
|
-
margin: 0,
|
|
1696
|
-
padding: "40px 0"
|
|
1697
|
-
};
|
|
1698
|
-
}
|
|
1699
|
-
function containerStyle(theme) {
|
|
1700
|
-
return {
|
|
1701
|
-
maxWidth: `${theme.layout.maxWidth}px`,
|
|
1702
|
-
margin: "0 auto",
|
|
1703
|
-
backgroundColor: theme.colors.background,
|
|
1704
|
-
borderRadius: `${theme.layout.borderRadius}px`,
|
|
1705
|
-
overflow: "hidden"
|
|
1706
|
-
};
|
|
1707
|
-
}
|
|
1708
|
-
function headerStyle(theme) {
|
|
1709
|
-
return {
|
|
1710
|
-
padding: `${theme.layout.padding}px`,
|
|
1711
|
-
textAlign: "center",
|
|
1712
|
-
borderBottom: `1px solid ${theme.colors.border}`
|
|
1713
|
-
};
|
|
1714
|
-
}
|
|
1715
|
-
function brandTextStyle(theme) {
|
|
1716
|
-
return {
|
|
1717
|
-
fontSize: "24px",
|
|
1718
|
-
fontWeight: "bold",
|
|
1719
|
-
color: theme.colors.text,
|
|
1720
|
-
fontFamily: theme.fonts.heading,
|
|
1721
|
-
margin: 0
|
|
1722
|
-
};
|
|
1723
|
-
}
|
|
1724
|
-
function contentStyle(theme) {
|
|
1725
|
-
return {
|
|
1726
|
-
padding: `${theme.layout.padding}px`
|
|
1727
|
-
};
|
|
1728
|
-
}
|
|
1729
|
-
function footerStyle(theme) {
|
|
1730
|
-
return {
|
|
1731
|
-
padding: `${theme.layout.padding}px`,
|
|
1732
|
-
textAlign: "center"
|
|
1733
|
-
};
|
|
1734
|
-
}
|
|
1735
|
-
function footerTextStyle(theme) {
|
|
1736
|
-
return {
|
|
1737
|
-
fontSize: "12px",
|
|
1738
|
-
color: theme.colors.muted,
|
|
1739
|
-
margin: "4px 0",
|
|
1740
|
-
lineHeight: "1.5"
|
|
1741
|
-
};
|
|
1742
|
-
}
|
|
1743
|
-
function dividerStyle(theme) {
|
|
1744
|
-
return {
|
|
1745
|
-
borderColor: theme.colors.border,
|
|
1746
|
-
borderTop: "none",
|
|
1747
|
-
marginBottom: "16px"
|
|
1748
|
-
};
|
|
1749
|
-
}
|
|
1750
|
-
function socialLinkStyle(theme) {
|
|
1751
|
-
return {
|
|
1752
|
-
color: theme.colors.muted,
|
|
1753
|
-
fontSize: "12px",
|
|
1754
|
-
marginRight: "16px",
|
|
1755
|
-
textDecoration: "none"
|
|
1756
|
-
};
|
|
1757
|
-
}
|
|
1758
|
-
function unsubscribeStyle(theme) {
|
|
1759
|
-
return {
|
|
1760
|
-
fontSize: "11px",
|
|
1761
|
-
color: theme.colors.muted,
|
|
1762
|
-
marginTop: "16px"
|
|
1763
|
-
};
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
// libs/mail/templates/src/lib/components/layout/Section.tsx
|
|
1767
|
-
var import_components2 = require("@react-email/components");
|
|
1768
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1769
|
-
function Section2({ children, padding = true, style }) {
|
|
1770
|
-
const theme = useMailTheme();
|
|
1771
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1772
|
-
import_components2.Section,
|
|
1773
|
-
{
|
|
1774
|
-
style: {
|
|
1775
|
-
padding: padding ? `${theme.layout.padding / 2}px 0` : void 0,
|
|
1776
|
-
...style
|
|
1777
|
-
},
|
|
1778
|
-
children
|
|
1779
|
-
}
|
|
1780
|
-
);
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
// libs/mail/templates/src/lib/components/layout/Card.tsx
|
|
1784
|
-
var import_components3 = require("@react-email/components");
|
|
1785
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1786
|
-
function Card({ children, style }) {
|
|
1787
|
-
const theme = useMailTheme();
|
|
1788
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1789
|
-
import_components3.Section,
|
|
1790
|
-
{
|
|
1791
|
-
style: {
|
|
1792
|
-
backgroundColor: theme.colors.surface,
|
|
1793
|
-
borderRadius: `${theme.layout.borderRadius}px`,
|
|
1794
|
-
padding: `${theme.layout.padding}px`,
|
|
1795
|
-
border: `1px solid ${theme.colors.border}`,
|
|
1796
|
-
margin: "16px 0",
|
|
1797
|
-
...style
|
|
1798
|
-
},
|
|
1799
|
-
children
|
|
1800
|
-
}
|
|
1801
|
-
);
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
// libs/mail/templates/src/lib/components/layout/Divider.tsx
|
|
1805
|
-
var import_components4 = require("@react-email/components");
|
|
1806
|
-
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1807
|
-
function Divider({ style }) {
|
|
1808
|
-
const theme = useMailTheme();
|
|
1809
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1810
|
-
import_components4.Hr,
|
|
1811
|
-
{
|
|
1812
|
-
style: {
|
|
1813
|
-
borderColor: theme.colors.border,
|
|
1814
|
-
borderTop: "none",
|
|
1815
|
-
margin: "24px 0",
|
|
1816
|
-
...style
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
);
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
// libs/mail/templates/src/lib/components/layout/Spacer.tsx
|
|
1823
|
-
var import_components5 = require("@react-email/components");
|
|
1824
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1825
|
-
|
|
1826
|
-
// libs/mail/templates/src/lib/components/typography/Heading.tsx
|
|
1827
|
-
var import_components6 = require("@react-email/components");
|
|
1828
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1829
|
-
var fontSizes = {
|
|
1830
|
-
h1: 28,
|
|
1831
|
-
h2: 24,
|
|
1832
|
-
h3: 20,
|
|
1833
|
-
h4: 18,
|
|
1834
|
-
h5: 16,
|
|
1835
|
-
h6: 14
|
|
1836
|
-
};
|
|
1837
|
-
var margins = {
|
|
1838
|
-
h1: "0 0 16px 0",
|
|
1839
|
-
h2: "24px 0 12px 0",
|
|
1840
|
-
h3: "20px 0 10px 0",
|
|
1841
|
-
h4: "16px 0 8px 0",
|
|
1842
|
-
h5: "12px 0 6px 0",
|
|
1843
|
-
h6: "8px 0 4px 0"
|
|
1844
|
-
};
|
|
1845
|
-
function Heading({ as = "h1", children, style }) {
|
|
1846
|
-
const theme = useMailTheme();
|
|
1847
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1848
|
-
import_components6.Heading,
|
|
1849
|
-
{
|
|
1850
|
-
as,
|
|
1851
|
-
style: {
|
|
1852
|
-
fontFamily: theme.fonts.heading,
|
|
1853
|
-
fontSize: `${fontSizes[as]}px`,
|
|
1854
|
-
fontWeight: "600",
|
|
1855
|
-
color: theme.colors.text,
|
|
1856
|
-
margin: margins[as],
|
|
1857
|
-
lineHeight: "1.3",
|
|
1858
|
-
...style
|
|
1859
|
-
},
|
|
1860
|
-
children
|
|
1861
|
-
}
|
|
1862
|
-
);
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
// libs/mail/templates/src/lib/components/typography/Text.tsx
|
|
1866
|
-
var import_components7 = require("@react-email/components");
|
|
1867
|
-
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1868
|
-
function Text2({
|
|
1869
|
-
children,
|
|
1870
|
-
muted = false,
|
|
1871
|
-
small = false,
|
|
1872
|
-
center = false,
|
|
1873
|
-
style
|
|
1874
|
-
}) {
|
|
1875
|
-
const theme = useMailTheme();
|
|
1876
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1877
|
-
import_components7.Text,
|
|
1878
|
-
{
|
|
1879
|
-
style: {
|
|
1880
|
-
fontFamily: theme.fonts.body,
|
|
1881
|
-
fontSize: small ? "14px" : "16px",
|
|
1882
|
-
color: muted ? theme.colors.muted : theme.colors.text,
|
|
1883
|
-
lineHeight: "1.6",
|
|
1884
|
-
margin: "0 0 16px 0",
|
|
1885
|
-
textAlign: center ? "center" : void 0,
|
|
1886
|
-
...style
|
|
1887
|
-
},
|
|
1888
|
-
children
|
|
1889
|
-
}
|
|
1890
|
-
);
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
|
-
// libs/mail/templates/src/lib/components/typography/Link.tsx
|
|
1894
|
-
var import_components8 = require("@react-email/components");
|
|
1895
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1896
|
-
|
|
1897
|
-
// libs/mail/templates/src/lib/components/typography/Code.tsx
|
|
1898
|
-
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1899
|
-
|
|
1900
|
-
// libs/mail/templates/src/lib/components/actions/Button.tsx
|
|
1901
|
-
var import_components9 = require("@react-email/components");
|
|
1902
|
-
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1903
|
-
function Button({
|
|
1904
|
-
href,
|
|
1905
|
-
children,
|
|
1906
|
-
variant = "primary",
|
|
1907
|
-
size = "md",
|
|
1908
|
-
fullWidth = false,
|
|
1909
|
-
style
|
|
1910
|
-
}) {
|
|
1911
|
-
const theme = useMailTheme();
|
|
1912
|
-
const sizes = {
|
|
1913
|
-
sm: { padding: "8px 16px", fontSize: "14px" },
|
|
1914
|
-
md: { padding: "12px 24px", fontSize: "16px" },
|
|
1915
|
-
lg: { padding: "16px 32px", fontSize: "18px" }
|
|
1916
|
-
};
|
|
1917
|
-
const variants = {
|
|
1918
|
-
primary: {
|
|
1919
|
-
backgroundColor: theme.colors.primary,
|
|
1920
|
-
color: "#ffffff",
|
|
1921
|
-
border: "none"
|
|
1922
|
-
},
|
|
1923
|
-
secondary: {
|
|
1924
|
-
backgroundColor: theme.colors.secondary,
|
|
1925
|
-
color: "#ffffff",
|
|
1926
|
-
border: "none"
|
|
1927
|
-
},
|
|
1928
|
-
outline: {
|
|
1929
|
-
backgroundColor: "transparent",
|
|
1930
|
-
color: theme.colors.primary,
|
|
1931
|
-
border: `2px solid ${theme.colors.primary}`
|
|
1932
|
-
}
|
|
1933
|
-
};
|
|
1934
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1935
|
-
import_components9.Button,
|
|
1936
|
-
{
|
|
1937
|
-
href,
|
|
1938
|
-
style: {
|
|
1939
|
-
display: fullWidth ? "block" : "inline-block",
|
|
1940
|
-
width: fullWidth ? "100%" : void 0,
|
|
1941
|
-
textAlign: "center",
|
|
1942
|
-
fontFamily: theme.fonts.body,
|
|
1943
|
-
fontWeight: "600",
|
|
1944
|
-
textDecoration: "none",
|
|
1945
|
-
borderRadius: `${theme.layout.borderRadius}px`,
|
|
1946
|
-
...sizes[size],
|
|
1947
|
-
...variants[variant],
|
|
1948
|
-
...style
|
|
1949
|
-
},
|
|
1950
|
-
children
|
|
1951
|
-
}
|
|
1952
|
-
);
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
// libs/mail/templates/src/lib/components/data/Badge.tsx
|
|
1956
|
-
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1957
|
-
|
|
1958
|
-
// libs/mail/templates/src/lib/components/data/Alert.tsx
|
|
1959
|
-
var import_components10 = require("@react-email/components");
|
|
1960
|
-
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1961
|
-
function Alert({
|
|
1962
|
-
type = "info",
|
|
1963
|
-
title,
|
|
1964
|
-
children,
|
|
1965
|
-
style
|
|
1966
|
-
}) {
|
|
1967
|
-
const theme = useMailTheme();
|
|
1968
|
-
const types = {
|
|
1969
|
-
info: {
|
|
1970
|
-
bg: "#eff6ff",
|
|
1971
|
-
border: "#3b82f6",
|
|
1972
|
-
title: "#1e40af",
|
|
1973
|
-
text: "#1e3a5f"
|
|
1974
|
-
},
|
|
1975
|
-
success: {
|
|
1976
|
-
bg: "#f0fdf4",
|
|
1977
|
-
border: "#22c55e",
|
|
1978
|
-
title: "#166534",
|
|
1979
|
-
text: "#14532d"
|
|
1980
|
-
},
|
|
1981
|
-
warning: {
|
|
1982
|
-
bg: "#fffbeb",
|
|
1983
|
-
border: "#f59e0b",
|
|
1984
|
-
title: "#92400e",
|
|
1985
|
-
text: "#78350f"
|
|
1986
|
-
},
|
|
1987
|
-
error: {
|
|
1988
|
-
bg: "#fef2f2",
|
|
1989
|
-
border: "#ef4444",
|
|
1990
|
-
title: "#991b1b",
|
|
1991
|
-
text: "#7f1d1d"
|
|
1992
|
-
}
|
|
1993
|
-
};
|
|
1994
|
-
const colors = types[type];
|
|
1995
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1996
|
-
import_components10.Section,
|
|
1997
|
-
{
|
|
1998
|
-
style: {
|
|
1999
|
-
backgroundColor: colors.bg,
|
|
2000
|
-
borderLeft: `4px solid ${colors.border}`,
|
|
2001
|
-
borderRadius: `${theme.layout.borderRadius}px`,
|
|
2002
|
-
padding: "16px",
|
|
2003
|
-
margin: "16px 0",
|
|
2004
|
-
...style
|
|
2005
|
-
},
|
|
2006
|
-
children: [
|
|
2007
|
-
title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2008
|
-
import_components10.Text,
|
|
2009
|
-
{
|
|
2010
|
-
style: {
|
|
2011
|
-
fontFamily: theme.fonts.heading,
|
|
2012
|
-
fontSize: "14px",
|
|
2013
|
-
fontWeight: "600",
|
|
2014
|
-
color: colors.title,
|
|
2015
|
-
margin: "0 0 8px 0"
|
|
2016
|
-
},
|
|
2017
|
-
children: title
|
|
2018
|
-
}
|
|
2019
|
-
),
|
|
2020
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2021
|
-
import_components10.Text,
|
|
2022
|
-
{
|
|
2023
|
-
style: {
|
|
2024
|
-
fontFamily: theme.fonts.body,
|
|
2025
|
-
fontSize: "14px",
|
|
2026
|
-
color: colors.text,
|
|
2027
|
-
margin: 0,
|
|
2028
|
-
lineHeight: "1.5"
|
|
2029
|
-
},
|
|
2030
|
-
children
|
|
2031
|
-
}
|
|
2032
|
-
)
|
|
2033
|
-
]
|
|
2034
|
-
}
|
|
2035
|
-
);
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
// libs/mail/templates/src/lib/engine/ReactEmailEngine.ts
|
|
2039
|
-
var React2 = __toESM(require("react"), 1);
|
|
2040
|
-
var import_effect30 = require("effect");
|
|
2041
|
-
var import_render = require("@react-email/render");
|
|
2042
|
-
|
|
2043
|
-
// libs/mail/core/src/lib/domain/types.ts
|
|
2044
|
-
var import_effect24 = require("effect");
|
|
2045
|
-
var MessageId = import_effect24.Brand.nominal();
|
|
2046
|
-
var EmailAddress = import_effect24.Brand.nominal();
|
|
2047
|
-
var Address = {
|
|
2048
|
-
make: (email, name) => ({
|
|
2049
|
-
email: EmailAddress(email),
|
|
2050
|
-
name
|
|
2051
|
-
}),
|
|
2052
|
-
/**
|
|
2053
|
-
* Format address as string: "Name <email>" or just "email"
|
|
2054
|
-
*/
|
|
2055
|
-
format: (address) => address.name ? `"${address.name}" <${address.email}>` : address.email,
|
|
2056
|
-
/**
|
|
2057
|
-
* Parse string like "Name <email>" into Address
|
|
2058
|
-
*/
|
|
2059
|
-
parse: (str) => {
|
|
2060
|
-
const match = str.match(/^(?:"?([^"]*)"?\s)?<?([^>]+@[^>]+)>?$/);
|
|
2061
|
-
if (!match) return null;
|
|
2062
|
-
return Address.make(match[2].trim(), match[1]?.trim());
|
|
2063
|
-
}
|
|
2064
|
-
};
|
|
2065
|
-
var Message = {
|
|
2066
|
-
/**
|
|
2067
|
-
* Generate a new MessageId
|
|
2068
|
-
*/
|
|
2069
|
-
generateId: () => MessageId(crypto.randomUUID()),
|
|
2070
|
-
/**
|
|
2071
|
-
* Create a new message with defaults
|
|
2072
|
-
*/
|
|
2073
|
-
make: (envelope, content, options) => ({
|
|
2074
|
-
id: Message.generateId(),
|
|
2075
|
-
envelope,
|
|
2076
|
-
content,
|
|
2077
|
-
attachments: options?.attachments ?? [],
|
|
2078
|
-
headers: options?.headers ?? /* @__PURE__ */ new Map(),
|
|
2079
|
-
priority: options?.priority ?? "normal",
|
|
2080
|
-
tags: options?.tags,
|
|
2081
|
-
metadata: options?.metadata,
|
|
2082
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
2083
|
-
})
|
|
2084
|
-
};
|
|
2085
|
-
|
|
2086
|
-
// libs/mail/core/src/lib/domain/errors.ts
|
|
2087
|
-
var import_effect25 = require("effect");
|
|
2088
|
-
var MailError = class extends import_effect25.Data.TaggedError("MailError") {
|
|
2089
|
-
};
|
|
2090
|
-
var MailConnectionError = class extends import_effect25.Data.TaggedError("MailConnectionError") {
|
|
2091
|
-
};
|
|
2092
|
-
var MailValidationError = class extends import_effect25.Data.TaggedError("MailValidationError") {
|
|
2093
|
-
};
|
|
2094
|
-
var MailDeliveryError = class extends import_effect25.Data.TaggedError("MailDeliveryError") {
|
|
2095
|
-
};
|
|
2096
|
-
var TemplateError = class extends import_effect25.Data.TaggedError("TemplateError") {
|
|
2097
|
-
};
|
|
2098
|
-
var TemplateNotFoundError = class extends import_effect25.Data.TaggedError("TemplateNotFoundError") {
|
|
2099
|
-
};
|
|
2100
|
-
var AttachmentError = class extends import_effect25.Data.TaggedError("AttachmentError") {
|
|
2101
|
-
};
|
|
2102
|
-
var MailConfigError = class extends import_effect25.Data.TaggedError("MailConfigError") {
|
|
2103
|
-
};
|
|
2104
|
-
|
|
2105
|
-
// libs/mail/core/src/lib/domain/mailable.ts
|
|
2106
|
-
var import_effect26 = require("effect");
|
|
2107
|
-
var BaseMailable = class {
|
|
2108
|
-
state = {
|
|
2109
|
-
to: [],
|
|
2110
|
-
cc: [],
|
|
2111
|
-
bcc: [],
|
|
2112
|
-
attachments: [],
|
|
2113
|
-
headers: /* @__PURE__ */ new Map(),
|
|
2114
|
-
priority: "normal",
|
|
2115
|
-
tags: [],
|
|
2116
|
-
metadata: {}
|
|
2117
|
-
};
|
|
2118
|
-
/**
|
|
2119
|
-
* Build the final message
|
|
2120
|
-
*/
|
|
2121
|
-
build() {
|
|
2122
|
-
return import_effect26.Effect.gen(this, function* () {
|
|
2123
|
-
const content = yield* this.getContent(this.state.data);
|
|
2124
|
-
const envelope = {
|
|
2125
|
-
from: this.state.from ?? Address.make("noreply@example.com"),
|
|
2126
|
-
to: this.state.to,
|
|
2127
|
-
cc: this.state.cc.length > 0 ? this.state.cc : void 0,
|
|
2128
|
-
bcc: this.state.bcc.length > 0 ? this.state.bcc : void 0,
|
|
2129
|
-
replyTo: this.state.replyTo
|
|
2130
|
-
};
|
|
2131
|
-
return Message.make(envelope, content, {
|
|
2132
|
-
attachments: this.state.attachments,
|
|
2133
|
-
headers: this.state.headers,
|
|
2134
|
-
priority: this.state.priority,
|
|
2135
|
-
tags: this.state.tags.length > 0 ? this.state.tags : void 0,
|
|
2136
|
-
metadata: Object.keys(this.state.metadata).length > 0 ? this.state.metadata : void 0
|
|
2137
|
-
});
|
|
2138
|
-
});
|
|
2139
|
-
}
|
|
2140
|
-
to(address) {
|
|
2141
|
-
const addresses = Array.isArray(address) ? address : [address];
|
|
2142
|
-
this.state.to.push(...addresses.map(this.normalizeAddress));
|
|
2143
|
-
return this;
|
|
2144
|
-
}
|
|
2145
|
-
cc(address) {
|
|
2146
|
-
const addresses = Array.isArray(address) ? address : [address];
|
|
2147
|
-
this.state.cc.push(...addresses.map(this.normalizeAddress));
|
|
2148
|
-
return this;
|
|
2149
|
-
}
|
|
2150
|
-
bcc(address) {
|
|
2151
|
-
const addresses = Array.isArray(address) ? address : [address];
|
|
2152
|
-
this.state.bcc.push(...addresses.map(this.normalizeAddress));
|
|
2153
|
-
return this;
|
|
2154
|
-
}
|
|
2155
|
-
from(address, name) {
|
|
2156
|
-
this.state.from = typeof address === "string" ? Address.make(address, name) : address;
|
|
2157
|
-
return this;
|
|
2158
|
-
}
|
|
2159
|
-
replyTo(address, name) {
|
|
2160
|
-
this.state.replyTo = typeof address === "string" ? Address.make(address, name) : address;
|
|
2161
|
-
return this;
|
|
2162
|
-
}
|
|
2163
|
-
subject(text) {
|
|
2164
|
-
this.state.subject = text;
|
|
2165
|
-
return this;
|
|
2166
|
-
}
|
|
2167
|
-
with(data) {
|
|
2168
|
-
this.state.data = data;
|
|
2169
|
-
return this;
|
|
2170
|
-
}
|
|
2171
|
-
attach(attachment) {
|
|
2172
|
-
this.state.attachments.push(attachment);
|
|
2173
|
-
return this;
|
|
2174
|
-
}
|
|
2175
|
-
header(name, value) {
|
|
2176
|
-
this.state.headers.set(name, value);
|
|
2177
|
-
return this;
|
|
2178
|
-
}
|
|
2179
|
-
priority(level) {
|
|
2180
|
-
this.state.priority = level;
|
|
2181
|
-
return this;
|
|
2182
|
-
}
|
|
2183
|
-
tag(tag) {
|
|
2184
|
-
this.state.tags.push(tag);
|
|
2185
|
-
return this;
|
|
2186
|
-
}
|
|
2187
|
-
metadata(key, value) {
|
|
2188
|
-
this.state.metadata[key] = value;
|
|
2189
|
-
return this;
|
|
2190
|
-
}
|
|
2191
|
-
/**
|
|
2192
|
-
* Get the template data
|
|
2193
|
-
*/
|
|
2194
|
-
getData() {
|
|
2195
|
-
return this.state.data;
|
|
2196
|
-
}
|
|
2197
|
-
/**
|
|
2198
|
-
* Get subject from state or abstract method
|
|
2199
|
-
*/
|
|
2200
|
-
getSubjectText() {
|
|
2201
|
-
return this.state.subject ?? this.getSubject();
|
|
2202
|
-
}
|
|
2203
|
-
normalizeAddress(addr) {
|
|
2204
|
-
if (typeof addr === "string") {
|
|
2205
|
-
return Address.parse(addr) ?? Address.make(addr);
|
|
2206
|
-
}
|
|
2207
|
-
return addr;
|
|
2208
|
-
}
|
|
2209
|
-
};
|
|
2210
|
-
|
|
2211
|
-
// libs/mail/core/src/lib/ports/MailDriver.ts
|
|
2212
|
-
var import_effect27 = require("effect");
|
|
2213
|
-
var MailDriverTag = class extends import_effect27.Context.Tag("@gello/MailDriver")() {
|
|
2214
|
-
};
|
|
2215
|
-
|
|
2216
|
-
// libs/mail/core/src/lib/ports/TemplateEngine.ts
|
|
2217
|
-
var import_effect28 = require("effect");
|
|
2218
|
-
var TemplateEngineTag = class extends import_effect28.Context.Tag("@gello/TemplateEngine")() {
|
|
2219
|
-
};
|
|
2220
|
-
|
|
2221
|
-
// libs/mail/core/src/lib/services/Mail.ts
|
|
2222
|
-
var import_effect29 = require("effect");
|
|
2223
|
-
var MailTag = class extends import_effect29.Context.Tag("@gello/Mail")() {
|
|
2224
|
-
};
|
|
2225
|
-
var makeMailService = import_effect29.Effect.gen(function* () {
|
|
2226
|
-
const driver = yield* MailDriverTag;
|
|
2227
|
-
const templateEngine = yield* import_effect29.Effect.serviceOption(TemplateEngineTag);
|
|
2228
|
-
const resolveContent = (content) => {
|
|
2229
|
-
if (!content.template) {
|
|
2230
|
-
return import_effect29.Effect.succeed(content);
|
|
2231
|
-
}
|
|
2232
|
-
return (0, import_effect29.pipe)(
|
|
2233
|
-
templateEngine,
|
|
2234
|
-
import_effect29.Option.match({
|
|
2235
|
-
onNone: () => (
|
|
2236
|
-
// No template engine, return content as-is
|
|
2237
|
-
import_effect29.Effect.succeed(content)
|
|
2238
|
-
),
|
|
2239
|
-
onSome: (engine) => (0, import_effect29.pipe)(
|
|
2240
|
-
engine.render(content.template.name, content.template.data),
|
|
2241
|
-
import_effect29.Effect.map(({ html, text }) => ({
|
|
2242
|
-
...content,
|
|
2243
|
-
html: html ?? content.html,
|
|
2244
|
-
text: text ?? content.text
|
|
2245
|
-
}))
|
|
2246
|
-
)
|
|
2247
|
-
})
|
|
2248
|
-
);
|
|
2249
|
-
};
|
|
2250
|
-
const normalizeAddresses = (input) => {
|
|
2251
|
-
const arr = Array.isArray(input) ? input : [input];
|
|
2252
|
-
return arr.map(
|
|
2253
|
-
(a) => typeof a === "string" ? Address.parse(a) ?? Address.make(a) : a
|
|
2254
|
-
);
|
|
2255
|
-
};
|
|
2256
|
-
const service = {
|
|
2257
|
-
send: (mailable) => import_effect29.Effect.gen(function* () {
|
|
2258
|
-
const message = yield* mailable.build();
|
|
2259
|
-
const resolvedContent = yield* resolveContent(message.content);
|
|
2260
|
-
const finalMessage = { ...message, content: resolvedContent };
|
|
2261
|
-
const messageToSend = mailable.beforeSend ? yield* mailable.beforeSend(finalMessage) : finalMessage;
|
|
2262
|
-
const result = yield* driver.send(messageToSend);
|
|
2263
|
-
if (mailable.afterSend) {
|
|
2264
|
-
yield* mailable.afterSend(result);
|
|
2265
|
-
}
|
|
2266
|
-
return result;
|
|
2267
|
-
}),
|
|
2268
|
-
sendRaw: (to, subject, content, options) => import_effect29.Effect.gen(function* () {
|
|
2269
|
-
const toAddresses = normalizeAddresses(to);
|
|
2270
|
-
const contentObj = typeof content === "string" ? { text: content } : content;
|
|
2271
|
-
const envelope = {
|
|
2272
|
-
from: options?.from != null ? typeof options.from === "string" ? Address.parse(options.from) ?? Address.make(options.from) : options.from : Address.make("noreply@example.com"),
|
|
2273
|
-
to: toAddresses,
|
|
2274
|
-
cc: options?.cc ? normalizeAddresses(options.cc) : void 0,
|
|
2275
|
-
bcc: options?.bcc ? normalizeAddresses(options.bcc) : void 0,
|
|
2276
|
-
replyTo: options?.replyTo != null ? typeof options.replyTo === "string" ? Address.parse(options.replyTo) ?? Address.make(options.replyTo) : options.replyTo : void 0
|
|
2277
|
-
};
|
|
2278
|
-
const message = Message.make(
|
|
2279
|
-
envelope,
|
|
2280
|
-
{ subject, ...contentObj },
|
|
2281
|
-
{
|
|
2282
|
-
attachments: options?.attachments,
|
|
2283
|
-
headers: options?.headers ? new Map(Object.entries(options.headers)) : void 0,
|
|
2284
|
-
priority: options?.priority,
|
|
2285
|
-
tags: options?.tags
|
|
2286
|
-
}
|
|
2287
|
-
);
|
|
2288
|
-
return yield* driver.send(message);
|
|
2289
|
-
}),
|
|
2290
|
-
driver: () => driver,
|
|
2291
|
-
mailer: (_driverName) => {
|
|
2292
|
-
return service;
|
|
2293
|
-
}
|
|
2294
|
-
};
|
|
2295
|
-
return service;
|
|
2296
|
-
});
|
|
2297
|
-
var MailServiceLive = import_effect29.Layer.effect(MailTag, makeMailService);
|
|
2298
|
-
|
|
2299
|
-
// libs/mail/templates/src/lib/engine/ReactEmailEngine.ts
|
|
2300
|
-
var templateRegistry = /* @__PURE__ */ new Map();
|
|
2301
|
-
var renderToHtml = (element) => import_effect30.Effect.tryPromise({
|
|
2302
|
-
try: () => (0, import_render.render)(element, { pretty: true }),
|
|
2303
|
-
catch: (error) => new TemplateError({
|
|
2304
|
-
message: `Failed to render HTML: ${error}`,
|
|
2305
|
-
cause: error
|
|
2306
|
-
})
|
|
2307
|
-
});
|
|
2308
|
-
var renderToText = (element) => import_effect30.Effect.tryPromise({
|
|
2309
|
-
try: () => (0, import_render.render)(element, { plainText: true }),
|
|
2310
|
-
catch: (error) => new TemplateError({
|
|
2311
|
-
message: `Failed to render text: ${error}`,
|
|
2312
|
-
cause: error
|
|
2313
|
-
})
|
|
2314
|
-
});
|
|
2315
|
-
var renderEmail = (element) => import_effect30.Effect.gen(function* () {
|
|
2316
|
-
const html = yield* renderToHtml(element);
|
|
2317
|
-
const text = yield* renderToText(element);
|
|
2318
|
-
return { html, text };
|
|
2319
|
-
});
|
|
2320
|
-
var makeReactEmailEngine = () => ({
|
|
2321
|
-
name: "react-email",
|
|
2322
|
-
render: (template, data) => import_effect30.Effect.gen(function* () {
|
|
2323
|
-
const Template = templateRegistry.get(template);
|
|
2324
|
-
if (!Template) {
|
|
2325
|
-
return yield* import_effect30.Effect.fail(
|
|
2326
|
-
new TemplateNotFoundError({
|
|
2327
|
-
message: `Template "${template}" not found`,
|
|
2328
|
-
template
|
|
2329
|
-
})
|
|
2330
|
-
);
|
|
2331
|
-
}
|
|
2332
|
-
const element = React2.createElement(Template, data);
|
|
2333
|
-
return yield* renderEmail(element);
|
|
2334
|
-
}),
|
|
2335
|
-
compile: (template) => import_effect30.Effect.gen(function* () {
|
|
2336
|
-
const Template = templateRegistry.get(template);
|
|
2337
|
-
if (!Template) {
|
|
2338
|
-
return yield* import_effect30.Effect.fail(
|
|
2339
|
-
new TemplateNotFoundError({
|
|
2340
|
-
message: `Template "${template}" not found`,
|
|
2341
|
-
template
|
|
2342
|
-
})
|
|
2343
|
-
);
|
|
2344
|
-
}
|
|
2345
|
-
return {
|
|
2346
|
-
render: (data) => {
|
|
2347
|
-
const element = React2.createElement(Template, data);
|
|
2348
|
-
return renderEmail(element);
|
|
2349
|
-
}
|
|
2350
|
-
};
|
|
2351
|
-
}),
|
|
2352
|
-
exists: (template) => import_effect30.Effect.succeed(templateRegistry.has(template))
|
|
2353
|
-
});
|
|
2354
|
-
var ReactEmailEngineLive = import_effect30.Layer.succeed(
|
|
2355
|
-
TemplateEngineTag,
|
|
2356
|
-
makeReactEmailEngine()
|
|
2357
|
-
);
|
|
2358
|
-
|
|
2359
|
-
// libs/auth/templates/src/lib/templates/VerifyEmail.tsx
|
|
2360
|
-
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2361
|
-
var VerifyEmail = ({
|
|
2362
|
-
userName,
|
|
2363
|
-
verificationUrl,
|
|
2364
|
-
expiresIn = "60 minutes",
|
|
2365
|
-
appName = "Our App",
|
|
2366
|
-
theme = defaultTheme
|
|
2367
|
-
}) => {
|
|
2368
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(BaseLayout, { theme, preview: `Verify your email address for ${appName}`, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Section2, { children: [
|
|
2369
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Heading, { as: "h1", children: "Verify Your Email Address" }),
|
|
2370
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text2, { children: [
|
|
2371
|
-
"Hi ",
|
|
2372
|
-
userName,
|
|
2373
|
-
","
|
|
2374
|
-
] }),
|
|
2375
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text2, { children: [
|
|
2376
|
-
"Thank you for signing up for ",
|
|
2377
|
-
appName,
|
|
2378
|
-
"! Please verify your email address by clicking the button below."
|
|
2379
|
-
] }),
|
|
2380
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button, { href: verificationUrl, children: "Verify Email Address" }),
|
|
2381
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text2, { muted: true, children: [
|
|
2382
|
-
"This verification link will expire in ",
|
|
2383
|
-
expiresIn,
|
|
2384
|
-
". If you didn't create an account, you can safely ignore this email."
|
|
2385
|
-
] }),
|
|
2386
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Alert, { type: "info", children: [
|
|
2387
|
-
"If the button above doesn't work, copy and paste this URL into your browser: ",
|
|
2388
|
-
verificationUrl
|
|
2389
|
-
] })
|
|
2390
|
-
] }) });
|
|
2391
|
-
};
|
|
2392
|
-
|
|
2393
|
-
// libs/auth/templates/src/lib/templates/ResetPassword.tsx
|
|
2394
|
-
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2395
|
-
var ResetPassword = ({
|
|
2396
|
-
userName,
|
|
2397
|
-
resetUrl,
|
|
2398
|
-
expiresIn = "60 minutes",
|
|
2399
|
-
appName = "Our App",
|
|
2400
|
-
ipAddress,
|
|
2401
|
-
theme = defaultTheme
|
|
2402
|
-
}) => {
|
|
2403
|
-
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(BaseLayout, { theme, preview: `Reset your password for ${appName}`, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Section2, { children: [
|
|
2404
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Heading, { as: "h1", children: "Reset Your Password" }),
|
|
2405
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text2, { children: [
|
|
2406
|
-
"Hi ",
|
|
2407
|
-
userName,
|
|
2408
|
-
","
|
|
2409
|
-
] }),
|
|
2410
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text2, { children: [
|
|
2411
|
-
"We received a request to reset your password for your ",
|
|
2412
|
-
appName,
|
|
2413
|
-
" ",
|
|
2414
|
-
"account. Click the button below to set a new password."
|
|
2415
|
-
] }),
|
|
2416
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { href: resetUrl, children: "Reset Password" }),
|
|
2417
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text2, { muted: true, children: [
|
|
2418
|
-
"This password reset link will expire in ",
|
|
2419
|
-
expiresIn,
|
|
2420
|
-
"."
|
|
2421
|
-
] }),
|
|
2422
|
-
ipAddress && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text2, { muted: true, children: [
|
|
2423
|
-
"This request was made from IP address: ",
|
|
2424
|
-
ipAddress
|
|
2425
|
-
] }),
|
|
2426
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Alert, { type: "warning", children: "If you didn't request a password reset, please ignore this email or contact support if you believe your account has been compromised." }),
|
|
2427
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text2, { muted: true, children: [
|
|
2428
|
-
"If the button above doesn't work, copy and paste this URL into your browser: ",
|
|
2429
|
-
resetUrl
|
|
2430
|
-
] })
|
|
2431
|
-
] }) });
|
|
2432
|
-
};
|
|
2433
|
-
|
|
2434
|
-
// libs/auth/templates/src/lib/templates/WelcomeEmail.tsx
|
|
2435
|
-
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2436
|
-
var WelcomeEmail = ({
|
|
2437
|
-
userName,
|
|
2438
|
-
loginUrl,
|
|
2439
|
-
appName = "Our App",
|
|
2440
|
-
features = [],
|
|
2441
|
-
theme = defaultTheme
|
|
2442
|
-
}) => {
|
|
2443
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BaseLayout, { theme, preview: `Welcome to ${appName}!`, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Section2, { children: [
|
|
2444
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Heading, { as: "h1", children: [
|
|
2445
|
-
"Welcome to ",
|
|
2446
|
-
appName,
|
|
2447
|
-
"!"
|
|
2448
|
-
] }),
|
|
2449
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Text2, { children: [
|
|
2450
|
-
"Hi ",
|
|
2451
|
-
userName,
|
|
2452
|
-
","
|
|
2453
|
-
] }),
|
|
2454
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Text2, { children: [
|
|
2455
|
-
"Thank you for joining ",
|
|
2456
|
-
appName,
|
|
2457
|
-
"! We're excited to have you on board. Your account is now ready to use."
|
|
2458
|
-
] }),
|
|
2459
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Button, { href: loginUrl, children: "Get Started" }),
|
|
2460
|
-
features.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2461
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Divider, {}),
|
|
2462
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Heading, { as: "h2", children: "What you can do:" }),
|
|
2463
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { children: features.map((feature, index) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text2, { children: feature }) }, index)) })
|
|
2464
|
-
] }),
|
|
2465
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Divider, {}),
|
|
2466
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text2, { muted: true, children: "If you have any questions, feel free to reply to this email. We're here to help!" })
|
|
2467
|
-
] }) });
|
|
2468
|
-
};
|
|
2469
|
-
|
|
2470
|
-
// libs/auth/templates/src/lib/templates/NewLogin.tsx
|
|
2471
|
-
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2472
|
-
var NewLogin = ({
|
|
2473
|
-
userName,
|
|
2474
|
-
loginTime,
|
|
2475
|
-
ipAddress,
|
|
2476
|
-
location,
|
|
2477
|
-
device,
|
|
2478
|
-
browser,
|
|
2479
|
-
securityUrl,
|
|
2480
|
-
appName = "Our App",
|
|
2481
|
-
theme = defaultTheme
|
|
2482
|
-
}) => {
|
|
2483
|
-
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BaseLayout, { theme, preview: `New login to your ${appName} account`, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Section2, { children: [
|
|
2484
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Heading, { as: "h1", children: "New Login Detected" }),
|
|
2485
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text2, { children: [
|
|
2486
|
-
"Hi ",
|
|
2487
|
-
userName,
|
|
2488
|
-
","
|
|
2489
|
-
] }),
|
|
2490
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text2, { children: [
|
|
2491
|
-
"We detected a new login to your ",
|
|
2492
|
-
appName,
|
|
2493
|
-
" account. If this was you, you can safely ignore this email."
|
|
2494
|
-
] }),
|
|
2495
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Card, { children: [
|
|
2496
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text2, { children: [
|
|
2497
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "Time:" }),
|
|
2498
|
-
" ",
|
|
2499
|
-
loginTime
|
|
2500
|
-
] }),
|
|
2501
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text2, { children: [
|
|
2502
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "IP Address:" }),
|
|
2503
|
-
" ",
|
|
2504
|
-
ipAddress
|
|
2505
|
-
] }),
|
|
2506
|
-
location && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text2, { children: [
|
|
2507
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "Location:" }),
|
|
2508
|
-
" ",
|
|
2509
|
-
location
|
|
2510
|
-
] }),
|
|
2511
|
-
device && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text2, { children: [
|
|
2512
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "Device:" }),
|
|
2513
|
-
" ",
|
|
2514
|
-
device
|
|
2515
|
-
] }),
|
|
2516
|
-
browser && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Text2, { children: [
|
|
2517
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { children: "Browser:" }),
|
|
2518
|
-
" ",
|
|
2519
|
-
browser
|
|
2520
|
-
] })
|
|
2521
|
-
] }),
|
|
2522
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Alert, { type: "warning", children: "If you didn't sign in, your account may have been compromised. Please change your password immediately and review your account security." }),
|
|
2523
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Button, { href: securityUrl, children: "Review Account Security" }),
|
|
2524
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text2, { muted: true, children: "For your security, we send these notifications whenever there's a new login to your account." })
|
|
2525
|
-
] }) });
|
|
2526
|
-
};
|
|
2527
|
-
|
|
2528
|
-
// libs/auth/templates/src/lib/templates/PasswordChanged.tsx
|
|
2529
|
-
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2530
|
-
var PasswordChanged = ({
|
|
2531
|
-
userName,
|
|
2532
|
-
changedAt,
|
|
2533
|
-
ipAddress,
|
|
2534
|
-
securityUrl,
|
|
2535
|
-
appName = "Our App",
|
|
2536
|
-
theme = defaultTheme
|
|
2537
|
-
}) => {
|
|
2538
|
-
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(BaseLayout, { theme, preview: `Your ${appName} password was changed`, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Section2, { children: [
|
|
2539
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Heading, { as: "h1", children: "Password Changed" }),
|
|
2540
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text2, { children: [
|
|
2541
|
-
"Hi ",
|
|
2542
|
-
userName,
|
|
2543
|
-
","
|
|
2544
|
-
] }),
|
|
2545
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text2, { children: [
|
|
2546
|
-
"Your password for your ",
|
|
2547
|
-
appName,
|
|
2548
|
-
" account was successfully changed on",
|
|
2549
|
-
" ",
|
|
2550
|
-
changedAt,
|
|
2551
|
-
"."
|
|
2552
|
-
] }),
|
|
2553
|
-
ipAddress && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Text2, { muted: true, children: [
|
|
2554
|
-
"This change was made from IP address: ",
|
|
2555
|
-
ipAddress
|
|
2556
|
-
] }),
|
|
2557
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Alert, { type: "warning", children: "If you didn't make this change, your account may have been compromised. Please reset your password immediately and review your account security." }),
|
|
2558
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { href: securityUrl, children: "Review Account Security" }),
|
|
2559
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text2, { muted: true, children: "For your security, we send these notifications whenever your password is changed." })
|
|
2560
|
-
] }) });
|
|
2561
|
-
};
|
|
2562
|
-
|
|
2563
|
-
// libs/auth/templates/src/lib/mailables/VerifyEmailMailable.ts
|
|
2564
|
-
var import_effect31 = require("effect");
|
|
2565
|
-
var VerifyEmailMailable = class extends BaseMailable {
|
|
2566
|
-
getSubject() {
|
|
2567
|
-
const data = this.getData();
|
|
2568
|
-
return `Verify your email address for ${data.appName ?? "Our App"}`;
|
|
2569
|
-
}
|
|
2570
|
-
getContent() {
|
|
2571
|
-
const data = this.getData();
|
|
2572
|
-
return import_effect31.Effect.succeed({
|
|
2573
|
-
subject: this.getSubject(),
|
|
2574
|
-
template: {
|
|
2575
|
-
name: "auth/verify-email",
|
|
2576
|
-
data: {
|
|
2577
|
-
userName: data.userName,
|
|
2578
|
-
verificationUrl: data.verificationUrl,
|
|
2579
|
-
expiresIn: data.expiresIn ?? "60 minutes",
|
|
2580
|
-
appName: data.appName ?? "Our App"
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
});
|
|
2584
|
-
}
|
|
2585
|
-
};
|
|
2586
|
-
var verifyEmail = (data) => {
|
|
2587
|
-
return new VerifyEmailMailable().to(data.email).with(data);
|
|
2588
|
-
};
|
|
2589
|
-
|
|
2590
|
-
// libs/auth/templates/src/lib/mailables/ResetPasswordMailable.ts
|
|
2591
|
-
var import_effect32 = require("effect");
|
|
2592
|
-
var ResetPasswordMailable = class extends BaseMailable {
|
|
2593
|
-
getSubject() {
|
|
2594
|
-
const data = this.getData();
|
|
2595
|
-
return `Reset your password for ${data.appName ?? "Our App"}`;
|
|
2596
|
-
}
|
|
2597
|
-
getContent() {
|
|
2598
|
-
const data = this.getData();
|
|
2599
|
-
return import_effect32.Effect.succeed({
|
|
2600
|
-
subject: this.getSubject(),
|
|
2601
|
-
template: {
|
|
2602
|
-
name: "auth/reset-password",
|
|
2603
|
-
data: {
|
|
2604
|
-
userName: data.userName,
|
|
2605
|
-
resetUrl: data.resetUrl,
|
|
2606
|
-
expiresIn: data.expiresIn ?? "60 minutes",
|
|
2607
|
-
appName: data.appName ?? "Our App",
|
|
2608
|
-
ipAddress: data.ipAddress
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
});
|
|
2612
|
-
}
|
|
2613
|
-
};
|
|
2614
|
-
var resetPassword = (data) => {
|
|
2615
|
-
return new ResetPasswordMailable().to(data.email).with(data);
|
|
2616
|
-
};
|
|
2617
|
-
|
|
2618
|
-
// libs/auth/templates/src/lib/mailables/WelcomeEmailMailable.ts
|
|
2619
|
-
var import_effect33 = require("effect");
|
|
2620
|
-
var WelcomeEmailMailable = class extends BaseMailable {
|
|
2621
|
-
getSubject() {
|
|
2622
|
-
const data = this.getData();
|
|
2623
|
-
return `Welcome to ${data.appName ?? "Our App"}!`;
|
|
2624
|
-
}
|
|
2625
|
-
getContent() {
|
|
2626
|
-
const data = this.getData();
|
|
2627
|
-
return import_effect33.Effect.succeed({
|
|
2628
|
-
subject: this.getSubject(),
|
|
2629
|
-
template: {
|
|
2630
|
-
name: "auth/welcome",
|
|
2631
|
-
data: {
|
|
2632
|
-
userName: data.userName,
|
|
2633
|
-
loginUrl: data.loginUrl,
|
|
2634
|
-
appName: data.appName ?? "Our App",
|
|
2635
|
-
features: data.features ?? []
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
});
|
|
2639
|
-
}
|
|
2640
|
-
};
|
|
2641
|
-
var welcomeEmail = (data) => {
|
|
2642
|
-
return new WelcomeEmailMailable().to(data.email).with(data);
|
|
2643
|
-
};
|
|
2644
|
-
|
|
2645
|
-
// libs/auth/templates/src/lib/mailables/NewLoginMailable.ts
|
|
2646
|
-
var import_effect34 = require("effect");
|
|
2647
|
-
var NewLoginMailable = class extends BaseMailable {
|
|
2648
|
-
getSubject() {
|
|
2649
|
-
const data = this.getData();
|
|
2650
|
-
return `New login to your ${data.appName ?? "Our App"} account`;
|
|
2651
|
-
}
|
|
2652
|
-
getContent() {
|
|
2653
|
-
const data = this.getData();
|
|
2654
|
-
return import_effect34.Effect.succeed({
|
|
2655
|
-
subject: this.getSubject(),
|
|
2656
|
-
template: {
|
|
2657
|
-
name: "auth/new-login",
|
|
2658
|
-
data: {
|
|
2659
|
-
userName: data.userName,
|
|
2660
|
-
loginTime: data.loginTime,
|
|
2661
|
-
ipAddress: data.ipAddress,
|
|
2662
|
-
location: data.location,
|
|
2663
|
-
device: data.device,
|
|
2664
|
-
browser: data.browser,
|
|
2665
|
-
securityUrl: data.securityUrl,
|
|
2666
|
-
appName: data.appName ?? "Our App"
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
});
|
|
2670
|
-
}
|
|
2671
|
-
};
|
|
2672
|
-
var newLogin = (data) => {
|
|
2673
|
-
return new NewLoginMailable().to(data.email).with(data);
|
|
2674
|
-
};
|
|
2675
|
-
|
|
2676
|
-
// libs/auth/testing/src/lib/mocks/MockTokenStore.ts
|
|
2677
|
-
var import_effect35 = require("effect");
|
|
2678
|
-
var makeMockTokenStore = () => {
|
|
2679
|
-
const tokens = /* @__PURE__ */ new Map();
|
|
2680
|
-
return {
|
|
2681
|
-
create: (token) => import_effect35.Effect.sync(() => {
|
|
2682
|
-
tokens.set(token.id, token);
|
|
2683
|
-
return token;
|
|
2684
|
-
}),
|
|
2685
|
-
findByToken: (hashedToken) => import_effect35.Effect.sync(() => {
|
|
2686
|
-
for (const token of tokens.values()) {
|
|
2687
|
-
if (token.token === hashedToken) {
|
|
2688
|
-
return import_effect35.Option.some(token);
|
|
2689
|
-
}
|
|
2690
|
-
}
|
|
2691
|
-
return import_effect35.Option.none();
|
|
2692
|
-
}),
|
|
2693
|
-
findById: (id) => import_effect35.Effect.sync(() => {
|
|
2694
|
-
const token = tokens.get(id);
|
|
2695
|
-
return token ? import_effect35.Option.some(token) : import_effect35.Option.none();
|
|
2696
|
-
}),
|
|
2697
|
-
findByUser: (userId) => import_effect35.Effect.sync(() => {
|
|
2698
|
-
return Array.from(tokens.values()).filter((t) => t.userId === userId);
|
|
2699
|
-
}),
|
|
2700
|
-
updateLastUsed: (id, lastUsedAt) => import_effect35.Effect.sync(() => {
|
|
2701
|
-
const token = tokens.get(id);
|
|
2702
|
-
if (!token) {
|
|
2703
|
-
throw new TokenNotFoundError({
|
|
2704
|
-
message: "Token not found",
|
|
2705
|
-
tokenId: id
|
|
2706
|
-
});
|
|
2707
|
-
}
|
|
2708
|
-
tokens.set(id, { ...token, lastUsedAt });
|
|
2709
|
-
}),
|
|
2710
|
-
delete: (id) => import_effect35.Effect.sync(() => {
|
|
2711
|
-
if (!tokens.has(id)) {
|
|
2712
|
-
throw new TokenNotFoundError({
|
|
2713
|
-
message: "Token not found",
|
|
2714
|
-
tokenId: id
|
|
2715
|
-
});
|
|
2716
|
-
}
|
|
2717
|
-
tokens.delete(id);
|
|
2718
|
-
}),
|
|
2719
|
-
deleteByUser: (userId) => import_effect35.Effect.sync(() => {
|
|
2720
|
-
let count = 0;
|
|
2721
|
-
for (const [id, token] of tokens) {
|
|
2722
|
-
if (token.userId === userId) {
|
|
2723
|
-
tokens.delete(id);
|
|
2724
|
-
count++;
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
return count;
|
|
2728
|
-
}),
|
|
2729
|
-
deleteExpired: () => import_effect35.Effect.sync(() => {
|
|
2730
|
-
const now = /* @__PURE__ */ new Date();
|
|
2731
|
-
let count = 0;
|
|
2732
|
-
for (const [id, token] of tokens) {
|
|
2733
|
-
if (token.expiresAt && token.expiresAt < now) {
|
|
2734
|
-
tokens.delete(id);
|
|
2735
|
-
count++;
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
return count;
|
|
2739
|
-
})
|
|
2740
|
-
};
|
|
2741
|
-
};
|
|
2742
|
-
var MockTokenStoreLive = import_effect35.Layer.succeed(
|
|
2743
|
-
TokenStoreTag,
|
|
2744
|
-
makeMockTokenStore()
|
|
2745
|
-
);
|
|
2746
|
-
|
|
2747
|
-
// libs/auth/testing/src/lib/mocks/MockPasswordHasher.ts
|
|
2748
|
-
var import_effect36 = require("effect");
|
|
2749
|
-
var makeMockPasswordHasher = () => ({
|
|
2750
|
-
hash: (password) => import_effect36.Effect.sync(() => {
|
|
2751
|
-
return `mock:${Buffer.from(password).toString("base64")}`;
|
|
2752
|
-
}),
|
|
2753
|
-
verify: (password, hash) => import_effect36.Effect.sync(() => {
|
|
2754
|
-
if (!hash.startsWith("mock:")) {
|
|
2755
|
-
return false;
|
|
2756
|
-
}
|
|
2757
|
-
const decoded = Buffer.from(hash.slice(5), "base64").toString("utf-8");
|
|
2758
|
-
return password === decoded;
|
|
2759
|
-
}),
|
|
2760
|
-
needsRehash: (hash) => !hash.startsWith("mock:")
|
|
2761
|
-
});
|
|
2762
|
-
var MockPasswordHasherLive = import_effect36.Layer.succeed(
|
|
2763
|
-
PasswordHasherTag,
|
|
2764
|
-
makeMockPasswordHasher()
|
|
2765
|
-
);
|
|
2766
|
-
var makeMockTokenHasher = () => ({
|
|
2767
|
-
hash: (token) => {
|
|
2768
|
-
return `hash:${Buffer.from(token).toString("base64")}`;
|
|
2769
|
-
},
|
|
2770
|
-
verify: (token, hash) => {
|
|
2771
|
-
if (!hash.startsWith("hash:")) {
|
|
2772
|
-
return false;
|
|
2773
|
-
}
|
|
2774
|
-
const decoded = Buffer.from(hash.slice(5), "base64").toString("utf-8");
|
|
2775
|
-
return token === decoded;
|
|
2776
|
-
}
|
|
2777
|
-
});
|
|
2778
|
-
var MockTokenHasherLive = import_effect36.Layer.succeed(
|
|
2779
|
-
TokenHasherTag,
|
|
2780
|
-
makeMockTokenHasher()
|
|
2781
|
-
);
|
|
2782
|
-
|
|
2783
|
-
// libs/auth/testing/src/lib/mocks/MockUserProvider.ts
|
|
2784
|
-
var import_effect37 = require("effect");
|
|
2785
|
-
var makeMockUserProvider = (users = []) => {
|
|
2786
|
-
const userMap = /* @__PURE__ */ new Map();
|
|
2787
|
-
const emailMap = /* @__PURE__ */ new Map();
|
|
2788
|
-
for (const user of users) {
|
|
2789
|
-
userMap.set(user.id, user);
|
|
2790
|
-
emailMap.set(user.email.toLowerCase(), user);
|
|
2791
|
-
}
|
|
2792
|
-
return {
|
|
2793
|
-
findById: (id) => import_effect37.Effect.sync(() => {
|
|
2794
|
-
const user = userMap.get(id);
|
|
2795
|
-
return user ? import_effect37.Option.some(user) : import_effect37.Option.none();
|
|
2796
|
-
}),
|
|
2797
|
-
findByEmail: (email) => import_effect37.Effect.sync(() => {
|
|
2798
|
-
const user = emailMap.get(email.toLowerCase());
|
|
2799
|
-
return user ? import_effect37.Option.some(user) : import_effect37.Option.none();
|
|
2800
|
-
}),
|
|
2801
|
-
findByCredentials: (credentials) => import_effect37.Effect.sync(() => {
|
|
2802
|
-
for (const user of userMap.values()) {
|
|
2803
|
-
const userObj = user;
|
|
2804
|
-
const matches = Object.entries(credentials).every(
|
|
2805
|
-
([key, value]) => userObj[key] === value
|
|
2806
|
-
);
|
|
2807
|
-
if (matches) {
|
|
2808
|
-
return import_effect37.Option.some(user);
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
return import_effect37.Option.none();
|
|
2812
|
-
})
|
|
2813
|
-
};
|
|
2814
|
-
};
|
|
2815
|
-
var MockUserProviderLive = (users = []) => import_effect37.Layer.succeed(UserProviderTag, makeMockUserProvider(users));
|
|
2816
|
-
var createMockUser = (overrides) => ({
|
|
2817
|
-
id: UserId(crypto.randomUUID()),
|
|
2818
|
-
password: "mock:dGVzdA==",
|
|
2819
|
-
// "test" encoded
|
|
2820
|
-
...overrides
|
|
2821
|
-
});
|
|
2822
|
-
|
|
2823
|
-
// libs/auth/testing/src/lib/utilities/actingAs.ts
|
|
2824
|
-
var import_effect38 = require("effect");
|
|
2825
|
-
var createAuthenticatedUser = (userId, options = {}) => {
|
|
2826
|
-
const scopes = options.scopes ?? ["*"];
|
|
2827
|
-
const tokenName = options.tokenName ?? "test-token";
|
|
2828
|
-
const token = {
|
|
2829
|
-
id: TokenId(crypto.randomUUID()),
|
|
2830
|
-
userId,
|
|
2831
|
-
name: tokenName,
|
|
2832
|
-
token: HashedToken("test-hashed-token"),
|
|
2833
|
-
scopes,
|
|
2834
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
2835
|
-
};
|
|
2836
|
-
return {
|
|
2837
|
-
id: userId,
|
|
2838
|
-
token
|
|
2839
|
-
};
|
|
2840
|
-
};
|
|
2841
|
-
var actingAsLayer = (userId, options = {}) => {
|
|
2842
|
-
const user = createAuthenticatedUser(userId, options);
|
|
2843
|
-
return import_effect38.Layer.succeed(AuthenticatedUserTag, user);
|
|
2844
|
-
};
|
|
2845
|
-
var actingAs = (userId, options, effect) => {
|
|
2846
|
-
return effect.pipe(import_effect38.Effect.provide(actingAsLayer(userId, options)));
|
|
2847
|
-
};
|
|
2848
|
-
var Auth = {
|
|
2849
|
-
/**
|
|
2850
|
-
* Run an effect as an authenticated user
|
|
2851
|
-
*/
|
|
2852
|
-
actingAs: (userId, effect, options = {}) => {
|
|
2853
|
-
return actingAs(userId, options, effect);
|
|
2854
|
-
},
|
|
2855
|
-
/**
|
|
2856
|
-
* Create a layer for authenticated user
|
|
2857
|
-
*/
|
|
2858
|
-
actingAsLayer
|
|
2859
|
-
};
|
|
2860
|
-
|
|
2861
|
-
// libs/auth/testing/src/lib/utilities/assertions.ts
|
|
2862
|
-
var import_effect39 = require("effect");
|
|
2863
|
-
var assertAuthenticated = () => import_effect39.Effect.gen(function* () {
|
|
2864
|
-
const result = yield* import_effect39.Effect.either(AuthenticatedUserTag);
|
|
2865
|
-
if (result._tag === "Left") {
|
|
2866
|
-
return yield* import_effect39.Effect.fail(
|
|
2867
|
-
new AuthenticationError({
|
|
2868
|
-
message: "Expected to be authenticated, but was not",
|
|
2869
|
-
reason: "missing_token"
|
|
2870
|
-
})
|
|
2871
|
-
);
|
|
2872
|
-
}
|
|
2873
|
-
return result.right;
|
|
2874
|
-
});
|
|
2875
|
-
var assertGuest = () => import_effect39.Effect.gen(function* () {
|
|
2876
|
-
const result = yield* import_effect39.Effect.either(AuthenticatedUserTag);
|
|
2877
|
-
if (result._tag === "Right") {
|
|
2878
|
-
return yield* import_effect39.Effect.fail(
|
|
2879
|
-
new Error("Expected to be guest, but was authenticated")
|
|
2880
|
-
);
|
|
2881
|
-
}
|
|
2882
|
-
});
|
|
2883
|
-
var assertCan = (action, subject) => import_effect39.Effect.gen(function* () {
|
|
2884
|
-
const abilities = yield* AbilitiesTag;
|
|
2885
|
-
const canDo = abilities.can(action, subject);
|
|
2886
|
-
if (!canDo) {
|
|
2887
|
-
return yield* import_effect39.Effect.fail(
|
|
2888
|
-
new Error(`Expected to be able to ${action} ${String(subject)}, but cannot`)
|
|
2889
|
-
);
|
|
2890
|
-
}
|
|
2891
|
-
});
|
|
2892
|
-
var assertCannot = (action, subject) => import_effect39.Effect.gen(function* () {
|
|
2893
|
-
const abilities = yield* AbilitiesTag;
|
|
2894
|
-
const canDo = abilities.can(action, subject);
|
|
2895
|
-
if (canDo) {
|
|
2896
|
-
return yield* import_effect39.Effect.fail(
|
|
2897
|
-
new Error(`Expected NOT to be able to ${action} ${String(subject)}, but can`)
|
|
2898
|
-
);
|
|
2899
|
-
}
|
|
2900
|
-
});
|
|
2901
|
-
var assertHasScope = (scope) => import_effect39.Effect.gen(function* () {
|
|
2902
|
-
const user = yield* AuthenticatedUserTag;
|
|
2903
|
-
if (!user.token) {
|
|
2904
|
-
return yield* import_effect39.Effect.fail(
|
|
2905
|
-
new Error("No token present to check scope")
|
|
2906
|
-
);
|
|
2907
|
-
}
|
|
2908
|
-
const hasScope2 = user.token.scopes.includes("*") || user.token.scopes.includes(scope);
|
|
2909
|
-
if (!hasScope2) {
|
|
2910
|
-
return yield* import_effect39.Effect.fail(
|
|
2911
|
-
new Error(`Expected token to have scope "${scope}", but it doesn't`)
|
|
2912
|
-
);
|
|
2913
|
-
}
|
|
2914
|
-
});
|
|
2915
|
-
var assertLacksScope = (scope) => import_effect39.Effect.gen(function* () {
|
|
2916
|
-
const user = yield* AuthenticatedUserTag;
|
|
2917
|
-
if (!user.token) {
|
|
2918
|
-
return;
|
|
2919
|
-
}
|
|
2920
|
-
const hasScope2 = user.token.scopes.includes("*") || user.token.scopes.includes(scope);
|
|
2921
|
-
if (hasScope2) {
|
|
2922
|
-
return yield* import_effect39.Effect.fail(
|
|
2923
|
-
new Error(`Expected token NOT to have scope "${scope}", but it does`)
|
|
2924
|
-
);
|
|
2925
|
-
}
|
|
2926
|
-
});
|
|
2927
|
-
|
|
2928
|
-
// libs/auth/testing/src/lib/utilities/layers.ts
|
|
2929
|
-
var import_effect40 = require("effect");
|
|
2930
|
-
var TestAuthLayer = (users = []) => import_effect40.Layer.mergeAll(
|
|
2931
|
-
MockTokenStoreLive,
|
|
2932
|
-
MockPasswordHasherLive,
|
|
2933
|
-
MockTokenHasherLive,
|
|
2934
|
-
MockUserProviderLive(users)
|
|
2935
|
-
).pipe(
|
|
2936
|
-
import_effect40.Layer.provideMerge(TokenServiceLive),
|
|
2937
|
-
import_effect40.Layer.provideMerge(AuthServiceLive)
|
|
2938
|
-
);
|
|
2939
|
-
var TestSessionLayer = MemorySessionStoreLive;
|
|
2940
|
-
var FullTestAuthLayer = (users = []) => import_effect40.Layer.mergeAll(TestAuthLayer(users), TestSessionLayer);
|
|
2941
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
2942
|
-
0 && (module.exports = {
|
|
2943
|
-
AbilitiesLive,
|
|
2944
|
-
AbilitiesTag,
|
|
2945
|
-
AbstractProvider,
|
|
2946
|
-
Auth,
|
|
2947
|
-
AuthError,
|
|
2948
|
-
AuthGuard,
|
|
2949
|
-
AuthServiceLive,
|
|
2950
|
-
AuthTag,
|
|
2951
|
-
AuthenticatedUserTag,
|
|
2952
|
-
AuthenticationError,
|
|
2953
|
-
AuthorizationError,
|
|
2954
|
-
CommonAbilities,
|
|
2955
|
-
CsrfMismatchError,
|
|
2956
|
-
CsrfToken,
|
|
2957
|
-
CsrfTokenTag,
|
|
2958
|
-
CurrentSessionTag,
|
|
2959
|
-
ForbiddenError,
|
|
2960
|
-
FullTestAuthLayer,
|
|
2961
|
-
GithubProvider,
|
|
2962
|
-
GoogleProvider,
|
|
2963
|
-
HashedToken,
|
|
2964
|
-
HashingError,
|
|
2965
|
-
InsufficientScopeError,
|
|
2966
|
-
InvalidPasswordError,
|
|
2967
|
-
JwtError,
|
|
2968
|
-
JwtServiceLive,
|
|
2969
|
-
JwtServiceTag,
|
|
2970
|
-
MemorySessionStoreLive,
|
|
2971
|
-
MockPasswordHasherLive,
|
|
2972
|
-
MockTokenHasherLive,
|
|
2973
|
-
MockTokenStoreLive,
|
|
2974
|
-
MockUserProviderLive,
|
|
2975
|
-
NewLogin,
|
|
2976
|
-
NewLoginMailable,
|
|
2977
|
-
OAuthConfigError,
|
|
2978
|
-
OAuthError,
|
|
2979
|
-
OAuthProviderNotFoundError,
|
|
2980
|
-
OAuthStateMismatchError,
|
|
2981
|
-
OAuthTokenError,
|
|
2982
|
-
OAuthUserError,
|
|
2983
|
-
PasswordChanged,
|
|
2984
|
-
PasswordHasherTag,
|
|
2985
|
-
PersonalAccessToken,
|
|
2986
|
-
PlainTextToken,
|
|
2987
|
-
RateLimitError,
|
|
2988
|
-
ResetPassword,
|
|
2989
|
-
ResetPasswordMailable,
|
|
2990
|
-
Session,
|
|
2991
|
-
SessionError,
|
|
2992
|
-
SessionExpiredError,
|
|
2993
|
-
SessionId,
|
|
2994
|
-
SessionNotFoundError,
|
|
2995
|
-
SessionStoreTag,
|
|
2996
|
-
Social,
|
|
2997
|
-
SocialLive,
|
|
2998
|
-
SocialTag,
|
|
2999
|
-
SocialToken,
|
|
3000
|
-
SocialUser,
|
|
3001
|
-
TestAuthLayer,
|
|
3002
|
-
TestSessionLayer,
|
|
3003
|
-
TokenExpiredError,
|
|
3004
|
-
TokenHasherTag,
|
|
3005
|
-
TokenId,
|
|
3006
|
-
TokenNotFoundError,
|
|
3007
|
-
TokenServiceLive,
|
|
3008
|
-
TokenServiceTag,
|
|
3009
|
-
TokenStoreTag,
|
|
3010
|
-
UserId,
|
|
3011
|
-
UserNotFoundError,
|
|
3012
|
-
UserProviderTag,
|
|
3013
|
-
VerifyEmail,
|
|
3014
|
-
VerifyEmailMailable,
|
|
3015
|
-
WelcomeEmail,
|
|
3016
|
-
WelcomeEmailMailable,
|
|
3017
|
-
actingAs,
|
|
3018
|
-
actingAsLayer,
|
|
3019
|
-
assertAuthenticated,
|
|
3020
|
-
assertCan,
|
|
3021
|
-
assertCannot,
|
|
3022
|
-
assertGuest,
|
|
3023
|
-
assertHasScope,
|
|
3024
|
-
assertLacksScope,
|
|
3025
|
-
authenticate,
|
|
3026
|
-
authorize,
|
|
3027
|
-
authorizeAction,
|
|
3028
|
-
authorizeAll,
|
|
3029
|
-
authorizeAny,
|
|
3030
|
-
authorizeMiddleware,
|
|
3031
|
-
can,
|
|
3032
|
-
cannot,
|
|
3033
|
-
createAbilities,
|
|
3034
|
-
createAuthenticatedUser,
|
|
3035
|
-
createGithubProvider,
|
|
3036
|
-
createGoogleProvider,
|
|
3037
|
-
createMockUser,
|
|
3038
|
-
csrf,
|
|
3039
|
-
currentUser,
|
|
3040
|
-
defaultCsrfConfig,
|
|
3041
|
-
defaultSessionConfig,
|
|
3042
|
-
defineAbilitiesFor,
|
|
3043
|
-
endSession,
|
|
3044
|
-
generateCsrfCookie,
|
|
3045
|
-
getSessionData,
|
|
3046
|
-
getSubjectType,
|
|
3047
|
-
hasApiTokens,
|
|
3048
|
-
hasScope,
|
|
3049
|
-
isAuthenticated,
|
|
3050
|
-
makeJwtService,
|
|
3051
|
-
makeMemorySessionStore,
|
|
3052
|
-
makeMockPasswordHasher,
|
|
3053
|
-
makeMockTokenHasher,
|
|
3054
|
-
makeMockTokenStore,
|
|
3055
|
-
makeMockUserProvider,
|
|
3056
|
-
makeSocialService,
|
|
3057
|
-
matchAction,
|
|
3058
|
-
matchConditions,
|
|
3059
|
-
matchSubject,
|
|
3060
|
-
newLogin,
|
|
3061
|
-
requireAdmin,
|
|
3062
|
-
requireAny,
|
|
3063
|
-
requireScope,
|
|
3064
|
-
resetPassword,
|
|
3065
|
-
session,
|
|
3066
|
-
setSessionData,
|
|
3067
|
-
startSession,
|
|
3068
|
-
tokenScope,
|
|
3069
|
-
tokenScopes,
|
|
3070
|
-
verifyCsrfToken,
|
|
3071
|
-
verifyEmail,
|
|
3072
|
-
welcomeEmail,
|
|
3073
|
-
withApiTokens
|
|
3074
|
-
});
|