@owox/idp-owox-better-auth 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +119 -0
  2. package/dist/client/IdentityOwoxClient.d.ts +41 -0
  3. package/dist/client/IdentityOwoxClient.d.ts.map +1 -0
  4. package/dist/client/IdentityOwoxClient.js +128 -0
  5. package/dist/client/dto/authFlowDto.d.ts +27 -0
  6. package/dist/client/dto/authFlowDto.d.ts.map +1 -0
  7. package/dist/client/dto/authFlowDto.js +5 -0
  8. package/dist/client/dto/idpOwoxPayloadDto.d.ts +29 -0
  9. package/dist/client/dto/idpOwoxPayloadDto.d.ts.map +1 -0
  10. package/dist/client/dto/idpOwoxPayloadDto.js +28 -0
  11. package/dist/client/dto/index.d.ts +11 -0
  12. package/dist/client/dto/index.d.ts.map +1 -0
  13. package/dist/client/dto/index.js +10 -0
  14. package/dist/client/dto/introspectionDto.d.ts +70 -0
  15. package/dist/client/dto/introspectionDto.d.ts.map +1 -0
  16. package/dist/client/dto/introspectionDto.js +15 -0
  17. package/dist/client/dto/jwksDto.d.ts +102 -0
  18. package/dist/client/dto/jwksDto.d.ts.map +1 -0
  19. package/dist/client/dto/jwksDto.js +18 -0
  20. package/dist/client/dto/revocationDto.d.ts +11 -0
  21. package/dist/client/dto/revocationDto.d.ts.map +1 -0
  22. package/dist/client/dto/revocationDto.js +1 -0
  23. package/dist/client/dto/tokenDto.d.ts +33 -0
  24. package/dist/client/dto/tokenDto.d.ts.map +1 -0
  25. package/dist/client/dto/tokenDto.js +9 -0
  26. package/dist/client/dto/tokenType.d.ts +5 -0
  27. package/dist/client/dto/tokenType.d.ts.map +1 -0
  28. package/dist/client/dto/tokenType.js +1 -0
  29. package/dist/client/index.d.ts +6 -0
  30. package/dist/client/index.d.ts.map +1 -0
  31. package/dist/client/index.js +5 -0
  32. package/dist/config/idp-better-auth-config.d.ts +9 -0
  33. package/dist/config/idp-better-auth-config.d.ts.map +1 -0
  34. package/dist/config/idp-better-auth-config.js +101 -0
  35. package/dist/config/idp-owox-config.d.ts +195 -0
  36. package/dist/config/idp-owox-config.d.ts.map +1 -0
  37. package/dist/config/idp-owox-config.js +252 -0
  38. package/dist/config/index.d.ts +6 -0
  39. package/dist/config/index.d.ts.map +1 -0
  40. package/dist/config/index.js +5 -0
  41. package/dist/core/constants.d.ts +14 -0
  42. package/dist/core/constants.d.ts.map +1 -0
  43. package/dist/core/constants.js +13 -0
  44. package/dist/core/exceptions.d.ts +27 -0
  45. package/dist/core/exceptions.d.ts.map +1 -0
  46. package/dist/core/exceptions.js +36 -0
  47. package/dist/core/logger.d.ts +17 -0
  48. package/dist/core/logger.d.ts.map +1 -0
  49. package/dist/core/logger.js +66 -0
  50. package/dist/core/pkce.d.ts +21 -0
  51. package/dist/core/pkce.d.ts.map +1 -0
  52. package/dist/core/pkce.js +27 -0
  53. package/dist/facades/owox-token-facade.d.ts +27 -0
  54. package/dist/facades/owox-token-facade.d.ts.map +1 -0
  55. package/dist/facades/owox-token-facade.js +117 -0
  56. package/dist/index.d.ts +13 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +13 -0
  59. package/dist/jwt/jwksCache.d.ts +24 -0
  60. package/dist/jwt/jwksCache.d.ts.map +1 -0
  61. package/dist/jwt/jwksCache.js +41 -0
  62. package/dist/jwt/parseToken.d.ts +15 -0
  63. package/dist/jwt/parseToken.d.ts.map +1 -0
  64. package/dist/jwt/parseToken.js +26 -0
  65. package/dist/jwt/verifyJwt.d.ts +13 -0
  66. package/dist/jwt/verifyJwt.d.ts.map +1 -0
  67. package/dist/jwt/verifyJwt.js +23 -0
  68. package/dist/mappers/client-payload-mapper.d.ts +6 -0
  69. package/dist/mappers/client-payload-mapper.d.ts.map +1 -0
  70. package/dist/mappers/client-payload-mapper.js +17 -0
  71. package/dist/mappers/user-info-payload-builder.d.ts +11 -0
  72. package/dist/mappers/user-info-payload-builder.d.ts.map +1 -0
  73. package/dist/mappers/user-info-payload-builder.js +28 -0
  74. package/dist/owox-better-auth-idp.d.ts +40 -0
  75. package/dist/owox-better-auth-idp.d.ts.map +1 -0
  76. package/dist/owox-better-auth-idp.js +239 -0
  77. package/dist/resources/templates/layouts/auth.ejs +29 -0
  78. package/dist/resources/templates/pages/sign-in.ejs +66 -0
  79. package/dist/resources/templates/pages/sign-up.ejs +65 -0
  80. package/dist/resources/templates/partials/brand-panel.ejs +24 -0
  81. package/dist/resources/templates/partials/footer.ejs +10 -0
  82. package/dist/resources/templates/partials/head.ejs +64 -0
  83. package/dist/resources/templates/partials/header.ejs +7 -0
  84. package/dist/services/auth/better-auth-session-service.d.ts +28 -0
  85. package/dist/services/auth/better-auth-session-service.d.ts.map +1 -0
  86. package/dist/services/auth/better-auth-session-service.js +121 -0
  87. package/dist/services/auth/pkce-flow-orchestrator.d.ts +33 -0
  88. package/dist/services/auth/pkce-flow-orchestrator.d.ts.map +1 -0
  89. package/dist/services/auth/pkce-flow-orchestrator.js +134 -0
  90. package/dist/services/auth/platform-auth-flow-client.d.ts +16 -0
  91. package/dist/services/auth/platform-auth-flow-client.d.ts.map +1 -0
  92. package/dist/services/auth/platform-auth-flow-client.js +32 -0
  93. package/dist/services/core/token-service.d.ts +25 -0
  94. package/dist/services/core/token-service.d.ts.map +1 -0
  95. package/dist/services/core/token-service.js +56 -0
  96. package/dist/services/core/user-context-service.d.ts +23 -0
  97. package/dist/services/core/user-context-service.d.ts.map +1 -0
  98. package/dist/services/core/user-context-service.js +54 -0
  99. package/dist/services/middleware/middleware-service.d.ts +19 -0
  100. package/dist/services/middleware/middleware-service.d.ts.map +1 -0
  101. package/dist/services/middleware/middleware-service.js +62 -0
  102. package/dist/services/middleware/request-handler-service.d.ts +18 -0
  103. package/dist/services/middleware/request-handler-service.d.ts.map +1 -0
  104. package/dist/services/middleware/request-handler-service.js +131 -0
  105. package/dist/services/rendering/page-service.d.ts +11 -0
  106. package/dist/services/rendering/page-service.d.ts.map +1 -0
  107. package/dist/services/rendering/page-service.js +26 -0
  108. package/dist/services/rendering/template-service.d.ts +17 -0
  109. package/dist/services/rendering/template-service.d.ts.map +1 -0
  110. package/dist/services/rendering/template-service.js +52 -0
  111. package/dist/social/google-provider.d.ts +35 -0
  112. package/dist/social/google-provider.d.ts.map +1 -0
  113. package/dist/social/google-provider.js +55 -0
  114. package/dist/social/social-provider.d.ts +23 -0
  115. package/dist/social/social-provider.d.ts.map +1 -0
  116. package/dist/social/social-provider.js +1 -0
  117. package/dist/store/database-store-factory.d.ts +8 -0
  118. package/dist/store/database-store-factory.d.ts.map +1 -0
  119. package/dist/store/database-store-factory.js +38 -0
  120. package/dist/store/database-store.d.ts +20 -0
  121. package/dist/store/database-store.d.ts.map +1 -0
  122. package/dist/store/database-store.js +1 -0
  123. package/dist/store/mysql-database-store.d.ts +40 -0
  124. package/dist/store/mysql-database-store.d.ts.map +1 -0
  125. package/dist/store/mysql-database-store.js +213 -0
  126. package/dist/store/sqlite-database-store.d.ts +32 -0
  127. package/dist/store/sqlite-database-store.d.ts.map +1 -0
  128. package/dist/store/sqlite-database-store.js +205 -0
  129. package/dist/store/store-result.d.ts +16 -0
  130. package/dist/store/store-result.d.ts.map +1 -0
  131. package/dist/store/store-result.js +25 -0
  132. package/dist/types/auth-request-context.d.ts +15 -0
  133. package/dist/types/auth-request-context.d.ts.map +1 -0
  134. package/dist/types/auth-request-context.js +12 -0
  135. package/dist/types/auth-session.d.ts +25 -0
  136. package/dist/types/auth-session.d.ts.map +1 -0
  137. package/dist/types/auth-session.js +1 -0
  138. package/dist/types/database-models.d.ts +39 -0
  139. package/dist/types/database-models.d.ts.map +1 -0
  140. package/dist/types/database-models.js +1 -0
  141. package/dist/types/index.d.ts +45 -0
  142. package/dist/types/index.d.ts.map +1 -0
  143. package/dist/types/index.js +2 -0
  144. package/dist/utils/cookie-policy.d.ts +16 -0
  145. package/dist/utils/cookie-policy.d.ts.map +1 -0
  146. package/dist/utils/cookie-policy.js +27 -0
  147. package/dist/utils/platform-redirect-builder.d.ts +29 -0
  148. package/dist/utils/platform-redirect-builder.d.ts.map +1 -0
  149. package/dist/utils/platform-redirect-builder.js +86 -0
  150. package/dist/utils/request-utils.d.ts +87 -0
  151. package/dist/utils/request-utils.d.ts.map +1 -0
  152. package/dist/utils/request-utils.js +171 -0
  153. package/dist/utils/string-utils.d.ts +13 -0
  154. package/dist/utils/string-utils.d.ts.map +1 -0
  155. package/dist/utils/string-utils.js +21 -0
  156. package/package.json +71 -0
