@foir/sdk 0.10.0 → 0.12.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 (52) hide show
  1. package/CHANGELOG.md +353 -2
  2. package/README.md +10 -2
  3. package/dist/app/client.d.ts +371 -0
  4. package/dist/app/contract/index.d.ts +23 -0
  5. package/dist/app/contract/proto/apps/v1/apps_pb.d.ts +1465 -0
  6. package/dist/app/contract/proto/apps/v1/discovery_pb.d.ts +678 -0
  7. package/dist/app/dispatch.d.ts +64 -0
  8. package/dist/app/errors.d.ts +50 -0
  9. package/dist/app/index.d.ts +40 -0
  10. package/dist/app/manifest.d.ts +56 -0
  11. package/dist/app/naming.d.ts +31 -0
  12. package/dist/app/record-key.d.ts +17 -0
  13. package/dist/app/ticket.d.ts +37 -0
  14. package/dist/app/vocabulary.generated.d.ts +20 -0
  15. package/dist/app-contract.js +124 -0
  16. package/dist/app.js +998 -0
  17. package/dist/auth/auth.d.ts +24 -6
  18. package/dist/auth/index.d.ts +2 -1
  19. package/dist/auth/ops.d.ts +8 -0
  20. package/dist/auth.js +230 -60
  21. package/dist/client.js +80 -15
  22. package/dist/codegen/preset.d.ts +3 -2
  23. package/dist/data/builtins.d.ts +17 -1
  24. package/dist/data/client.d.ts +10 -8
  25. package/dist/data/document.d.ts +13 -0
  26. package/dist/data/index.d.ts +15 -5
  27. package/dist/data/manifest.d.ts +31 -3
  28. package/dist/data/schema.d.ts +16 -9
  29. package/dist/data/selection.d.ts +15 -0
  30. package/dist/data/transport.d.ts +277 -23
  31. package/dist/data/ws-transport.d.ts +8 -2
  32. package/dist/data.js +313 -72
  33. package/dist/graphql/client.d.ts +1 -1
  34. package/dist/graphql/foir-request.d.ts +1 -1
  35. package/dist/index.js +0 -9
  36. package/dist/live/drift.d.ts +9 -10
  37. package/dist/live.js +1 -1
  38. package/dist/next.js +158 -37
  39. package/dist/placement/index.d.ts +129 -0
  40. package/dist/placement.js +144 -0
  41. package/dist/push.js +212 -50
  42. package/dist/react.js +158 -37
  43. package/dist/server/callback-client.d.ts +30 -9
  44. package/dist/server/continue-run.d.ts +56 -0
  45. package/dist/server/index.d.ts +6 -3
  46. package/dist/server/operation-types.d.ts +114 -65
  47. package/dist/server/read-dispatch.d.ts +53 -0
  48. package/dist/server/require-scoped-token.d.ts +71 -7
  49. package/dist/server/verify-scoped-token.d.ts +109 -3
  50. package/dist/server.js +566 -138
  51. package/dist/transport/foir-http.d.ts +103 -5
  52. package/package.json +24 -6
@@ -13,6 +13,11 @@
13
13
  * anything else — `sk_`, a customer bearer, a mutation, the draft channel
14
14
  * → `POST /graphql` with the `x-api-key` header and the APQ body.
15
15
  *
16
+ * a call made AS an operation, with its dispatch token
17
+ * → `POST /graphql` with `X-Foir-Token` and the dispatch's `X-Foir-Context`,
18
+ * and no `x-api-key`. Always this lane: the shared one takes its
19
+ * credential from the URL, and a dispatch token has no key to put there.
20
+ *
16
21
  * All five conditions the server's shared-lane gate refuses on are evaluated
17
22
  * HERE, locally: this code is constructing the URL, it can read the key prefix,
18
23
  * it knows whether a bearer resolved, the manifest told it whether the op is a
@@ -55,7 +60,7 @@
55
60
  * with more force here — this client is per-credential, so anything cached
56
61
  * across instances is cached across tenants.
57
62
  */
58
- import { FoirRequestError, type FoirTokenProvider } from '../transport/foir-http';
63
+ import { FoirRequestError, type FoirTokenProvider, type FoirVisitorSource } from '../transport/foir-http';
59
64
  import type { FoirDocumentPlan, FoirNamespaceSelections, FoirSelections, FoirSelectionUnions } from './document';
60
65
  import type { FoirSchemaDefinition } from './schema';
61
66
  import type { FoirSubjectRegistry } from './subjects';
