@passlock/client 0.9.22 → 0.9.23

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 (151) hide show
  1. package/dist/authentication/authenticate.d.ts +15 -15
  2. package/dist/authentication/authenticate.fixture.d.ts +20 -6
  3. package/dist/authentication/authenticate.fixture.js +7 -5
  4. package/dist/authentication/authenticate.fixture.js.map +1 -1
  5. package/dist/authentication/authenticate.js +19 -8
  6. package/dist/authentication/authenticate.js.map +1 -1
  7. package/dist/capabilities/capabilities.d.ts +8 -4
  8. package/dist/capabilities/capabilities.js +10 -1
  9. package/dist/capabilities/capabilities.js.map +1 -1
  10. package/dist/connection/connection.d.ts +11 -7
  11. package/dist/connection/connection.fixture.d.ts +2 -2
  12. package/dist/connection/connection.fixture.js +2 -1
  13. package/dist/connection/connection.fixture.js.map +1 -1
  14. package/dist/connection/connection.js +12 -3
  15. package/dist/connection/connection.js.map +1 -1
  16. package/dist/effect.d.ts +22 -45
  17. package/dist/effect.js +55 -51
  18. package/dist/effect.js.map +1 -1
  19. package/dist/email/email.d.ts +38 -11
  20. package/dist/email/email.fixture.d.ts +19 -5
  21. package/dist/email/email.fixture.js +4 -3
  22. package/dist/email/email.fixture.js.map +1 -1
  23. package/dist/email/email.js +43 -7
  24. package/dist/email/email.js.map +1 -1
  25. package/dist/event/event.d.ts +3 -1
  26. package/dist/event/event.js +3 -0
  27. package/dist/event/event.js.map +1 -1
  28. package/dist/index.d.ts +105 -27
  29. package/dist/index.js +101 -50
  30. package/dist/index.js.map +1 -1
  31. package/dist/logging/eventLogger.d.ts +13 -1
  32. package/dist/logging/eventLogger.js +13 -0
  33. package/dist/logging/eventLogger.js.map +1 -1
  34. package/dist/registration/register.d.ts +18 -21
  35. package/dist/registration/register.fixture.d.ts +19 -5
  36. package/dist/registration/register.fixture.js +14 -7
  37. package/dist/registration/register.fixture.js.map +1 -1
  38. package/dist/registration/register.js +18 -9
  39. package/dist/registration/register.js.map +1 -1
  40. package/dist/rpc/authentication.d.ts +0 -1
  41. package/dist/rpc/authentication.js +1 -0
  42. package/dist/rpc/authentication.js.map +1 -1
  43. package/dist/rpc/client.d.ts +4 -1
  44. package/dist/rpc/client.js +12 -2
  45. package/dist/rpc/client.js.map +1 -1
  46. package/dist/rpc/config.d.ts +0 -1
  47. package/dist/rpc/connection.d.ts +0 -1
  48. package/dist/rpc/connection.js +1 -0
  49. package/dist/rpc/connection.js.map +1 -1
  50. package/dist/rpc/registration.d.ts +0 -1
  51. package/dist/rpc/registration.js +1 -0
  52. package/dist/rpc/registration.js.map +1 -1
  53. package/dist/rpc/social.d.ts +0 -1
  54. package/dist/rpc/social.js +1 -0
  55. package/dist/rpc/social.js.map +1 -1
  56. package/dist/rpc/user.d.ts +0 -1
  57. package/dist/rpc/user.js +1 -0
  58. package/dist/rpc/user.js.map +1 -1
  59. package/dist/social/social.d.ts +16 -23
  60. package/dist/social/social.fixture.d.ts +21 -9
  61. package/dist/social/social.fixture.js +8 -14
  62. package/dist/social/social.fixture.js.map +1 -1
  63. package/dist/social/social.js +14 -10
  64. package/dist/social/social.js.map +1 -1
  65. package/dist/storage/storage.d.ts +40 -12
  66. package/dist/storage/storage.fixture.d.ts +2 -2
  67. package/dist/storage/storage.fixture.js +2 -2
  68. package/dist/storage/storage.fixture.js.map +1 -1
  69. package/dist/storage/storage.js +48 -15
  70. package/dist/storage/storage.js.map +1 -1
  71. package/dist/test/fixtures.d.ts +1 -2
  72. package/dist/test/fixtures.js +20 -5
  73. package/dist/test/fixtures.js.map +1 -1
  74. package/dist/user/user.d.ts +8 -5
  75. package/dist/user/user.fixture.d.ts +2 -2
  76. package/dist/user/user.fixture.js +9 -5
  77. package/dist/user/user.fixture.js.map +1 -1
  78. package/dist/user/user.js +9 -3
  79. package/dist/user/user.js.map +1 -1
  80. package/dist/version.d.ts +1 -2
  81. package/dist/version.js +1 -1
  82. package/dist/version.js.map +1 -1
  83. package/package.json +30 -26
  84. package/src/authentication/authenticate.fixture.ts +8 -7
  85. package/src/authentication/authenticate.test.ts +59 -17
  86. package/src/authentication/authenticate.ts +34 -32
  87. package/src/capabilities/capabilities.ts +9 -8
  88. package/src/connection/connection.fixture.ts +2 -1
  89. package/src/connection/connection.test.ts +3 -3
  90. package/src/connection/connection.ts +9 -8
  91. package/src/effect.ts +129 -128
  92. package/src/email/email.fixture.ts +4 -3
  93. package/src/email/email.test.ts +4 -4
  94. package/src/email/email.ts +24 -16
  95. package/src/index.ts +225 -169
  96. package/src/logging/eventLogger.test.ts +1 -1
  97. package/src/logging/eventLogger.ts +2 -2
  98. package/src/registration/register.fixture.ts +14 -8
  99. package/src/registration/register.test.ts +13 -9
  100. package/src/registration/register.ts +37 -34
  101. package/src/rpc/authentication.ts +31 -0
  102. package/src/rpc/client.ts +173 -0
  103. package/src/rpc/config.ts +18 -0
  104. package/src/rpc/connection.ts +24 -0
  105. package/src/rpc/registration.ts +31 -0
  106. package/src/rpc/social.ts +36 -0
  107. package/src/rpc/user.ts +42 -0
  108. package/src/social/social.fixture.ts +10 -18
  109. package/src/social/social.test.ts +13 -29
  110. package/src/social/social.ts +20 -47
  111. package/src/storage/storage.fixture.ts +3 -4
  112. package/src/storage/storage.test.ts +28 -19
  113. package/src/storage/storage.ts +36 -36
  114. package/src/test/fixtures.ts +21 -6
  115. package/src/user/user.fixture.ts +17 -7
  116. package/src/user/user.test.ts +2 -5
  117. package/src/user/user.ts +13 -9
  118. package/src/version.ts +1 -0
  119. package/dist/authentication/authenticate.d.ts.map +0 -1
  120. package/dist/authentication/authenticate.fixture.d.ts.map +0 -1
  121. package/dist/capabilities/capabilities.d.ts.map +0 -1
  122. package/dist/config.d.ts +0 -18
  123. package/dist/config.d.ts.map +0 -1
  124. package/dist/config.js +0 -20
  125. package/dist/config.js.map +0 -1
  126. package/dist/connection/connection.d.ts.map +0 -1
  127. package/dist/connection/connection.fixture.d.ts.map +0 -1
  128. package/dist/effect.d.ts.map +0 -1
  129. package/dist/email/email.d.ts.map +0 -1
  130. package/dist/email/email.fixture.d.ts.map +0 -1
  131. package/dist/event/event.d.ts.map +0 -1
  132. package/dist/index.d.ts.map +0 -1
  133. package/dist/logging/eventLogger.d.ts.map +0 -1
  134. package/dist/registration/register.d.ts.map +0 -1
  135. package/dist/registration/register.fixture.d.ts.map +0 -1
  136. package/dist/rpc/authentication.d.ts.map +0 -1
  137. package/dist/rpc/client.d.ts.map +0 -1
  138. package/dist/rpc/config.d.ts.map +0 -1
  139. package/dist/rpc/connection.d.ts.map +0 -1
  140. package/dist/rpc/registration.d.ts.map +0 -1
  141. package/dist/rpc/social.d.ts.map +0 -1
  142. package/dist/rpc/user.d.ts.map +0 -1
  143. package/dist/social/social.d.ts.map +0 -1
  144. package/dist/social/social.fixture.d.ts.map +0 -1
  145. package/dist/storage/storage.d.ts.map +0 -1
  146. package/dist/storage/storage.fixture.d.ts.map +0 -1
  147. package/dist/test/fixtures.d.ts.map +0 -1
  148. package/dist/user/user.d.ts.map +0 -1
  149. package/dist/user/user.fixture.d.ts.map +0 -1
  150. package/dist/version.d.ts.map +0 -1
  151. package/src/config.ts +0 -42
