@nxgt/janus 0.23.0 → 0.24.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 (50) hide show
  1. package/README.md +102 -17
  2. package/dist/auth/email-flows/prove-first.d.ts +23 -1
  3. package/dist/auth/email-flows/prove-first.d.ts.map +1 -1
  4. package/dist/auth/index.d.ts +1 -1
  5. package/dist/auth/index.d.ts.map +1 -1
  6. package/dist/auth/outage.d.ts +15 -4
  7. package/dist/auth/outage.d.ts.map +1 -1
  8. package/dist/auth/types/email-flows.d.ts +13 -2
  9. package/dist/auth/types/email-flows.d.ts.map +1 -1
  10. package/dist/auth/types/find-or-create.d.ts +59 -0
  11. package/dist/auth/types/find-or-create.d.ts.map +1 -0
  12. package/dist/auth/types/index.d.ts +1 -0
  13. package/dist/auth/types/index.d.ts.map +1 -1
  14. package/dist/auth/types/janus.d.ts +2 -1
  15. package/dist/auth/types/janus.d.ts.map +1 -1
  16. package/dist/auth/users/any-type-api.d.ts +2 -2
  17. package/dist/auth/users/any-type-api.d.ts.map +1 -1
  18. package/dist/auth/users/find-or-create.d.ts +12 -0
  19. package/dist/auth/users/find-or-create.d.ts.map +1 -0
  20. package/dist/auth/users/insert.d.ts +8 -3
  21. package/dist/auth/users/insert.d.ts.map +1 -1
  22. package/dist/auth/users/type-api.d.ts +1 -1
  23. package/dist/auth/users/type-api.d.ts.map +1 -1
  24. package/dist/index.js +149 -36
  25. package/dist/index.js.map +8 -7
  26. package/dist/permissions/engine.d.ts.map +1 -1
  27. package/dist/permissions/index.d.ts +1 -1
  28. package/dist/permissions/index.d.ts.map +1 -1
  29. package/dist/permissions/index.js +49 -16
  30. package/dist/permissions/index.js.map +6 -5
  31. package/dist/permissions/input.d.ts +13 -1
  32. package/dist/permissions/input.d.ts.map +1 -1
  33. package/dist/permissions/model/grant.d.ts +14 -1
  34. package/dist/permissions/model/grant.d.ts.map +1 -1
  35. package/dist/permissions/model/permissions.d.ts +13 -1
  36. package/dist/permissions/model/permissions.d.ts.map +1 -1
  37. package/dist/permissions/port/types.d.ts +8 -2
  38. package/dist/permissions/port/types.d.ts.map +1 -1
  39. package/dist/permissions/replace.d.ts +25 -0
  40. package/dist/permissions/replace.d.ts.map +1 -0
  41. package/docs/README.md +2 -2
  42. package/docs/guide/email-flows.md +47 -11
  43. package/docs/guide/errors.md +1 -1
  44. package/docs/guide/permissions.md +42 -1
  45. package/docs/guide/sessions.md +6 -0
  46. package/docs/guide/users.md +44 -0
  47. package/docs/guide/vocabulary.md +2 -2
  48. package/docs/roadmap.md +25 -30
  49. package/docs/troubleshooting.md +44 -15
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -37,7 +37,7 @@ supported.
37
37
  | Import | What it holds |
38
38
  | --- | --- |
39
39
  | `@nxgt/janus` | **Identities**: `janus()`, the identity stores' port and its in-memory reference (`createMemoryStores`), the hashers, the user events (`UserEvent`, `UserEventListener`, `UserEventType`). And the **shared vocabulary**: errors, subjects and the tuple notation, ids, pagination, time |
40
- | `@nxgt/janus/permissions` | **Permissions**: `defineModel`, `fromField`, `when`, `permissions()` — `can`, `list`, `grant`, `revoke` — the relation store's port and its in-memory reference (`createMemoryRelations`) |
40
+ | `@nxgt/janus/permissions` | **Permissions**: `defineModel`, `fromField`, `when`, `permissions()` — `can`, `list`, `grant`, `revoke`, `replace` — the relation store's port and its in-memory reference (`createMemoryRelations`) |
41
41
  | `@nxgt/janus/conformance` | **For adapters**: the suites a store runs — `describeJanusStores`, `describeRelationStores` — their cases as data, and the reference harnesses |
42
42
 
43
43
  A subpath appears in `exports` only once it exports something you should call:
@@ -333,6 +333,7 @@ await auth.magicLink.request(email); // { token, email, expiresAt,
333
333
  await auth.magicLink.confirm(token); // { status: 'signedIn', user, session, token }
334
334
  await auth.magicLink.prepare(email); // counted, nobody looked up: { send() } — and signInCode, resetPassword
335
335
  await auth.findByEmail(email); // User | null — server-side only, counts nothing; every type with an e-mail
336
+ await auth.findOrCreate(key, () => input); // { user, created } — by login (e-mail without a password); input() only when nobody holds the key
336
337
  await auth.signInProven(user.id); // privileged: after a proof YOUR server verified — { status: 'signedIn', … }