@@ -84,8 +89,18 @@ import type { FoirReconnectPolicy, FoirWebSocketFactory } from './ws-transport';
84
89
  * Its runtime value is `undefined` by design and nothing reads it.
85
90
  */
86
91
  export interface FoirGeneratedModule<S extends FoirSchemaDefinition = FoirSchemaDefinition> {
87
- /** `foirSchemaVersion` — the manifest version these types came from. Drives the drift check. */
88
- readonly foirSchemaVersion: number;
92
+ /**
93
+ * `foirSurfaceVersion`: the surface these types were generated from, a hash
94
+ * over exactly the parts of the manifest `foir types` reads. Drives the drift
95
+ * check, which compares it with the one the server is serving now.
96
+ */
97
+ readonly foirSurfaceVersion: string;
98
+ /**
99
+ * `foirSurfaceHashes`: the components that version is folded from, keyed as
100
+ * the manifest keys them (`models.<key>`, `namespaces.<name>`, …). What lets a
101
+ * drift name the models that moved rather than just say something did.
102
+ */
103
+ readonly foirSurfaceHashes: Readonly<Record<string, string>>;
89
104
  /** `foirSelections` — one entry per model, in the printer's own input shape. */
90
105
  readonly foirSelections: FoirSelections;
91
106
  /** `foirNamespaceSelections` — the same, per namespace op. */
@@ -107,8 +122,70 @@ export interface FoirGeneratedModule<S extends FoirSchemaDefinition = FoirSchema
107
122
  readonly foirSubscriptions?: readonly FoirManifestSubscription[];
108
123
  /** Type carrier for `FoirSchema`. Never read: its runtime value is `undefined`. */
109
124
  readonly foirSchemaType: S;
125
+ /**
126
+ * `foirCredential`: present when the module was generated for an operation
127
+ * (`foir types --operation <key>`), naming it. Its types are the surface that
128
+ * operation's dispatch token is served, so the module is for a client built
129
+ * from that token and for no other: a dispatch client requires it, and a key
130
+ * client refuses it. Absent from a module generated for a key.
131
+ */
132
+ readonly foirCredential?: FoirOperationCredential;
133
+ }
134
+ /**
135
+ * What a module `foir types --operation <key>` generated says about itself:
136
+ * the operation whose dispatch token its types describe.
137
+ */
138
+ export interface FoirOperationCredential {
139
+ readonly operation: string;
110
140
  }