@@ -2,41 +2,38 @@
2
2
  * User & passkey registration effects
3
3
  */
4
4
  import {
5
- parseCreationOptionsFromJSON,
6
5
  type CredentialCreationOptionsJSON,
6
+ parseCreationOptionsFromJSON,
7
7
  } from '@github/webauthn-json/browser-ponyfill'
8
- import type { NotSupported } from '@passlock/shared/dist/error/error.js'
9
- import { Duplicate, InternalBrowserError } from '@passlock/shared/dist/error/error.js'
8
+ import type { Duplicate, NotSupported } from '@passlock/shared/dist/error/error.js'
9
+ import { InternalBrowserError } from '@passlock/shared/dist/error/error.js'
10
10
  import type { OptionsErrors, VerificationErrors } from '@passlock/shared/dist/rpc/registration.js'
11
- import { OptionsReq, RegistrationClient, VerificationReq } from '@passlock/shared/dist/rpc/registration.js'
12
- import { VerifyEmail } from '@passlock/shared/dist/schema/email.js'
13
- import type {
14
- RegistrationCredential,
15
- UserVerification,
16
- } from '@passlock/shared/dist/schema/passkey.js'
17
- import { Principal } from '@passlock/shared/dist/schema/principal.js'
11
+ import {
12
+ OptionsReq,
13
+ VerificationReq,
14
+ } from '@passlock/shared/dist/rpc/registration.js'
15
+ import type { RegistrationCredential } from '@passlock/shared/dist/schema/passkey.js'
16
+ import type { Principal } from '@passlock/shared/dist/schema/principal.js'
18
17
  import { Context, Effect as E, Layer, flow, pipe } from 'effect'