@@ -0,0 +1,23 @@
1
+ import { Payload } from '@owox/idp-protocol';
2
+ import { Logger } from '@owox/internal-helpers';
3
+ import { OwoxTokenFacade } from '../../facades/owox-token-facade.js';
4
+ import type { DatabaseStore } from '../../store/database-store.js';
5
+ import type { DatabaseAccount, DatabaseUser } from '../../types/database-models.js';
6
+ export interface UserContext {
7
+ payload: Payload;
8
+ user: DatabaseUser;
9
+ account: DatabaseAccount;
10
+ }
11
+ /**
12
+ * Responsible for resolving Better Auth user/account from any JWT token
13
+ * (access or refresh) that contains an email claim.
14
+ */
15
+ export declare class UserContextService {
16
+ private readonly store;
17
+ private readonly tokenFacade;
18
+ private readonly logger?;
19
+ constructor(store: DatabaseStore, tokenFacade: OwoxTokenFacade, logger?: Logger | undefined);
20
+ resolveFromToken(token: string): Promise<UserContext>;
21
+ private normalizeEmail;
22
+ }
23
+ //# sourceMappingURL=user-context-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-context-service.d.ts","sourceRoot":"","sources":["../../../src/services/core/user-context-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEpF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAFP,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,eAAe,EAC5B,MAAM,CAAC,EAAE,MAAM,YAAA;IAG5B,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA2C3D,OAAO,CAAC,cAAc;CAIvB"}
@@ -0,0 +1,54 @@
1
+ import { AuthenticationException } from '../../core/exceptions.js';
2
+ /**
3
+ * Responsible for resolving Better Auth user/account from any JWT token
4
+ * (access or refresh) that contains an email claim.
5
+ */
6
+ export class UserContextService {
7
+ store;
8
+ tokenFacade;
9
+ logger;
10
+ constructor(store, tokenFacade, logger) {
11
+ this.store = store;
12
+ this.tokenFacade = tokenFacade;
13
+ this.logger = logger;
14
+ }
15
+ async resolveFromToken(token) {
16
+ const payload = await this.tokenFacade.parseToken(token);
17
+ if (!payload || !payload.email) {
18
+ throw new AuthenticationException('Invalid token payload: email is missing');
19
+ }
20
+ const normalizedEmail = this.normalizeEmail(payload.email);
21
+ if (!normalizedEmail) {
22
+ throw new AuthenticationException('Invalid token payload: email is malformed', {
23
+ context: { email: payload.email },
24
+ });
25
+ }
26
+ const user = await this.store.getUserByEmail(normalizedEmail);
27
+ if (!user) {
28
+ throw new AuthenticationException('User not found in Better Auth DB', {
29
+ context: { email: normalizedEmail },
30
+ });
31
+ }
32
+ if (user.emailVerified !== true) {
33
+ throw new AuthenticationException('User email is not verified in Better Auth DB', {
34
+ context: { email: normalizedEmail, userId: user.id, emailVerified: user.emailVerified },
35
+ });
36
+ }
37
+ const account = await this.store.getAccountByUserId(user.id);
38
+ if (!account) {
39
+ throw new AuthenticationException('Account not found for user', {
40
+ context: { userId: user.id, email: normalizedEmail },
41
+ });
42
+ }
43
+ this.logger?.debug?.('Resolved user context from token', {
44
+ email: normalizedEmail,
45
+ userId: user.id,
46
+ accountId: account.accountId,
47
+ });
48
+ return { payload, user, account };
49
+ }
50
+ normalizeEmail(email) {
51
+ const normalized = email.trim().toLowerCase();
52
+ return normalized.length > 0 ? normalized : null;
53
+ }
54
+ }
@@ -0,0 +1,19 @@
1
+ import { type NextFunction, type Request, type Response } from 'express';
2
+ import { IdpOwoxConfig } from '../../config/idp-owox-config.js';
3
+ import type { DatabaseStore } from '../../store/database-store.js';
4
+ import { PkceFlowOrchestrator } from '../auth/pkce-flow-orchestrator.js';
5
+ import { PageService } from '../rendering/page-service.js';
6
+ /**
7
+ * Express middleware handlers for starting PKCE and fast-path sign-in.
8
+ */
9
+ export declare class MiddlewareService {
10
+ private readonly pageService;
11
+ private readonly idpOwoxConfig;
12
+ private readonly store;
13
+ private readonly pkceFlowOrchestrator;
14
+ constructor(pageService: PageService, idpOwoxConfig: IdpOwoxConfig, store: DatabaseStore, pkceFlowOrchestrator: PkceFlowOrchestrator);
15
+ idpStartMiddleware(req: Request, res: Response): Promise<void>;
16
+ signInMiddleware(req: Request, res: Response, _next: NextFunction): Promise<void | Response>;
17
+ signUpMiddleware(req: Request, res: Response, _next: NextFunction): Promise<void | Response>;
18
+ }
19
+ //# sourceMappingURL=middleware-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware-service.d.ts","sourceRoot":"","sources":["../../../src/services/middleware/middleware-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAIhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAInE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE3D;;GAEG;AACH,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;gBAHpB,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,aAAa,EACpB,oBAAoB,EAAE,oBAAoB;IAGvD,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B9D,gBAAgB,CACpB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;IAoBrB,gBAAgB,CACpB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;CAG5B"}
@@ -0,0 +1,62 @@
1
+ import ms from 'ms';
2
+ import { SOURCE } from '../../core/constants.js';
3
+ import { logger } from '../../core/logger.js';
4
+ import { generatePkce, generateState } from '../../core/pkce.js';
5
+ import { buildAuthRequestContext } from '../../types/auth-request-context.js';
6
+ import { buildPlatformEntryUrl } from '../../utils/platform-redirect-builder.js';
7
+ import { extractPlatformParams } from '../../utils/request-utils.js';
8
+ /**
9
+ * Express middleware handlers for starting PKCE and fast-path sign-in.
10
+ */
11
+ export class MiddlewareService {
12
+ pageService;
13
+ idpOwoxConfig;
14
+ store;
15
+ pkceFlowOrchestrator;
16
+ constructor(pageService, idpOwoxConfig, store, pkceFlowOrchestrator) {
17
+ this.pageService = pageService;
18
+ this.idpOwoxConfig = idpOwoxConfig;
19
+ this.store = store;
20
+ this.pkceFlowOrchestrator = pkceFlowOrchestrator;
21
+ }
22
+ async idpStartMiddleware(req, res) {
23
+ try {
24
+ await this.store.initialize();
25
+ const { codeVerifier, codeChallenge } = await generatePkce();
26
+ const state = generateState();
27
+ const expiresAt = new Date(Date.now() + ms('1m'));
28
+ await this.store.saveAuthState(state, codeVerifier, expiresAt);
29
+ const { redirectTo, appRedirectTo, projectId } = extractPlatformParams(req);
30
+ const platformUrl = buildPlatformEntryUrl({
31
+ authUrl: this.idpOwoxConfig.idpConfig.platformSignInUrl,
32
+ defaultSource: SOURCE.APP,
33
+ params: { redirectTo, appRedirectTo, projectId },
34
+ allowedRedirectOrigins: this.idpOwoxConfig.idpConfig.allowedRedirectOrigins,
35
+ });
36
+ platformUrl.searchParams.set('state', state);
37
+ platformUrl.searchParams.set('codeChallenge', codeChallenge);
38
+ platformUrl.searchParams.set('clientId', this.idpOwoxConfig.idpConfig.clientId);
39
+ return res.redirect(platformUrl.toString());
40
+ }
41
+ catch (error) {
42
+ logger.error('Failed to start IDP flow', {}, error);
43
+ res.status(500).end('Failed to start IDP flow');
44
+ }
45
+ }
46
+ async signInMiddleware(req, res, _next) {
47
+ const context = buildAuthRequestContext(req);
48
+ if (context.state && context.refreshToken) {
49
+ const fastRedirect = await this.pkceFlowOrchestrator.completeWithIdentityRefreshToken(context.refreshToken, context.platformParams, req, res);
50
+ if (fastRedirect) {
51
+ logger.info('Fast-path completeAuthFlow redirectUrl', {
52
+ redirectUrl: fastRedirect.toString(),
53
+ });
54
+ return res.redirect(fastRedirect.toString());
55
+ }
56
+ }
57
+ return this.pageService.signInPage.bind(this.pageService)(req, res);
58
+ }
59
+ async signUpMiddleware(req, res, _next) {
60
+ return this.pageService.signUpPage.bind(this.pageService)(req, res);
61
+ }
62
+ }
@@ -0,0 +1,18 @@
1
+ import { type Express, type Request as ExpressRequest } from 'express';
2
+ import { PkceFlowOrchestrator } from '../auth/pkce-flow-orchestrator.js';
3
+ import { createBetterAuthConfig } from '../../config/idp-better-auth-config.js';
4
+ /**
5
+ * Proxies Better Auth requests and completes social login flow.
6
+ */
7
+ export declare class RequestHandlerService {
8
+ private readonly auth;
9
+ private readonly pkceFlowOrchestrator;
10
+ private static readonly AUTH_ROUTE_PREFIX;
11
+ constructor(auth: Awaited<ReturnType<typeof createBetterAuthConfig>>, pkceFlowOrchestrator: PkceFlowOrchestrator);
12
+ setupBetterAuthHandler(expressApp: Express): void;
13
+ private getSessionTokenFromResponse;
14
+ private escapeRegex;
15
+ private tryCompleteAuthFlow;
16
+ convertExpressToFetchRequest(req: ExpressRequest): Request;
17
+ }
18
+ //# sourceMappingURL=request-handler-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-handler-service.d.ts","sourceRoot":"","sources":["../../../src/services/middleware/request-handler-service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,OAAO,IAAI,cAAc,EAG/B,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAKhF;;GAEG;AACH,qBAAa,qBAAqB;IAI9B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAJvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAuB;gBAG7C,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,EACxD,oBAAoB,EAAE,oBAAoB;IAG7D,sBAAsB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI;IA6BjD,OAAO,CAAC,2BAA2B;IA0BnC,OAAO,CAAC,WAAW;YAIL,mBAAmB;IAqBjC,4BAA4B,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO;CAuD3D"}
@@ -0,0 +1,131 @@
1
+ import { BETTER_AUTH_SESSION_COOKIE } from '../../core/constants.js';
2
+ import { logger } from '../../core/logger.js';
3
+ import { extractPlatformParams } from '../../utils/request-utils.js';
4
+ /**
5
+ * Proxies Better Auth requests and completes social login flow.
6
+ */
7
+ export class RequestHandlerService {
8
+ auth;
9
+ pkceFlowOrchestrator;
10
+ static AUTH_ROUTE_PREFIX = '/auth/better-auth';
11
+ constructor(auth, pkceFlowOrchestrator) {
12
+ this.auth = auth;
13
+ this.pkceFlowOrchestrator = pkceFlowOrchestrator;
14
+ }
15
+ setupBetterAuthHandler(expressApp) {
16
+ expressApp.use(async (req, res, next) => {
17
+ if (!req.path.startsWith(RequestHandlerService.AUTH_ROUTE_PREFIX)) {
18
+ return next();
19
+ }
20
+ try {
21
+ const fetchRequest = this.convertExpressToFetchRequest(req);
22
+ const response = await this.auth.handler(fetchRequest);
23
+ const redirected = await this.tryCompleteAuthFlow(req, response, res);
24
+ if (redirected) {
25
+ return;
26
+ }
27
+ response.headers.forEach((value, key) => {
28
+ res.set(key, value);
29
+ });
30
+ res.status(response.status);
31
+ const body = await response.text();
32
+ res.send(body);
33
+ }
34
+ catch (error) {
35
+ logger.error('Auth handler error', { path: req.path }, error);
36
+ res.status(500).json({ error: 'Internal server error' });
37
+ }
38
+ });
39
+ }
40
+ getSessionTokenFromResponse(response) {
41
+ const rawHeaders = [];
42
+ const getSetCookieFn = response.headers
43
+ .getSetCookie;
44
+ const getSetCookie = typeof getSetCookieFn === 'function'
45
+ ? getSetCookieFn.bind(response.headers)
46
+ : undefined;
47
+ if (typeof getSetCookie === 'function') {
48
+ rawHeaders.push(...getSetCookie());
49
+ }
50
+ const header = response.headers.get('set-cookie');
51
+ if (header) {
52
+ rawHeaders.push(...header.split(/,(?=[^;]+=[^;]+)/g));
53
+ }
54
+ const cookieName = this.escapeRegex(BETTER_AUTH_SESSION_COOKIE);
55
+ for (const h of rawHeaders) {
56
+ const match = h.match(new RegExp(`${cookieName}=([^;]+)`));
57
+ if (match && match[1]) {
58
+ return decodeURIComponent(match[1]);
59
+ }
60
+ }
61
+ return null;
62
+ }
63
+ escapeRegex(value) {
64
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
65
+ }
66
+ async tryCompleteAuthFlow(req, response, res) {
67
+ const sessionToken = this.getSessionTokenFromResponse(response);
68
+ if (!sessionToken)
69
+ return false;
70
+ const redirectUrl = await this.pkceFlowOrchestrator.completeWithSocialSessionToken(sessionToken, extractPlatformParams(req), req, res);
71
+ if (redirectUrl) {
72
+ res.redirect(redirectUrl.toString());
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+ convertExpressToFetchRequest(req) {
78
+ try {
79
+ const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
80
+ const method = req.method.toUpperCase();
81
+ const headers = new Headers();
82
+ for (const [key, value] of Object.entries(req.headers)) {
83
+ if (!value)
84
+ continue;
85
+ if (Array.isArray(value)) {
86
+ headers.set(key, value.join(', '));
87
+ }
88
+ else {
89
+ headers.set(key, String(value));
90
+ }
91
+ }
92
+ let body = null;
93
+ if (method !== 'GET' && method !== 'HEAD') {
94
+ const rawBody = req.body;
95
+ if (rawBody !== undefined && rawBody !== null) {
96
+ if (Buffer.isBuffer(rawBody)) {
97
+ body = rawBody;
98
+ }
99
+ else if (typeof rawBody === 'string') {
100
+ body = rawBody;
101
+ }
102
+ else {
103
+ body = JSON.stringify(rawBody);
104
+ if (!headers.has('content-type')) {
105
+ headers.set('content-type', 'application/json');
106
+ }
107
+ }
108
+ // Ensure content-length matches the rebuilt payload (or let fetch set it)
109
+ headers.delete('content-length');
110
+ if (typeof body === 'string') {
111
+ headers.set('content-length', Buffer.byteLength(body).toString());
112
+ }
113
+ else if (Buffer.isBuffer(body)) {
114
+ headers.set('content-length', body.byteLength.toString());
115
+ }
116
+ }
117
+ }
118
+ const fetchBody = body === null ? null : typeof body === 'string' ? body : new Uint8Array(body);
119
+ const fetchRequest = new Request(url, {
120
+ method,
121
+ headers,
122
+ body: fetchBody ?? undefined,
123
+ });
124
+ return fetchRequest;
125
+ }
126
+ catch (error) {
127
+ logger.error('Failed to convert Express request to Fetch request', {}, error);
128
+ throw new Error('Failed to convert request format');
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,11 @@
1
+ import { type Express, type Request as ExpressRequest, type Response as ExpressResponse } from 'express';
2
+ /**
3
+ * Renders static auth pages and persists platform context.
4
+ */
5
+ export declare class PageService {
6
+ private persistPlatformContext;
7
+ signInPage(req: ExpressRequest, res: ExpressResponse): Promise<void>;
8
+ signUpPage(req: ExpressRequest, res: ExpressResponse): Promise<void>;
9
+ registerRoutes(express: Express): void;
10
+ }
11
+ //# sourceMappingURL=page-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-service.d.ts","sourceRoot":"","sources":["../../../src/services/rendering/page-service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,OAAO,IAAI,cAAc,EAC9B,KAAK,QAAQ,IAAI,eAAe,EACjC,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,sBAAsB;IAMxB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpE,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1E,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAIvC"}
@@ -0,0 +1,26 @@
1
+ import { ProtocolRoute } from '@owox/idp-protocol';
2
+ import { TemplateService } from '../rendering/template-service.js';
3
+ import { extractPlatformParams, persistPlatformContext } from '../../utils/request-utils.js';
4
+ const AUTH_BASE_PATH = '/auth';
5
+ /**
6
+ * Renders static auth pages and persists platform context.
7
+ */
8
+ export class PageService {
9
+ persistPlatformContext(req, res) {
10
+ const state = typeof req.query?.state === 'string' ? req.query.state : undefined;
11
+ const params = extractPlatformParams(req);
12
+ persistPlatformContext(req, res, { state, params });
13
+ }
14
+ async signInPage(req, res) {
15
+ this.persistPlatformContext(req, res);
16
+ res.send(TemplateService.renderSignIn());
17
+ }
18
+ async signUpPage(req, res) {
19
+ this.persistPlatformContext(req, res);
20
+ res.send(TemplateService.renderSignUp());
21
+ }
22
+ registerRoutes(express) {
23
+ const signInPath = `${AUTH_BASE_PATH}${ProtocolRoute.SIGN_IN}`;
24
+ express.get(AUTH_BASE_PATH, (_req, res) => res.redirect(signInPath));
25
+ }
26
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Loads and renders EJS templates for auth pages with layout composition.
3
+ */
4
+ export declare class TemplateService {
5
+ private static getTemplatePath;
6
+ private static loadTemplate;
7
+ /**
8
+ * Renders an EJS template with layout composition.
9
+ * @param pagePath - Path to page template (e.g., 'pages/sign-in.ejs')
10
+ * @param layoutPath - Path to layout template (e.g., 'layouts/auth.ejs')
11
+ * @param data - Data to pass to templates
12
+ */
13
+ private static renderWithLayout;
14
+ static renderSignIn(): string;
15
+ static renderSignUp(): string;
16
+ }
17
+ //# sourceMappingURL=template-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-service.d.ts","sourceRoot":"","sources":["../../../src/services/rendering/template-service.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,eAAe;IAqB9B,OAAO,CAAC,MAAM,CAAC,YAAY;IAK3B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAsBjB,YAAY,IAAI,MAAM;WAOtB,YAAY,IAAI,MAAM;CAMrC"}
@@ -0,0 +1,52 @@
1
+ import ejs from 'ejs';
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ /**
6
+ * Loads and renders EJS templates for auth pages with layout composition.
7
+ */
8
+ export class TemplateService {
9
+ static getTemplatePath(templateName) {
10
+ const currentDir = dirname(fileURLToPath(import.meta.url));
11
+ const distPath = join(currentDir, '..', '..', 'resources', 'templates', templateName);
12
+ if (existsSync(distPath)) {
13
+ return distPath;
14
+ }
15
+ const srcPath = join(currentDir, '..', '..', '..', 'src', 'resources', 'templates', templateName);
16
+ return srcPath;
17
+ }
18
+ static loadTemplate(templateName) {
19
+ const templatePath = this.getTemplatePath(templateName);
20
+ return readFileSync(templatePath, 'utf-8');
21
+ }
22
+ /**
23
+ * Renders an EJS template with layout composition.
24
+ * @param pagePath - Path to page template (e.g., 'pages/sign-in.ejs')
25
+ * @param layoutPath - Path to layout template (e.g., 'layouts/auth.ejs')
26
+ * @param data - Data to pass to templates
27
+ */
28
+ static renderWithLayout(pagePath, layoutPath, data) {
29
+ // 1. Load and render page content
30
+ const pageTemplate = this.loadTemplate(pagePath);
31
+ const pageContent = ejs.render(pageTemplate, data, {
32
+ filename: this.getTemplatePath(pagePath),
33
+ });
34
+ // 2. Load and render layout with page content injected
35
+ const layoutTemplate = this.loadTemplate(layoutPath);
36
+ return ejs.render(layoutTemplate, { ...data, body: pageContent }, {
37
+ filename: this.getTemplatePath(layoutPath),
38
+ });
39
+ }
40
+ static renderSignIn() {
41
+ return this.renderWithLayout('pages/sign-in.ejs', 'layouts/auth.ejs', {
42
+ pageTitle: 'Sign In - OWOX Data Marts',
43
+ heading: 'Sign in to OWOX using your Google account',
44
+ });
45
+ }
46
+ static renderSignUp() {
47
+ return this.renderWithLayout('pages/sign-up.ejs', 'layouts/auth.ejs', {
48
+ pageTitle: 'Sign Up - OWOX Data Marts',
49
+ heading: 'Sign up to OWOX using your Google account',
50
+ });
51
+ }
52
+ }
@@ -0,0 +1,35 @@
1
+ import { Profile, ProviderLogger, SocialProvider, SocialUser } from './social-provider.js';
2
+ export type GoogleProviderOptions = {
3
+ clientId?: string;
4
+ clientSecret?: string;
5
+ redirectURI?: string;
6
+ prompt?: string;
7
+ accessType?: string;
8
+ logger?: ProviderLogger;
9
+ };
10
+ /**
11
+ * Maps Google OAuth profile data into the app user format.
12
+ */
13
+ export declare class GoogleProvider implements SocialProvider {
14
+ private readonly opts;
15
+ providerId: string;
16
+ constructor(opts: GoogleProviderOptions);
17
+ private validate;
18
+ private selectAccountId;
19
+ buildConfig(): {
20
+ clientId: string | undefined;
21
+ clientSecret: string | undefined;
22
+ redirectURI: string | undefined;
23
+ prompt: string;
24
+ accessType: string;
25
+ mapProfileToUser: (profile: Record<string, unknown>) => {
26
+ id: string;
27
+ email: string | null;
28
+ name: string | null;
29
+ image: string | null;
30
+ emailVerified: boolean;
31
+ };
32
+ };
33
+ mapProfile(profile: Profile): SocialUser;
34
+ }
35
+ //# sourceMappingURL=google-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-provider.d.ts","sourceRoot":"","sources":["../../src/social/google-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE3F,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,qBAAa,cAAe,YAAW,cAAc;IAGvC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAF1B,UAAU,SAAY;gBAEA,IAAI,EAAE,qBAAqB;IAIxD,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,eAAe;IAKvB,WAAW;;;;;;oCAOqB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;IAQvD,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU;CA8BzC"}
@@ -0,0 +1,55 @@
1
+ import { LogLevel } from '@owox/internal-helpers';
2
+ /**
3
+ * Maps Google OAuth profile data into the app user format.
4
+ */
5
+ export class GoogleProvider {
6
+ opts;
7
+ providerId = 'google';
8
+ constructor(opts) {
9
+ this.opts = opts;
10
+ this.validate();
11
+ }
12
+ validate() {
13
+ const missing = ['clientId', 'clientSecret'].filter(key => !this.opts[key]);
14
+ if (missing.length) {
15
+ throw new Error(`[google] Missing required secrets: ${missing.join(', ')}`);
16
+ }
17
+ }
18
+ selectAccountId(profile) {
19
+ const p = profile;
20
+ return p.sub ? String(p.sub) : '';
21
+ }
22
+ buildConfig() {
23
+ return {
24
+ clientId: this.opts.clientId,
25
+ clientSecret: this.opts.clientSecret,
26
+ redirectURI: this.opts.redirectURI,
27
+ prompt: this.opts.prompt ?? 'select_account',
28
+ accessType: this.opts.accessType ?? 'offline',
29
+ mapProfileToUser: (profile) => {
30
+ const mapped = this.mapProfile(profile);
31
+ const { accountId, ...user } = mapped;
32
+ return { ...user, id: accountId };
33
+ },
34
+ };
35
+ }
36
+ mapProfile(profile) {
37
+ this.opts.logger?.log(LogLevel.INFO, `${this.providerId}-profile`, { profile });
38
+ const p = profile;
39
+ const accountId = this.selectAccountId(p);
40
+ if (!accountId) {
41
+ throw new Error('[google] Unable to resolve accountId (sub is missing)');
42
+ }
43
+ const email = p.email ?? null;
44
+ if (!email) {
45
+ throw new Error('[google] Email is required in profile');
46
+ }
47
+ return {
48
+ accountId,
49
+ email,
50
+ name: p.name ?? p.given_name ?? null,
51
+ image: p.picture ?? null,
52
+ emailVerified: Boolean(p.email_verified),
53
+ };
54
+ }
55
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Shared types and contracts for social auth providers.
3
+ */
4
+ import { LogLevel } from '@owox/internal-helpers';
5
+ export type Profile = Record<string, unknown>;
6
+ export type SocialUser = {
7
+ accountId: string;
8
+ email: string | null;
9
+ name: string | null;
10
+ image: string | null;
11
+ emailVerified: boolean;
12
+ };
13
+ export type ProviderLogger = {
14
+ log: (level: LogLevel, message: string, meta?: Record<string, unknown>) => void;
15
+ };
16
+ /**
17
+ * Defines how a social provider maps a profile to a user object.
18
+ */
19
+ export interface SocialProvider {
20
+ providerId: string;
21
+ mapProfile(profile: Profile): SocialUser;
22
+ }
23
+ //# sourceMappingURL=social-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social-provider.d.ts","sourceRoot":"","sources":["../../src/social/social-provider.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACjF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC;CAC1C"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { DbConfig } from '../config/idp-owox-config.js';
2
+ import type { DatabaseConfig } from '../types/index.js';
3
+ import type { DatabaseStore } from './database-store.js';
4
+ /**
5
+ * Creates a database store implementation based on config.
6
+ */
7
+ export declare function createDatabaseStore(database: DatabaseConfig | DbConfig): DatabaseStore;
8
+ //# sourceMappingURL=database-store-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-store-factory.d.ts","sourceRoot":"","sources":["../../src/store/database-store-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,mBAAmB,CAAC;AACnF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAezD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,QAAQ,GAAG,aAAa,CA2BtF"}
@@ -0,0 +1,38 @@
1
+ import { MysqlDatabaseStore } from './mysql-database-store.js';
2
+ import { SqliteDatabaseStore } from './sqlite-database-store.js';
3
+ function normalizeConfig(database) {
4
+ const config = database;
5
+ if (config.type === 'sqlite' || config.type === 'mysql') {
6
+ return config;
7
+ }
8
+ throw new Error(`Unsupported database config shape: ${database.type ?? 'unknown'}`);
9
+ }
10
+ /**
11
+ * Creates a database store implementation based on config.
12
+ */
13
+ export function createDatabaseStore(database) {
14
+ const normalized = normalizeConfig(database);
15
+ switch (normalized.type) {
16
+ case 'sqlite': {
17
+ const cfg = normalized;
18
+ if (!cfg.filename) {
19
+ throw new Error('SQLite filename is required but missing');
20
+ }
21
+ const dbPath = cfg.filename;
22
+ return new SqliteDatabaseStore(dbPath);
23
+ }
24
+ case 'mysql': {
25
+ const cfg = normalized;
26
+ return new MysqlDatabaseStore({
27
+ host: cfg.host,
28
+ user: cfg.user,
29
+ password: cfg.password,
30
+ database: cfg.database,
31
+ port: cfg.port ?? 3306,
32
+ ssl: cfg.ssl,
33
+ });
34
+ }
35
+ default:
36
+ throw new Error(`Unsupported database type for store: ${normalized.type}`);
37
+ }
38
+ }
@@ -0,0 +1,20 @@
1
+ import { DatabaseAccount, DatabaseOperationResult, DatabaseUser } from '../types/index.js';
2
+ import { StoreResult } from './store-result.js';
3
+ /**
4
+ * Contract for database operations used by Better Auth and PKCE storage.
5
+ */
6
+ export interface DatabaseStore {
7
+ initialize(): Promise<void>;
8
+ isHealthy(): Promise<boolean>;
9
+ cleanupExpiredSessions(): Promise<DatabaseOperationResult>;
10
+ shutdown(): Promise<void>;
11
+ getAdapter(): Promise<unknown>;
12
+ getUserById(userId: string): Promise<DatabaseUser | null>;
13
+ getUserByEmail(email: string): Promise<DatabaseUser | null>;
14
+ getAccountByUserId(userId: string): Promise<DatabaseAccount | null>;
15
+ saveAuthState(state: string, codeVerifier: string, expiresAt?: Date | null): Promise<void>;
16
+ getAuthState(state: string): Promise<StoreResult>;
17
+ deleteAuthState(state: string): Promise<void>;
18
+ purgeExpiredAuthStates(): Promise<number>;
19
+ }
20
+ //# sourceMappingURL=database-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-store.d.ts","sourceRoot":"","sources":["../../src/store/database-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,aAAa;IAE5B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,sBAAsB,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC3D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG1B,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAG/B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC1D,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC5D,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAGpE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3F,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAClD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C"}
@@ -0,0 +1 @@
1
+ export {};