@withwiz/toolkit 0.8.0 → 0.9.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/README.ko.md +16 -25
- package/README.md +16 -25
- package/dist/{chunk-XXPB4C37.js → chunk-2GK4LDPV.js} +6 -2
- package/dist/chunk-2JUFL4KX.js +41 -0
- package/dist/{chunk-NTZBMWB6.js → chunk-3P4TZM3O.js} +1 -1
- package/dist/chunk-43XXILCX.js +118 -0
- package/dist/{chunk-VWAB4PM7.js → chunk-5S7M2AJL.js} +1 -1
- package/dist/chunk-5VDT3ZB4.js +139 -0
- package/dist/{chunk-SCAFDDJP.js → chunk-BICSVLN7.js} +2 -2
- package/dist/chunk-E4TKORMQ.js +59 -0
- package/dist/chunk-GSRAIDD7.js +134 -0
- package/dist/{chunk-BRBOGLFT.js → chunk-KHXS4DGA.js} +3 -3
- package/dist/{chunk-V3WZFUY3.js → chunk-QMBNFCCK.js} +1 -1
- package/dist/{chunk-OII5BXCZ.js → chunk-U2TGXJ3A.js} +1 -1
- package/dist/{chunk-NLV7IEYM.js → chunk-WSHRBK6Y.js} +1 -1
- package/dist/{chunk-VH57Y5Z4.js → chunk-XDB3K74B.js} +1 -1
- package/dist/{chunk-JHUWZ6YW.js → chunk-YYTA6ERS.js} +6 -36
- package/dist/core/auth/adapters/sql/dialect.d.ts +21 -0
- package/dist/core/auth/adapters/sql/dialect.js +11 -0
- package/dist/core/auth/adapters/sql/email-token-repository.d.ts +17 -0
- package/dist/core/auth/adapters/sql/email-token-repository.js +9 -0
- package/dist/core/auth/adapters/sql/index.d.ts +7 -0
- package/dist/core/auth/adapters/sql/index.js +27 -0
- package/dist/core/auth/adapters/sql/oauth-account-repository.d.ts +18 -0
- package/dist/core/auth/adapters/sql/oauth-account-repository.js +9 -0
- package/dist/core/auth/adapters/sql/types.d.ts +73 -0
- package/dist/core/auth/adapters/sql/types.js +7 -0
- package/dist/core/auth/adapters/sql/user-repository.d.ts +20 -0
- package/dist/core/auth/adapters/sql/user-repository.js +9 -0
- package/dist/core/auth/index.js +9 -9
- package/dist/core/auth/oauth/index.js +7 -7
- package/dist/core/auth/oauth/providers/index.js +6 -6
- package/dist/core/auth/services/index.js +4 -4
- package/dist/core/cache/cache-factory.js +3 -3
- package/dist/core/cache/cache-invalidation.js +4 -4
- package/dist/core/cache/cache-wrapper.js +4 -4
- package/dist/core/cache/cache.js +5 -5
- package/dist/core/cache/hybrid-cache-manager.js +2 -2
- package/dist/core/cache/index.js +5 -5
- package/dist/core/cache/inmemory-cache-manager.d.ts +0 -9
- package/dist/core/cache/inmemory-cache-manager.js +1 -1
- package/dist/core/error/friendly-messages-v2.js +3 -3
- package/dist/core/error/index.js +6 -6
- package/dist/core/error/messages/index.js +2 -2
- package/dist/core/geolocation/index.js +7 -7
- package/dist/core/geolocation/providers/index.js +7 -7
- package/dist/core/system/health-check.js +2 -2
- package/dist/core/system/index.js +5 -5
- package/dist/initialize.js +6 -6
- package/dist/next/auth-handlers/index.js +16 -16
- package/dist/next/auth-handlers/oauth-authorize.handler.js +4 -4
- package/dist/next/auth-handlers/oauth-callback.handler.js +5 -5
- package/dist/next/error/index.js +7 -7
- package/dist/next/middleware/error-handler.js +4 -4
- package/dist/next/middleware/index.js +6 -6
- package/dist/next/middleware/wrappers.js +6 -6
- package/dist/next/utils/index.js +19 -19
- package/package.json +3 -2
- package/dist/{chunk-KKZCNF6U.js → chunk-7WG7XPXC.js} +3 -3
- package/dist/{chunk-SXBDP3CK.js → chunk-JOYW3OZB.js} +3 -3
- package/dist/{chunk-ER6LSA4M.js → chunk-P7WHMKN5.js} +6 -6
- package/dist/{chunk-VDRLJU6C.js → chunk-Q5RPNLET.js} +6 -6
package/README.ko.md
CHANGED
|
@@ -7,13 +7,12 @@ Shared utility library for [withwiz](https://github.com/greeun) projects — 인
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **Composition root** — 단일 `initialize()`로 모든 티어 설정을 한 번에 조립
|
|
10
|
-
- **Framework tiers** — `core` / `next` / `
|
|
10
|
+
- **Framework tiers** — `core` / `next` / `prisma` 3개 티어로 의존성 분리 (0.8+)
|
|
11
11
|
- **Auth** — JWT, password hashing, OAuth helpers (Google / GitHub / Kakao / Microsoft / Meta), state-cookie CSRF 바인딩, Prisma adapter
|
|
12
12
|
- **Cache** — Redis / in-memory / hybrid / noop 백엔드, factory, invalidation, defaults
|
|
13
13
|
- **Constants** — Error codes, messages, pagination, security constants
|
|
14
|
-
- **Error** — Typed `AppError`, framework-aware error handler
|
|
14
|
+
- **Error** — Typed `AppError`, framework-aware error handler (core · next)
|
|
15
15
|
- **Geolocation** — GeoIP lookup, batch processing, provider factory
|
|
16
|
-
- **Hooks** — `useDataTable`, `useDebounce`, `useExitIntent`, `useTimezone`
|
|
17
16
|
- **Logger** — Winston-based structured logger with daily rotation
|
|
18
17
|
- **Middleware** — Auth, rate-limiting, CORS, security middleware wrappers (Next.js)
|
|
19
18
|
- **Storage** — Cloudflare R2 (AWS S3-compatible) storage
|
|
@@ -38,15 +37,17 @@ yarn add @withwiz/toolkit
|
|
|
38
37
|
|
|
39
38
|
```bash
|
|
40
39
|
# next 티어를 쓸 때
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# react 티어를 쓸 때
|
|
44
|
-
npm install react react-dom
|
|
40
|
+
# (React 도 필요 — next/error/ErrorBoundary 가 React 컴포넌트)
|
|
41
|
+
npm install next react react-dom
|
|
45
42
|
|
|
46
43
|
# core 티어만 쓰는 백엔드 / CLI
|
|
47
44
|
# (별도 peer 설치 불필요)
|
|
48
45
|
```
|
|
49
46
|
|
|
47
|
+
> **React 컴포넌트 분리 (0.8).** `react` 티어(UI 컴포넌트, hooks, `error-display`)는
|
|
48
|
+
> 독립 패키지 [`@withwiz/ui`](https://github.com/greeun) 로 추출되었습니다.
|
|
49
|
+
> `@withwiz/toolkit/react/*` → `@withwiz/ui/react/*` 로 import 경로를 치환하세요.
|
|
50
|
+
|
|
50
51
|
## Quick start
|
|
51
52
|
|
|
52
53
|
```ts
|
|
@@ -87,13 +88,6 @@ const geo = await getGeoLocation('8.8.8.8')
|
|
|
87
88
|
// { country: 'US', city: 'Mountain View', ... }
|
|
88
89
|
```
|
|
89
90
|
|
|
90
|
-
```tsx
|
|
91
|
-
// Hooks (React)
|
|
92
|
-
import { useDebounce } from '@withwiz/toolkit/react/hooks/useDebounce'
|
|
93
|
-
|
|
94
|
-
const debouncedQuery = useDebounce(query, 300)
|
|
95
|
-
```
|
|
96
|
-
|
|
97
91
|
```ts
|
|
98
92
|
// Utils
|
|
99
93
|
import { sanitizeInput } from '@withwiz/toolkit/core/utils/sanitizer'
|
|
@@ -132,9 +126,10 @@ createAuthHandlers({ ...options, tokenDelivery: 'header' });
|
|
|
132
126
|
|
|
133
127
|
## Module reference
|
|
134
128
|
|
|
135
|
-
0.7부터 모든 subpath는 프레임워크 의존성에 따라
|
|
129
|
+
0.7부터 모든 subpath는 프레임워크 의존성에 따라 티어로 분리됩니다
|
|
130
|
+
(0.8부터 `core` / `next` / `prisma` 3개 — `react` 티어는 `@withwiz/ui` 로 이동).
|
|
136
131
|
자세한 티어 모델·규칙·마이그레이션 매핑은 [`docs/FRAMEWORK_TIERS.md`](docs/FRAMEWORK_TIERS.md)
|
|
137
|
-
및 [`CHANGELOG.md`](CHANGELOG.md) 의 0.7.0 항목을 참조하세요.
|
|
132
|
+
및 [`CHANGELOG.md`](CHANGELOG.md) 의 0.7.0 / 0.8.0 항목을 참조하세요.
|
|
138
133
|
|
|
139
134
|
### Composition root
|
|
140
135
|
|
|
@@ -179,14 +174,11 @@ createAuthHandlers({ ...options, tokenDelivery: 'header' });
|
|
|
179
174
|
| `/next/error` | `error-handler` (NextResponse), `LocaleDetector`, `ErrorBoundary` |
|
|
180
175
|
| `/next/utils` | `api-helpers`, `cors`, `csv-export`, `error-processor` |
|
|
181
176
|
|
|
182
|
-
### `react` —
|
|
177
|
+
### `react` — `@withwiz/ui` 로 이동 (0.8)
|
|
183
178
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
| `/react/hooks/{useDataTable,useDebounce,useExitIntent,useTimezone}` | React hooks |
|
|
188
|
-
| `/react/error` | `error-display` (sonner toast) |
|
|
189
|
-
| `/react/utils/{client-utils,qr-code}` | Browser-context utilities |
|
|
179
|
+
React 티어(UI 컴포넌트, hooks, `error-display`, 브라우저 컨텍스트 utils)는 독립 패키지
|
|
180
|
+
[`@withwiz/ui`](https://github.com/greeun) 로 추출되었습니다.
|
|
181
|
+
import 경로 치환: `@withwiz/toolkit/react/*` → `@withwiz/ui/react/*`.
|
|
190
182
|
|
|
191
183
|
### `prisma` — Prisma 의존
|
|
192
184
|
|
|
@@ -205,8 +197,7 @@ createAuthHandlers({ ...options, tokenDelivery: 'header' });
|
|
|
205
197
|
사용하는 티어가 요구하는 peer만 설치하세요:
|
|
206
198
|
|
|
207
199
|
- `core` 티어만 쓰는 백엔드 / CLI: peer 불필요
|
|
208
|
-
- `next` 티어: Next.js >= 15
|
|
209
|
-
- `react` 티어: React >= 18, React-DOM >= 18
|
|
200
|
+
- `next` 티어: Next.js >= 15, 그리고 React >= 18 / React-DOM >= 18 (`next/error/ErrorBoundary` 가 React 컴포넌트)
|
|
210
201
|
- `prisma` 티어: Prisma 호환 클라이언트 (덕 타이핑)
|
|
211
202
|
- Prisma 어댑터의 `EmailTokenRepository` 등 일부 모듈: `date-fns >= 3` (optional)
|
|
212
203
|
- 이메일 전송: `nodemailer >= 6` (optional)
|
package/README.md
CHANGED
|
@@ -7,13 +7,12 @@ Shared utility library for [withwiz](https://github.com/greeun) projects — a c
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **Composition root** — assemble every tier's configuration at once with a single `initialize()`
|
|
10
|
-
- **Framework tiers** — dependencies split across
|
|
10
|
+
- **Framework tiers** — dependencies split across three tiers: `core` / `next` / `prisma` (0.8+)
|
|
11
11
|
- **Auth** — JWT, password hashing, OAuth helpers (Google / GitHub / Kakao / Microsoft / Meta), state-cookie CSRF binding, Prisma adapter
|
|
12
12
|
- **Cache** — Redis / in-memory / hybrid / noop backends, factory, invalidation, defaults
|
|
13
13
|
- **Constants** — Error codes, messages, pagination, security constants
|
|
14
|
-
- **Error** — Typed `AppError`, framework-aware error handler
|
|
14
|
+
- **Error** — Typed `AppError`, framework-aware error handler (core · next)
|
|
15
15
|
- **Geolocation** — GeoIP lookup, batch processing, provider factory
|
|
16
|
-
- **Hooks** — `useDataTable`, `useDebounce`, `useExitIntent`, `useTimezone`
|
|
17
16
|
- **Logger** — Winston-based structured logger with daily rotation
|
|
18
17
|
- **Middleware** — Auth, rate-limiting, CORS, security middleware wrappers (Next.js)
|
|
19
18
|
- **Storage** — Cloudflare R2 (AWS S3-compatible) storage
|
|
@@ -38,15 +37,17 @@ yarn add @withwiz/toolkit
|
|
|
38
37
|
|
|
39
38
|
```bash
|
|
40
39
|
# When using the next tier
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# When using the react tier
|
|
44
|
-
npm install react react-dom
|
|
40
|
+
# (React is also needed — next/error/ErrorBoundary is a React component)
|
|
41
|
+
npm install next react react-dom
|
|
45
42
|
|
|
46
43
|
# Backend / CLI using only the core tier
|
|
47
44
|
# (no extra peers required)
|
|
48
45
|
```
|
|
49
46
|
|
|
47
|
+
> **React components moved out (0.8).** The `react` tier (UI components, hooks,
|
|
48
|
+
> `error-display`) was extracted to [`@withwiz/ui`](https://github.com/greeun).
|
|
49
|
+
> Migrate by replacing `@withwiz/toolkit/react/*` → `@withwiz/ui/react/*`.
|
|
50
|
+
|
|
50
51
|
## Quick start
|
|
51
52
|
|
|
52
53
|
```ts
|
|
@@ -87,13 +88,6 @@ const geo = await getGeoLocation('8.8.8.8')
|
|
|
87
88
|
// { country: 'US', city: 'Mountain View', ... }
|
|
88
89
|
```
|
|
89
90
|
|
|
90
|
-
```tsx
|
|
91
|
-
// Hooks (React)
|
|
92
|
-
import { useDebounce } from '@withwiz/toolkit/react/hooks/useDebounce'
|
|
93
|
-
|
|
94
|
-
const debouncedQuery = useDebounce(query, 300)
|
|
95
|
-
```
|
|
96
|
-
|
|
97
91
|
```ts
|
|
98
92
|
// Utils
|
|
99
93
|
import { sanitizeInput } from '@withwiz/toolkit/core/utils/sanitizer'
|
|
@@ -132,9 +126,10 @@ createAuthHandlers({ ...options, tokenDelivery: 'header' });
|
|
|
132
126
|
|
|
133
127
|
## Module reference
|
|
134
128
|
|
|
135
|
-
Since 0.7, every subpath is split into
|
|
129
|
+
Since 0.7, every subpath is split into tiers based on its framework dependencies
|
|
130
|
+
(`core` / `next` / `prisma` since 0.8 — the `react` tier moved to `@withwiz/ui`).
|
|
136
131
|
For the detailed tier model, rules, and migration mapping, see [`docs/FRAMEWORK_TIERS.md`](docs/FRAMEWORK_TIERS.md)
|
|
137
|
-
and the 0.7.0
|
|
132
|
+
and the 0.7.0 / 0.8.0 entries in [`CHANGELOG.md`](CHANGELOG.md).
|
|
138
133
|
|
|
139
134
|
### Composition root
|
|
140
135
|
|
|
@@ -179,14 +174,11 @@ and the 0.7.0 entry in [`CHANGELOG.md`](CHANGELOG.md).
|
|
|
179
174
|
| `/next/error` | `error-handler` (NextResponse), `LocaleDetector`, `ErrorBoundary` |
|
|
180
175
|
| `/next/utils` | `api-helpers`, `cors`, `csv-export`, `error-processor` |
|
|
181
176
|
|
|
182
|
-
### `react` —
|
|
177
|
+
### `react` — moved to `@withwiz/ui` (0.8)
|
|
183
178
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
| `/react/hooks/{useDataTable,useDebounce,useExitIntent,useTimezone}` | React hooks |
|
|
188
|
-
| `/react/error` | `error-display` (sonner toast) |
|
|
189
|
-
| `/react/utils/{client-utils,qr-code}` | Browser-context utilities |
|
|
179
|
+
The React tier (UI components, hooks, `error-display`, browser-context utils) was
|
|
180
|
+
extracted to the standalone [`@withwiz/ui`](https://github.com/greeun) package.
|
|
181
|
+
Migrate imports: `@withwiz/toolkit/react/*` → `@withwiz/ui/react/*`.
|
|
190
182
|
|
|
191
183
|
### `prisma` — depends on Prisma
|
|
192
184
|
|
|
@@ -205,8 +197,7 @@ and the 0.7.0 entry in [`CHANGELOG.md`](CHANGELOG.md).
|
|
|
205
197
|
Install only the peers required by the tiers you use:
|
|
206
198
|
|
|
207
199
|
- Backend / CLI using only the `core` tier: no peers required
|
|
208
|
-
- `next` tier: Next.js >= 15
|
|
209
|
-
- `react` tier: React >= 18, React-DOM >= 18
|
|
200
|
+
- `next` tier: Next.js >= 15, plus React >= 18 / React-DOM >= 18 (`next/error/ErrorBoundary` is a React component)
|
|
210
201
|
- `prisma` tier: a Prisma-compatible client (duck-typed)
|
|
211
202
|
- Some modules such as the Prisma adapter's `EmailTokenRepository`: `date-fns >= 3` (optional)
|
|
212
203
|
- Email delivery: `nodemailer >= 6` (optional)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InMemoryCacheManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YYTA6ERS.js";
|
|
4
4
|
import {
|
|
5
5
|
isRedisGloballyDisabled,
|
|
6
6
|
notifyRedisError
|
|
@@ -122,7 +122,11 @@ var HybridCacheManager = class _HybridCacheManager {
|
|
|
122
122
|
if (effectiveBackend !== "redis") {
|
|
123
123
|
promises.push(this.memoryCache.delete(key));
|
|
124
124
|
}
|
|
125
|
-
|
|
125
|
+
if (promises.length === 1) {
|
|
126
|
+
await promises[0];
|
|
127
|
+
} else if (promises.length > 1) {
|
|
128
|
+
await Promise.allSettled(promises);
|
|
129
|
+
}
|
|
126
130
|
}
|
|
127
131
|
/**
|
|
128
132
|
* 패턴 매칭으로 캐시 삭제
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/core/auth/adapters/sql/dialect.ts
|
|
2
|
+
var POSTGRES = {
|
|
3
|
+
placeholder: (index) => `$${index}`,
|
|
4
|
+
quoteId: (id) => `"${id}"`
|
|
5
|
+
};
|
|
6
|
+
var MYSQL = {
|
|
7
|
+
placeholder: () => "?",
|
|
8
|
+
quoteId: (id) => `\`${id}\``
|
|
9
|
+
};
|
|
10
|
+
function getDialect(dialect) {
|
|
11
|
+
switch (dialect) {
|
|
12
|
+
case "postgres":
|
|
13
|
+
return POSTGRES;
|
|
14
|
+
case "mysql":
|
|
15
|
+
return MYSQL;
|
|
16
|
+
default:
|
|
17
|
+
throw new Error(`Unsupported SQL dialect: ${dialect}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var ParamBuilder = class {
|
|
21
|
+
constructor(dialect) {
|
|
22
|
+
this.dialect = dialect;
|
|
23
|
+
this._params = [];
|
|
24
|
+
}
|
|
25
|
+
add(value) {
|
|
26
|
+
this._params.push(value);
|
|
27
|
+
return this.dialect.placeholder(this._params.length);
|
|
28
|
+
}
|
|
29
|
+
get params() {
|
|
30
|
+
return this._params;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function columnList(columns, dialect) {
|
|
34
|
+
return columns.map((c) => dialect.quoteId(c)).join(", ");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
getDialect,
|
|
39
|
+
ParamBuilder,
|
|
40
|
+
columnList
|
|
41
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveConfig
|
|
3
|
+
} from "./chunk-E4TKORMQ.js";
|
|
4
|
+
import {
|
|
5
|
+
ParamBuilder,
|
|
6
|
+
columnList,
|
|
7
|
+
getDialect
|
|
8
|
+
} from "./chunk-2JUFL4KX.js";
|
|
9
|
+
|
|
10
|
+
// src/core/auth/adapters/sql/email-token-repository.ts
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
var SqlEmailTokenRepository = class {
|
|
13
|
+
constructor(exec, config) {
|
|
14
|
+
this.exec = exec;
|
|
15
|
+
this.config = resolveConfig(config);
|
|
16
|
+
this.dialect = getDialect(this.config.dialect);
|
|
17
|
+
}
|
|
18
|
+
get cols() {
|
|
19
|
+
return this.config.emailTokenColumns;
|
|
20
|
+
}
|
|
21
|
+
tableName(type) {
|
|
22
|
+
switch (type) {
|
|
23
|
+
case "EMAIL_VERIFICATION":
|
|
24
|
+
return this.config.tables.emailVerification;
|
|
25
|
+
case "PASSWORD_RESET":
|
|
26
|
+
return this.config.tables.passwordReset;
|
|
27
|
+
case "MAGIC_LINK":
|
|
28
|
+
return this.config.tables.magicLink;
|
|
29
|
+
default:
|
|
30
|
+
throw new Error(`Unsupported token type: ${type}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
quotedTable(type) {
|
|
34
|
+
return this.dialect.quoteId(this.tableName(type));
|
|
35
|
+
}
|
|
36
|
+
selectColumns(includeUsed) {
|
|
37
|
+
const c = this.cols;
|
|
38
|
+
const base = [c.id, c.email, c.token, c.expires, c.createdAt];
|
|
39
|
+
return includeUsed ? [...base, c.used] : base;
|
|
40
|
+
}
|
|
41
|
+
async create(email, token, type, expiresAt) {
|
|
42
|
+
const c = this.cols;
|
|
43
|
+
const id = randomUUID();
|
|
44
|
+
const now = /* @__PURE__ */ new Date();
|
|
45
|
+
const isMagic = type === "MAGIC_LINK";
|
|
46
|
+
const pb = new ParamBuilder(this.dialect);
|
|
47
|
+
const entries = [
|
|
48
|
+
[c.id, id],
|
|
49
|
+
[c.email, email],
|
|
50
|
+
[c.token, token],
|
|
51
|
+
[c.expires, expiresAt],
|
|
52
|
+
[c.createdAt, now]
|
|
53
|
+
];
|
|
54
|
+
if (isMagic) entries.push([c.used, false]);
|
|
55
|
+
const colSql = entries.map(([col]) => this.dialect.quoteId(col)).join(", ");
|
|
56
|
+
const valSql = entries.map(([, val]) => pb.add(val)).join(", ");
|
|
57
|
+
const sql = `INSERT INTO ${this.quotedTable(type)} (${colSql}) VALUES (${valSql})`;
|
|
58
|
+
await this.exec.query(sql, pb.params);
|
|
59
|
+
return {
|
|
60
|
+
id,
|
|
61
|
+
email,
|
|
62
|
+
token,
|
|
63
|
+
type,
|
|
64
|
+
expires: expiresAt,
|
|
65
|
+
used: false,
|
|
66
|
+
createdAt: now
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async findByEmailAndToken(email, token, type) {
|
|
70
|
+
const c = this.cols;
|
|
71
|
+
const isMagic = type === "MAGIC_LINK";
|
|
72
|
+
const pb = new ParamBuilder(this.dialect);
|
|
73
|
+
const sql = `SELECT ${columnList(this.selectColumns(isMagic), this.dialect)} FROM ${this.quotedTable(type)} WHERE ${this.dialect.quoteId(c.email)} = ${pb.add(email)} AND ${this.dialect.quoteId(c.token)} = ${pb.add(token)}`;
|
|
74
|
+
const rows = await this.exec.query(sql, pb.params);
|
|
75
|
+
const row = rows[0];
|
|
76
|
+
if (!row) return null;
|
|
77
|
+
return {
|
|
78
|
+
id: row[c.id],
|
|
79
|
+
email: row[c.email],
|
|
80
|
+
token: row[c.token],
|
|
81
|
+
type,
|
|
82
|
+
expires: row[c.expires],
|
|
83
|
+
used: isMagic ? Boolean(row[c.used]) : false,
|
|
84
|
+
createdAt: row[c.createdAt]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async delete(email, token, type) {
|
|
88
|
+
const c = this.cols;
|
|
89
|
+
const pb = new ParamBuilder(this.dialect);
|
|
90
|
+
const sql = `DELETE FROM ${this.quotedTable(type)} WHERE ${this.dialect.quoteId(c.email)} = ${pb.add(email)} AND ${this.dialect.quoteId(c.token)} = ${pb.add(token)}`;
|
|
91
|
+
await this.exec.query(sql, pb.params);
|
|
92
|
+
}
|
|
93
|
+
async deleteExpired() {
|
|
94
|
+
const c = this.cols;
|
|
95
|
+
const tables = [
|
|
96
|
+
this.config.tables.emailVerification,
|
|
97
|
+
this.config.tables.passwordReset,
|
|
98
|
+
this.config.tables.magicLink
|
|
99
|
+
];
|
|
100
|
+
await Promise.all(
|
|
101
|
+
tables.map((t) => {
|
|
102
|
+
const pb = new ParamBuilder(this.dialect);
|
|
103
|
+
const sql = `DELETE FROM ${this.dialect.quoteId(t)} WHERE ${this.dialect.quoteId(c.expires)} < ${pb.add(/* @__PURE__ */ new Date())}`;
|
|
104
|
+
return this.exec.query(sql, pb.params);
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
async markAsUsed(id) {
|
|
109
|
+
const c = this.cols;
|
|
110
|
+
const pb = new ParamBuilder(this.dialect);
|
|
111
|
+
const sql = `UPDATE ${this.dialect.quoteId(this.config.tables.magicLink)} SET ${this.dialect.quoteId(c.used)} = ${pb.add(true)} WHERE ${this.dialect.quoteId(c.id)} = ${pb.add(id)}`;
|
|
112
|
+
await this.exec.query(sql, pb.params);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
SqlEmailTokenRepository
|
|
118
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveConfig
|
|
3
|
+
} from "./chunk-E4TKORMQ.js";
|
|
4
|
+
import {
|
|
5
|
+
ParamBuilder,
|
|
6
|
+
columnList,
|
|
7
|
+
getDialect
|
|
8
|
+
} from "./chunk-2JUFL4KX.js";
|
|
9
|
+
|
|
10
|
+
// src/core/auth/adapters/sql/oauth-account-repository.ts
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
var SqlOAuthAccountRepository = class {
|
|
13
|
+
constructor(exec, config) {
|
|
14
|
+
this.exec = exec;
|
|
15
|
+
this.config = resolveConfig(config);
|
|
16
|
+
this.dialect = getDialect(this.config.dialect);
|
|
17
|
+
}
|
|
18
|
+
get table() {
|
|
19
|
+
return this.dialect.quoteId(this.config.tables.oauthAccount);
|
|
20
|
+
}
|
|
21
|
+
get cols() {
|
|
22
|
+
return this.config.oauthColumns;
|
|
23
|
+
}
|
|
24
|
+
selectColumns() {
|
|
25
|
+
const c = this.cols;
|
|
26
|
+
return [
|
|
27
|
+
c.id,
|
|
28
|
+
c.userId,
|
|
29
|
+
c.provider,
|
|
30
|
+
c.providerAccountId,
|
|
31
|
+
c.accessToken,
|
|
32
|
+
c.refreshToken,
|
|
33
|
+
c.expiresAt,
|
|
34
|
+
c.tokenType,
|
|
35
|
+
c.scope,
|
|
36
|
+
c.createdAt,
|
|
37
|
+
c.updatedAt
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
async findByProvider(provider, providerAccountId) {
|
|
41
|
+
const c = this.cols;
|
|
42
|
+
const pb = new ParamBuilder(this.dialect);
|
|
43
|
+
const sql = `SELECT ${columnList(this.selectColumns(), this.dialect)} FROM ${this.table} WHERE ${this.dialect.quoteId(c.provider)} = ${pb.add(provider)} AND ${this.dialect.quoteId(c.providerAccountId)} = ${pb.add(providerAccountId)}`;
|
|
44
|
+
const rows = await this.exec.query(sql, pb.params);
|
|
45
|
+
return rows[0] ? this.mapToOAuthAccount(rows[0]) : null;
|
|
46
|
+
}
|
|
47
|
+
async findByUserId(userId) {
|
|
48
|
+
const pb = new ParamBuilder(this.dialect);
|
|
49
|
+
const sql = `SELECT ${columnList(this.selectColumns(), this.dialect)} FROM ${this.table} WHERE ${this.dialect.quoteId(this.cols.userId)} = ${pb.add(userId)}`;
|
|
50
|
+
const rows = await this.exec.query(sql, pb.params);
|
|
51
|
+
return rows.map((r) => this.mapToOAuthAccount(r));
|
|
52
|
+
}
|
|
53
|
+
async create(data) {
|
|
54
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
55
|
+
const c = this.cols;
|
|
56
|
+
const id = randomUUID();
|
|
57
|
+
const now = /* @__PURE__ */ new Date();
|
|
58
|
+
const pb = new ParamBuilder(this.dialect);
|
|
59
|
+
const entries = [
|
|
60
|
+
[c.id, id],
|
|
61
|
+
[c.userId, data.userId],
|
|
62
|
+
[c.provider, data.provider],
|
|
63
|
+
[c.providerAccountId, data.providerAccountId],
|
|
64
|
+
[c.accessToken, (_a = data.accessToken) != null ? _a : null],
|
|
65
|
+
[c.refreshToken, (_b = data.refreshToken) != null ? _b : null],
|
|
66
|
+
[c.expiresAt, (_c = data.expiresAt) != null ? _c : null],
|
|
67
|
+
[c.tokenType, (_d = data.tokenType) != null ? _d : null],
|
|
68
|
+
[c.scope, (_e = data.scope) != null ? _e : null],
|
|
69
|
+
[c.createdAt, now],
|
|
70
|
+
[c.updatedAt, now]
|
|
71
|
+
];
|
|
72
|
+
const colSql = entries.map(([col]) => this.dialect.quoteId(col)).join(", ");
|
|
73
|
+
const valSql = entries.map(([, val]) => pb.add(val)).join(", ");
|
|
74
|
+
const sql = `INSERT INTO ${this.table} (${colSql}) VALUES (${valSql})`;
|
|
75
|
+
await this.exec.query(sql, pb.params);
|
|
76
|
+
return {
|
|
77
|
+
id,
|
|
78
|
+
userId: data.userId,
|
|
79
|
+
provider: data.provider,
|
|
80
|
+
providerAccountId: data.providerAccountId,
|
|
81
|
+
accessToken: (_f = data.accessToken) != null ? _f : null,
|
|
82
|
+
refreshToken: (_g = data.refreshToken) != null ? _g : null,
|
|
83
|
+
expiresAt: (_h = data.expiresAt) != null ? _h : null,
|
|
84
|
+
tokenType: (_i = data.tokenType) != null ? _i : null,
|
|
85
|
+
scope: (_j = data.scope) != null ? _j : null,
|
|
86
|
+
createdAt: now,
|
|
87
|
+
updatedAt: now
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async update(id, data) {
|
|
91
|
+
const c = this.cols;
|
|
92
|
+
const pb = new ParamBuilder(this.dialect);
|
|
93
|
+
const sets = [];
|
|
94
|
+
const assign = (col, val) => sets.push(`${this.dialect.quoteId(col)} = ${pb.add(val)}`);
|
|
95
|
+
if (data.accessToken !== void 0) assign(c.accessToken, data.accessToken);
|
|
96
|
+
if (data.refreshToken !== void 0) assign(c.refreshToken, data.refreshToken);
|
|
97
|
+
if (data.expiresAt !== void 0) assign(c.expiresAt, data.expiresAt);
|
|
98
|
+
if (data.tokenType !== void 0) assign(c.tokenType, data.tokenType);
|
|
99
|
+
if (data.scope !== void 0) assign(c.scope, data.scope);
|
|
100
|
+
assign(c.updatedAt, /* @__PURE__ */ new Date());
|
|
101
|
+
const sql = `UPDATE ${this.table} SET ${sets.join(", ")} WHERE ${this.dialect.quoteId(c.id)} = ${pb.add(id)}`;
|
|
102
|
+
await this.exec.query(sql, pb.params);
|
|
103
|
+
const updated = await this.selectById(id);
|
|
104
|
+
if (!updated) throw new Error(`OAuth account not found after update: ${id}`);
|
|
105
|
+
return updated;
|
|
106
|
+
}
|
|
107
|
+
async delete(id) {
|
|
108
|
+
const pb = new ParamBuilder(this.dialect);
|
|
109
|
+
const sql = `DELETE FROM ${this.table} WHERE ${this.dialect.quoteId(this.cols.id)} = ${pb.add(id)}`;
|
|
110
|
+
await this.exec.query(sql, pb.params);
|
|
111
|
+
}
|
|
112
|
+
async selectById(id) {
|
|
113
|
+
const pb = new ParamBuilder(this.dialect);
|
|
114
|
+
const sql = `SELECT ${columnList(this.selectColumns(), this.dialect)} FROM ${this.table} WHERE ${this.dialect.quoteId(this.cols.id)} = ${pb.add(id)}`;
|
|
115
|
+
const rows = await this.exec.query(sql, pb.params);
|
|
116
|
+
return rows[0] ? this.mapToOAuthAccount(rows[0]) : null;
|
|
117
|
+
}
|
|
118
|
+
mapToOAuthAccount(row) {
|
|
119
|
+
var _a, _b, _c, _d, _e, _f;
|
|
120
|
+
const c = this.cols;
|
|
121
|
+
return {
|
|
122
|
+
id: row[c.id],
|
|
123
|
+
userId: row[c.userId],
|
|
124
|
+
provider: row[c.provider],
|
|
125
|
+
providerAccountId: row[c.providerAccountId],
|
|
126
|
+
accessToken: (_a = row[c.accessToken]) != null ? _a : null,
|
|
127
|
+
refreshToken: (_b = row[c.refreshToken]) != null ? _b : null,
|
|
128
|
+
expiresAt: (_c = row[c.expiresAt]) != null ? _c : null,
|
|
129
|
+
tokenType: (_d = row[c.tokenType]) != null ? _d : null,
|
|
130
|
+
scope: (_e = row[c.scope]) != null ? _e : null,
|
|
131
|
+
createdAt: row[c.createdAt],
|
|
132
|
+
updatedAt: (_f = row[c.updatedAt]) != null ? _f : row[c.createdAt]
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
SqlOAuthAccountRepository
|
|
139
|
+
};
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
} from "./chunk-4TGPAUJV.js";
|
|
7
7
|
import {
|
|
8
8
|
HybridCacheManager
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2GK4LDPV.js";
|
|
10
10
|
import {
|
|
11
11
|
InMemoryCacheManager
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YYTA6ERS.js";
|
|
13
13
|
import {
|
|
14
14
|
isRedisAvailableNow,
|
|
15
15
|
isRedisGloballyDisabled
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__spreadValues
|
|
3
|
+
} from "./chunk-N3ETBM74.js";
|
|
4
|
+
|
|
5
|
+
// src/core/auth/adapters/sql/types.ts
|
|
6
|
+
var DEFAULT_TABLES = {
|
|
7
|
+
user: "users",
|
|
8
|
+
oauthAccount: "accounts",
|
|
9
|
+
emailVerification: "email_verification_tokens",
|
|
10
|
+
passwordReset: "password_reset_tokens",
|
|
11
|
+
magicLink: "magic_link_tokens"
|
|
12
|
+
};
|
|
13
|
+
var DEFAULT_USER_COLUMNS = {
|
|
14
|
+
id: "id",
|
|
15
|
+
email: "email",
|
|
16
|
+
name: "name",
|
|
17
|
+
password: "password",
|
|
18
|
+
role: "role",
|
|
19
|
+
image: "image",
|
|
20
|
+
emailVerified: "email_verified",
|
|
21
|
+
isActive: "is_active",
|
|
22
|
+
createdAt: "created_at",
|
|
23
|
+
updatedAt: "updated_at",
|
|
24
|
+
lastLoginAt: "last_login_at"
|
|
25
|
+
};
|
|
26
|
+
var DEFAULT_OAUTH_COLUMNS = {
|
|
27
|
+
id: "id",
|
|
28
|
+
userId: "user_id",
|
|
29
|
+
provider: "provider",
|
|
30
|
+
providerAccountId: "provider_account_id",
|
|
31
|
+
accessToken: "access_token",
|
|
32
|
+
refreshToken: "refresh_token",
|
|
33
|
+
expiresAt: "expires_at",
|
|
34
|
+
tokenType: "token_type",
|
|
35
|
+
scope: "scope",
|
|
36
|
+
createdAt: "created_at",
|
|
37
|
+
updatedAt: "updated_at"
|
|
38
|
+
};
|
|
39
|
+
var DEFAULT_EMAIL_TOKEN_COLUMNS = {
|
|
40
|
+
id: "id",
|
|
41
|
+
email: "email",
|
|
42
|
+
token: "token",
|
|
43
|
+
expires: "expires",
|
|
44
|
+
used: "used",
|
|
45
|
+
createdAt: "created_at"
|
|
46
|
+
};
|
|
47
|
+
function resolveConfig(config) {
|
|
48
|
+
return {
|
|
49
|
+
dialect: config.dialect,
|
|
50
|
+
tables: __spreadValues(__spreadValues({}, DEFAULT_TABLES), config.tables),
|
|
51
|
+
userColumns: __spreadValues(__spreadValues({}, DEFAULT_USER_COLUMNS), config.userColumns),
|
|
52
|
+
oauthColumns: __spreadValues(__spreadValues({}, DEFAULT_OAUTH_COLUMNS), config.oauthColumns),
|
|
53
|
+
emailTokenColumns: __spreadValues(__spreadValues({}, DEFAULT_EMAIL_TOKEN_COLUMNS), config.emailTokenColumns)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
resolveConfig
|
|
59
|
+
};
|