19
18
  import { Capabilities } from '../capabilities/capabilities.js'
19
+ import { RegistrationClient } from '../rpc/registration.js'
20
20
  import { StorageService } from '../storage/storage.js'
21
- import { UserService } from '../user/user.js'
21
+ import type { UserService } from '../user/user.js'
22
22
 
23
23
  /* Requests */
24
24
 
25
- export type RegistrationRequest = {
26
- email: string
27
- givenName: string
28
- familyName: string
29
- userVerification?: UserVerification
30
- verifyEmail?: VerifyEmail
31
- }
25
+ export type RegistrationRequest = OptionsReq
32
26
 
33
27
  /* Dependencies */
34
28
 
35
- export type CreateCredential = (
36
- request: CredentialCreationOptions,
37
- ) => E.Effect<RegistrationCredential, InternalBrowserError | Duplicate>
38
-
39
- export const CreateCredential = Context.GenericTag<CreateCredential>('@services/Create')
29
+ export class CreateCredential extends Context.Tag('@services/CreateCredential')<
30
+ CreateCredential,
31
+ {
32
+ createCredential: (
33
+ request: CredentialCreationOptions,
34
+ ) => E.Effect<RegistrationCredential, InternalBrowserError | Duplicate>
35
+ }
36
+ >() {}
40
37
 
41
38
  /* Errors */
42
39
 
@@ -44,13 +41,12 @@ export type RegistrationErrors = NotSupported | OptionsErrors | VerificationErro
44
41
 
45
42
  /* Service */
46
43
 
47
- export type RegistrationService = {
48
- registerPasskey: (request: RegistrationRequest) => E.Effect<Principal, RegistrationErrors>
49
- }
50
-
51
- export const RegistrationService = Context.GenericTag<RegistrationService>(
52
- '@services/RegistrationService',
53
- )
44
+ export class RegistrationService extends Context.Tag('@services/RegistrationService')<
45
+ RegistrationService,
46
+ {
47
+ registerPasskey: (request: RegistrationRequest) => E.Effect<Principal, RegistrationErrors>
48
+ }
49
+ >() {}
54
50
 
55
51
  /* Utilities */
56
52
 