337
338
  await auth.stepUp.request(user); // { via: 'email', code, challenge, email, expiresAt, user }
338
339
  await auth.stepUp.confirm(request, challenge, code); // the request's session, authenticatedAt: now
@@ -761,11 +762,12 @@ password and the second factor go with it, so whoever holds the new inbox
761
762
  and follows the link signs in with them. The mail to the new address should
762
763
  say so, and say "ignore it if it was not you".
763
764
 
764
- **For now, send the link to the new address with your own mailer**:
765
- `@nxgt/janus-mail` has no template for it yet (it sends the notice to the
766
- former address, `emailChanged`). `mail.verifyEmail(issued)` is not the
767
- change-of-e-mail mail: it would tell the new address to verify, not that the
768
- account moves there.
765
+ **Send the link to the new address with `@nxgt/janus-mail` 0.10.0 or later**:
766
+ `mail.changeEmail(issued, { name })` (its `links.changeEmail` is the page
767
+ above), and it sends the notice to the former address, `emailChanged`, once
768
+ the link is redeemed. `mail.verifyEmail(issued)` is not the change-of-e-mail
769
+ mail: it would tell the new address to verify, not that the account moves
770
+ there. Without janus-mail, send it with your own mailer.
769
771
 
770
772
  - **On every user type with an e-mail**, with a password or without one. The
771
773
  address is validated by the type's schema as `update` would: a malformed
@@ -919,6 +921,55 @@ password and reset flows on it.
919
921
  `STORE_FAILED`, never `null`.
920
922
  - **Server-side only**: it tells whether an account exists. See *Traps*.
921
923
 
