@foir/sdk 0.30.0 → 0.31.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.
@@ -46,6 +46,7 @@
46
46
  * against `api-public/internal/schema/field_mapping.go`.
47
47
  */
48
48
  import type { FoirConnection } from './schema';
49
+ import type { StepReply } from '../runs/step-reply';
49
50
  /** A `JSON` scalar. Deliberately not `any`: reading through it needs a check. */
50
51
  export type FoirJsonValue = string | number | boolean | null | FoirJsonValue[] | {
51
52
  [key: string]: FoirJsonValue;
@@ -84,6 +85,8 @@ export interface Image {
84
85
  * `url: String` — so this follows.
85
86
  */
86
87
  __typename?: 'Image';
88
+ /** The item's own id when this is an item of a list or an entry of a prose field; null on a plain field's value. */
89
+ _id: string | null;
87
90
  url: string | null;
88
91
  width: number | null;
89
92
  height: number | null;
@@ -102,6 +105,8 @@ export interface Image {
102
105
  }
103
106
  export interface Video {
104
107
  __typename?: 'Video';
108
+ /** The item's own id when this is an item of a list or an entry of a prose field; null on a plain field's value. */
109
+ _id: string | null;
105
110
  url: string | null;
106
111
  thumbnailUrl: string | null;
107
112
  previewUrl: string | null;
@@ -116,6 +121,8 @@ export interface Video {
116
121
  }
117
122
  export interface FileValue {
118
123
  __typename?: 'FileValue';
124
+ /** The item's own id when this is an item of a list or an entry of a prose field; null on a plain field's value. */
125
+ _id: string | null;
119
126
  url: string | null;
120
127
  filename: string | null;
121
128
  mimeType: string | null;
@@ -123,6 +130,8 @@ export interface FileValue {
123
130
  }
124
131
  export interface GeoPoint {
125
132
  __typename?: 'GeoPoint';
133
+ /** The item's own id when this is an item of a list or an entry of a prose field; null on a plain field's value. */
134
+ _id: string | null;
126
135
  lat: number | null;
127
136
  lng: number | null;
128
137
  }
@@ -157,7 +166,8 @@ export interface ProseSpan {
157
166
  */
158
167
  export interface Passage {
159
168
  __typename?: 'Passage';
160
- _key: string | null;
169
+ /** The passage's own id: minted once, never changed, unique within its record. */
170
+ _id: string | null;
161
171
  kind: string | null;
162
172
  token: string | null;
163
173
  align: string | null;
@@ -209,30 +219,25 @@ export interface RecordAncestor {
209
219
  *
210
220
  * # What makes it followable
211
221
  *
212
- * `_targetModelKey` names the model and `_targetId` OR `_targetNaturalKey`
213
- * identifies the row within it: `foir[_targetModelKey].findUnique({ where: { _id:
214
- * _targetId } })`. Both identifier shapes are live — on the dev database 101 of
215
- * 193 stored reference values carry an id and 92 carry a natural key and no id
216
- * — so a consumer must handle either.
217
- *
218
- * `_id` and `_type` are NOT here, and their absence is the correction
219
- * rather than an omission. They were the wrapper's own metadata: `_id` was the
220
- * ITEM's id (never the target's, which is why `_targetId` exists separately) and
221
- * `_type` was the union discriminator, reading `"reference"` on every reference
222
- * ever stored. The wrapper is gone, so neither field is in the schema, and a
223
- * consumer selecting them got `undefined` while the types promised a string.
222
+ * `_targetModelKey` names the model and `_targetId` the row within it:
223
+ * `foir[_targetModelKey].findUnique({ where: { _id: _targetId } })`. A reference
224
+ * names its target by id and nothing else.
225
+ *
226
+ * `_id` is the entry's own id, never the target's, which is why `_targetId`
227
+ * exists separately. It is null on a reference field's value, which is not an
228
+ * entry.
224
229
  */
225
230
  export interface Reference<TRecord = FoirUnexpandedRecord> {
226
231
  __typename?: 'Reference';
232
+ /** The entry's own id, when the reference is an entry of a prose field. */
233
+ _id: string | null;
227
234
  /** The model to fetch the target from. Null when the target no longer exists. */
228
235
  _targetModelKey: string | null;
229
- /** The target's id, when the reference stored one. */
236
+ /** The target's id. */
230
237
  _targetId: string | null;
231
- /** The target's natural key, when the reference names its target that way. */
232
- _targetNaturalKey: string | null;
233
238
  /**
234
239
  * The resolved target, over the project's `Record` union — every model in the
235
- * project. Left as a type parameter so the generated file can name that
240
+ * project that bears records. Left as a type parameter so the generated file can name that
236
241
  * union; the default is the honest answer for a consumer who has not narrowed
237
242
  * it.
238
243
  */
@@ -397,13 +402,8 @@ export type FoirUnexpandedRecord = FoirDegraded;
397
402
  */
398
403
  export type FoirVisibility = 'PRIVATE' | 'PUBLIC';
399
404
  export type FoirAccessLevel = 'READ' | 'WRITE' | 'DELETE';
400
- export type FoirAccessSource = 'PUBLIC' | 'OWNER' | 'GRANT' | 'ROLE';
405
+ export type FoirAccessSource = 'OWNER' | 'GRANT' | 'ROLE';
401
406
  export type FoirGranteeKind = 'USER';
402
- /** `myAccess` — this principal's effective access to one record. */
403
- export interface FoirMyAccess {
404
- access: FoirAccessLevel;
405
- source: FoirAccessSource;
406
- }
407
407
  export interface FoirAccessGrant {
408
408
  access: FoirAccessLevel;
409
409
  createdAt: string;
@@ -416,7 +416,7 @@ export interface FoirAccessor {
416
416
  granteeKind: FoirGranteeKind;
417
417
  source: FoirAccessSource;
418
418
  }
419
- /** `access` — the full ACL of one record. */
419
+ /** `access` — who can reach one record, or one user's access to it. */
420
420
  export interface FoirResourceAccess {
421
421
  accessors: FoirAccessor[];
422
422
  grants: FoirAccessGrant[];
@@ -592,14 +592,17 @@ export interface FoirWriteNoteArgs {
592
592
  /** A visitor may write about a record and nothing else. */
593
593
  subjectType: FoirNoteSubjectType;
594
594
  subjectId: string;
595
- /** Required when `subjectType` is `'record'`, refused otherwise. */
595
+ /**
596
+ * @deprecated Not read: a note on a record takes the record's own model.
597
+ * Still accepted so code that sends it keeps working.
598
+ */
596
599
  subjectModelKey?: string;
597
600
  body: string;
598
601
  /** Reply within an existing thread. Refused where nobody is signed in. */
599
602
  parentNoteId?: string;
600
603
  /**
601
- * Files to submit with this note, already uploaded through
602
- * `createFileUpload` / `confirmFileUpload`. Refused where nobody is signed in.
604
+ * Files to submit with this note, already uploaded through `uploadFile`.
605
+ * Refused where nobody is signed in.
603
606
  *
604
607
  * A file this caller cannot read is REFUSED and the note is not written. That
605
608
  * is not a rough edge to code around: a create that accepted the note and
@@ -626,14 +629,16 @@ export interface FoirWriteNoteArgs {
626
629
  export interface FoirCreateMyNoteArgs {
627
630
  subjectType: FoirNoteSubjectType;
628
631
  subjectId: string;
629
- /** Required when `subjectType` is `'record'`, refused otherwise. */
632
+ /**
633
+ * @deprecated Not read: a note on a record takes the record's own model.
634
+ * Still accepted so code that sends it keeps working.
635
+ */
630
636
  subjectModelKey?: string;
631
637
  body: string;
632
638
  /** Reply within an existing thread. */
633
639
  parentNoteId?: string;
634
640
  /**
635
- * Files to submit with this note, already uploaded through
636
- * `createFileUpload` / `confirmFileUpload`.
641
+ * Files to submit with this note, already uploaded through `uploadFile`.
637
642
  *
638
643
  * A file this caller cannot read is REFUSED and the note is not written. That
639
644
  * is not a rough edge to code around: a create that accepted the note and
@@ -693,14 +698,35 @@ export interface FoirAgentApproval {
693
698
  decision: 'PENDING' | 'APPROVED' | 'DENIED' | 'EXPIRED';
694
699
  reason?: string | null;
695
700
  }
701
+ /**
702
+ * An act held for a decision: a step a run holds, or an act proposed on a
703
+ * note. `myAgentApprovals` lists them and `decideAgentApproval` decides one.
704
+ */
705
+ export interface FoirAgentHeldAct {
706
+ /** Pass it to `decideAgentApproval`. */
707
+ id: string;
708
+ heldIn: 'RUN' | 'NOTE';
709
+ /** RUN: the run that holds it. */
710
+ runId?: string | null;
711
+ /** NOTE: the note it is held on. */
712
+ noteId?: string | null;
713
+ /** The tool, operation or agent it would call, by its name. */
714
+ action: string;
715
+ /** What it would be called with. */
716
+ input?: unknown;
717
+ approval?: FoirAgentApproval | null;
718
+ requestedBy?: string | null;
719
+ /** ISO-8601. */
720
+ createdAt?: string | null;
721
+ }
696
722
  /** One step of a run: a model turn, a tool call, a check, or a child agent. */
697
723
  export interface FoirAgentRunNode {
698
724
  id: string;
699
725
  parentId?: string | null;
700
726
  /**
701
727
  * PAGE_TOOL is a call to a tool the caller's page offered with the message,
702
- * HELD until the page answers it with `answerPageTool`; DATA is data sent to
703
- * the app's components, delivered as a DATA event.
728
+ * HELD until the page answers it with its `reply`; DATA is data sent to the
729
+ * app's components, delivered as a DATA event.
704
730
  */
705
731
  kind: 'AGENT' | 'OPERATION' | 'VERB' | 'MCP_TOOL' | 'MODEL_TURN' | 'CHECK' | 'PAGE_TOOL' | 'DATA';
706
732
  /** The tool, operation or agent the step is, by its key. */
@@ -714,6 +740,11 @@ export interface FoirAgentRunNode {
714
740
  startedAt?: string | null;
715
741
  /** ISO-8601. */
716
742
  completedAt?: string | null;
743
+ /**
744
+ * A HELD PAGE_TOOL step, for the run's own caller: how the page answers it,
745
+ * with `replyToStep`. Nobody else is handed one.
746
+ */
747
+ reply?: StepReply | null;
717
748
  }
718
749
  /**
719
750
  * A run of an agent: its answer, once given, and every step it took. A refusal
@@ -793,7 +824,7 @@ export interface FoirStartAgentRunArgs {
793
824
  /**
794
825
  * Tools of the caller's own page the agent may call during this run, for
795
826
  * an agent that allows the page's tools. A call is HELD until the page
796
- * answers it with `answerPageTool`. At most 20. `foirChatTransport`'s
827
+ * answers it with its `reply`. At most 20. `foirChatTransport`'s
797
828
  * `pageTools` sends these and answers the calls for you.
798
829
  */
799
830
  pageTools?: FoirPageToolOffer[];
@@ -59,7 +59,7 @@ export { planFoirModelCall, planFoirNamespaceCall } from './document';
59
59
  export type { FoirCall, FoirDocumentPlan, FoirNamespaceCall, FoirNamespaceSelections, FoirRecordPath, FoirSelectionModel, FoirSelectionNamespace, FoirSelections, FoirSelectionSource, FoirSelectionUnions, } from './document';
60
60
  export { createFoirSubjectRegistry, recordFoirSubjects } from './subjects';
61
61
  export type { FoirFieldMark, FoirFieldMarkAttributes, FoirSubject, FoirSubjectEntry, FoirSubjectOptions, FoirSubjectOrigin, FoirSubjectRegistry, } from './subjects';
62
- export type { CollabInfo, FileValue, FoirAccessGrant, FoirAccessLevel, FoirAccessSource, FoirAccessor, FoirCreateMyNoteArgs, FoirWriteNoteArgs, FoirUserNote, FoirUserNoteAttachment, FoirDegraded, FoirDepthBelow, FoirDepthLevel, FoirDeviceType, FoirGranteeKind, FoirJsonValue, FoirMyAccess, FoirMyNotesArgs, FoirMyNotesResult, FoirAgentApproval, FoirAgentAttachment, FoirAgentAttachmentUpload, FoirAgentRun, FoirAgentRunNode, FoirAgentRunStatus, FoirAgentThread, FoirAgentThreadMessage, FoirAgentThreadsResult, FoirMcpConnection, FoirStartAgentRunArgs, FoirPageToolOffer, FoirCreateAgentAttachmentArgs, FoirMessageTag, FoirMessageTagKind, FoirNoteConfig, FoirNoteReceipt, FoirNoteSubjectType, FoirPlatformType, FoirRecordOwner, FoirResolveContext, FoirResourceAccess, FoirUnexpandedRecord, FoirUpdateManyPayload, FoirVisibility, GeoPoint, Image, ImageFormat, ImageVariant, ImageVariantSet, MediaValue, Passage, Prose, ProseSpan, RecordAncestor, Reference, Richtext, Video, } from './builtins';
62
+ export type { CollabInfo, FileValue, FoirAccessGrant, FoirAccessLevel, FoirAccessSource, FoirAccessor, FoirCreateMyNoteArgs, FoirWriteNoteArgs, FoirUserNote, FoirUserNoteAttachment, FoirDegraded, FoirDepthBelow, FoirDepthLevel, FoirDeviceType, FoirGranteeKind, FoirJsonValue, FoirMyNotesArgs, FoirMyNotesResult, FoirAgentApproval, FoirAgentAttachment, FoirAgentAttachmentUpload, FoirAgentRun, FoirAgentHeldAct, FoirAgentRunNode, FoirAgentRunStatus, FoirAgentThread, FoirAgentThreadMessage, FoirAgentThreadsResult, FoirMcpConnection, FoirStartAgentRunArgs, FoirPageToolOffer, FoirCreateAgentAttachmentArgs, FoirMessageTag, FoirMessageTagKind, FoirNoteConfig, FoirNoteReceipt, FoirNoteSubjectType, FoirPlatformType, FoirRecordOwner, FoirResolveContext, FoirResourceAccess, FoirUnexpandedRecord, FoirUpdateManyPayload, FoirVisibility, GeoPoint, Image, ImageFormat, ImageVariant, ImageVariantSet, MediaValue, Passage, Prose, ProseSpan, RecordAncestor, Reference, Richtext, Video, } from './builtins';
63
63
  /**
64
64
  * Pre-rename spellings. The `<Name>Block` wrappers and the `<Name>Value`
65
65
  * split are the same thing as the types above — an item IS its value — so each
@@ -82,6 +82,6 @@ export type { FoirInclude, FoirModelOperation, FoirNamespaceOperation, FoirOmitt
82
82
  export { FoirDecimal, decimal } from './decimal';
83
83
  export type { FoirDecimalDivOptions, FoirDecimalInput, FoirDecimalRounding, } from './decimal';
84
84
  export { FoirRequestError, FoirAuthDowngradedError, FoirSessionExpiredError, FOIR_VISITOR_IP_HEADER, stepUpRequired, } from '../transport/foir-http';
85
- export type { FoirEnv, FoirGraphQLErrorItem, FoirTokenProvider, FoirAuthDowngrade, FoirStepUp, FoirVisitor, FoirVisitorSource, } from '../transport/foir-http';
85
+ export type { FoirEnv, FoirGraphQLErrorItem, FoirTokenProvider, FoirAuthDowngrade, FoirStepUp, FoirStepUpAsk, FoirVisitor, FoirVisitorSource, } from '../transport/foir-http';
86
86
  export { askAgent, uploadAgentFile, visitorToken } from './agents';
87
87
  export type { AgentReply, AskAgentOptions, FoirAgentFile, FoirAgentFilesClient, FoirAgentsClient } from './agents';
@@ -97,7 +97,7 @@ export interface FoirManifestOp {
97
97
  *
98
98
  * The model's own `selection` answers for `findUnique` and for nothing else:
99
99
  * `findMany` returns a connection OVER the model, `search` returns a wrapper
100
- * CARRYING it, `delete` returns `Boolean!`, and `myAccess` returns a payload
100
+ * CARRYING it, `delete` returns `Boolean!`, and `access` returns a payload
101
101
  * that is not the model in any sense. A consumer that bridged that gap by
102
102
  * knowing from the OP NAME which shape to print was carrying a schema under
103
103
  * another name.
@@ -151,7 +151,7 @@
151
151
  * (`findUnique`, `findMine`).
152
152
  * - `connection` — a Relay connection over the model (`findMany`).
153
153
  * - `list` — a plain array of the model.
154
- * - `opaque` — the op does not return the model at all (`myAccess`). Its
154
+ * - `opaque` — the op does not return the model at all (`access`). Its
155
155
  * `type` is used verbatim and `include`/`omit` are not offered.
156
156
  *
157
157
  * 7. **Unions are emitted statically.** A polymorphic field's type is written
@@ -274,9 +274,9 @@ type MemberOf<U, T> = Extract<U, {
274
274
  *
275
275
  * `__typename` is excluded because it is not optional: the printer emits it at
276
276
  * the union level whatever the selection says, so offering it would suggest a
277
- * choice that does not exist. Block-meta keys (`_id`, `_key`, `_type`,
278
- * `_label`) ARE offered — unlike a record's `_id` the server does not force
279
- * them inside a fragment, so naming them is the only way to keep them.
277
+ * choice that does not exist. An entry's own `_id` IS offered — unlike a
278
+ * record's `_id` the server does not force it inside a fragment, so naming it is
279
+ * the only way to keep it.
280
280
  */
281
281
  type MemberKeys<U, T> = Exclude<Extract<keyof MemberOf<U, T>, string>, '__typename'>;
282
282
  /**
package/dist/data.js CHANGED
@@ -14,8 +14,11 @@ function stepUpRequired(error) {
14
14
  if (!(error instanceof FoirRequestError)) return null;
15
15
  for (const item of error.graphQLErrors ?? []) {
16
16
  if (item.extensions?.code !== "STEP_UP_REQUIRED") continue;
17
- const asked = item.extensions.stepUp;
18
- if (asked === "second_factor" || asked === "sign_in_method") return asked;
17
+ const kind = item.extensions.stepUp;
18
+ if (kind === "proof" || kind === "second_factor" || kind === "sign_in_method") {
19
+ const url = item.extensions.stepUpUrl;
20
+ return { kind, url: typeof url === "string" && url !== "" ? url : null };
21
+ }
19
22
  }
20
23
  return null;
21
24
  }
@@ -13,7 +13,7 @@
13
13
  */
14
14
  export { createFoirCookieAdapter, foirCookieName, } from './cookieAdapter';
15
15
  export type { FoirCookieStore, CookieAdapterOptions } from './cookieAdapter';
16
- export { getFoirSession, getFoirToken, foirSignOut, foirSignInWithPassword, foirRegisterWithPassword, foirSignInWithOtp, completeFoirProviderLogin, } from './session';
16
+ export { getFoirSession, getFoirToken, foirSignOut, foirSignInWithPassword, foirSignUpWithPassword, foirSignInWithOtp, startFoirProviderSignIn, finishFoirProviderSignIn, } from './session';
17
17
  export { readFoirCacheTags, foirCacheTags, createFoirTaggingFetch, foirTagsForRecordEvent, revalidateFoirRecordEvent, FOIR_SURROGATE_KEY_HEADER, FOIR_NEXT_TAG_MAX_ITEMS, FOIR_NEXT_TAG_MAX_LENGTH, FOIR_UNTAGGED_TAG, } from './cacheTags';
18
18
  export type { FoirHeaderSource, FoirCacheTags, FoirCacheTagsOptions, FoirTaggingReport, FoirTaggingFetchOptions, FoirRecordChange, } from './cacheTags';
19
19
  export { createFoirMiddleware } from './middleware';
@@ -1,4 +1,4 @@
1
- import type { FoirUser } from '../auth';
1
+ import type { FoirSignInStep, FoirUser, ProviderSignInInput } from '../auth';
2
2
  import type { FoirEnv } from '../graphql/foir-request';
3
3
  import type { CookieAdapterOptions, FoirCookieStore } from './cookieAdapter';
4
4
  /**
@@ -28,25 +28,41 @@ interface PasswordArgs {
28
28
  /** Password sign-in in a route handler; persists the session to cookies. */
29
29
  export declare function foirSignInWithPassword({ env, store, email, password, cookieOptions, }: PasswordArgs): Promise<FoirUser>;
30
30
  /**
31
- * Start a sign-up in a route handler: the address is mailed a code, and
32
- * {@link foirSignInWithOtp} with it finishes the account and persists the
31
+ * Start a sign-up in a route handler: the address is mailed a code. Keep the
32
+ * step this answers (in the form, or a cookie) for {@link foirSignInWithOtp},
33
+ * which brings the code back to it, finishes the account and persists the
33
34
  * session to cookies.
34
35
  */
35
- export declare function foirRegisterWithPassword({ env, email, password, }: Pick<PasswordArgs, 'env' | 'email' | 'password'>): Promise<void>;
36
- /** Complete an OTP sign-in in a route handler; persists the session to cookies. */
37
- export declare function foirSignInWithOtp({ env, store, email, otp, cookieOptions, }: {
36
+ export declare function foirSignUpWithPassword({ env, email, password, }: Pick<PasswordArgs, 'env' | 'email' | 'password'>): Promise<FoirSignInStep>;
37
+ /**
38
+ * Complete a code sign-in in a route handler: bring the code back to the step
39
+ * `requestOTP` or {@link foirSignUpWithPassword} answered. Persists the
40
+ * session to cookies.
41
+ */
42
+ export declare function foirSignInWithOtp({ env, store, step, otp, cookieOptions, }: {
38
43
  env: FoirEnv;
39
44
  store: FoirCookieStore;
40
- email: string;
45
+ step: FoirSignInStep;
41
46
  otp: string;
42
47
  cookieOptions?: CookieAdapterOptions['cookieOptions'];
43
48
  }): Promise<FoirUser>;
44
49
  /**
45
- * Complete a provider (OAuth/OIDC) login in your callback route handler:
46
- * exchange the `code` + `state` for tokens and persist them to the session
47
- * cookies. Returns the user.
50
+ * Start a provider (OAuth/OIDC) sign-in in a route handler: answers where to
51
+ * send the browser, and keeps the step its finish is brought back to in the
52
+ * session cookies for {@link finishFoirProviderSignIn}.
53
+ */
54
+ export declare function startFoirProviderSignIn(opts: {
55
+ env: FoirEnv;
56
+ store: FoirCookieStore;
57
+ input: ProviderSignInInput;
58
+ cookieOptions?: CookieAdapterOptions['cookieOptions'];
59
+ }): Promise<string | null>;
60
+ /**
61
+ * Finish a provider (OAuth/OIDC) sign-in in your callback route handler:
62
+ * bring the `code` + `state` back to the step {@link startFoirProviderSignIn}
63
+ * kept, and persist the session to cookies. Returns the user.
48
64
  */
49
- export declare function completeFoirProviderLogin(opts: {
65
+ export declare function finishFoirProviderSignIn(opts: {
50
66
  env: FoirEnv;
51
67
  store: FoirCookieStore;
52
68
  providerKey: string;
package/dist/next.js CHANGED
@@ -949,7 +949,7 @@ async function fetchFoirManifest(config, options = {}) {
949
949
 
950
950
  // src/auth/ops.ts
951
951
  var AUTH_OPS = {
952
- // Pre-credential reads a login screen renders itself from.
952
+ // Pre-credential reads a sign-in screen renders itself from.
953
953
  authConfig: "authConfig",
954
954
  authProviders: "authProviders",
955
955
  defaultAuthProvider: "defaultAuthProvider",
@@ -961,17 +961,15 @@ var AUTH_OPS = {
961
961
  // they hold. `./access` reads it.
962
962
  whoami: "whoami",
963
963
  // Methods.
964
- login: "login",
964
+ signIn: "signIn",
965
965
  register: "register",
966
966
  requestOTP: "requestOTP",
967
- loginWithOTP: "loginWithOTP",
968
967
  // Session lifecycle.
969
968
  refreshToken: "refreshToken",
970
- logout: "logout",
969
+ signOut: "signOut",
971
970
  joinWaitlist: "joinWaitlist",
972
971
  // Password management and account lifecycle.
973
972
  requestPasswordReset: "requestPasswordReset",
974
- resetPassword: "resetPassword",
975
973
  updatePassword: "updatePassword",
976
974
  deleteMyAccount: "deleteMyAccount",
977
975
  // Ending their own membership where the request stands.
@@ -985,10 +983,10 @@ var AUTH_OPS = {
985
983
  mySessions: "mySessions",
986
984
  endMySession: "endMySession",
987
985
  signOutEverywhere: "signOutEverywhere",
988
- // Their email addresses: one added is kept once the code it was sent proves it.
986
+ // Their email addresses: one added is kept once the code it was sent comes
987
+ // back on the step addMyAddress answers, on its own route (proveMyAddress).
989
988
  myAddresses: "myAddresses",
990
989
  addMyAddress: "addMyAddress",
991
- proveMyAddress: "proveMyAddress",
992
990
  makeMyAddressPrimary: "makeMyAddressPrimary",
993
991
  removeMyAddress: "removeMyAddress",
994
992
  // A copy of their own data.
@@ -999,10 +997,8 @@ var AUTH_OPS = {
999
997
  disconnectSignInMethod: "disconnectSignInMethod",
1000
998
  // Their second factors. A change needs one proven first, by a step-up.
1001
999
  myFactors: "myFactors",
1002
- beginStepUp: "beginStepUp",
1003
- completeStepUp: "completeStepUp",
1000
+ stepUp: "stepUp",
1004
1001
  beginFactorEnrollment: "beginFactorEnrollment",
1005
- completeFactorEnrollment: "completeFactorEnrollment",
1006
1002
  removeFactor: "removeFactor",
1007
1003
  renameFactor: "renameFactor",
1008
1004
  makeRecoveryCodes: "makeRecoveryCodes",
@@ -1016,13 +1012,7 @@ var AUTH_OPS = {
1016
1012
  // Founding one. Present in a manifest only when the project opted in.
1017
1013
  registerOrganization: "registerOrganization",
1018
1014
  // External identity providers.
1019
- loginWithProvider: "loginWithProvider",
1020
- providerCallback: "providerCallback",
1021
- providerVerifyOTP: "providerVerifyOTP",
1022
- // A provider sign-in that names an address somebody already holds, waiting
1023
- // for the code sent to it.
1024
- confirmProviderLink: "confirmProviderLink",
1025
- resendProviderLinkCode: "resendProviderLinkCode"
1015
+ signInWithProvider: "signInWithProvider"
1026
1016
  };
1027
1017
  function authClientConfig(env, options) {
1028
1018
  const apiKey = resolveFoirApiKey(env);
@@ -1082,22 +1072,88 @@ function stripUndefined(args) {
1082
1072
  return out;
1083
1073
  }
1084
1074
 
1075
+ // src/auth/steps.ts
1076
+ async function postSignInStep(env, path, body, stepToken) {
1077
+ const apiUrl = env.PLATFORM_API_URL;
1078
+ if (!apiUrl) {
1079
+ throw new FoirRequestError("[foir] no api url configured: set PLATFORM_API_URL");
1080
+ }
1081
+ const headers = { "Content-Type": "application/json" };
1082
+ if (stepToken) headers["X-Foir-Token"] = stepToken;
1083
+ const res = await fetch(foirApiEndpoint(apiUrl, path), {
1084
+ method: "POST",
1085
+ headers,
1086
+ body: JSON.stringify(body)
1087
+ });
1088
+ const data = await res.json().catch(() => ({}));
1089
+ if (!res.ok) {
1090
+ const message = typeof data.error === "string" ? data.error : `[foir] POST ${path} failed: HTTP ${res.status}`;
1091
+ const code = typeof data.code === "string" ? data.code : void 0;
1092
+ const extensions = {};
1093
+ if (code) extensions.code = code;
1094
+ if (data.signIn && typeof data.signIn === "object") extensions.signIn = data.signIn;
1095
+ if (data.providerSignIn && typeof data.providerSignIn === "object") extensions.providerSignIn = data.providerSignIn;
1096
+ throw new FoirRequestError(message, {
1097
+ status: res.status,
1098
+ graphQLErrors: Object.keys(extensions).length > 0 ? [{ message, extensions }] : void 0
1099
+ });
1100
+ }
1101
+ return data;
1102
+ }
1103
+
1085
1104
  // src/auth/auth.ts
1086
1105
  var TOKEN_KEY = "foir:token";
1087
1106
  var REFRESH_KEY = "foir:refreshToken";
1088
1107
  var USER_ID_KEY = "foir:userId";
1089
1108
  var EMAIL_KEY = "foir:email";
1090
- async function login(env, vars) {
1091
- return authOp(env, AUTH_OPS.login, vars);
1109
+ async function codeSignIn(env, vars) {
1110
+ const signedIn = await postSignInStep(env, `/v1/sign-ins/${encodeURIComponent(vars.step.id)}/code`, { code: vars.otp }, vars.step.token);
1111
+ return {
1112
+ token: signedIn.accessToken ?? "",
1113
+ refreshToken: signedIn.refreshToken ?? "",
1114
+ userId: signedIn.user?.id ?? "",
1115
+ email: signedIn.user?.email ?? ""
1116
+ };
1092
1117
  }
1093
- async function register(env, vars) {
1094
- return authOp(env, AUTH_OPS.register, vars);
1118
+ async function signInWithProvider(adapter, env, input) {
1119
+ const started = await authOp(
1120
+ env,
1121
+ AUTH_OPS.signInWithProvider,
1122
+ { input }
1123
+ );
1124
+ if (started.step) await adapter.set(PROVIDER_STEP_KEY, JSON.stringify(started.step));
1125
+ else await adapter.unset(PROVIDER_STEP_KEY);
1126
+ return { redirectUrl: started.redirectUrl ?? null };
1127
+ }
1128
+ async function finishProviderSignIn(adapter, env, input) {
1129
+ const step = await keptProviderStep(adapter, "no provider sign-in was started here: start one before finishing it");
1130
+ return signedInOnStep(adapter, env, step, "provider", {
1131
+ providerKey: input.providerKey,
1132
+ code: input.code,
1133
+ state: input.state
1134
+ });
1095
1135
  }
1096
- async function loginWithOTP(env, vars) {
1097
- return authOp(env, AUTH_OPS.loginWithOTP, vars);
1136
+ var PROVIDER_STEP_KEY = "foir:providerStep";
1137
+ async function keptProviderStep(adapter, missing) {
1138
+ const raw = await adapter.get(PROVIDER_STEP_KEY);
1139
+ if (raw) {
1140
+ try {
1141
+ const step = JSON.parse(raw);
1142
+ if (typeof step.id === "string" && typeof step.token === "string") return step;
1143
+ } catch {
1144
+ }
1145
+ }
1146
+ throw new Error(`[foir] ${missing}`);
1098
1147
  }
1099
- async function providerCallback(env, input) {
1100
- return authOp(env, AUTH_OPS.providerCallback, { input });
1148
+ async function signedInOnStep(adapter, env, step, route, body) {
1149
+ const signedIn = await postSignInStep(env, `/v1/sign-ins/${encodeURIComponent(step.id)}/${route}`, body, step.token);
1150
+ await adapter.unset(PROVIDER_STEP_KEY);
1151
+ return saveSession(adapter, {
1152
+ token: signedIn.accessToken ?? "",
1153
+ refreshToken: signedIn.refreshToken ?? "",
1154
+ userId: signedIn.userId ?? "",
1155
+ email: signedIn.email ?? ""
1156
+ });
1101
1157
  }
1102
1158
  async function saveSession(adapter, result) {
1103
1159
  await adapter.set(TOKEN_KEY, result.token);
@@ -1125,13 +1181,13 @@ async function getSessionUser(adapter) {
1125
1181
  return { id, email: email ?? "" };
1126
1182
  }
1127
1183
  async function signInWithPassword(adapter, env, vars) {
1128
- return saveSession(adapter, await login(env, vars));
1184
+ return saveSession(adapter, await authOp(env, AUTH_OPS.signIn, vars));
1129
1185
  }
1130
1186
  async function signUpWithPassword(env, vars) {
1131
- await register(env, vars);
1187
+ return authOp(env, AUTH_OPS.register, vars);
1132
1188
  }
1133
1189
  async function signInWithOTP(adapter, env, vars) {
1134
- return saveSession(adapter, await loginWithOTP(env, vars));
1190
+ return saveSession(adapter, await codeSignIn(env, vars));
1135
1191
  }
1136
1192
 
1137
1193
  // src/next/session.ts
@@ -1156,7 +1212,7 @@ function foirSignInWithPassword({
1156
1212
  password
1157
1213
  });
1158
1214
  }
1159
- function foirRegisterWithPassword({
1215
+ function foirSignUpWithPassword({
1160
1216
  env,
1161
1217
  email,
1162
1218
  password
@@ -1166,25 +1222,26 @@ function foirRegisterWithPassword({
1166
1222
  function foirSignInWithOtp({
1167
1223
  env,
1168
1224
  store,
1169
- email,
1225
+ step,
1170
1226
  otp,
1171
1227
  cookieOptions
1172
1228
  }) {
1173
1229
  return signInWithOTP(createFoirCookieAdapter(store, { cookieOptions }), env, {
1174
- email,
1230
+ step,
1175
1231
  otp
1176
1232
  });
1177
1233
  }
1178
- async function completeFoirProviderLogin(opts) {
1179
- const result = await providerCallback(opts.env, {
1234
+ async function startFoirProviderSignIn(opts) {
1235
+ const adapter = createFoirCookieAdapter(opts.store, { cookieOptions: opts.cookieOptions });
1236
+ return (await signInWithProvider(adapter, opts.env, opts.input)).redirectUrl;
1237
+ }
1238
+ async function finishFoirProviderSignIn(opts) {
1239
+ const adapter = createFoirCookieAdapter(opts.store, { cookieOptions: opts.cookieOptions });
1240
+ return finishProviderSignIn(adapter, opts.env, {
1180
1241
  providerKey: opts.providerKey,
1181
1242
  code: opts.code,
1182
1243
  state: opts.state
1183
1244
  });
1184
- return saveSession(
1185
- createFoirCookieAdapter(opts.store, { cookieOptions: opts.cookieOptions }),
1186
- result
1187
- );
1188
1245
  }
1189
1246
 
1190
1247
  // src/live/events.ts
@@ -1304,19 +1361,20 @@ export {
1304
1361
  FOIR_NEXT_TAG_MAX_LENGTH,
1305
1362
  FOIR_SURROGATE_KEY_HEADER,
1306
1363
  FOIR_UNTAGGED_TAG,
1307
- completeFoirProviderLogin,
1308
1364
  createFoirCookieAdapter,
1309
1365
  createFoirMiddleware,
1310
1366
  createFoirTaggingFetch,
1367
+ finishFoirProviderSignIn,
1311
1368
  foirCacheTags,
1312
1369
  foirCookieName,
1313
- foirRegisterWithPassword,
1314
1370
  foirSignInWithOtp,
1315
1371
  foirSignInWithPassword,
1316
1372
  foirSignOut,
1373
+ foirSignUpWithPassword,
1317
1374
  foirTagsForRecordEvent,
1318
1375
  getFoirSession,
1319
1376
  getFoirToken,
1320
1377
  readFoirCacheTags,
1321
- revalidateFoirRecordEvent
1378
+ revalidateFoirRecordEvent,
1379
+ startFoirProviderSignIn
1322
1380
  };