111
- export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDefinition> {
141
+ /** A module generated for an api key: the kind a key client takes. */
142
+ export type FoirKeyGeneratedModule<S extends FoirSchemaDefinition = FoirSchemaDefinition> = FoirGeneratedModule<S> & {
143
+ readonly foirCredential?: undefined;
144
+ };
145
+ /** A module generated for an operation: the kind a dispatch client takes. */
146
+ export type FoirOperationGeneratedModule<S extends FoirSchemaDefinition = FoirSchemaDefinition> = FoirGeneratedModule<S> & {
147
+ readonly foirCredential: FoirOperationCredential;
148
+ };
149
+ /**
150
+ * The credential of a client built inside an operation endpoint: the dispatch
151
+ * token the platform handed it.
152
+ *
153
+ * ```ts
154
+ * const { dispatch } = await requireScopedToken(request, { expectedProjectId });
155
+ * const foir = createFoirClient({ apiUrl, dispatch, generated });
156
+ * ```
157
+ *
158
+ * The endpoint calls Foir AS the operation, with the capabilities the operation
159
+ * declared, and holds no key of its own. api-public accepts the token as a
160
+ * caller in its own right: it builds the schema overlay from the token's
161
+ * capabilities, and it reads the organization and the customer from the
162
+ * token's claims. So nothing a key client configures beside its key
163
+ * (`bearerToken`, `orgId`, `channel`) means anything here, and none of it is
164
+ * accepted.
165
+ *
166
+ * `requireScopedToken` returns one of these, built from the token it verified.
167
+ * Build one by hand only when the token reaches you some other way.
168
+ */
169
+ export interface FoirDispatchCredential {
170
+ /**
171
+ * The dispatch token, sent as `X-Foir-Token`. Resolved before every request,
172
+ * so a caller holding a refreshed token hands the client the current one
173
+ * rather than the one it was built with. A provider that yields nothing fails
174
+ * the request: there is no other credential to fall back to.
175
+ */
176
+ token: FoirTokenProvider;
177
+ /**
178
+ * The dispatch's `X-Foir-Context` header, forwarded as it arrived so the
179
+ * requests the operation makes carry the run's correlation metadata. Pass
180
+ * `request.headers.get('X-Foir-Context')` as it is; absent is fine.
181
+ */
182
+ context?: string | null;
183
+ }
184
+ /**
185
+ * A client that calls Foir with an api key: a storefront's `pk_`, a server's
186
+ * `sk_`, optionally for a signed-in customer.
187
+ */
188
+ export interface FoirKeyClientConfig<S extends FoirSchemaDefinition = FoirSchemaDefinition> {
112
189
  /**
113
190
  * Foir public API origin, e.g. `https://api.foir.dev`.
114
191
  *
@@ -119,10 +196,13 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
119
196
  apiUrl: string;
120
197
  /** `pk_…` or `sk_…`. The manifest, and therefore the generated types, are shaped by it. */
121
198
  apiKey: string;
199
+ /** A key client has no dispatch token; see {@link FoirDispatchClientConfig}. */
200
+ dispatch?: never;
122
201
  /**
123
- * Read the draft schema channel (`X-Foir-Schema-Channel: draft`). Needs a
124
- * `sk_` key with `drafts:read`; the header is ignored under public keys.
125
- * Defaults to `'published'`.
202
+ * Read the draft schema channel (`X-Foir-Schema-Channel: draft`). Needs a key
203
+ * holding `drafts:read`: a preview environment's own `pk_`, or an `sk_`. A
204
+ * public key without it is served the published copy; a secret key without
205
+ * it is refused. Defaults to `'published'`.
126
206
  */
127
207
  channel?: 'published' | 'draft';
128
208
  /**
@@ -131,6 +211,18 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
131
211
  * per-request; a bare string is used as-is.
132
212
  */
133
213
  bearerToken?: string | FoirTokenProvider;
214
+ /**
215
+ * Renew the customer bearer when an answer comes back served without it
216
+ * because it expired. Resolve the new token, or null when the session is
217
+ * over. It is called at most once per request, and one call is shared by the
218
+ * client's requests that find the bearer lapsed at the same time.
219
+ *
220
+ * With it, such a request is asked again with the new token, and fails with
221
+ * `FoirSessionExpiredError` when there is none. Without it, the request fails
222
+ * with `FoirAuthDowngradedError`. Either way it is never handed the signed-out
223
+ * answer as though it were the signed-in one.
224
+ */
225
+ refreshBearer?: () => Promise<string | null>;
134
226
  /**
135
227
  * The organization every request from this client acts in, sent as
136
228
  * `X-Org-Id`.
@@ -166,6 +258,51 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
166
258
  * appear.
167
259
  */
168
260
  orgId?: string;
261
+ /**
262
+ * The address of the visitor a server rendering pages is calling for, or a
263
+ * function resolving it for each request, sent as `X-Foir-Visitor-IP`. The
264
+ * public API's per-visitor limit then applies to that visitor rather than to
265
+ * your server, which otherwise counts as one visitor for every page it
266
+ * renders.
267
+ *
268
+ * Give it the address your host guarantees: the one its edge sets and a
269
+ * visitor cannot. A header a visitor can write lets them name a fresh address
270
+ * per request, which is exactly what the limit exists to stop. The docs list
271
+ * the value for each common host.
272
+ *
273
+ * A secret key only, and one holding `visitors:forward`: the API refuses the
274
+ * header, naming the permission, from any other. A client built from a public
275
+ * key is refused `visitor` here, because a browser already calls as its own
276
+ * visitor and a public key cannot hold the permission.
277
+ */
278
+ visitor?: FoirVisitorSource;
279
+ /**
280
+ * The organization every request from this client acts in, named by the
281
+ * address of the page it is for, sent as `X-Org-Url`.
282
+ *
283
+ * For an app that serves many organizations from its own addresses: a path
284
+ * per organization (`saasapp.com/soho`), a subdomain each, or a domain an
285
+ * organization brought. Pass the page URL and the server finds the
286
+ * organization whose registered address (its app base URL, or a verified
287
+ * custom domain bound to it) is the longest prefix of that URL at a
288
+ * path-segment boundary, so `saasapp.com/soho` answers for `/soho/menu` and
289
+ * never for `/sohonews`. It works with the publishable key the page already
290
+ * ships.
291
+ *
292
+ * ```ts
293
+ * const foir = createFoirClient({ apiUrl, apiKey, generated, orgUrl: window.location.href });
294
+ * ```
295
+ *
296
+ * It is `orgId` by another spelling and bounded the same way: a URL no
297
+ * organization's address covers answers `NOT_FOUND` exactly as an unknown id
298
+ * does, a credential confined to an organization is answered about that
299
+ * organization alone, and the organization is a selection rather than
300
+ * authority. Sent with `orgId`, the two must name the same organization.
301
+ * `resolveOrganizationAddress(url)` answers the same id up front.
302
+ *
303
+ * Routing: always the origin endpoint, for the reason `orgId` is.
304
+ */
305
+ orgUrl?: string;
169
306
  /**
170
307
  * The generated module, whole — **the documented way to wire a client**.
171
308
  *
@@ -174,8 +311,8 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
174
311
  * const foir = createFoirClient({ apiUrl, apiKey, generated });
175
312
  * ```
176
313
  *
177
- * It supplies `schemaVersion`, `selections`, `namespaceSelections` and
178
- * `selectionUnions` in one member, and carries `FoirSchema` with it so the
314
+ * It supplies `surfaceVersion`, `surfaceHashes`, `selections`,
315
+ * `namespaceSelections` and `selectionUnions` in one member, and carries `FoirSchema` with it so the
179
316
  * type parameter is inferred rather than restated. An artifact added to
180
317
  * `foir types` later then costs a call site nothing.
181
318
  *
@@ -188,8 +325,12 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
188
325
  * Omitting `generated` AND `selections` asks for the manifest fallback, which
189
326
  * is supported, announced (see `announceManifestFallback`) and never reached
190
327
  * by accident.
328
+ *
329
+ * A module generated for an operation (`foir types --operation`) is refused
330
+ * here: its types are what that operation's dispatch token is served, not
331
+ * what this key is.
191
332
  */
192
- generated?: FoirGeneratedModule<S>;
333
+ generated?: FoirKeyGeneratedModule<S>;
193
334
  /**
194
335
  * The runtime half of `foir.generated.ts` — `foirSelections`, the resolved
195
336
  * complete-by-default selection per model.
@@ -236,7 +377,11 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
236
377
  * `subjects.add(...)`.
237
378
  */
238
379
  subjects?: FoirSubjectRegistry;
239
- /** Extra headers, applied last so they can override the ones above. */
380
+ /**
381
+ * Extra headers, applied last so they can override the ones above. A client
382
+ * built from a dispatch token refuses a credential header here (`x-api-key`,
383
+ * `Authorization`, `X-Foir-Token`, `X-Foir-Context`).
384
+ */
240
385
  headers?: Readonly<Record<string, string>>;
241
386
  /**
242
387
  * `fetch` implementation. Defaults to `globalThis.fetch`. Injectable for
@@ -250,11 +395,17 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
250
395
  */
251
396
  requestInit?: Omit<RequestInit, 'method' | 'headers' | 'body'>;
252
397
  /**
253
- * `foirSchemaVersion` from the generated module. When set, the client
254
- * compares it against the server's manifest once and warns on mismatch.
255
- * Carried by `generated`; set it here only to override that.
398
+ * `foirSurfaceVersion` from the generated module. When set, the client
399
+ * compares it against the surface the server is serving, once, and reports
400
+ * drift when they differ. Carried by `generated`; set it here only to
401
+ * override that.
402
+ */
403
+ surfaceVersion?: string;
404
+ /**
405
+ * `foirSurfaceHashes` from the generated module, so a drift can name what
406
+ * moved. Carried by `generated`; set it here only to override that.
256
407
  */
257
- schemaVersion?: number;
408
+ surfaceHashes?: Readonly<Record<string, string>>;
258
409
  /**
259
410
  * Subscription descriptors, from the generated module's `foirSubscriptions`.
260
411
  *
@@ -263,7 +414,7 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
263
414
  * when absent it fetches once, as it always has.
264
415
  */
265
416
  subscriptions?: readonly FoirManifestSubscription[];
266
- /** Set `false` to suppress the drift check even when `schemaVersion` is set. */
417
+ /** Set `false` to suppress the drift check even when `surfaceVersion` is set. */
267
418
  checkSchemaDrift?: boolean;
268
419
  /** Called instead of `console.warn` when drift is detected. */
269
420
  onSchemaDrift?: (drift: FoirSchemaDrift) => void;
@@ -295,7 +446,12 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
295
446
  * class of bug by construction, and refusing it would punish the escape
296
447
  * hatch people reach for precisely because it is immune.
297
448
  *
298
- * Opt out at the call site, where a reader can see it:
449
+ * Turning it off is never the remedy for a drift. The version it compares
450
+ * is the SURFACE the types were generated from, which moves only when
451
+ * regenerating would write a different file, so every drift it reports is a
452
+ * real one and the fix is `foir types`. The opt-out exists for an app that
453
+ * knowingly ships a stale module for a while, and it says so at the call
454
+ * site, where a reader can see it:
299
455
  *
300
456
  * ```ts
301
457
  * createFoirClient({ strictSchemaDrift: false }) // serve stale, warn only
@@ -352,12 +508,76 @@ export interface FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDef
352
508
  */
353
509
  connectionParams?: () => Record<string, string> | Promise<Record<string, string>>;
354
510
  }
511
+ /**
512
+ * The options that belong to a key: the key itself, what it is presented with
513
+ * (a customer bearer, an organization, a channel), and the subscription socket,
514
+ * which only a key can open.
515
+ */
516
+ type FoirKeySideOption = 'apiKey' | 'dispatch' | 'channel' | 'bearerToken' | 'refreshBearer' | 'orgId' | 'orgUrl' | 'visitor' | 'wsUrl' | 'webSocketImpl' | 'reconnect' | 'connectionParams';
517
+ /**
518
+ * A client that calls Foir AS an operation, from inside the endpoint the
519
+ * operation dispatched to. See {@link FoirDispatchCredential}.
520
+ *
521
+ * Everything a key client takes except the key-side options, which are typed
522
+ * `never` rather than left out, so passing one is a compile error that names
523
+ * it. No `apiKey` above all: the dispatch token is the credential, and an
524
+ * endpoint that holds a key as well has become the server in the middle the
525
+ * operation exists to replace.
526
+ */
527
+ export interface FoirDispatchClientConfig<S extends FoirSchemaDefinition = FoirSchemaDefinition> extends Omit<FoirKeyClientConfig<S>, FoirKeySideOption | 'generated'> {
528
+ /** The dispatch token this endpoint was handed, and the dispatch's context header. */
529
+ dispatch: FoirDispatchCredential;
530
+ /**
531
+ * The module `foir types --operation <key>` generated for this operation.
532
+ * Required: its types are the surface this operation's token is served, and
533
+ * a module generated for a key would type calls the token is refused. A
534
+ * dispatch whose context names a different operation is refused too.
535
+ *
536
+ * ```ts
537
+ * // foir types --operation checkout --out src/checkout.generated.ts
538
+ * import * as generated from './checkout.generated';
539
+ * const foir = createFoirClient({ apiUrl, dispatch, generated });
540
+ * ```
541
+ */
542
+ generated: FoirOperationGeneratedModule<S>;
543
+ apiKey?: never;
544
+ channel?: never;
545
+ bearerToken?: never;
546
+ refreshBearer?: never;
547
+ orgId?: never;
548
+ orgUrl?: never;
549
+ visitor?: never;
550
+ wsUrl?: never;
551
+ webSocketImpl?: never;
552
+ reconnect?: never;
553
+ connectionParams?: never;
554
+ }
555
+ /**
556
+ * How a data client is configured: with an api key, or with an operation's
557
+ * dispatch token. Exactly one of the two. They are different actors, and there
558
+ * is no configuration that accepts either.
559
+ */
560
+ export type FoirClientConfig<S extends FoirSchemaDefinition = FoirSchemaDefinition> = FoirKeyClientConfig<S> | FoirDispatchClientConfig<S>;
561
+ /**
562
+ * The credential and transport half of a client config, which is everything a
563
+ * request reads. A manifest fetch takes this rather than a whole client config:
564
+ * what it asks for does not depend on a generated module, and a dispatch token
565
+ * asks for its own surface before any module exists.
566
+ */
567
+ export type FoirRequestConfig = Omit<FoirKeyClientConfig, 'generated'> | Omit<FoirDispatchClientConfig, 'generated'>;
355
568
  export interface FoirSchemaDrift {
356
- /** Manifest version the local types were generated from. */
357
- generated: number;
358
- /** Manifest version the server is serving now. */
359
- server: number;
569
+ /** The surface version the local types were generated from. */
570
+ generated: string;
571
+ /** The surface version the server is serving now. */
572
+ server: string;
360
573
  channel: string;
574
+ /**
575
+ * What moved, as the manifest keys its components: `models.<key>`,
576
+ * `namespaces.<name>`, `selectionUnions.<name>`, `subscriptions`,
577
+ * `composedFor`. A component added or removed since generation is listed
578
+ * too. Empty only when the module was wired without `surfaceHashes`.
579
+ */
580
+ changed: readonly string[];
361
581
  }
362
582
  /**
363
583
  * Thrown by a typed call under `strictSchemaDrift` once drift is detected.
@@ -380,8 +600,39 @@ export declare class FoirSchemaDriftError extends FoirRequestError {
380
600
  * than a re-export so `@foir/sdk/data`'s public surface is unchanged.
381
601
  */
382
602
  export declare function foirDataEndpoint(apiUrl: string, path: `/${string}`): string;
383
- /** Resolve `config.bearerToken`, whether it is a string, a function, or absent. */
384
- export declare function resolveBearerToken(config: Pick<FoirClientConfig, 'bearerToken'>): Promise<string | undefined>;
603
+ /** One request's credential, resolved now. */
604
+ export interface FoirResolvedAuth {
605
+ /** The headers the request carries, `extra` included. */
606
+ headers: Record<string, string>;
607
+ /**
608
+ * The key and the resolved bearer, when the credential is a key. Absent for a
609
+ * dispatch token, which has no key to put in a URL and so never takes the
610
+ * shared lane.
611
+ */
612
+ key?: {
613
+ apiKey: string;
614
+ bearerToken?: string;
615
+ };
616
+ }
617
+ /**
618
+ * The credential a request carries, resolved at the moment it is sent.
619
+ *
620
+ * The one place every transport (`/graphql`, `GET /v1/manifest`, the socket's
621
+ * `connection_init`) asks, so the three cannot drift into authenticating a
622
+ * client differently. Providers are called here, once per request, and not at
623
+ * construction.
624
+ */
625
+ export declare function resolveFoirAuth(config: FoirRequestConfig): Promise<FoirResolvedAuth>;
626
+ /**
627
+ * Refuse a config that names no credential, or two.
628
+ *
629
+ * The types already say this. It is checked again at runtime for a caller in
630
+ * plain JavaScript, or one who spread two configs together, because the
631
+ * failure it prevents is quiet: a client handed both a key and a dispatch token
632
+ * would send one and discard the other, and which actor made each request
633
+ * would depend on a precedence rule nobody chose.
634
+ */
635
+ export declare function assertFoirCredential(config: FoirClientConfig): void;
385
636
  /** Which GraphQL endpoint a call is sent to. */
386
637
  export type FoirLane = 'shared' | 'origin';
387
638
  /**
@@ -400,6 +651,8 @@ export interface FoirRouteInput {
400
651
  bearerToken?: string;
401
652
  /** The organization the call acts in (`X-Org-Id`), when it names one. */
402
653
  orgId?: string;
654
+ /** The page URL the call names its organization by (`X-Org-Url`), when it does. */
655
+ orgUrl?: string;
403
656
  mutation: boolean;
404
657
  channel?: 'published' | 'draft';
405
658
  /**
@@ -440,3 +693,4 @@ export declare function executeFoirDocument<T>(config: FoirClientConfig, plan: F
440
693
  export declare function executeFoirRawDocument<T>(config: FoirClientConfig, document: string, mutation: boolean, variables?: Record<string, unknown>, options?: {
441
694
  strict?: boolean;
442
695
  }): Promise<T>;
696
+ export {};
@@ -41,9 +41,15 @@
41
41
  * `config.connectionParams`, which is a FUNCTION and is called on every
42
42
  * connect — so a short-lived credential is re-resolved on reconnect instead of
43
43
  * being replayed after it expired.
44
+ *
45
+ * An operation's dispatch token is not one of them. The server's
46
+ * `connection_init` authenticates a key, an admin token or a customer bearer
47
+ * and has no path for a dispatch token, so a client built from one is refused
48
+ * here, by name, rather than opening a socket the server will close as "Not
49
+ * authenticated".
44
50
  */
45
51
  import { FoirRequestError } from '../transport/foir-http';
46
- import { type FoirClientConfig } from './transport';
52
+ import { type FoirRequestConfig } from './transport';
47
53
  /**
48
54
  * The slice of `WebSocket` this transport uses.
49
55
  *
@@ -143,7 +149,7 @@ export declare class FoirSocketTransport {
143
149
  private disposed;
144
150
  private keepAlive;
145
151
  private retryTimer;
146
- constructor(config: FoirClientConfig);
152
+ constructor(config: FoirRequestConfig);
147
153
  /**
148
154
  * Start a subscription.
149
155
  *