@passlock/client 0.9.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.
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/authentication/authenticate.d.ts +22 -0
- package/dist/authentication/authenticate.d.ts.map +1 -0
- package/dist/authentication/authenticate.fixture.d.ts +36 -0
- package/dist/authentication/authenticate.fixture.d.ts.map +1 -0
- package/dist/authentication/authenticate.fixture.js +52 -0
- package/dist/authentication/authenticate.fixture.js.map +1 -0
- package/dist/authentication/authenticate.js +69 -0
- package/dist/authentication/authenticate.js.map +1 -0
- package/dist/authentication/authenticate.test.d.ts +2 -0
- package/dist/authentication/authenticate.test.d.ts.map +1 -0
- package/dist/authentication/authenticate.test.js +111 -0
- package/dist/authentication/authenticate.test.js.map +1 -0
- package/dist/capabilities/capabilities.d.ts +18 -0
- package/dist/capabilities/capabilities.d.ts.map +1 -0
- package/dist/capabilities/capabilities.js +35 -0
- package/dist/capabilities/capabilities.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +20 -0
- package/dist/config.js.map +1 -0
- package/dist/connection/connection.d.ts +9 -0
- package/dist/connection/connection.d.ts.map +1 -0
- package/dist/connection/connection.fixture.d.ts +12 -0
- package/dist/connection/connection.fixture.d.ts.map +1 -0
- package/dist/connection/connection.fixture.js +20 -0
- package/dist/connection/connection.fixture.js.map +1 -0
- package/dist/connection/connection.js +21 -0
- package/dist/connection/connection.js.map +1 -0
- package/dist/connection/connection.test.d.ts +2 -0
- package/dist/connection/connection.test.d.ts.map +1 -0
- package/dist/connection/connection.test.js +34 -0
- package/dist/connection/connection.test.js.map +1 -0
- package/dist/effect.d.ts +33 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/effect.js +62 -0
- package/dist/effect.js.map +1 -0
- package/dist/email/email.d.ts +37 -0
- package/dist/email/email.d.ts.map +1 -0
- package/dist/email/email.fixture.d.ts +33 -0
- package/dist/email/email.fixture.d.ts.map +1 -0
- package/dist/email/email.fixture.js +30 -0
- package/dist/email/email.fixture.js.map +1 -0
- package/dist/email/email.js +78 -0
- package/dist/email/email.js.map +1 -0
- package/dist/email/email.test.d.ts +2 -0
- package/dist/email/email.test.d.ts.map +1 -0
- package/dist/email/email.test.js +101 -0
- package/dist/email/email.test.js.map +1 -0
- package/dist/event/event.d.ts +9 -0
- package/dist/event/event.d.ts.map +1 -0
- package/dist/event/event.js +23 -0
- package/dist/event/event.js.map +1 -0
- package/dist/event/event.node.test.d.ts +2 -0
- package/dist/event/event.node.test.d.ts.map +1 -0
- package/dist/event/event.node.test.js +14 -0
- package/dist/event/event.node.test.js.map +1 -0
- package/dist/event/event.test.d.ts +2 -0
- package/dist/event/event.test.d.ts.map +1 -0
- package/dist/event/event.test.js +30 -0
- package/dist/event/event.test.js.map +1 -0
- package/dist/exit.d.ts +64 -0
- package/dist/exit.d.ts.map +1 -0
- package/dist/exit.js +106 -0
- package/dist/exit.js.map +1 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/eventLogger.d.ts +18 -0
- package/dist/logging/eventLogger.d.ts.map +1 -0
- package/dist/logging/eventLogger.js +32 -0
- package/dist/logging/eventLogger.js.map +1 -0
- package/dist/logging/eventLogger.test.d.ts +2 -0
- package/dist/logging/eventLogger.test.d.ts.map +1 -0
- package/dist/logging/eventLogger.test.js +74 -0
- package/dist/logging/eventLogger.test.js.map +1 -0
- package/dist/registration/register.d.ts +29 -0
- package/dist/registration/register.d.ts.map +1 -0
- package/dist/registration/register.fixture.d.ts +40 -0
- package/dist/registration/register.fixture.d.ts.map +1 -0
- package/dist/registration/register.fixture.js +65 -0
- package/dist/registration/register.fixture.js.map +1 -0
- package/dist/registration/register.js +84 -0
- package/dist/registration/register.js.map +1 -0
- package/dist/registration/register.test.d.ts +2 -0
- package/dist/registration/register.test.d.ts.map +1 -0
- package/dist/registration/register.test.js +122 -0
- package/dist/registration/register.test.js.map +1 -0
- package/dist/storage/storage.d.ts +53 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.fixture.d.ts +6 -0
- package/dist/storage/storage.fixture.d.ts.map +1 -0
- package/dist/storage/storage.fixture.js +26 -0
- package/dist/storage/storage.fixture.js.map +1 -0
- package/dist/storage/storage.js +102 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/storage/storage.test.d.ts +2 -0
- package/dist/storage/storage.test.d.ts.map +1 -0
- package/dist/storage/storage.test.js +122 -0
- package/dist/storage/storage.test.js.map +1 -0
- package/dist/test/fixtures.d.ts +14 -0
- package/dist/test/fixtures.d.ts.map +1 -0
- package/dist/test/fixtures.js +39 -0
- package/dist/test/fixtures.js.map +1 -0
- package/dist/user/user.d.ts +18 -0
- package/dist/user/user.d.ts.map +1 -0
- package/dist/user/user.fixture.d.ts +8 -0
- package/dist/user/user.fixture.d.ts.map +1 -0
- package/dist/user/user.fixture.js +17 -0
- package/dist/user/user.fixture.js.map +1 -0
- package/dist/user/user.js +23 -0
- package/dist/user/user.js.map +1 -0
- package/dist/user/user.test.d.ts +2 -0
- package/dist/user/user.test.d.ts.map +1 -0
- package/dist/user/user.test.js +37 -0
- package/dist/user/user.test.js.map +1 -0
- package/package.json +87 -0
- package/src/authentication/authenticate.fixture.ts +72 -0
- package/src/authentication/authenticate.test.ts +207 -0
- package/src/authentication/authenticate.ts +147 -0
- package/src/capabilities/capabilities.ts +81 -0
- package/src/config.ts +43 -0
- package/src/connection/connection.fixture.ts +27 -0
- package/src/connection/connection.test.ts +61 -0
- package/src/connection/connection.ts +51 -0
- package/src/effect.ts +278 -0
- package/src/email/email.fixture.ts +49 -0
- package/src/email/email.test.ts +186 -0
- package/src/email/email.ts +139 -0
- package/src/event/event.node.test.ts +20 -0
- package/src/event/event.test.ts +37 -0
- package/src/event/event.ts +25 -0
- package/src/index.ts +275 -0
- package/src/logging/eventLogger.test.ts +102 -0
- package/src/logging/eventLogger.ts +35 -0
- package/src/registration/register.fixture.ts +94 -0
- package/src/registration/register.test.ts +247 -0
- package/src/registration/register.ts +178 -0
- package/src/storage/storage.fixture.ts +33 -0
- package/src/storage/storage.test.ts +196 -0
- package/src/storage/storage.ts +165 -0
- package/src/test/fixtures.ts +51 -0
- package/src/user/user.fixture.ts +23 -0
- package/src/user/user.test.ts +53 -0
- package/src/user/user.ts +50 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrapper around local storage that allows us to store
|
|
3
|
+
* authentication tokens in local storage for a short period.
|
|
4
|
+
*/
|
|
5
|
+
import type { Principal } from '@passlock/shared/dist/schema/schema'
|
|
6
|
+
import { Context, Effect as E, Layer, Option as O, flow, pipe } from 'effect'
|
|
7
|
+
import type { NoSuchElementException } from 'effect/Cause'
|
|
8
|
+
|
|
9
|
+
/* Requests */
|
|
10
|
+
|
|
11
|
+
export type AuthType = 'email' | 'passkey'
|
|
12
|
+
|
|
13
|
+
export type StoredToken = {
|
|
14
|
+
token: string
|
|
15
|
+
authType: AuthType
|
|
16
|
+
expireAt: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Service */
|
|
20
|
+
|
|
21
|
+
export type StorageService = {
|
|
22
|
+
storeToken: (principal: Principal) => E.Effect<void>
|
|
23
|
+
getToken: (authType: AuthType) => E.Effect<StoredToken, NoSuchElementException>
|
|
24
|
+
clearToken: (authType: AuthType) => E.Effect<void>
|
|
25
|
+
clearExpiredToken: (authType: AuthType) => E.Effect<void>
|
|
26
|
+
clearExpiredTokens: E.Effect<void>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Utilities */
|
|
30
|
+
|
|
31
|
+
export const StorageService = Context.GenericTag<StorageService>('@services/StorageService')
|
|
32
|
+
|
|
33
|
+
// inject window.localStorage to make testing easier
|
|
34
|
+
export const Storage = Context.GenericTag<Storage>('@services/Storage')
|
|
35
|
+
|
|
36
|
+
export const buildKey = (authType: AuthType) => `passlock:${authType}:token`
|
|
37
|
+
|
|
38
|
+
// principal => token:expireAt
|
|
39
|
+
export const compressToken = (principal: Principal): string => {
|
|
40
|
+
const expireAt = principal.expireAt.getTime()
|
|
41
|
+
const token = principal.token
|
|
42
|
+
return `${token}:${expireAt}`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// token:expireAt => { authType, token, expireAt }
|
|
46
|
+
export const expandToken =
|
|
47
|
+
(authType: AuthType) =>
|
|
48
|
+
(s: string): O.Option<StoredToken> => {
|
|
49
|
+
const tokens = s.split(':')
|
|
50
|
+
if (tokens.length !== 2) return O.none()
|
|
51
|
+
|
|
52
|
+
const [token, expireAtString] = tokens
|
|
53
|
+
const parse = O.liftThrowable(Number.parseInt)
|
|
54
|
+
const expireAt = parse(expireAtString)
|
|
55
|
+
|
|
56
|
+
return O.map(expireAt, expireAt => ({ authType, token, expireAt }))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Effects */
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Store compressed token in local storage
|
|
63
|
+
* @param principal
|
|
64
|
+
* @returns
|
|
65
|
+
*/
|
|
66
|
+
export const storeToken = (principal: Principal): E.Effect<void, never, Storage> => {
|
|
67
|
+
return E.gen(function* (_) {
|
|
68
|
+
const localStorage = yield* _(Storage)
|
|
69
|
+
|
|
70
|
+
const storeEffect = E.try(() => {
|
|
71
|
+
const compressed = compressToken(principal)
|
|
72
|
+
const key = buildKey(principal.authStatement.authType)
|
|
73
|
+
localStorage.setItem(key, compressed)
|
|
74
|
+
}).pipe(E.orElse(() => E.unit)) // We dont care if it fails
|
|
75
|
+
|
|
76
|
+
return yield* _(storeEffect)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get stored token from local storage
|
|
82
|
+
* @param authType
|
|
83
|
+
* @returns
|
|
84
|
+
*/
|
|
85
|
+
export const getToken = (
|
|
86
|
+
authType: AuthType,
|
|
87
|
+
): E.Effect<StoredToken, NoSuchElementException, Storage> => {
|
|
88
|
+
return E.gen(function* (_) {
|
|
89
|
+
const localStorage = yield* _(Storage)
|
|
90
|
+
|
|
91
|
+
const getEffect = pipe(
|
|
92
|
+
O.some(buildKey(authType)),
|
|
93
|
+
O.flatMap(key => pipe(localStorage.getItem(key), O.fromNullable)),
|
|
94
|
+
O.flatMap(expandToken(authType)),
|
|
95
|
+
O.filter(({ expireAt: expireAt }) => expireAt > Date.now()),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return yield* _(getEffect)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Remove token from local storage
|
|
104
|
+
* @param authType
|
|
105
|
+
* @returns
|
|
106
|
+
*/
|
|
107
|
+
export const clearToken = (authType: AuthType): E.Effect<void, never, Storage> => {
|
|
108
|
+
return E.gen(function* (_) {
|
|
109
|
+
const localStorage = yield* _(Storage)
|
|
110
|
+
localStorage.removeItem(buildKey(authType))
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Only clear if now > token.expireAt
|
|
116
|
+
* @param authType
|
|
117
|
+
* @param defer
|
|
118
|
+
* @returns
|
|
119
|
+
*/
|
|
120
|
+
export const clearExpiredToken = (authType: AuthType): E.Effect<void, never, Storage> => {
|
|
121
|
+
const key = buildKey(authType)
|
|
122
|
+
|
|
123
|
+
const effect = E.gen(function* (_) {
|
|
124
|
+
const storage = yield* _(Storage)
|
|
125
|
+
const item = yield* _(O.fromNullable(storage.getItem(key)))
|
|
126
|
+
const token = yield* _(expandToken(authType)(item))
|
|
127
|
+
|
|
128
|
+
if (token.expireAt < Date.now()) {
|
|
129
|
+
storage.removeItem(key)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// we don't care if it fails
|
|
134
|
+
return pipe(
|
|
135
|
+
effect,
|
|
136
|
+
E.match({
|
|
137
|
+
onSuccess: () => E.unit,
|
|
138
|
+
onFailure: () => E.unit,
|
|
139
|
+
}),
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const clearExpiredTokens: E.Effect<void, never, Storage> = E.all([
|
|
144
|
+
clearExpiredToken('passkey'),
|
|
145
|
+
clearExpiredToken('email'),
|
|
146
|
+
])
|
|
147
|
+
|
|
148
|
+
/* Live */
|
|
149
|
+
|
|
150
|
+
/* v8 ignore start */
|
|
151
|
+
export const StorageServiceLive = Layer.effect(
|
|
152
|
+
StorageService,
|
|
153
|
+
E.gen(function* (_) {
|
|
154
|
+
const context = yield* _(E.context<Storage>())
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
storeToken: flow(storeToken, E.provide(context)),
|
|
158
|
+
getToken: flow(getToken, E.provide(context)),
|
|
159
|
+
clearToken: flow(clearToken, E.provide(context)),
|
|
160
|
+
clearExpiredToken: flow(clearExpiredToken, E.provide(context)),
|
|
161
|
+
clearExpiredTokens: pipe(clearExpiredTokens, E.provide(context)),
|
|
162
|
+
}
|
|
163
|
+
}),
|
|
164
|
+
)
|
|
165
|
+
/* v8 ignore stop */
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Principal } from '@passlock/shared/dist/schema/schema'
|
|
2
|
+
import { Effect as E, Layer as L } from 'effect'
|
|
3
|
+
import { Capabilities } from '../capabilities/capabilities'
|
|
4
|
+
import { StorageService, type StoredToken } from '../storage/storage'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export const session = 'session'
|
|
8
|
+
export const token = 'token'
|
|
9
|
+
export const code = 'code'
|
|
10
|
+
export const authType = 'passkey'
|
|
11
|
+
export const expireAt = Date.now() + 10000
|
|
12
|
+
|
|
13
|
+
export const principal: Principal = {
|
|
14
|
+
token: 'token',
|
|
15
|
+
subject: {
|
|
16
|
+
id: '1',
|
|
17
|
+
email: 'john.doe@gmail.com',
|
|
18
|
+
firstName: 'john',
|
|
19
|
+
lastName: 'doe',
|
|
20
|
+
emailVerified: false,
|
|
21
|
+
},
|
|
22
|
+
authStatement: {
|
|
23
|
+
authType: 'email',
|
|
24
|
+
userVerified: false,
|
|
25
|
+
authTimestamp: new Date(0),
|
|
26
|
+
},
|
|
27
|
+
expireAt: new Date(0),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const capabilitiesTest = L.succeed(
|
|
31
|
+
Capabilities,
|
|
32
|
+
Capabilities.of({
|
|
33
|
+
passkeySupport: E.unit,
|
|
34
|
+
isPasskeySupport: E.succeed(true),
|
|
35
|
+
autofillSupport: E.unit,
|
|
36
|
+
isAutofillSupport: E.succeed(true),
|
|
37
|
+
}),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
export const storedToken: StoredToken = { token, authType, expireAt }
|
|
41
|
+
|
|
42
|
+
export const storageServiceTest = L.succeed(
|
|
43
|
+
StorageService,
|
|
44
|
+
StorageService.of({
|
|
45
|
+
storeToken: () => E.unit,
|
|
46
|
+
getToken: () => E.succeed(storedToken),
|
|
47
|
+
clearToken: () => E.unit,
|
|
48
|
+
clearExpiredToken: () => E.unit,
|
|
49
|
+
clearExpiredTokens: E.unit,
|
|
50
|
+
}),
|
|
51
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BadRequest } from '@passlock/shared/dist/error/error'
|
|
2
|
+
import { RpcClient } from '@passlock/shared/dist/rpc/rpc'
|
|
3
|
+
import { IsExistingUserReq, IsExistingUserRes } from '@passlock/shared/dist/rpc/user'
|
|
4
|
+
import { Effect as E, Layer as L } from 'effect'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export const email = 'jdoe@gmail.com'
|
|
8
|
+
|
|
9
|
+
export const rpcClientTest = L.succeed(
|
|
10
|
+
RpcClient,
|
|
11
|
+
RpcClient.of({
|
|
12
|
+
preConnect: () => E.succeed({ warmed: true }),
|
|
13
|
+
isExistingUser: () => E.succeed({ existingUser: true }),
|
|
14
|
+
verifyEmail: () => E.succeed({ verified: true }),
|
|
15
|
+
getRegistrationOptions: () => E.fail(new BadRequest({ message: 'Not implemeneted' })),
|
|
16
|
+
verifyRegistrationCredential: () => E.fail(new BadRequest({ message: 'Not implemeneted' })),
|
|
17
|
+
getAuthenticationOptions: () => E.fail(new BadRequest({ message: 'Not implemeneted' })),
|
|
18
|
+
verifyAuthenticationCredential: () => E.fail(new BadRequest({ message: 'Not implemeneted' })),
|
|
19
|
+
}),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export const isRegisteredReq = new IsExistingUserReq({ email })
|
|
23
|
+
export const isRegisteredRes = new IsExistingUserRes({ existingUser: false })
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type RouterOps, RpcClient } from '@passlock/shared/dist/rpc/rpc'
|
|
2
|
+
import { Effect as E, Layer as L, Layer, LogLevel, Logger, pipe } from 'effect'
|
|
3
|
+
import { describe, expect, test } from 'vitest'
|
|
4
|
+
import { mock } from 'vitest-mock-extended'
|
|
5
|
+
import { UserService, UserServiceLive } from './user'
|
|
6
|
+
import * as Fixture from './user.fixture'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
describe('isExistingUser should', () => {
|
|
10
|
+
test('return true when the user already has a passkey', async () => {
|
|
11
|
+
const assertions = E.gen(function* (_) {
|
|
12
|
+
const service = yield* _(UserService)
|
|
13
|
+
const result = yield* _(service.isExistingUser({ email: Fixture.email }))
|
|
14
|
+
|
|
15
|
+
expect(result).toBe(true)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const service = pipe(UserServiceLive, L.provide(Fixture.rpcClientTest))
|
|
19
|
+
|
|
20
|
+
const effect = pipe(E.provide(assertions, service), Logger.withMinimumLogLevel(LogLevel.None))
|
|
21
|
+
|
|
22
|
+
return E.runPromise(effect)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('send the email to the backend', async () => {
|
|
26
|
+
const assertions = E.gen(function* (_) {
|
|
27
|
+
const service = yield* _(UserService)
|
|
28
|
+
const result = yield* _(service.isExistingUser({ email: Fixture.email }))
|
|
29
|
+
|
|
30
|
+
expect(result).toBe(false)
|
|
31
|
+
const rpcClient = yield* _(RpcClient)
|
|
32
|
+
expect(rpcClient.isExistingUser).toBeCalledWith(Fixture.isRegisteredReq)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const rpcClientTest = Layer.effect(
|
|
36
|
+
RpcClient,
|
|
37
|
+
E.sync(() => {
|
|
38
|
+
const rpcMock = mock<RouterOps>()
|
|
39
|
+
|
|
40
|
+
rpcMock.isExistingUser.mockReturnValue(E.succeed(Fixture.isRegisteredRes))
|
|
41
|
+
|
|
42
|
+
return rpcMock
|
|
43
|
+
}),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const service = pipe(UserServiceLive, L.provide(rpcClientTest))
|
|
47
|
+
|
|
48
|
+
const layers = L.merge(service, rpcClientTest)
|
|
49
|
+
const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
|
|
50
|
+
|
|
51
|
+
return E.runPromise(effect)
|
|
52
|
+
})
|
|
53
|
+
})
|
package/src/user/user.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check for an existing user
|
|
3
|
+
*/
|
|
4
|
+
import type { BadRequest } from '@passlock/shared/dist/error/error'
|
|
5
|
+
import { RpcClient } from '@passlock/shared/dist/rpc/rpc'
|
|
6
|
+
import { IsExistingUserReq } from '@passlock/shared/dist/rpc/user'
|
|
7
|
+
import { Context, Effect as E, Layer, flow } from 'effect'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/* Requests */
|
|
11
|
+
|
|
12
|
+
export type Email = { email: string }
|
|
13
|
+
|
|
14
|
+
/* Service */
|
|
15
|
+
|
|
16
|
+
export type UserService = {
|
|
17
|
+
isExistingUser: (email: Email) => E.Effect<boolean, BadRequest>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const UserService = Context.GenericTag<UserService>('@services/UserService')
|
|
21
|
+
|
|
22
|
+
/* Effects */
|
|
23
|
+
|
|
24
|
+
type Dependencies = RpcClient
|
|
25
|
+
|
|
26
|
+
export const isExistingUser = (request: Email): E.Effect<boolean, BadRequest, Dependencies> => {
|
|
27
|
+
return E.gen(function* (_) {
|
|
28
|
+
yield* _(E.logInfo('Checking registration status'))
|
|
29
|
+
const rpcClient = yield* _(RpcClient)
|
|
30
|
+
|
|
31
|
+
yield* _(E.logDebug('Making RPC request'))
|
|
32
|
+
const { existingUser } = yield* _(rpcClient.isExistingUser(new IsExistingUserReq(request)))
|
|
33
|
+
|
|
34
|
+
return existingUser
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Live */
|
|
39
|
+
|
|
40
|
+
/* v8 ignore start */
|
|
41
|
+
export const UserServiceLive = Layer.effect(
|
|
42
|
+
UserService,
|
|
43
|
+
E.gen(function* (_) {
|
|
44
|
+
const context = yield* _(E.context<RpcClient>())
|
|
45
|
+
return UserService.of({
|
|
46
|
+
isExistingUser: flow(isExistingUser, E.provide(context)),
|
|
47
|
+
})
|
|
48
|
+
}),
|
|
49
|
+
)
|
|
50
|
+
/* v8 ignore stop */
|