@foir/sdk 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -1
- package/README.md +9 -9
- package/dist/app/client.d.ts +90 -84
- package/dist/app/contract/proto/apps/v1/apps_pb.d.ts +14 -13
- package/dist/app/dispatch.d.ts +5 -5
- package/dist/app/errors.d.ts +11 -11
- package/dist/app/index.d.ts +4 -4
- package/dist/app/naming.d.ts +1 -1
- package/dist/app/ticket.d.ts +2 -2
- package/dist/app/vocabulary.generated.d.ts +2 -2
- package/dist/app-contract.js +3 -3
- package/dist/app.js +92 -90
- package/dist/auth/access.d.ts +19 -19
- package/dist/auth/auth.d.ts +216 -114
- package/dist/auth/hosted.d.ts +14 -14
- package/dist/auth/index.d.ts +7 -7
- package/dist/auth/operations.d.ts +128 -32
- package/dist/auth/ops.d.ts +39 -21
- package/dist/auth/session-client.d.ts +3 -3
- package/dist/auth-session.js +3 -3
- package/dist/auth.js +291 -146
- package/dist/client/platform-client.d.ts +1 -1
- package/dist/client.js +32 -5
- package/dist/codegen/index.js +1 -1
- package/dist/data/builtins.d.ts +41 -82
- package/dist/data/client.d.ts +2 -2
- package/dist/data/decimal.d.ts +1 -1
- package/dist/data/document.d.ts +14 -0
- package/dist/data/index.d.ts +2 -2
- package/dist/data/manifest.d.ts +1 -1
- package/dist/data/schema.d.ts +1 -1
- package/dist/data/transport.d.ts +33 -26
- package/dist/data/ws-transport.d.ts +1 -1
- package/dist/data.js +97 -19
- package/dist/graphql/content-client.d.ts +13 -13
- package/dist/index.js +24 -0
- package/dist/live/drift.d.ts +1 -1
- package/dist/media/screen-capture.d.ts +1 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/session.d.ts +10 -10
- package/dist/next.js +121 -61
- package/dist/placement/index.d.ts +15 -32
- package/dist/placement.js +18 -20
- package/dist/push.js +113 -46
- package/dist/react/FoirFeedback.d.ts +2 -2
- package/dist/react/FoirProvider.d.ts +23 -23
- package/dist/react/SignIn.d.ts +4 -4
- package/dist/react/UserButton.d.ts +1 -1
- package/dist/react/feedback-notes.d.ts +4 -4
- package/dist/react/index.d.ts +3 -3
- package/dist/react.js +168 -105
- package/dist/realtime/index.d.ts +1 -1
- package/dist/server/callback-client.d.ts +4 -4
- package/dist/server/events-client.d.ts +18 -18
- package/dist/server/operation-types.d.ts +8 -8
- package/dist/server/require-scoped-token.d.ts +3 -3
- package/dist/server/server-client.d.ts +1 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/server/verify-scoped-token.d.ts +4 -28
- package/dist/server.js +67 -112
- package/dist/transport/foir-http.d.ts +57 -18
- package/dist/transport/key-kind.d.ts +40 -0
- package/dist/types/index.d.ts +2 -2
- package/package.json +3 -3
- package/dist/server/older-claims.generated.d.ts +0 -8
|
@@ -17,7 +17,7 @@ export declare class PlatformClientError extends Error {
|
|
|
17
17
|
* ```ts
|
|
18
18
|
* const client = createPlatformClient({
|
|
19
19
|
* apiUrl: 'https://api.example.com/graphql',
|
|
20
|
-
* apiKey: '
|
|
20
|
+
* apiKey: 'foir_pub_...', // runs in the browser: a publishable key only
|
|
21
21
|
* });
|
|
22
22
|
*
|
|
23
23
|
* const { data } = await client.query(
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
// src/graphql/foir-request.ts
|
|
2
2
|
import { print as print2 } from "graphql";
|
|
3
3
|
|
|
4
|
+
// src/transport/key-kind.ts
|
|
5
|
+
var KEY_PREFIXES = [
|
|
6
|
+
["foir_pub_", "publishable"],
|
|
7
|
+
["foir_srv_", "server"],
|
|
8
|
+
["foir_adm_", "admin"],
|
|
9
|
+
["pk_", "publishable"],
|
|
10
|
+
["sk_", "older"],
|
|
11
|
+
["tc_", "older"]
|
|
12
|
+
];
|
|
13
|
+
var FOIR_CREDENTIALS_DOC = "https://docs.foir.dev/credentials";
|
|
14
|
+
function foirKeyKind(apiKey) {
|
|
15
|
+
if (typeof apiKey !== "string") return void 0;
|
|
16
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
17
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
function adminKeyRefusal(client) {
|
|
22
|
+
return `[foir] ${client} was given an admin key. An admin key is a machine's own: nobody signs in through it, and an app never holds one. Build this client from your publishable key, or from your server key on the server that renders your pages. A machine that acts for itself uses createServerClient from @foir/sdk/server. See ${FOIR_CREDENTIALS_DOC}`;
|
|
23
|
+
}
|
|
24
|
+
function isPublishableFoirKey(apiKey) {
|
|
25
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
26
|
+
}
|
|
27
|
+
|
|
4
28
|
// src/graphql/cdn-route.ts
|
|
5
29
|
import { print } from "graphql";
|
|
6
30
|
|
|
@@ -176,8 +200,8 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
176
200
|
downgrade;
|
|
177
201
|
constructor(downgrade) {
|
|
178
202
|
super(
|
|
179
|
-
`[foir] your
|
|
180
|
-
{ graphQLErrors: [{ message: "
|
|
203
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
|
|
204
|
+
{ graphQLErrors: [{ message: "user token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
|
|
181
205
|
);
|
|
182
206
|
this.name = "FoirAuthDowngradedError";
|
|
183
207
|
this.downgrade = downgrade;
|
|
@@ -185,7 +209,7 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
185
209
|
};
|
|
186
210
|
var FoirSessionExpiredError = class extends FoirRequestError {
|
|
187
211
|
constructor(options) {
|
|
188
|
-
super("[foir] the
|
|
212
|
+
super("[foir] the user session has expired and could not be refreshed: sign in again", {
|
|
189
213
|
cause: options?.cause,
|
|
190
214
|
graphQLErrors: [{ message: "session expired", extensions: { code: "SESSION_EXPIRED" } }]
|
|
191
215
|
});
|
|
@@ -202,7 +226,7 @@ function reportAuthDowngrade(envelope) {
|
|
|
202
226
|
if (warnedDowngrade === downgrade.reason) return;
|
|
203
227
|
warnedDowngrade = downgrade.reason;
|
|
204
228
|
console.warn(
|
|
205
|
-
`[foir] your
|
|
229
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}) \u2014 this request was served as ${downgrade.servedAs}, so auth-targeted content resolved to its anonymous variant. ` + (downgrade.reason === "expired" ? "Re-exchange the token and retry." : "Check which credential you are sending.")
|
|
206
230
|
);
|
|
207
231
|
}
|
|
208
232
|
function refusePrunedDocument(json, options) {
|
|
@@ -282,7 +306,7 @@ function isCdnEligible(env, documentText, bearerToken, orgId) {
|
|
|
282
306
|
if (orgId) return false;
|
|
283
307
|
if (isFoirPreview(env)) return false;
|
|
284
308
|
const apiKey = resolveFoirApiKey(env);
|
|
285
|
-
if (!apiKey
|
|
309
|
+
if (!isPublishableFoirKey(apiKey)) return false;
|
|
286
310
|
return !/\bmutation\b/.test(documentText);
|
|
287
311
|
}
|
|
288
312
|
|
|
@@ -329,6 +353,9 @@ async function foirRequest(env, document, variables, options) {
|
|
|
329
353
|
"[foir] PLATFORM_API_URL or PLATFORM_API_KEY is not set \u2014 add them to .env"
|
|
330
354
|
);
|
|
331
355
|
}
|
|
356
|
+
if (foirKeyKind(apiKey) === "admin") {
|
|
357
|
+
throw new FoirRequestError(adminKeyRefusal("foirRequest"));
|
|
358
|
+
}
|
|
332
359
|
const headers = buildFoirHeaders({
|
|
333
360
|
apiKey,
|
|
334
361
|
draftChannel: isFoirPreview(env),
|
package/dist/codegen/index.js
CHANGED
|
@@ -15,7 +15,7 @@ function schemaEntry(apiUrl, key) {
|
|
|
15
15
|
headers: {
|
|
16
16
|
"x-api-key": key ?? "",
|
|
17
17
|
// Read the draft channel so freshly pushed changes are visible
|
|
18
|
-
// (
|
|
18
|
+
// (ignored by an app's key that does not hold drafts:read).
|
|
19
19
|
"X-Foir-Schema-Channel": "draft"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/dist/data/builtins.d.ts
CHANGED
|
@@ -398,7 +398,7 @@ export type FoirUnexpandedRecord = FoirDegraded;
|
|
|
398
398
|
export type FoirVisibility = 'PRIVATE' | 'PUBLIC';
|
|
399
399
|
export type FoirAccessLevel = 'READ' | 'WRITE' | 'DELETE';
|
|
400
400
|
export type FoirAccessSource = 'PUBLIC' | 'OWNER' | 'GRANT' | 'ROLE';
|
|
401
|
-
export type FoirGranteeKind = '
|
|
401
|
+
export type FoirGranteeKind = 'USER';
|
|
402
402
|
/** `myAccess` — this principal's effective access to one record. */
|
|
403
403
|
export interface FoirMyAccess {
|
|
404
404
|
access: FoirAccessLevel;
|
|
@@ -435,9 +435,9 @@ export interface FoirUpdateManyPayload {
|
|
|
435
435
|
}
|
|
436
436
|
/**
|
|
437
437
|
* `setOwner`: who the record belongs to after an operation handed it to
|
|
438
|
-
* another
|
|
438
|
+
* another user. It carries the owner and not the record, because the
|
|
439
439
|
* operation may have been acting for the previous holder, who can no longer
|
|
440
|
-
* read it. `ownerKind` is always `'
|
|
440
|
+
* read it. `ownerKind` is always `'user'` from this op.
|
|
441
441
|
*/
|
|
442
442
|
export interface FoirRecordOwner {
|
|
443
443
|
id: string;
|
|
@@ -445,7 +445,7 @@ export interface FoirRecordOwner {
|
|
|
445
445
|
ownerKind: string;
|
|
446
446
|
}
|
|
447
447
|
/**
|
|
448
|
-
* What a
|
|
448
|
+
* What a user's note can be ABOUT: the user-plane subset of the closed
|
|
449
449
|
* set the platform's write path enforces.
|
|
450
450
|
*
|
|
451
451
|
* On the wire it is `String!`, not a GraphQL enum, so the manifest publishes no
|
|
@@ -458,23 +458,23 @@ export interface FoirRecordOwner {
|
|
|
458
458
|
* record is `'record'` plus the model key on `subjectModelKey`.
|
|
459
459
|
*
|
|
460
460
|
* The platform set also holds `project` and `organization`, and this union
|
|
461
|
-
* deliberately does not. Both are
|
|
462
|
-
* note about either through a row policy no
|
|
461
|
+
* deliberately does not. Both are the project's own constructs: the platform answers a
|
|
462
|
+
* note about either through a row policy no user claim passes, so no note
|
|
463
463
|
* this surface reads or writes can carry them. This mirrors the part of the
|
|
464
|
-
* platform set a
|
|
465
|
-
* (see the file header) applies: a kind the platform opens to
|
|
464
|
+
* platform set a user can reach, and the obligation that comes with a mirror
|
|
465
|
+
* (see the file header) applies: a kind the platform opens to users widens
|
|
466
466
|
* this union in a minor release, and until then a value outside it is a row no
|
|
467
|
-
*
|
|
467
|
+
* user writer could have produced.
|
|
468
468
|
*/
|
|
469
|
-
export type FoirNoteSubjectType = 'record' | 'file' | 'model' | 'operation' | 'schedule' | '
|
|
469
|
+
export type FoirNoteSubjectType = 'record' | 'file' | 'model' | 'operation' | 'schedule' | 'user';
|
|
470
470
|
/**
|
|
471
|
-
* One file the
|
|
471
|
+
* One file the user submitted with their note.
|
|
472
472
|
*
|
|
473
473
|
* The file's own facts, and nothing about the platform's handling of it: when
|
|
474
474
|
* the link row was written and which retention clock it starts are not the
|
|
475
475
|
* author's business. What is here is enough to render "you sent this".
|
|
476
476
|
*/
|
|
477
|
-
export interface
|
|
477
|
+
export interface FoirUserNoteAttachment {
|
|
478
478
|
/** The file's id — the same one `createMyNote` was given. */
|
|
479
479
|
fileId: string;
|
|
480
480
|
filename: string;
|
|
@@ -484,15 +484,15 @@ export interface FoirCustomerNoteAttachment {
|
|
|
484
484
|
url: string;
|
|
485
485
|
}
|
|
486
486
|
/**
|
|
487
|
-
* One note on the
|
|
487
|
+
* One note on the user plane — `foir.ns.notes.myNotes` and
|
|
488
488
|
* `foir.ns.notes.createMyNote`.
|
|
489
489
|
*
|
|
490
490
|
* Deliberately narrower than the note your team sees in the dashboard. There is
|
|
491
491
|
* no visibility, no resolution note, no mentions and no metadata: those are the
|
|
492
|
-
* project's view of a thread, not the author's, and the
|
|
492
|
+
* project's view of a thread, not the author's, and the user-facing type
|
|
493
493
|
* does not merely omit them from the document — the surface does not carry them.
|
|
494
494
|
*/
|
|
495
|
-
export interface
|
|
495
|
+
export interface FoirUserNote {
|
|
496
496
|
id: string;
|
|
497
497
|
/** The note's prose. */
|
|
498
498
|
body: string | null;
|
|
@@ -507,12 +507,12 @@ export interface FoirCustomerNote {
|
|
|
507
507
|
/**
|
|
508
508
|
* The files submitted with this note. Empty when there are none.
|
|
509
509
|
*
|
|
510
|
-
* The one place the
|
|
510
|
+
* The one place the user surface is WIDER than "the author's own prose",
|
|
511
511
|
* and deliberately: an author who attached a screenshot has to be able to see
|
|
512
512
|
* that it arrived, or the only readings left to them are that it failed
|
|
513
513
|
* silently or that it was taken away.
|
|
514
514
|
*/
|
|
515
|
-
attachments:
|
|
515
|
+
attachments: FoirUserNoteAttachment[];
|
|
516
516
|
/** ISO-8601. */
|
|
517
517
|
createdAt: string;
|
|
518
518
|
}
|
|
@@ -521,7 +521,7 @@ export interface FoirCustomerNote {
|
|
|
521
521
|
* — `foir.ns.notes.noteConfig`.
|
|
522
522
|
*
|
|
523
523
|
* Charged `notes:create` rather than `notes:read`, and refused to a caller with
|
|
524
|
-
* no
|
|
524
|
+
* no user session, because the only caller it is useful to is one that can
|
|
525
525
|
* submit. So a composer can read it as its single readiness probe: an answer
|
|
526
526
|
* means this visitor may write, and a refusal names which half is missing.
|
|
527
527
|
*/
|
|
@@ -546,16 +546,16 @@ export interface FoirMyNotesArgs {
|
|
|
546
546
|
threadRootId?: string;
|
|
547
547
|
}
|
|
548
548
|
/**
|
|
549
|
-
* `myNotes` — the notes this
|
|
550
|
-
* deliberately sent them. Never another
|
|
549
|
+
* `myNotes` — the notes this user wrote, plus the replies your team
|
|
550
|
+
* deliberately sent them. Never another user's, and never your team's
|
|
551
551
|
* internal ones.
|
|
552
552
|
*/
|
|
553
|
-
export type FoirMyNotesResult = FoirConnection<
|
|
553
|
+
export type FoirMyNotesResult = FoirConnection<FoirUserNote>;
|
|
554
554
|
/**
|
|
555
|
-
* `writeNote` arguments: the one note create, for a signed-in
|
|
555
|
+
* `writeNote` arguments: the one note create, for a signed-in user and for
|
|
556
556
|
* a visitor alike. It asks `notes:create` of both.
|
|
557
557
|
*
|
|
558
|
-
* A signed-in
|
|
558
|
+
* A signed-in user's note is theirs and comes back in `myNotes`. A
|
|
559
559
|
* visitor's, through a key that gives `notes:create` to whoever comes through
|
|
560
560
|
* it, on a project that takes visitors' notes, is nobody's: ask for its `id`
|
|
561
561
|
* and `createdAt` and nothing else, because nothing reads it back.
|
|
@@ -584,8 +584,8 @@ export interface FoirWriteNoteArgs {
|
|
|
584
584
|
*/
|
|
585
585
|
attachmentFileIds?: string[];
|
|
586
586
|
/**
|
|
587
|
-
* Where
|
|
588
|
-
*
|
|
587
|
+
* Where your team may reply to a visitor. Refused from a signed-in
|
|
588
|
+
* user, whose note has an author.
|
|
589
589
|
*
|
|
590
590
|
* Captured as typed and NEVER verified: it is not treated as proof of who
|
|
591
591
|
* wrote the note, and nothing is sent to it to confirm it. Do not build a
|
|
@@ -597,7 +597,7 @@ export interface FoirWriteNoteArgs {
|
|
|
597
597
|
* `createMyNote` arguments.
|
|
598
598
|
*
|
|
599
599
|
* @deprecated `createMyNote` is the older name of `writeNote` for a signed-in
|
|
600
|
-
*
|
|
600
|
+
* user. Use `writeNote` and {@link FoirWriteNoteArgs}.
|
|
601
601
|
*/
|
|
602
602
|
export interface FoirCreateMyNoteArgs {
|
|
603
603
|
subjectType: FoirNoteSubjectType;
|
|
@@ -618,62 +618,6 @@ export interface FoirCreateMyNoteArgs {
|
|
|
618
618
|
*/
|
|
619
619
|
attachmentFileIds?: string[];
|
|
620
620
|
}
|
|
621
|
-
/**
|
|
622
|
-
* `submitAnonymousNote` arguments — feedback from a visitor with no account.
|
|
623
|
-
*
|
|
624
|
-
* Present on the project's schema only while the project has opted in
|
|
625
|
-
* (Notes settings → "Accept anonymous notes"). The feedback widget does NOT
|
|
626
|
-
* use this: a signed-out visitor there is still asked to sign in, so this
|
|
627
|
-
* mutation is the only path a visitor's note can take.
|
|
628
|
-
*
|
|
629
|
-
* WRITE-ONLY. Nothing reads an anonymous note back on the public plane, which
|
|
630
|
-
* is why the result is a receipt rather than the note.
|
|
631
|
-
*
|
|
632
|
-
* @deprecated `submitAnonymousNote` is the older name of `writeNote` for a
|
|
633
|
-
* visitor. Use `writeNote` and {@link FoirWriteNoteArgs}, asking for `id` and
|
|
634
|
-
* `createdAt`.
|
|
635
|
-
*/
|
|
636
|
-
export interface FoirSubmitAnonymousNoteArgs {
|
|
637
|
-
/**
|
|
638
|
-
* Must be `'record'` — the only thing a visitor may write a note about.
|
|
639
|
-
* Narrower than `FoirCreateMyNoteArgs`, where a customer may also address a
|
|
640
|
-
* file or a model.
|
|
641
|
-
*/
|
|
642
|
-
subjectType: 'record';
|
|
643
|
-
subjectId: string;
|
|
644
|
-
/**
|
|
645
|
-
* The record's model key. REQUIRED here, unlike on `createMyNote` where it is
|
|
646
|
-
* conditional: the subject is always a record on this lane, so the condition
|
|
647
|
-
* is always met.
|
|
648
|
-
*/
|
|
649
|
-
subjectModelKey: string;
|
|
650
|
-
body: string;
|
|
651
|
-
/**
|
|
652
|
-
* Optional. Where the operators may reply.
|
|
653
|
-
*
|
|
654
|
-
* Captured as typed and NEVER verified — it is not treated as proof of who
|
|
655
|
-
* wrote the note, and nothing is sent to it to confirm it. Do not build a
|
|
656
|
-
* flow that assumes the address is reachable.
|
|
657
|
-
*/
|
|
658
|
-
contactEmail?: string;
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* What `submitAnonymousNote` returns.
|
|
662
|
-
*
|
|
663
|
-
* @deprecated With the field it answers. `writeNote` answers a visitor with
|
|
664
|
-
* the note's `id` and `createdAt`.
|
|
665
|
-
*/
|
|
666
|
-
export interface FoirAnonymousNoteReceipt {
|
|
667
|
-
/**
|
|
668
|
-
* A reference for this submission, to quote when following it up.
|
|
669
|
-
*
|
|
670
|
-
* NOT a handle: there is no read that takes it. Showing it to the visitor is
|
|
671
|
-
* the whole of its purpose.
|
|
672
|
-
*/
|
|
673
|
-
reference: string;
|
|
674
|
-
/** When the platform recorded the submission. */
|
|
675
|
-
submittedAt: string;
|
|
676
|
-
}
|
|
677
621
|
export type FoirDeviceType = 'DESKTOP' | 'MOBILE' | 'TABLET';
|
|
678
622
|
export type FoirPlatformType = 'IOS' | 'ANDROID' | 'WEB';
|
|
679
623
|
/**
|
|
@@ -694,4 +638,19 @@ export interface FoirResolveContext {
|
|
|
694
638
|
locale?: string | null;
|
|
695
639
|
platform?: FoirPlatformType | null;
|
|
696
640
|
preview?: boolean | null;
|
|
641
|
+
/**
|
|
642
|
+
* The country of the visitor a server is calling for, as an ISO 3166-1
|
|
643
|
+
* alpha-2 code. Only a key holding `visitors:forward` may say it, and a
|
|
644
|
+
* client given a `visitor` attaches it to every read, so there is rarely a
|
|
645
|
+
* reason to write it on one.
|
|
646
|
+
*/
|
|
647
|
+
country?: string | null;
|
|
648
|
+
/**
|
|
649
|
+
* Where the visitor a server is calling for is, in decimal degrees. Only a
|
|
650
|
+
* key holding `visitors:forward` may say it.
|
|
651
|
+
*/
|
|
652
|
+
location?: {
|
|
653
|
+
latitude: number;
|
|
654
|
+
longitude: number;
|
|
655
|
+
} | null;
|
|
697
656
|
}
|
package/dist/data/client.d.ts
CHANGED
|
@@ -166,7 +166,7 @@ export interface FoirRawOptions {
|
|
|
166
166
|
}
|
|
167
167
|
export interface FoirClientCore<S extends FoirSchemaDefinition> {
|
|
168
168
|
/**
|
|
169
|
-
* Non-model root namespaces (`search`, `
|
|
169
|
+
* Non-model root namespaces (`search`, `users`, `notifications`).
|
|
170
170
|
* Separate accessor because the addressing has to distinguish a model from a
|
|
171
171
|
* namespace and the client has no runtime schema to disambiguate with.
|
|
172
172
|
*/
|
|
@@ -181,7 +181,7 @@ export interface FoirClientCore<S extends FoirSchemaDefinition> {
|
|
|
181
181
|
* hatch that could only say what the JSON op could say would be an escape from
|
|
182
182
|
* nothing. As a document it reaches everything the credential can reach —
|
|
183
183
|
* including the root fields no model or namespace claims (`designTokens`,
|
|
184
|
-
* `record`, `locales`, `resolvedLocale`, the whole
|
|
184
|
+
* `record`, `locales`, `resolvedLocale`, the whole user-auth mutation
|
|
185
185
|
* set), which the JSON op could not address at all.
|
|
186
186
|
*
|
|
187
187
|
* Returns the whole `data` object rather than a single root field, because a
|
package/dist/data/decimal.d.ts
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
*
|
|
54
54
|
* TypeScript already rejects `price + tax` at compile time. The throw is for
|
|
55
55
|
* JavaScript callers and for anything that reaches this through `any`, where a
|
|
56
|
-
* silent `"19.992.00"` would otherwise reach a
|
|
56
|
+
* silent `"19.992.00"` would otherwise reach a user. Loud beats quiet: the
|
|
57
57
|
* whole reason this type exists is that the float bug was silent.
|
|
58
58
|
*
|
|
59
59
|
* `JSON.stringify` is answered by `toJSON`, so a decimal serialises back to the
|
package/dist/data/document.d.ts
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
* inferring a literal would reinstate the defect above under a condition no
|
|
52
52
|
* caller can see.
|
|
53
53
|
*/
|
|
54
|
+
import { type FoirVisitorPlace } from '../transport/foir-http';
|
|
54
55
|
import type { FoirManifestOp, FoirManifestOpPayload, FoirManifestRelation, FoirManifestSelection, FoirManifestSelectionField, FoirManifestSelectionUnion } from './manifest';
|
|
55
56
|
/**
|
|
56
57
|
* One model, as the printer needs it.
|
|
@@ -260,6 +261,19 @@ export declare function planFoirModelCall(source: FoirSelectionSource, call: Foi
|
|
|
260
261
|
* choose, since only the caller knows which calls are the same one.
|
|
261
262
|
*/
|
|
262
263
|
export declare function withCreateIdempotencyKey(source: FoirSelectionSource, model: string, op: string, args: Record<string, unknown>): Record<string, unknown>;
|
|
264
|
+
/**
|
|
265
|
+
* A read's arguments with where its stated visitor is, when the op takes
|
|
266
|
+
* `contexts` and the client was given a visitor with a place.
|
|
267
|
+
*
|
|
268
|
+
* It is attached here, to every read, and not left to each call: a page
|
|
269
|
+
* renders with many reads, and one that forgot would be answered as a visitor
|
|
270
|
+
* nobody could place while the read beside it was not.
|
|
271
|
+
*
|
|
272
|
+
* What a call says of the place itself wins, part by part: a caller that
|
|
273
|
+
* passes `contexts.country` for one read has said something more exact than
|
|
274
|
+
* the client's visitor.
|
|
275
|
+
*/
|
|
276
|
+
export declare function withStatedVisitor(op: FoirManifestOp | undefined, args: Record<string, unknown>, place: FoirVisitorPlace): Record<string, unknown>;
|
|
263
277
|
/**
|
|
264
278
|
* Turn a namespace call into a document.
|
|
265
279
|
*
|
package/dist/data/index.d.ts
CHANGED
|
@@ -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 { FoirSubject, FoirSubjectEntry, FoirSubjectOptions, FoirSubjectOrigin, FoirSubjectRegistry, } from './subjects';
|
|
62
|
-
export type { CollabInfo, FileValue, FoirAccessGrant, FoirAccessLevel, FoirAccessSource, FoirAccessor, FoirCreateMyNoteArgs, FoirWriteNoteArgs,
|
|
62
|
+
export type { CollabInfo, FileValue, FoirAccessGrant, FoirAccessLevel, FoirAccessSource, FoirAccessor, FoirCreateMyNoteArgs, FoirWriteNoteArgs, FoirUserNote, FoirUserNoteAttachment, FoirDegraded, FoirDepthBelow, FoirDepthLevel, FoirDeviceType, FoirGranteeKind, FoirJsonValue, FoirMyAccess, FoirMyNotesArgs, FoirMyNotesResult, FoirNoteConfig, 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,4 +82,4 @@ 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, } from '../transport/foir-http';
|
|
85
|
-
export type { FoirEnv, FoirGraphQLErrorItem, FoirTokenProvider, FoirAuthDowngrade, FoirVisitorSource, } from '../transport/foir-http';
|
|
85
|
+
export type { FoirEnv, FoirGraphQLErrorItem, FoirTokenProvider, FoirAuthDowngrade, FoirVisitor, FoirVisitorSource, } from '../transport/foir-http';
|
package/dist/data/manifest.d.ts
CHANGED
|
@@ -303,7 +303,7 @@ export interface FoirManifest {
|
|
|
303
303
|
composedFor?: string;
|
|
304
304
|
models: Record<string, FoirManifestModel>;
|
|
305
305
|
/**
|
|
306
|
-
* Keyed by namespace name (`auth`, `
|
|
306
|
+
* Keyed by namespace name (`auth`, `users`, `files`, `notifications`,
|
|
307
307
|
* `operations`, `schedules`, `search`, `secrets`). Deliberately an open
|
|
308
308
|
* record rather than the eight names: a namespace the platform adds must
|
|
309
309
|
* reach the generator, and a closed union would silently drop it.
|
package/dist/data/schema.d.ts
CHANGED
|
@@ -272,7 +272,7 @@ export interface FoirModelDefinition {
|
|
|
272
272
|
readonly ops: Readonly<Record<string, FoirOpDefinition>>;
|
|
273
273
|
}
|
|
274
274
|
/**
|
|
275
|
-
* One op on a non-model namespace (`search`, `
|
|
275
|
+
* One op on a non-model namespace (`search`, `users`, `notifications`).
|
|
276
276
|
* Namespaces have no model behind them, so there is no `include`/`omit` and
|
|
277
277
|
* the result type is used verbatim.
|
|
278
278
|
*/
|
package/dist/data/transport.d.ts
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* Where a call goes
|
|
6
6
|
* ---------------------------------------------------------------------------
|
|
7
7
|
*
|
|
8
|
-
* read + `pk_` key + no
|
|
8
|
+
* read + `pk_` key + no user bearer + published channel
|
|
9
9
|
* → `GET /cdn/graphql?key=pk_…&extensions={"persistedQuery":{"sha256Hash":…}}`
|
|
10
10
|
* A shared cache in front of the caller stores the response, because the
|
|
11
11
|
* request is a GET and the credential is in the URL.
|
|
12
12
|
*
|
|
13
|
-
* anything else — `sk_`, a
|
|
13
|
+
* anything else — `sk_`, a user bearer, a mutation, the draft channel
|
|
14
14
|
* → `POST /graphql` with the `x-api-key` header and the APQ body.
|
|
15
15
|
*
|
|
16
16
|
* a call made AS an operation, with its dispatch token
|
|
@@ -158,7 +158,7 @@ export type FoirOperationGeneratedModule<S extends FoirSchemaDefinition = FoirSc
|
|
|
158
158
|
* The endpoint calls Foir AS the operation, with the grants the operation
|
|
159
159
|
* declared, and holds no key of its own. api-public accepts the token as a
|
|
160
160
|
* caller in its own right: it builds the schema overlay from the token's
|
|
161
|
-
* grants, and it reads the organization and the
|
|
161
|
+
* grants, and it reads the organization and the user from the
|
|
162
162
|
* token's claims. So nothing a key client configures beside its key
|
|
163
163
|
* (`bearerToken`, `orgId`, `channel`) means anything here, and none of it is
|
|
164
164
|
* accepted.
|
|
@@ -183,7 +183,7 @@ export interface FoirDispatchCredential {
|
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
185
|
* A client that calls Foir with an api key: a storefront's `pk_`, a server's
|
|
186
|
-
* `sk_`, optionally for a signed-in
|
|
186
|
+
* `sk_`, optionally for a signed-in user.
|
|
187
187
|
*/
|
|
188
188
|
export interface FoirKeyClientConfig<S extends FoirSchemaDefinition = FoirSchemaDefinition> {
|
|
189
189
|
/**
|
|
@@ -200,19 +200,19 @@ export interface FoirKeyClientConfig<S extends FoirSchemaDefinition = FoirSchema
|
|
|
200
200
|
dispatch?: never;
|
|
201
201
|
/**
|
|
202
202
|
* Read the draft schema channel (`X-Foir-Schema-Channel: draft`). Needs a key
|
|
203
|
-
* holding `drafts:read`: a preview environment's own
|
|
204
|
-
*
|
|
205
|
-
*
|
|
203
|
+
* holding `drafts:read`: a preview environment's own key. A publishable or
|
|
204
|
+
* a server key without it is served the published copy. Defaults to
|
|
205
|
+
* `'published'`.
|
|
206
206
|
*/
|
|
207
207
|
channel?: 'published' | 'draft';
|
|
208
208
|
/**
|
|
209
|
-
*
|
|
209
|
+
* User bearer for self-scoped reads (`myProfile`, `myTillyNotes`). A
|
|
210
210
|
* function is resolved before every request so a cookie session can be read
|
|
211
211
|
* per-request; a bare string is used as-is.
|
|
212
212
|
*/
|
|
213
213
|
bearerToken?: string | FoirTokenProvider;
|
|
214
214
|
/**
|
|
215
|
-
* Renew the
|
|
215
|
+
* Renew the user bearer when an answer comes back served without it
|
|
216
216
|
* because it expired. Resolve the new token, or null when the session is
|
|
217
217
|
* over. It is called at most once per request, and one call is shared by the
|
|
218
218
|
* client's requests that find the bearer lapsed at the same time.
|
|
@@ -247,8 +247,8 @@ export interface FoirKeyClientConfig<S extends FoirSchemaDefinition = FoirSchema
|
|
|
247
247
|
*
|
|
248
248
|
* A credential already confined to an organization may only name its own
|
|
249
249
|
* (`PERMISSION_DENIED` otherwise), so this cannot steer an organization key
|
|
250
|
-
* elsewhere. And a request that also carries a
|
|
251
|
-
* unless that
|
|
250
|
+
* elsewhere. And a request that also carries a user bearer is refused
|
|
251
|
+
* unless that user stands in the named organization. The storefront
|
|
252
252
|
* pattern is still the confined credential, one per organization; this is
|
|
253
253
|
* for the side of the product that holds the project key and has to choose.
|
|
254
254
|
*
|
|
@@ -259,21 +259,28 @@ export interface FoirKeyClientConfig<S extends FoirSchemaDefinition = FoirSchema
|
|
|
259
259
|
*/
|
|
260
260
|
orgId?: string;
|
|
261
261
|
/**
|
|
262
|
-
* The
|
|
263
|
-
*
|
|
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.
|
|
262
|
+
* The visitor a server rendering pages is calling for, or a function
|
|
263
|
+
* resolving them for each request: their address, and where they are.
|
|
267
264
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
265
|
+
* The address is sent as `X-Foir-Visitor-IP`. The public API's per-visitor
|
|
266
|
+
* limit then applies to that visitor rather than to your server, which
|
|
267
|
+
* otherwise counts as one visitor for every page it renders. A string is the
|
|
268
|
+
* address alone.
|
|
272
269
|
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* key
|
|
276
|
-
*
|
|
270
|
+
* The country and the coordinates are attached to every read, in its
|
|
271
|
+
* `contexts`, so that location targeting is decided by where the visitor is.
|
|
272
|
+
* Through a key holding `visitors:forward` the API never uses where the
|
|
273
|
+
* caller is: a read that says no place is a visitor nobody could place.
|
|
274
|
+
*
|
|
275
|
+
* Give it what your host guarantees: the values its edge sets and a visitor
|
|
276
|
+
* cannot. A header a visitor can write lets them name a fresh address per
|
|
277
|
+
* request, which is exactly what the limit exists to stop. The docs list
|
|
278
|
+
* the values for each common host.
|
|
279
|
+
*
|
|
280
|
+
* A server key only, and one holding `visitors:forward`: the API refuses the
|
|
281
|
+
* header, naming the permission, from any other. A client built from a
|
|
282
|
+
* publishable key is refused `visitor` here, because a browser already calls
|
|
283
|
+
* as its own visitor and a publishable key cannot hold the permission.
|
|
277
284
|
*/
|
|
278
285
|
visitor?: FoirVisitorSource;
|
|
279
286
|
/**
|
|
@@ -520,7 +527,7 @@ export interface FoirKeyClientConfig<S extends FoirSchemaDefinition = FoirSchema
|
|
|
520
527
|
}
|
|
521
528
|
/**
|
|
522
529
|
* The options that belong to a key: the key itself, what it is presented with
|
|
523
|
-
* (a
|
|
530
|
+
* (a user bearer, an organization, a channel), and the subscription socket,
|
|
524
531
|
* which only a key can open.
|
|
525
532
|
*/
|
|
526
533
|
type FoirKeySideOption = 'apiKey' | 'dispatch' | 'channel' | 'bearerToken' | 'refreshBearer' | 'orgId' | 'orgUrl' | 'visitor' | 'wsUrl' | 'webSocketImpl' | 'reconnect' | 'connectionParams';
|
|
@@ -593,7 +600,7 @@ export interface FoirSchemaDrift {
|
|
|
593
600
|
* way, as paths into the manifest, sorted:
|
|
594
601
|
* `models.order.selection.status`, `models.order.ops.update`,
|
|
595
602
|
* `namespaces.search.ops.searchRecords.args.query`,
|
|
596
|
-
* `models.order.relations.
|
|
603
|
+
* `models.order.relations.user`, `selectionUnions.Block.HeroBlock.title`.
|
|
597
604
|
*
|
|
598
605
|
* `changed` says what moved; this says what BROKE. Empty when the surface
|
|
599
606
|
* only grew, which is the usual drift, and then no typed call is refused.
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* being replayed after it expired.
|
|
44
44
|
*
|
|
45
45
|
* An operation's dispatch token is not one of them. The server's
|
|
46
|
-
* `connection_init` authenticates a key,
|
|
46
|
+
* `connection_init` authenticates a key, a console token or a user bearer
|
|
47
47
|
* and has no path for a dispatch token, so a client built from one is refused
|
|
48
48
|
* here, by name, rather than opening a socket the server will close as "Not
|
|
49
49
|
* authenticated".
|