924
+ ### Finding a user, or creating them — `findOrCreate`
925
+
926
+ ```ts
927
+ // Accepting an invitation: the account may exist; if not, it is created only when a password was given.
928
+ const invitation = await invitations.redeem(body.invitationToken); // yours: verified first
929
+ if (invitation === null) return new Response(null, { status: 404 });
930
+
931
+ const { user } = await auth.findOrCreate(invitation.email, () => {
932
+ if (body.password === undefined) throw new Error('a password is required'); // refused: nothing is written
933
+ return { email: invitation.email, name: body.name }; // no password here: proveFirst drops it, setPassword sets it once
934
+ });
935
+ await auth.verifyEmail.proveFirst(user); // drops a squatter's password, factor and sessions: first
936
+ if (typeof body.password === 'string') await auth.setPassword(user, body.password); // then the invitee's own
937
+ const result = await auth.signInProven(user); // last
938
+ ```
939
+
940
+ The user of this type holding `key`, or the one `input()` describes,
941
+ created — in one call, with the lost race handled. Typed by the exported
942
+ `FindOrCreateApi`; the
943
+ [users guide](docs/guide/users.md#finding-a-user-or-creating-them--findorcreate)
944
+ has the details. On **every type with a
945
+ login or an e-mail**: the key is the login, normalised as `findByLogin`
946
+ normalises it, on a type with a password, and the e-mail, normalised as
947
+ `findByEmail` does, on a type without one. On a type whose login is a
948
+ username, an e-mail key finds an existing user (e-mails are stored as
949
+ logins) but can never create one: the input's username is not the key, so
950
+ it is `USER_INVALID`.
951
+
952
+ - **Found: nothing else happens.** The user is answered
953
+ `{ user, created: false }` — an inactive one included — `input` is not
954
+ called and nothing is written.
955
+ - **`input` is the refusal point.** It runs only when nobody holds the key,
956
+ and what it throws propagates with nothing written: refuse there what the
957
+ caller must refuse before a write, such as a missing password.
958
+ - **The key is a string.** Any other value is `USER_INVALID` before `input()`
959
+ runs.
960
+ - **The input must name the key.** Once validated, its login (its e-mail,
961
+ without a password) must equal the normalised `key`, or the call is
962
+ `USER_INVALID` before the write.
963
+ - **A lost race is not an error.** When another call created the user between
964
+ the read and the write, the store's `LOGIN_TAKEN` is followed by one more
965
+ read of the key; the winner is answered `{ user, created: false }`. The
966
+ `LOGIN_TAKEN` is rethrown only when that read finds nobody — another login
967
+ of the new user is taken.
968
+ - **Created as `create` creates**: the same validation, the same hashing,
969
+ `PASSWORD_TOO_SHORT` below the policy, and `user.created` sent only when
970
+ this call created. Signs nobody in.
971
+ - **Server-side only**, an account-existence oracle as `findByEmail` is.
972
+
922
973
  ### Proving an e-mail on your own proof — `verifyEmail.proveFirst`
923
974
 
924
975
  ```ts
@@ -948,8 +999,15 @@ type with an e-mail; `signInProven` alone proves nothing.
948
999
  or sign-in-throttle count.
949
1000
  - **Refused as `signInProven` refuses**: an inactive user is
950
1001
  `USER_INACTIVE`; a malformed id, an unknown one or one of another type is
951
- `NOT_FOUND`. A user written between its read and its write is
952
- `VERSION_CONFLICT`, with nothing proved; an outage is `STORE_FAILED`.
1002
+ `NOT_FOUND`. A write landing between the read and the proof is
1003
+ retried once on a fresh read (`proved: false` when that write proved the
1004
+ e-mail); `VERSION_CONFLICT`, with nothing proved, when it changed the
1005
+ address or the retry conflicts too; an outage is `STORE_FAILED`.
1006
+ - **The address you hold is checked.** Given a `User` (an object carrying
1007
+ the type's e-mail field), it compares that address, normalised as e-mails
1008
+ are, with the one it reads, on the first read and on the retry read:
1009
+ another address is `VERSION_CONFLICT` and nothing is proved. Given a bare
1010
+ id, or an object without the e-mail field, it proves the current address.
953
1011
  - **Privileged, and the order matters**: see *Traps*.
954
1012
 
955
1013
  [E-mail flows](docs/guide/email-flows.md#proving-an-e-mail-on-your-own-proof--provefirst)
@@ -1214,6 +1272,28 @@ would have to read another object's field.
1214
1272
  [permissions guide](docs/guide/permissions.md#a-function-of-your-own-around-can)
1215
1273
  has the example.
1216
1274
 
1275
+ **Sync a relation to a wanted list with `replace`.** When the application
1276
+ holds the list a group's members should match, `replace(object, relation,
1277
+ subjects)` adds the missing holders and removes the others in one write, and
1278
+ answers the tuples added and the tuples removed:
1279
+
1280
+ ```ts
1281
+ const wanted = await db.staff.membersOf('cardiology'); // your own source of truth
1282
+ const { added, removed } = await access.replace({ type: 'team', id: 'cardiology' }, 'members', wanted); // RelationTuple[] each
1283
+ await access.replace({ type: 'team', id: 'cardiology' }, 'members', []); // empties the group
1284
+ ```
1285
+
1286
+ The subjects are typed as `grant`'s is, as an array, and every one is checked
1287
+ before anything is read, so a refusal writes nothing. Duplicates count once,
1288
+ holders already right are not written (no write at all when nothing changes),
1289
+ and a subject set is not its entity. Other relations and objects are untouched.
1290
+ **Two concurrent `replace` calls on one object and relation are not
1291
+ serialised**: each reads, then writes its diff, so the result can hold the
1292
+ union of both adds — serialise them yourself when one must win. A relation
1293
+ store must answer every holder of a relation to `findEntities` and
1294
+ `findSubjectSets`, since `replace` reads them all; the
1295
+ [permissions guide](docs/guide/permissions.md#replace) has the details.
1296
+
1217
1297
  **A user type may also be an object type**: declare `staff` under `types`, and
1218
1298
  a staff member is an object too — who may edit them is a relation on them. See
1219
1299
  [permissions on a user](docs/guide/permissions.md#permissions-on-a-user).
@@ -1372,8 +1452,9 @@ acceptance answer 429.
1372
1452
  every session.** On an e-mail never proved, it does what a first sign-in by
1373
1453
  link does, so a password set before it is erased — the invitee's own,
1374
1454
  chosen on your acceptance form, included — and a session opened before it
1375
- is revoked. Accept an invitation in this order: `findByEmail` (or
1376
- `create`), then `verifyEmail.proveFirst`, then `setPassword` on a type with
1455
+ is revoked. Accept an invitation in this order: `findOrCreate` (find or
1456
+ create in one call; `findByEmail` then `create` does the same in two), then
1457
+ `verifyEmail.proveFirst`, then `setPassword` on a type with
1377
1458
  a password, then `signInProven`. Called after `setPassword`, the person
1378
1459
  signs in once and then finds `signIn` answering `CREDENTIALS_INVALID`
1379
1460
  (`reason: 'noPassword'`). **It is privileged, like `signInProven`**: it
@@ -1383,10 +1464,14 @@ address — on the user that proof names, whose e-mail is the address the
1383
1464
  proof reached; never with an id a request supplied, or anybody can wipe
1384
1465
  anybody's password and second factor and sign them out.
1385
1466
 
1386
- **`findByEmail` is an account-existence oracle — never expose it to an
1387
- anonymous client.** It answers whether somebody of the type holds an
1388
- address, at once, with no throttle: a route that calls it on an address a
1389
- visitor typed tells anybody who has an account. Call it on the server, after
1467
+ **`findByEmail` and `findOrCreate` are account-existence oracles — never
1468
+ expose either to an anonymous client.** `findByEmail` answers whether
1469
+ somebody of the type holds an address, at once, with no throttle;
1470
+ `findOrCreate` is worse, since it also creates accounts: an address nobody
1471
+ holds becomes one. Call `findOrCreate` on the server only, after your own
1472
+ proof, with the key read from that proof's record. A route that calls
1473
+ `findByEmail` on an address a visitor typed tells anybody who has an account.
1474
+ Call `findByEmail` on the server, after
1390
1475
  a proof of your own — an invitation token your server issued and just
1391
1476
  verified — with the address read from that proof's record, never from the
1392
1477
  request. A visitor who types an address goes through `magicLink.request` or
@@ -1697,7 +1782,7 @@ that sends one, since it is awaited: queue the event and return.
1697
1782
 
1698
1783
  ## Type safety, counted
1699
1784
 
1700
- **One hundred and seventy-two plausible mistakes, one hundred and seventy-two refused at compile time — and
1785
+ **One hundred and eighty plausible mistakes, one hundred and eighty refused at compile time — and
1701
1786
  two gaps, named.**
1702
1787
 
1703
1788
  The lists are typechecked and never run, with one `@ts-expect-error` per
@@ -1705,10 +1790,10 @@ mistake beside the shapes that must keep compiling. One is a single file:
1705
1790
  `test/types/refusals.ts` (fifteen, on the shared vocabulary). The other three
1706
1791
  are folders with one file per behaviour: `test/types/port/` (twenty-five, on
1707
1792
  the identity stores' port, from the point of view of the person implementing
1708
- it), `test/types/auth/` (eighty-four, on `janus()`, from the point of view of
1793
+ it), `test/types/auth/` (eighty-eight, on `janus()`, from the point of view of
1709
1794
  the application — twelve of them on the second factor, three on sign-in codes,
1710
1795
  five on user events, four on step-ups, six on the sign-in throttle, five on the mail throttle, four on
1711
- sign-in links, six on devices, two on `findMany`, five on requests in two steps, three on signing in on the application's proof, three on finding a user by e-mail, three on proving an e-mail on the application's proof, three on changing an e-mail) and `test/types/permissions/` (forty-eight, on the
1796
+ sign-in links, six on devices, two on `findMany`, five on requests in two steps, three on signing in on the application's proof, three on finding a user by e-mail, three on proving an e-mail on the application's proof, three on changing an e-mail, four on `findOrCreate`) and `test/types/permissions/` (fifty-two, on the
1712
1797
  permission model and the questions asked of it). The rule comes from
1713
1798
  `nxgt-data`, and so does the reason to distrust the claim without the files:
1714
1799
  when it was last measured on `@nxgt/mongo`, *seven of twelve plausible
@@ -4,7 +4,7 @@
4
4
  * sign-in by link or code gives an e-mail never proved (`../first-proof`).
5
5
  */
6
6
  import type { At } from '../at';
7
- import type { ResolvedType } from '../config';
7
+ import { type ResolvedType } from '../config';
8
8
  import { type AnyUser, type Context } from '../context';
9
9
  import type { VerifyEmailApi } from '../types';
10
10
  /**
@@ -15,6 +15,28 @@ import type { VerifyEmailApi } from '../types';
15
15
  * on the record read, `emailVerifiedAt === null`. An e-mail already proved
16
16
  * is answered as it is, with nothing written.
17
17
  *
18
+ * **The address the caller holds is checked.** When the `UserRef` is an
19
+ * object carrying the type's e-mail field — a `User` — that address,
20
+ * normalised as e-mails are, must be the one read, on the first read and on
21
+ * the retry read; otherwise it is a `VERSION_CONFLICT` and nothing is proved.
22
+ * With a bare id, or an object without the e-mail field, the current address
23
+ * is proved.
24
+ *
25
+ * **A write landing between the read and the proof is retried once.**
26
+ * `VERSION_CONFLICT` from `proveFirstEmail` runs the whole flow again on a
27
+ * fresh read: nobody, or an inactive user, is refused as before; an e-mail
28
+ * that is no longer the one first read — compared as e-mails are — is a
29
+ * `VERSION_CONFLICT` naming both versions, since the address changed since the
30
+ * first read (or is not the one the passed `User` carries) and the proof the
31
+ * caller holds is for the address it read; an e-mail proved meanwhile is
32
+ * answered `proved: false`; otherwise the proof runs under the new version.
33
+ * A second `VERSION_CONFLICT` propagates.
34
+ *
35
+ * Running `proveFirstEmail` twice is harmless: a failed attempt wrote
36
+ * nothing and sent no event (events follow the write); all it left is the
37
+ * sessions revoked and the tokens spent before the write, which the second
38
+ * attempt does again to the same end.
39
+ *
18
40
  * No throttle is counted and no count restarted, no e-mail sent and no
19
41
  * token issued; the only tokens spent are the ones `proveFirstEmail`
20
42
  * spends, what the password opened.
@@ -1 +1 @@
1
- {"version":3,"file":"prove-first.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/prove-first.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CAgBtD"}
1
+ {"version":3,"file":"prove-first.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/prove-first.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CA2BtD"}
@@ -8,5 +8,5 @@ export type { JanusStores, Json, JsonObject, PasswordRecord, SecondFactorRecord,
8
8
  export type { SealingKey } from './sealing';
9
9
  export { assertFresh } from './sessions/fresh';
10
10
  export type { StandardSchemaV1 } from './standard-schema';
11
- export type { Authenticated, ChangeEmailApi, Checked, EmailOf, FindByEmailApi, FirstProof, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, MagicLinkApi, PasswordApi, PreparedCode, PreparedRequest, ProvenSignInApi, RecoveredSignIn, RecoveryCodesIssued, RequestLike, RequiredStringKeys, ResetPasswordApi, SecondFactorApi, SecondFactorEnrolment, SecondFactorRequired, Session, SharedApi, SignedIn, SignInCodeApi, SignInOptions, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, TypeApi, TypesOf, User, UserBase, UserOf, UserRef, UserTypeApi, VerifyEmailApi, WriteOptions, } from './types';
11
+ export type { Authenticated, ChangeEmailApi, Checked, EmailOf, FindByEmailApi, FindOrCreateApi, FirstProof, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, MagicLinkApi, PasswordApi, PreparedCode, PreparedRequest, ProvenSignInApi, RecoveredSignIn, RecoveryCodesIssued, RequestLike, RequiredStringKeys, ResetPasswordApi, SecondFactorApi, SecondFactorEnrolment, SecondFactorRequired, Session, SharedApi, SignedIn, SignInCodeApi, SignInOptions, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, TypeApi, TypesOf, User, UserBase, UserOf, UserRef, UserTypeApi, VerifyEmailApi, WriteOptions, } from './types';
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,YAAY,EACZ,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,cAAc,EACd,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,YAAY,EACZ,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,cAAc,EACd,OAAO,EACP,OAAO,EACP,cAAc,EACd,eAAe,EACf,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
@@ -7,14 +7,14 @@
7
7
  * {@link guardStores} puts the three identity stores behind the shared guard
8
8
  * (`src/stores/guard.ts`), the one place a store's answer is caught.
9
9
  * {@link required} turns an absence the caller cannot accept into its refusal,
10
- * and {@link unlessVersionConflict} absorbs one named conflict and rethrows
11
- * everything else.
10
+ * {@link unlessVersionConflict} absorbs one named conflict and rethrows
11
+ * everything else, and {@link unlessLoginTaken} hands back the other one.
12
12
  *
13
13
  * `outage.scan.spec.ts` reads every other file of `src/auth/`, `src/permissions/`
14
14
  * and `src/stores/` and fails if a `catch` appears in one, because the failure
15
15
  * this design exists to prevent is a single careless `catch { return null }`.
16
16
  */
17
- import { type JanusError } from '../errors/janus-error';
17
+ import { type JanusError, StoreConflict } from '../errors/janus-error';
18
18
  import type { JanusStores } from './port/types';
19
19
  /**
20
20
  * The stores, with every method guarded.
@@ -49,7 +49,18 @@ export declare function required<T>(value: T | null, absent: () => JanusError):
49
49
  * taken login still throws, `NOT_FOUND` still throws: an outage never reads as
50
50
  * "somebody else wrote first". It exists for writes the caller did not ask for
51
51
  * — rewriting a password hash on sign-in — where losing to a concurrent update
52
- * means only that the next sign-in tries again.
52
+ * means only that the next sign-in tries again — and for `verifyEmail.proveFirst`,
53
+ * which reads again and retries once.
53
54
  */
54
55
  export declare function unlessVersionConflict<T>(write: Promise<T>): Promise<T | null>;
56
+ /**
57
+ * A write that may lose a race on a login, and says so: its answer, or the
58
+ * `StoreConflict('login')` the store refused it with, **as a value**, for the
59
+ * caller to read the login again and then rethrow or answer the winner.
60
+ *
61
+ * **Only `StoreConflict('login')` is handed back.** A failure still throws, a
62
+ * version conflict still throws: an outage never reads as "somebody else
63
+ * created the user first".
64
+ */
65
+ export declare function unlessLoginTaken<T>(write: Promise<T>): Promise<T | StoreConflict>;
55
66
  //# sourceMappingURL=outage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"outage.d.ts","sourceRoot":"","sources":["../../src/auth/outage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAiB,MAAM,uBAAuB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAM5D;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,UAAU,GAAG,CAAC,CAGxE;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC5C,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GACf,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAOnB"}
1
+ {"version":3,"file":"outage.d.ts","sourceRoot":"","sources":["../../src/auth/outage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAM5D;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,UAAU,GAAG,CAAC,CAGxE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC5C,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GACf,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAOnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACvC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GACf,OAAO,CAAC,CAAC,GAAG,aAAa,CAAC,CAO5B"}
@@ -119,8 +119,19 @@ export interface VerifyEmailApi<U> {
119
119
  * Counts nothing in the mail or the sign-in throttle, sends no e-mail
120
120
  * and issues no token. An inactive user is `USER_INACTIVE`; a
121
121
  * malformed id, an unknown one or one of another type is `NOT_FOUND`;
122
- * a user written since it was read is `VERSION_CONFLICT`, and nothing
123
- * is proved.
122
+ * a user written between the read and the proof is read again and the
123
+ * proof retried **once**: `proved: false` when that write proved the
124
+ * e-mail, `VERSION_CONFLICT` with nothing proved when it changed the
125
+ * address (the proof you hold is for the address you read) or when
126
+ * the retry conflicts too, `USER_INACTIVE` when it deactivated the
127
+ * user. Running the proof twice is harmless.
128
+ *
129
+ * **The address you hold is checked.** Given a `User` — an object
130
+ * carrying the type's e-mail field — it compares that address,
131
+ * normalised as e-mails are, with the one it reads, on the first read
132
+ * and on the retry read: a different one is `VERSION_CONFLICT` and
133
+ * nothing is proved. Given a bare id, or an object without the e-mail
134
+ * field, it proves the current address.
124
135
  */
125
136
  proveFirst(user: UserRef): Promise<FirstProof<U>>;
126
137
  };
@@ -1 +1 @@
1
- {"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../../src/auth/types/email-flows.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../../src/auth/types/email-flows.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What a user type with a login or an e-mail answers besides: finding its
3
+ * user by that key, or creating it when nobody holds it.
4
+ *
5
+ * Part of what `janus()` hands back; `./index` gathers it.
6
+ */
7
+ /**
8
+ * Finding a user by the key an invitation names, or creating them — the
9
+ * accept step of an invitation, without the race between the read and the
10
+ * write.
11
+ */
12
+ export interface FindOrCreateApi<U, In> {
13
+ /**
14
+ * The user of this type holding `key`, or the user `input()` describes,
15
+ * created. Answers `{ user, created }`.
16
+ *
17
+ * The key is looked up as the type's own lookup would: the login, normalised
18
+ * as `findByLogin` normalises it, on a type that signs in with a password;
19
+ * the e-mail, normalised as `findByEmail` normalises it, on a type that does
20
+ * not. A user found, an inactive one included, is answered
21
+ * `{ user, created: false }`: `input` is not called and nothing is written.
22
+ *
23
+ * When nobody holds the key, `input` is called (it may be async) and the
24
+ * user is created exactly as `create` creates one. **What `input` throws
25
+ * propagates and nothing is written** — that is how the caller refuses before
26
+ * any write, such as "a password is required". The input must name the same
27
+ * key: once validated, its login (its e-mail, on a type with no password)
28
+ * must equal the normalised `key`, or the call is refused with
29
+ * `USER_INVALID` before the write.
30
+ *
31
+ * A call that loses a race to another that creates the same user gets the
32
+ * `LOGIN_TAKEN` of the store, reads the key once more and answers the
33
+ * winner `{ user, created: false }`; the `LOGIN_TAKEN` is rethrown only
34
+ * when that read finds nobody — another login of the new user is taken.
35
+ *
36
+ * On a type whose login is a username, an e-mail key finds an existing user
37
+ * (e-mails are stored as logins) but can never create one: the input's
38
+ * username is not the key, so it is `USER_INVALID`.
39
+ *
40
+ * **An account-existence oracle, as `findByEmail` is: never expose it to an
41
+ * anonymous client.** A key that is no storable string is nobody's, and goes
42
+ * through `input` and the creation, which refuses it.
43
+ *
44
+ * Rejects with `USER_INVALID`, `PASSWORD_TOO_SHORT`, `LOGIN_TAKEN`, or
45
+ * `STORE_FAILED`, and with whatever `input` throws. Sends `user.created`
46
+ * only when it created.
47
+ */
48
+ findOrCreate(key: string, input: () => (In & {
49
+ readonly password?: string;
50
+ readonly active?: boolean;
51
+ }) | Promise<In & {
52
+ readonly password?: string;
53
+ readonly active?: boolean;
54
+ }>): Promise<{
55
+ readonly user: U;
56
+ readonly created: boolean;
57
+ }>;
58
+ }
59
+ //# sourceMappingURL=find-or-create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-or-create.d.ts","sourceRoot":"","sources":["../../../src/auth/types/find-or-create.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,EAAE,EAAE;IACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,YAAY,CACX,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MACJ,CAAC,EAAE,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,GAChE,OAAO,CAAC,EAAE,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,GACxE,OAAO,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC5D"}
@@ -11,6 +11,7 @@ export type { ChangeEmailApi } from './change-email';
11
11
  export type { Checked } from './checks';
12
12
  export type { FirstProof, IssuedCode, IssuedToken, ResetPasswordApi, SignInCodeApi, VerifyEmailApi, } from './email-flows';
13
13
  export type { FindByEmailApi } from './find-by-email';
14
+ export type { FindOrCreateApi } from './find-or-create';
14
15
  export type { EmailOf, LoginOf, RequiredStringKeys, TypesOf, UserOf, } from './inference';
15
16
  export type { Janus, SharedApi, TypeApi } from './janus';
16
17
  export type { MagicLinkApi } from './magic-link';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EACX,OAAO,EACP,OAAO,EACP,kBAAkB,EAClB,OAAO,EACP,MAAM,GACN,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,qBAAqB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,OAAO,GACP,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,YAAY,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EACX,OAAO,EACP,OAAO,EACP,kBAAkB,EAClB,OAAO,EACP,MAAM,GACN,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,qBAAqB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,OAAO,GACP,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,YAAY,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -8,6 +8,7 @@ import type { JanusConfig, MultiTypeConfig, SingleTypeConfig } from '../config';
8
8
  import type { ChangeEmailApi } from './change-email';
9
9
  import type { ResetPasswordApi, SignInCodeApi, VerifyEmailApi } from './email-flows';
10
10
  import type { FindByEmailApi } from './find-by-email';
11
+ import type { FindOrCreateApi } from './find-or-create';
11
12
  import type { EmailOf, FieldsInput, LoginOf, SingleAsType, UserOf, UserOfType } from './inference';
12
13
  import type { MagicLinkApi } from './magic-link';
13
14
  import type { PasswordApi } from './password';
@@ -29,7 +30,7 @@ type SignInAnswer<Name extends string, Def, TwoFactor extends boolean> = TwoFact
29
30
  * The whole surface of one user type, with only the flows its configuration
30
31
  * allows. `TwoFactor` is whether `janus()` was given a `secondFactor`.
31
32
  */
32
- export type TypeApi<Name extends string, Def, TwoFactor extends boolean = false> = UserTypeApi<UserOfType<Name, Def>, FieldsInput<Def>> & ProvenSignInApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & ([LoginOf<Def>] extends [never] ? unknown : TwoFactor extends true ? PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>, SignInResult<UserOfType<Name, Def>>> & SecondFactorApi<UserOfType<Name, Def>> : PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>>) & ([EmailOf<Def>] extends [never] ? unknown : FindByEmailApi<UserOfType<Name, Def>> & VerifyEmailApi<UserOfType<Name, Def>> & ChangeEmailApi<UserOfType<Name, Def>> & SignInCodeApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & MagicLinkApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & StepUpApi<UserOfType<Name, Def>, TwoFactor extends true ? [LoginOf<Def>] extends [never] ? StepUpByEmail<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>> | StepUpByApp<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>>>) & ([LoginOf<Def>] extends [never] ? unknown : [EmailOf<Def>] extends [never] ? unknown : ResetPasswordApi<UserOfType<Name, Def>>);
33
+ export type TypeApi<Name extends string, Def, TwoFactor extends boolean = false> = UserTypeApi<UserOfType<Name, Def>, FieldsInput<Def>> & ProvenSignInApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & ([LoginOf<Def>] extends [never] ? unknown : TwoFactor extends true ? PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>, SignInResult<UserOfType<Name, Def>>> & SecondFactorApi<UserOfType<Name, Def>> : PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>>) & ([LoginOf<Def>] extends [never] ? [EmailOf<Def>] extends [never] ? unknown : FindOrCreateApi<UserOfType<Name, Def>, FieldsInput<Def>> : FindOrCreateApi<UserOfType<Name, Def>, FieldsInput<Def>>) & ([EmailOf<Def>] extends [never] ? unknown : FindByEmailApi<UserOfType<Name, Def>> & VerifyEmailApi<UserOfType<Name, Def>> & ChangeEmailApi<UserOfType<Name, Def>> & SignInCodeApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & MagicLinkApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & StepUpApi<UserOfType<Name, Def>, TwoFactor extends true ? [LoginOf<Def>] extends [never] ? StepUpByEmail<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>> | StepUpByApp<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>>>) & ([LoginOf<Def>] extends [never] ? unknown : [EmailOf<Def>] extends [never] ? unknown : ResetPasswordApi<UserOfType<Name, Def>>);
33
34
  /** What every configuration answers, whatever its user types. */
34
35
  export interface SharedApi<U extends {
35
36
  readonly type: string;
@@ -1 +1 @@
1
- {"version":3,"file":"janus.d.ts","sourceRoot":"","sources":["../../../src/auth/types/janus.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"janus.d.ts","sourceRoot":"","sources":["../../../src/auth/types/janus.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { AnyUser } from '../context';
2
- import type { ChangeEmailApi, FindByEmailApi, MagicLinkApi, PasswordApi, ProvenSignInApi, ResetPasswordApi, SecondFactorApi, SignInCodeApi, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, UserTypeApi, VerifyEmailApi } from '../types';
2
+ import type { ChangeEmailApi, FindByEmailApi, FindOrCreateApi, MagicLinkApi, PasswordApi, ProvenSignInApi, ResetPasswordApi, SecondFactorApi, SignInCodeApi, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, UserTypeApi, VerifyEmailApi } from '../types';
3
3
  import type { Input } from './flow-types';
4
4
  /** Everything one user type answers. Which flows it has is decided by its types; all are built. */
5
- export type AnyTypeApi = UserTypeApi<AnyUser, Input> & PasswordApi<AnyUser, Input, string, SignInResult<AnyUser>> & SecondFactorApi<AnyUser> & ProvenSignInApi<AnyUser, SignInResult<AnyUser>> & SignInCodeApi<AnyUser, SignInResult<AnyUser>> & MagicLinkApi<AnyUser, SignInResult<AnyUser>> & StepUpApi<AnyUser, StepUpByEmail<AnyUser> | StepUpByApp<AnyUser>> & VerifyEmailApi<AnyUser> & ChangeEmailApi<AnyUser> & FindByEmailApi<AnyUser> & ResetPasswordApi<AnyUser>;
5
+ export type AnyTypeApi = UserTypeApi<AnyUser, Input> & PasswordApi<AnyUser, Input, string, SignInResult<AnyUser>> & SecondFactorApi<AnyUser> & ProvenSignInApi<AnyUser, SignInResult<AnyUser>> & SignInCodeApi<AnyUser, SignInResult<AnyUser>> & MagicLinkApi<AnyUser, SignInResult<AnyUser>> & StepUpApi<AnyUser, StepUpByEmail<AnyUser> | StepUpByApp<AnyUser>> & VerifyEmailApi<AnyUser> & ChangeEmailApi<AnyUser> & FindByEmailApi<AnyUser> & FindOrCreateApi<AnyUser, Input> & ResetPasswordApi<AnyUser>;
6
6
  //# sourceMappingURL=any-type-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"any-type-api.d.ts","sourceRoot":"","sources":["../../../src/auth/users/any-type-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EACX,cAAc,EACd,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,WAAW,EACX,cAAc,EACd,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,mGAAmG;AACnG,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,GACnD,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC1D,eAAe,CAAC,OAAO,CAAC,GACxB,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC/C,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC7C,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC5C,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,GACjE,cAAc,CAAC,OAAO,CAAC,GACvB,cAAc,CAAC,OAAO,CAAC,GACvB,cAAc,CAAC,OAAO,CAAC,GACvB,gBAAgB,CAAC,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"any-type-api.d.ts","sourceRoot":"","sources":["../../../src/auth/users/any-type-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EACX,cAAc,EACd,cAAc,EACd,eAAe,EACf,YAAY,EACZ,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,WAAW,EACX,cAAc,EACd,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,mGAAmG;AACnG,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,GACnD,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC1D,eAAe,CAAC,OAAO,CAAC,GACxB,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC/C,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC7C,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC5C,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,GACjE,cAAc,CAAC,OAAO,CAAC,GACvB,cAAc,CAAC,OAAO,CAAC,GACvB,cAAc,CAAC,OAAO,CAAC,GACvB,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,GAC/B,gBAAgB,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { At } from '../at';
2
+ import { type ResolvedType } from '../config';
3
+ import { type AnyUser, type Context } from '../context';
4
+ import type { FindOrCreateApi } from '../types';
5
+ import type { Input } from './flow-types';
6
+ /**
7
+ * `findOrCreate` for one user type: the lookup by login (e-mail on a type
8
+ * with no password), and when nobody holds the key, `input()` and the
9
+ * creation `create` does — once more the lookup if the write lost a race.
10
+ */
11
+ export declare function findOrCreateFlows(context: Context, type: ResolvedType, at: At): FindOrCreateApi<AnyUser, Input>;
12
+ //# sourceMappingURL=find-or-create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-or-create.d.ts","sourceRoot":"","sources":["../../../src/auth/users/find-or-create.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAyB,MAAM,YAAY,CAAC;AAG/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAqD1C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CA0BjC"}
@@ -1,7 +1,12 @@
1
1
  import type { ResolvedType } from '../config';
2
2
  import { type Context } from '../context';
3
- import type { UserRecord } from '../port/types';
3
+ import type { JsonObject, UserRecord } from '../port/types';
4
4
  import type { Input } from './flow-types';
5
- /** Validates, hashes, writes once. What `create` and `signUp` share. */
6
- export declare function insert(context: Context, type: ResolvedType, input: Input, where: string): Promise<UserRecord>;
5
+ /**
6
+ * Validates, hashes, writes once. What `create`, `signUp` and `findOrCreate`
7
+ * share. `accept` sees the validated fields and may throw to refuse before
8
+ * anything is written; it is `findOrCreate`'s check that the input names the
9
+ * key it was asked for.
10
+ */
11
+ export declare function insert(context: Context, type: ResolvedType, input: Input, where: string, accept?: (fields: JsonObject) => void): Promise<UserRecord>;
7
12
  //# sourceMappingURL=insert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../src/auth/users/insert.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,wEAAwE;AACxE,wBAAsB,MAAM,CAC3B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAiCrB"}
1
+ {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../src/auth/users/insert.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C;;;;;GAKG;AACH,wBAAsB,MAAM,CAC3B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GACnC,OAAO,CAAC,UAAU,CAAC,CAkCrB"}
@@ -4,7 +4,7 @@ import type { AnyTypeApi } from './any-type-api';
4
4
  /**
5
5
  * Everything one user type answers, gathered from its flows: its records,
6
6
  * its password, its sign-in on the application's proof, its second factor,
7
- * its sign-in code and link, its step-up, its e-mails and the lookup by e-mail.
7
+ * its sign-in code and link, its step-up, its e-mails, the lookup by e-mail and the lookup that creates.
8
8
  */
9
9
  export declare function typeApi(context: Context, type: ResolvedType): AnyTypeApi;
10
10
  //# sourceMappingURL=type-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"type-api.d.ts","sourceRoot":"","sources":["../../../src/auth/users/type-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAO1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKjD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,GAAG,UAAU,CAmBxE"}
1
+ {"version":3,"file":"type-api.d.ts","sourceRoot":"","sources":["../../../src/auth/users/type-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAO1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAMjD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,GAAG,UAAU,CAoBxE"}