@@ -94,7 +90,12 @@ const verifyCredential = (request: VerificationReq) => {
94
90
 
95
91
  /* Effects */
96
92
 
97
- type Dependencies = Capabilities | CreateCredential | StorageService | UserService | RegistrationClient
93
+ type Dependencies =
94
+ | Capabilities
95
+ | CreateCredential
96
+ | StorageService
97
+ | UserService
98
+ | RegistrationClient
98
99
 
99
100
  export const registerPasskey = (
100
101
  request: RegistrationRequest,
@@ -105,10 +106,10 @@ export const registerPasskey = (
105
106
  yield* _(capabilities.passkeySupport)
106
107
 
107
108
  yield* _(E.logInfo('Fetching registration options from Passlock'))
108
- const { options, session } = yield* _(fetchOptions(new OptionsReq(request)))
109
+ const { options, session } = yield* fetchOptions(new OptionsReq(request))
109
110
 
110
111
  yield* _(E.logInfo('Building new credential'))
111
- const createCredential = yield* _(CreateCredential)
112
+ const { createCredential } = yield* _(CreateCredential)
112
113
  const credential = yield* _(createCredential(options))
113
114
 
114
115
  yield* _(E.logInfo('Storing credential public key in Passlock'))
@@ -145,7 +146,9 @@ export const RegistrationServiceLive = Layer.effect(
145
146
  RegistrationService,
146
147
  E.gen(function* (_) {
147
148
  const context = yield* _(
148
- E.context<CreateCredential | RegistrationClient | Capabilities | StorageService | UserService>(),
149
+ E.context<
150
+ CreateCredential | RegistrationClient | Capabilities | StorageService | UserService
151
+ >(),
149
152
  )
150
153
 
151
154
  return RegistrationService.of({
@@ -0,0 +1,31 @@
1
+ import { OPTIONS_ENDPOINT, OptionsErrors, OptionsReq, OptionsRes, VerificationErrors, VerificationReq, VerificationRes, VERIFY_ENDPOINT, type AuthenticationService } from '@passlock/shared/dist/rpc/authentication.js'
2
+ import { Context, Effect as E, Layer } from 'effect'
3
+ import { Dispatcher, makePostRequest } from './client.js'
4
+
5
+ /* Client */
6
+
7
+ export class AuthenticationClient extends Context.Tag('@passkey/auth/client')<
8
+ AuthenticationClient,
9
+ AuthenticationService
10
+ >() {}
11
+
12
+ export const AuthenticationClientLive = Layer.effect(
13
+ AuthenticationClient,
14
+ E.gen(function* (_) {
15
+ const dispatcher = yield* _(Dispatcher)
16
+
17
+ const optionsResolver = makePostRequest(OptionsReq, OptionsRes, OptionsErrors, dispatcher)
18
+
19
+ const verifyResolver = makePostRequest(
20
+ VerificationReq,
21
+ VerificationRes,
22
+ VerificationErrors,
23
+ dispatcher,
24
+ )
25
+
26
+ return {
27
+ getAuthenticationOptions: req => optionsResolver(OPTIONS_ENDPOINT, req),
28
+ verifyAuthenticationCredential: req => verifyResolver(VERIFY_ENDPOINT, req),
29
+ }
30
+ }),
31
+ )
@@ -0,0 +1,173 @@
1
+ import * as S from '@effect/schema/Schema'
2
+ import { NetworkError } from '@passlock/shared/dist/error/error.js'
3
+ import { Context, Effect as E, Layer, pipe } from 'effect'
4
+ import { PASSLOCK_CLIENT_VERSION } from '../version.js'
5
+ import { RetrySchedule, RpcConfig } from './config.js'
6
+
7
+ export type DispatcherResponse = {
8
+ status: number
9
+ body: object
10
+ }
11
+
12
+ /** To send the JSON to the backend */
13
+ export class Dispatcher extends Context.Tag('@rpc/Dispatcher')<
14
+ Dispatcher,
15
+ {
16
+ get: (path: string) => E.Effect<DispatcherResponse, NetworkError>
17
+ post: (path: string, body: string) => E.Effect<DispatcherResponse, NetworkError>
18
+ }
19
+ >() {}
20
+
21
+ /** Fires off client requests using fetch */
22
+ /** TODO: Write tests */
23
+ /** TODO: Evaluate platform/http client (if now stable) */
24
+ export const DispatcherLive = Layer.effect(
25
+ Dispatcher,
26
+ E.gen(function* (_) {
27
+ const { schedule } = yield* _(RetrySchedule)
28
+ const { tenancyId, clientId, endpoint: maybeEndpoint } = yield* _(RpcConfig)
29
+
30
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
31
+ const parseJson = (res: Response, url: string) =>
32
+ E.tryPromise({
33
+ try: () => res.json() as Promise<unknown>,
34
+ catch: e =>
35
+ new NetworkError({
36
+ message: 'Unable to extract json response from ' + url,
37
+ detail: String(e),
38
+ }),
39
+ })
40
+
41
+ // 400 errors are reflected in the RPC response error channel
42
+ // so in network terms they're still "ok"
43
+ const assertNo500s = (res: Response, url: string) => {
44
+ if (res.status >= 500) {
45
+ return E.fail(
46
+ new NetworkError({
47
+ message: 'Received 500 response code from ' + url,
48
+ }),
49
+ )
50
+ } else return E.void
51
+ }
52
+
53
+ const parseJsonObject = (json: unknown) => {
54
+ return typeof json === 'object' && json !== null
55
+ ? E.succeed(json)
56
+ : E.fail(
57
+ new NetworkError({
58
+ message: `Expected JSON object to be returned from RPC endpoint, actual ${typeof json}`,
59
+ }),
60
+ )
61
+ }
62
+
63
+ const buildUrl = (_path: string) => {
64
+ const endpoint = maybeEndpoint || 'https://api.passlock.dev'
65
+ // drop leading /
66
+ const path = _path.replace(/^\//, '')
67
+ return `${endpoint}/${tenancyId}/${path}`
68
+ }
69
+
70
+ return {
71
+ get: (path: string) => {
72
+ const effect = E.gen(function* (_) {
73
+ const headers = {
74
+ 'Accept': 'application/json',
75
+ 'X-CLIENT-ID': clientId,
76
+ 'X-PASSLOCK-CLIENT-VERSION': PASSLOCK_CLIENT_VERSION
77
+ }
78
+
79
+ const url = buildUrl(path)
80
+
81
+ const res = yield* _(
82
+ E.tryPromise({
83
+ try: () => fetch(url, { method: 'GET', headers }),
84
+ catch: e =>
85
+ new NetworkError({ message: 'Unable to fetch from ' + url, detail: String(e) }),
86
+ }),
87
+ )
88
+
89
+ const json = yield* _(parseJson(res, url))
90
+ yield* _(assertNo500s(res, url))
91
+ const jsonObject = yield* _(parseJsonObject(json))
92
+
93
+ return { status: res.status, body: jsonObject }
94
+ })
95
+
96
+ return E.retry(effect, { schedule })
97
+ },
98
+
99
+ post: (_path: string, body: string) => {
100
+ const effect = E.gen(function* (_) {
101
+ const headers = {
102
+ 'Content-Type': 'application/json',
103
+ 'Accept': 'application/json',
104
+ 'X-CLIENT-ID': clientId,
105
+ 'X-PASSLOCK-CLIENT-VERSION': PASSLOCK_CLIENT_VERSION
106
+ }
107
+
108
+ // drop leading /
109
+ const url = buildUrl(_path)
110
+
111
+ const res = yield* _(
112
+ E.tryPromise({
113
+ try: () => fetch(url, { method: 'POST', headers, body }),
114
+ catch: e =>
115
+ new NetworkError({ message: 'Unable to fetch from ' + url, detail: String(e) }),
116
+ }),
117
+ )
118
+
119
+ const json = yield* _(parseJson(res, url))
120
+ yield* _(assertNo500s(res, url))
121
+ const jsonObject = yield* _(parseJsonObject(json))
122
+
123
+ return { status: res.status, body: jsonObject }
124
+ })
125
+
126
+ return E.retry(effect, { schedule })
127
+ },
128
+ }
129
+ }),
130
+ )
131
+
132
+ export const makeGetRequest =
133
+ <Res, ResEnc, Err, ErrEnc>(
134
+ responseSchema: S.Schema<Res, ResEnc, never>,
135
+ errorSchema: S.Schema<Err, ErrEnc, never>,
136
+ dispatcher: Dispatcher['Type'],
137
+ ) =>
138
+ (path: string) =>
139
+ pipe(
140
+ dispatcher.get(path),
141
+ E.flatMap(res => {
142
+ if (res.status === 200) return S.decodeUnknown(responseSchema)(res.body)
143
+ return pipe(
144
+ S.decodeUnknown(errorSchema)(res.body),
145
+ E.flatMap(err => E.fail(err)),
146
+ )
147
+ }),
148
+ E.catchTag('ParseError', e => E.die(e)),
149
+ E.catchTag('NetworkError', e => E.die(e)),
150
+ )
151
+
152
+ export const makePostRequest =
153
+ <Req, ReqEnc, Res, ResEnc, Err, ErrEnc>(
154
+ requestSchema: S.Schema<Req, ReqEnc, never>,
155
+ responseSchema: S.Schema<Res, ResEnc, never>,
156
+ errorSchema: S.Schema<Err, ErrEnc, never>,
157
+ dispatcher: Dispatcher['Type'],
158
+ ) =>
159
+ (path: string, request: Req) => {
160
+ return pipe(
161
+ S.encode(requestSchema)(request),
162
+ E.flatMap(request => dispatcher.post(path, JSON.stringify(request))),
163
+ E.flatMap(res => {
164
+ if (res.status === 200) return S.decodeUnknown(responseSchema)(res.body)
165
+ return pipe(
166
+ S.decodeUnknown(errorSchema)(res.body),
167
+ E.flatMap(err => E.fail(err)),
168
+ )
169
+ }),
170
+ E.catchTag('ParseError', e => E.die(e)),
171
+ E.catchTag('NetworkError', e => E.die(e)),
172
+ )
173
+ }
@@ -0,0 +1,18 @@
1
+ import type { Schedule } from 'effect'
2
+ import { Context } from 'effect'
3
+
4
+ export class RpcConfig extends Context.Tag('@rpc/RpcConfig')<
5
+ RpcConfig,
6
+ {
7
+ endpoint?: string
8
+ tenancyId: string
9
+ clientId: string
10
+ }
11
+ >() {}
12
+
13
+ export class RetrySchedule extends Context.Tag('@rpc/RetrySchedule')<
14
+ RetrySchedule,
15
+ {
16
+ schedule: Schedule.Schedule<unknown>
17
+ }
18
+ >() {}
@@ -0,0 +1,24 @@
1
+ import * as S from '@effect/schema/Schema'
2
+ import { CONNECT_ENDPOINT, ConnectRes, type ConnectionService } from '@passlock/shared/dist/rpc/connection.js'
3
+ import { Context, Effect as E, Layer } from 'effect'
4
+ import { Dispatcher, makeGetRequest } from './client.js'
5
+
6
+ /* Client */
7
+
8
+ export class ConnectionClient extends Context.Tag('@connection/client')<
9
+ ConnectionClient,
10
+ ConnectionService
11
+ >() {}
12
+
13
+ export const ConnectionClientLive = Layer.effect(
14
+ ConnectionClient,
15
+ E.gen(function* (_) {
16
+ const dispatcher = yield* _(Dispatcher)
17
+
18
+ const preConnectResolver = makeGetRequest(ConnectRes, S.Never, dispatcher)
19
+
20
+ return {
21
+ preConnect: () => preConnectResolver(CONNECT_ENDPOINT),
22
+ }
23
+ }),
24
+ )
@@ -0,0 +1,31 @@
1
+ import { OptionsErrors, OptionsReq, OptionsRes, VerificationErrors, VerificationReq, VerificationRes, type RegistrationService } from '@passlock/shared/dist/rpc/registration.js'
2
+ import { Context, Effect as E, Layer } from 'effect'
3
+ import { Dispatcher, makePostRequest } from './client.js'
4
+
5
+ /* Client */
6
+
7
+ export class RegistrationClient extends Context.Tag('@passkey/register/client')<
8
+ RegistrationClient,
9
+ RegistrationService
10
+ >() {}
11
+
12
+ export const RegistrationClientLive = Layer.effect(
13
+ RegistrationClient,
14
+ E.gen(function* (_) {
15
+ const dispatcher = yield* _(Dispatcher)
16
+
17
+ const optionsResolver = makePostRequest(OptionsReq, OptionsRes, OptionsErrors, dispatcher)
18
+
19
+ const verifyResolver = makePostRequest(
20
+ VerificationReq,
21
+ VerificationRes,
22
+ VerificationErrors,
23
+ dispatcher,
24
+ )
25
+
26
+ return {
27
+ getRegistrationOptions: req => optionsResolver('/passkey/register/options', req),
28
+ verifyRegistrationCredential: req => verifyResolver('/passkey/register/verify', req),
29
+ }
30
+ }),
31
+ )
@@ -0,0 +1,36 @@
1
+ import { AuthOidcErrors, AuthOidcReq, PrincipalRes, RegisterOidcErrors, RegisterOidcReq, type SocialService } from '@passlock/shared/dist/rpc/social.js'
2
+ import { Context, Effect as E, Layer } from 'effect'
3
+ import { Dispatcher, makePostRequest } from './client.js'
4
+
5
+ /* Client */
6
+
7
+ export const OIDC_REGISTER_ENDPOINT = '/social/oidc/register'
8
+ export const OIDC_AUTH_ENDPOINT = '/social/oidc/auth'
9
+
10
+ export class SocialClient extends Context.Tag('@social/client')<SocialClient, SocialService>() {}
11
+
12
+ export const SocialClientLive = Layer.effect(
13
+ SocialClient,
14
+ E.gen(function* (_) {
15
+ const dispatcher = yield* _(Dispatcher)
16
+
17
+ const registerResolver = makePostRequest(
18
+ RegisterOidcReq,
19
+ PrincipalRes,
20
+ RegisterOidcErrors,
21
+ dispatcher,
22
+ )
23
+
24
+ const authenticateResolver = makePostRequest(
25
+ AuthOidcReq,
26
+ PrincipalRes,
27
+ AuthOidcErrors,
28
+ dispatcher,
29
+ )
30
+
31
+ return {
32
+ registerOidc: req => registerResolver(OIDC_REGISTER_ENDPOINT, req),
33
+ authenticateOidc: req => authenticateResolver(OIDC_AUTH_ENDPOINT, req),
34
+ }
35
+ }),
36
+ )
@@ -0,0 +1,42 @@
1
+ import * as S from '@effect/schema/Schema'
2
+ import { IsExistingUserReq, IsExistingUserRes, RESEND_EMAIL_ENDPOINT, ResendEmailErrors, ResendEmailReq, ResendEmailRes, USER_STATUS_ENDPOINT, VERIFY_EMAIL_ENDPOINT, VerifyEmailErrors, VerifyEmailReq, VerifyEmailRes, type UserService } from '@passlock/shared/dist/rpc/user.js'
3
+ import { Context, Effect as E, Layer } from 'effect'
4
+ import { Dispatcher, makePostRequest } from './client.js'
5
+
6
+ /* Client */
7
+
8
+ export class UserClient extends Context.Tag('@user/client')<UserClient, UserService>() {}
9
+
10
+ export const UserClientLive = Layer.effect(
11
+ UserClient,
12
+ E.gen(function* (_) {
13
+ const dispatcher = yield* _(Dispatcher)
14
+
15
+ const isExistingUserResolver = makePostRequest(
16
+ IsExistingUserReq,
17
+ IsExistingUserRes,
18
+ S.Never,
19
+ dispatcher,
20
+ )
21
+
22
+ const verifyEmailResolver = makePostRequest(
23
+ VerifyEmailReq,
24
+ VerifyEmailRes,
25
+ VerifyEmailErrors,
26
+ dispatcher,
27
+ )
28
+
29
+ const resendEmailResolver = makePostRequest(
30
+ ResendEmailReq,
31
+ ResendEmailRes,
32
+ ResendEmailErrors,
33
+ dispatcher,
34
+ )
35
+
36
+ return {
37
+ isExistingUser: req => isExistingUserResolver(USER_STATUS_ENDPOINT, req),
38
+ verifyEmail: req => verifyEmailResolver(VERIFY_EMAIL_ENDPOINT, req),
39
+ resendVerificationEmail: req => resendEmailResolver(RESEND_EMAIL_ENDPOINT, req),
40
+ }
41
+ }),
42
+ )
@@ -1,8 +1,8 @@
1
1
  import * as Shared from '@passlock/shared/dist/rpc/social.js'
2
- import { SocialClient } from '@passlock/shared/dist/rpc/social.js'
3
- import { Effect as E, Layer as L } from 'effect'
2
+ import { Effect as E, Layer as L, Option as O } from 'effect'
3
+ import { SocialClient } from '../rpc/social.js'
4
4
  import * as Fixtures from '../test/fixtures.js'
5
- import type { AuthenticateOidcReq, RegisterOidcReq } from './social.js'
5
+ import type { AuthenticateOidcReq } from './social.js'
6
6
 
7
7
  export const session = 'session'
8
8
  export const token = 'token'
@@ -10,30 +10,22 @@ export const code = 'code'
10
10
  export const authType = 'passkey'
11
11
  export const expireAt = Date.now() + 10000
12
12
 
13
- export const registerOidcReq: RegisterOidcReq = {
13
+ export const registerOidcReq = new Shared.RegisterOidcReq({
14
14
  provider: 'google',
15
15
  idToken: 'google-token',
16
16
  nonce: 'nonce',
17
- givenName: 'john',
18
- familyName: 'doe'
19
- }
17
+ givenName: O.some('john'),
18
+ familyName: O.some('doe'),
19
+ })
20
20
 
21
- export const authOidcReq: AuthenticateOidcReq = {
21
+ export const authOidcReq: AuthenticateOidcReq = new Shared.AuthOidcReq({
22
22
  provider: 'google',
23
23
  idToken: 'google-token',
24
- nonce: 'nonce'
25
- }
26
-
27
- export const rpcRegisterReq = new Shared.RegisterOidcReq({
28
- ...registerOidcReq,
29
- ...(registerOidcReq.givenName ? { givenName: registerOidcReq.givenName } : {}),
30
- ...(registerOidcReq.familyName ? { familyName: registerOidcReq.familyName } : {})
24
+ nonce: 'nonce',
31
25
  })
32
26
 
33
27
  export const rpcRegisterRes = new Shared.PrincipalRes({ principal: Fixtures.principal })
34
28
 
35
- export const rpcAuthenticateReq = new Shared.AuthOidcReq({ ...authOidcReq })
36
-
37
29
  export const rpcAuthenticateRes = new Shared.PrincipalRes({ principal: Fixtures.principal })
38
30
 
39
31
  export const rpcClientTest = L.succeed(
@@ -41,7 +33,7 @@ export const rpcClientTest = L.succeed(
41
33
  SocialClient.of({
42
34
  registerOidc: () => E.fail(Fixtures.notImplemented),
43
35
  authenticateOidc: () => E.fail(Fixtures.notImplemented),
44
- })
36
+ }),
45
37
  )
46
38
 
47
39
  export const principal = Fixtures.principal
@@ -1,8 +1,8 @@
1
1
  import { Duplicate, NotFound } from '@passlock/shared/dist/error/error.js'
2
- import { SocialClient } from '@passlock/shared/dist/rpc/social.js'
3
2
  import { Effect as E, Layer as L, Layer, LogLevel, Logger, pipe } from 'effect'
4
3
  import { describe, expect, test } from 'vitest'
5
4
  import { mock } from 'vitest-mock-extended'
5
+ import { SocialClient } from '../rpc/social.js'
6
6
  import * as Fixture from './social.fixture.js'
7
7
  import { SocialService, SocialServiceLive } from './social.js'
8
8
 
@@ -25,10 +25,7 @@ describe('registerOidc should', () => {
25
25
  }),
26
26
  )
27
27
 
28
- const service = pipe(
29
- SocialServiceLive,
30
- L.provide(rpcClientTest),
31
- )
28
+ const service = pipe(SocialServiceLive, L.provide(rpcClientTest))
32
29
 
33
30
  const layers = Layer.merge(service, rpcClientTest)
34
31
  const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
@@ -42,7 +39,7 @@ describe('registerOidc should', () => {
42
39
  yield* _(service.registerOidc(Fixture.registerOidcReq))
43
40
 
44
41
  const rpcClient = yield* _(SocialClient)
45
- expect(rpcClient.registerOidc).toHaveBeenCalledWith(Fixture.rpcRegisterReq)
42
+ expect(rpcClient.registerOidc).toHaveBeenCalledWith(Fixture.registerOidcReq)
46
43
  })
47
44
 
48
45
  const rpcClientTest = L.effect(
@@ -56,10 +53,7 @@ describe('registerOidc should', () => {
56
53
  }),
57
54
  )
58
55
 
59
- const service = pipe(
60
- SocialServiceLive,
61
- L.provide(rpcClientTest),
62
- )
56
+ const service = pipe(SocialServiceLive, L.provide(rpcClientTest))
63
57
 
64
58
  const layers = Layer.merge(service, rpcClientTest)
65
59
  const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
@@ -81,16 +75,13 @@ describe('registerOidc should', () => {
81
75
  E.sync(() => {
82
76
  const rpcMock = mock<SocialClient['Type']>()
83
77
 
84
- rpcMock.registerOidc.mockReturnValue(E.fail(new Duplicate({ message: "Duplicate user" })))
78
+ rpcMock.registerOidc.mockReturnValue(E.fail(new Duplicate({ message: 'Duplicate user' })))
85
79
 
86
80
  return rpcMock
87
81
  }),
88
82
  )
89
83
 
90
- const service = pipe(
91
- SocialServiceLive,
92
- L.provide(rpcClientTest),
93
- )
84
+ const service = pipe(SocialServiceLive, L.provide(rpcClientTest))
94
85
 
95
86
  const layers = Layer.merge(service, rpcClientTest)
96
87
  const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
@@ -118,10 +109,7 @@ describe('authenticateIodc should', () => {
118
109
  }),
119
110
  )
120
111
 
121
- const service = pipe(
122
- SocialServiceLive,
123
- L.provide(rpcClientTest),
124
- )
112
+ const service = pipe(SocialServiceLive, L.provide(rpcClientTest))
125
113
 
126
114
  const layers = Layer.merge(service, rpcClientTest)
127
115
  const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
@@ -135,7 +123,7 @@ describe('authenticateIodc should', () => {
135
123
  yield* _(service.authenticateOidc(Fixture.authOidcReq))
136
124
 
137
125
  const rpcClient = yield* _(SocialClient)
138
- expect(rpcClient.authenticateOidc).toHaveBeenCalledWith(Fixture.rpcAuthenticateReq)
126
+ expect(rpcClient.authenticateOidc).toHaveBeenCalledWith(Fixture.authOidcReq)
139
127
  })
140
128
 
141
129
  const rpcClientTest = L.effect(
@@ -149,10 +137,7 @@ describe('authenticateIodc should', () => {
149
137
  }),
150
138
  )
151
139
 
152
- const service = pipe(
153
- SocialServiceLive,
154
- L.provide(rpcClientTest),
155
- )
140
+ const service = pipe(SocialServiceLive, L.provide(rpcClientTest))
156
141
 
157
142
  const layers = Layer.merge(service, rpcClientTest)
158
143
  const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
@@ -174,16 +159,15 @@ describe('authenticateIodc should', () => {
174
159
  E.sync(() => {
175
160
  const rpcMock = mock<SocialClient['Type']>()
176
161
 
177
- rpcMock.authenticateOidc.mockReturnValue(E.fail(new NotFound({ message: "User not found" })))
162
+ rpcMock.authenticateOidc.mockReturnValue(
163
+ E.fail(new NotFound({ message: 'User not found' })),
164
+ )
178
165
 
179
166
  return rpcMock
180
167
  }),
181
168
  )
182
169
 
183
- const service = pipe(
184
- SocialServiceLive,
185
- L.provide(rpcClientTest),
186
- )
170
+ const service = pipe(SocialServiceLive, L.provide(rpcClientTest))
187
171
 
188
172
  const layers = Layer.merge(service, rpcClientTest)
189
173
  const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))