@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.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
- });