@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
package/dist/data.js
CHANGED
|
@@ -98,6 +98,28 @@ function refuseCredentialHeaders(extra) {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
var FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
101
|
+
function sentCoordinate(value) {
|
|
102
|
+
return Math.round(value * 100) / 100;
|
|
103
|
+
}
|
|
104
|
+
async function resolveFoirVisitor(source) {
|
|
105
|
+
const value = typeof source === "function" ? await source() : source;
|
|
106
|
+
if (value === null || value === void 0) return { place: {} };
|
|
107
|
+
if (typeof value === "string") {
|
|
108
|
+
const address2 = value.trim();
|
|
109
|
+
return { address: address2 || void 0, place: {} };
|
|
110
|
+
}
|
|
111
|
+
const address = value.address?.trim();
|
|
112
|
+
const country = value.country?.trim();
|
|
113
|
+
const place = {};
|
|
114
|
+
if (country) place.country = country;
|
|
115
|
+
if (Number.isFinite(value.latitude) && Number.isFinite(value.longitude)) {
|
|
116
|
+
place.location = {
|
|
117
|
+
latitude: sentCoordinate(value.latitude),
|
|
118
|
+
longitude: sentCoordinate(value.longitude)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return { address: address || void 0, place };
|
|
122
|
+
}
|
|
101
123
|
function buildFoirHeaders(input) {
|
|
102
124
|
const headers = {
|
|
103
125
|
"Content-Type": "application/json"
|
|
@@ -144,8 +166,8 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
144
166
|
downgrade;
|
|
145
167
|
constructor(downgrade) {
|
|
146
168
|
super(
|
|
147
|
-
`[foir] your
|
|
148
|
-
{ graphQLErrors: [{ message: "
|
|
169
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
|
|
170
|
+
{ graphQLErrors: [{ message: "user token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
|
|
149
171
|
);
|
|
150
172
|
this.name = "FoirAuthDowngradedError";
|
|
151
173
|
this.downgrade = downgrade;
|
|
@@ -153,7 +175,7 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
153
175
|
};
|
|
154
176
|
var FoirSessionExpiredError = class extends FoirRequestError {
|
|
155
177
|
constructor(options) {
|
|
156
|
-
super("[foir] the
|
|
178
|
+
super("[foir] the user session has expired and could not be refreshed: sign in again", {
|
|
157
179
|
cause: options?.cause,
|
|
158
180
|
graphQLErrors: [{ message: "session expired", extensions: { code: "SESSION_EXPIRED" } }]
|
|
159
181
|
});
|
|
@@ -170,7 +192,7 @@ function reportAuthDowngrade(envelope) {
|
|
|
170
192
|
if (warnedDowngrade === downgrade.reason) return;
|
|
171
193
|
warnedDowngrade = downgrade.reason;
|
|
172
194
|
console.warn(
|
|
173
|
-
`[foir] your
|
|
195
|
+
`[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.")
|
|
174
196
|
);
|
|
175
197
|
}
|
|
176
198
|
function refusePrunedDocument(json, options) {
|
|
@@ -214,6 +236,30 @@ function readFoirEnvelope(json, options) {
|
|
|
214
236
|
return json.data;
|
|
215
237
|
}
|
|
216
238
|
|
|
239
|
+
// src/transport/key-kind.ts
|
|
240
|
+
var KEY_PREFIXES = [
|
|
241
|
+
["foir_pub_", "publishable"],
|
|
242
|
+
["foir_srv_", "server"],
|
|
243
|
+
["foir_adm_", "admin"],
|
|
244
|
+
["pk_", "publishable"],
|
|
245
|
+
["sk_", "older"],
|
|
246
|
+
["tc_", "older"]
|
|
247
|
+
];
|
|
248
|
+
var FOIR_CREDENTIALS_DOC = "https://docs.foir.dev/credentials";
|
|
249
|
+
function foirKeyKind(apiKey) {
|
|
250
|
+
if (typeof apiKey !== "string") return void 0;
|
|
251
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
252
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
253
|
+
}
|
|
254
|
+
return void 0;
|
|
255
|
+
}
|
|
256
|
+
function adminKeyRefusal(client) {
|
|
257
|
+
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}`;
|
|
258
|
+
}
|
|
259
|
+
function isPublishableFoirKey(apiKey) {
|
|
260
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
261
|
+
}
|
|
262
|
+
|
|
217
263
|
// src/transport/persisted-query.ts
|
|
218
264
|
var FOIR_CDN_GRAPHQL_PATH = "/cdn/graphql";
|
|
219
265
|
function persistedQueryExtension(hash) {
|
|
@@ -257,10 +303,7 @@ function foirDataEndpoint(apiUrl, path) {
|
|
|
257
303
|
return foirApiEndpoint(apiUrl, path);
|
|
258
304
|
}
|
|
259
305
|
async function resolveVisitorAddress(config) {
|
|
260
|
-
|
|
261
|
-
const value = typeof source === "function" ? await source() : source;
|
|
262
|
-
const address = value?.trim();
|
|
263
|
-
return address ? address : void 0;
|
|
306
|
+
return (await resolveFoirVisitor(config.visitor)).address;
|
|
264
307
|
}
|
|
265
308
|
async function resolveBearerToken(config) {
|
|
266
309
|
const source = config.bearerToken;
|
|
@@ -309,9 +352,13 @@ function assertFoirCredential(config) {
|
|
|
309
352
|
"[foir] createFoirClient needs both apiUrl and apiKey (or, inside an operation endpoint, apiUrl and dispatch)"
|
|
310
353
|
);
|
|
311
354
|
}
|
|
312
|
-
|
|
355
|
+
const kind = foirKeyKind(loose.apiKey);
|
|
356
|
+
if (kind === "admin") {
|
|
357
|
+
throw new FoirRequestError(adminKeyRefusal("createFoirClient"));
|
|
358
|
+
}
|
|
359
|
+
if (loose.visitor !== void 0 && kind !== "server" && kind !== "older") {
|
|
313
360
|
throw new FoirRequestError(
|
|
314
|
-
|
|
361
|
+
`[foir] createFoirClient: \`visitor\` names the visitor a server calls for, and only a server key holding visitors:forward may send it. A publishable key already calls as its visitor, and cannot hold the permission. Drop \`visitor\`, or build this client from your server key on the server that renders your pages. See ${FOIR_CREDENTIALS_DOC}`
|
|
315
362
|
);
|
|
316
363
|
}
|
|
317
364
|
if (operation !== void 0) {
|
|
@@ -336,7 +383,7 @@ function assertFoirCredential(config) {
|
|
|
336
383
|
].filter((name) => loose[name] !== void 0);
|
|
337
384
|
if (keySide.length > 0) {
|
|
338
385
|
throw new FoirRequestError(
|
|
339
|
-
`[foir] createFoirClient was given \`dispatch\` and ${keySide.map((n) => `\`${n}\``).join(", ")}. A dispatch token and an api key are different actors, and the dispatch token carries its own
|
|
386
|
+
`[foir] createFoirClient was given \`dispatch\` and ${keySide.map((n) => `\`${n}\``).join(", ")}. A dispatch token and an api key are different actors, and the dispatch token carries its own user, organization and channel. Pass one credential: \`dispatch\` inside an operation endpoint, \`apiKey\` everywhere else.`
|
|
340
387
|
);
|
|
341
388
|
}
|
|
342
389
|
const dispatch = loose.dispatch;
|
|
@@ -373,7 +420,7 @@ function contextOperation(context) {
|
|
|
373
420
|
return void 0;
|
|
374
421
|
}
|
|
375
422
|
function foirRoute(input) {
|
|
376
|
-
if (!input.apiKey
|
|
423
|
+
if (!isPublishableFoirKey(input.apiKey)) return { lane: "origin", reason: "key" };
|
|
377
424
|
if (input.bearerToken) return { lane: "origin", reason: "bearer" };
|
|
378
425
|
if (input.orgId || input.orgUrl) return { lane: "origin", reason: "org" };
|
|
379
426
|
if (input.mutation) return { lane: "origin", reason: "operation" };
|
|
@@ -894,13 +941,19 @@ function planFoirModelCall(source, call) {
|
|
|
894
941
|
}
|
|
895
942
|
return planCall(source, opDef, `${call.model}.${call.op}`, operationName(model.typeName, call.op), call);
|
|
896
943
|
}
|
|
897
|
-
var CREATING_OPS = /* @__PURE__ */ new Set(["create"
|
|
944
|
+
var CREATING_OPS = /* @__PURE__ */ new Set(["create"]);
|
|
898
945
|
function withCreateIdempotencyKey(source, model, op, args) {
|
|
899
946
|
if (!CREATING_OPS.has(op) || args.idempotencyKey !== void 0) return args;
|
|
900
947
|
const argTypes = source.models[model]?.ops?.[op]?.argTypes;
|
|
901
948
|
if (!argTypes || !("idempotencyKey" in argTypes)) return args;
|
|
902
949
|
return { ...args, idempotencyKey: globalThis.crypto.randomUUID() };
|
|
903
950
|
}
|
|
951
|
+
function withStatedVisitor(op, args, place) {
|
|
952
|
+
if (place.country === void 0 && place.location === void 0) return args;
|
|
953
|
+
if (!op?.argTypes || !("contexts" in op.argTypes)) return args;
|
|
954
|
+
const said = args.contexts ?? {};
|
|
955
|
+
return { ...args, contexts: { ...place, ...said } };
|
|
956
|
+
}
|
|
904
957
|
function planFoirNamespaceCall(source, call) {
|
|
905
958
|
const namespace = source.namespaces?.[call.namespace];
|
|
906
959
|
if (!namespace) {
|
|
@@ -1358,7 +1411,7 @@ var FoirSocketTransport = class {
|
|
|
1358
1411
|
constructor(config) {
|
|
1359
1412
|
if (config.dispatch !== void 0) {
|
|
1360
1413
|
throw new FoirRequestError(
|
|
1361
|
-
"[foir] a client built from a dispatch token cannot subscribe. The subscription socket authenticates an api key,
|
|
1414
|
+
"[foir] a client built from a dispatch token cannot subscribe. The subscription socket authenticates an api key, a console token or a user bearer, and has no path for a dispatch token. Queries and mutations made as the operation go to /graphql and work as they are."
|
|
1362
1415
|
);
|
|
1363
1416
|
}
|
|
1364
1417
|
this.config = config;
|
|
@@ -2260,20 +2313,43 @@ function createFoirClient(clientConfig) {
|
|
|
2260
2313
|
detectedDrift
|
|
2261
2314
|
);
|
|
2262
2315
|
};
|
|
2316
|
+
const visitorOfThisRequest = async () => {
|
|
2317
|
+
if (config.dispatch !== void 0 || config.visitor === void 0) {
|
|
2318
|
+
return { config, place: {} };
|
|
2319
|
+
}
|
|
2320
|
+
const { address, place } = await resolveFoirVisitor(config.visitor);
|
|
2321
|
+
return {
|
|
2322
|
+
config: {
|
|
2323
|
+
...config,
|
|
2324
|
+
visitor: {
|
|
2325
|
+
address,
|
|
2326
|
+
country: place.country,
|
|
2327
|
+
latitude: place.location?.latitude,
|
|
2328
|
+
longitude: place.location?.longitude
|
|
2329
|
+
}
|
|
2330
|
+
},
|
|
2331
|
+
place
|
|
2332
|
+
};
|
|
2333
|
+
};
|
|
2263
2334
|
const runModel = async (model, op, query) => {
|
|
2264
2335
|
assertStillServed(`${model}.${op}`, (served) => served.modelCall(model, op, query?.include));
|
|
2265
2336
|
const { args, include, omit, pick, depth } = splitQuery(query);
|
|
2266
2337
|
const source = await selectionSource();
|
|
2338
|
+
const visitor = await visitorOfThisRequest();
|
|
2267
2339
|
const plan = planFoirModelCall(source, {
|
|
2268
2340
|
model,
|
|
2269
2341
|
op,
|
|
2270
|
-
args:
|
|
2342
|
+
args: withStatedVisitor(
|
|
2343
|
+
source.models[model]?.ops?.[op],
|
|
2344
|
+
withCreateIdempotencyKey(source, model, op, args),
|
|
2345
|
+
visitor.place
|
|
2346
|
+
),
|
|
2271
2347
|
include,
|
|
2272
2348
|
omit,
|
|
2273
2349
|
pick,
|
|
2274
2350
|
depth
|
|
2275
2351
|
});
|
|
2276
|
-
const result = await executeFoirDocument(config, plan);
|
|
2352
|
+
const result = await executeFoirDocument(visitor.config, plan);
|
|
2277
2353
|
recordFoirSubjects(config.subjects, plan, result);
|
|
2278
2354
|
maybeCheckDrift();
|
|
2279
2355
|
return result;
|
|
@@ -2284,16 +2360,18 @@ function createFoirClient(clientConfig) {
|
|
|
2284
2360
|
(served) => served.namespaceCall(namespace, op, query?.include)
|
|
2285
2361
|
);
|
|
2286
2362
|
const { args, include, omit, pick, depth } = splitQuery(query);
|
|
2287
|
-
const
|
|
2363
|
+
const source = await selectionSource();
|
|
2364
|
+
const visitor = await visitorOfThisRequest();
|
|
2365
|
+
const plan = planFoirNamespaceCall(source, {
|
|
2288
2366
|
namespace,
|
|
2289
2367
|
op,
|
|
2290
|
-
args,
|
|
2368
|
+
args: withStatedVisitor(source.namespaces?.[namespace]?.ops?.[op], args, visitor.place),
|
|
2291
2369
|
include,
|
|
2292
2370
|
omit,
|
|
2293
2371
|
pick,
|
|
2294
2372
|
depth
|
|
2295
2373
|
});
|
|
2296
|
-
const result = await executeFoirDocument(config, plan);
|
|
2374
|
+
const result = await executeFoirDocument(visitor.config, plan);
|
|
2297
2375
|
recordFoirSubjects(config.subjects, plan, result);
|
|
2298
2376
|
maybeCheckDrift();
|
|
2299
2377
|
return result;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session-bound content client — forwards the
|
|
3
|
-
* when one is available, so auth/segment-targeted variants and
|
|
2
|
+
* Session-bound content client — forwards the user bearer on every read
|
|
3
|
+
* when one is available, so auth/segment-targeted variants and user-scoped
|
|
4
4
|
* records resolve out of the box, with no per-query wiring to forget.
|
|
5
5
|
*
|
|
6
6
|
* Resolution keys off the request's principal (see the platform's two-gate
|
|
7
7
|
* model): the `x-api-key` alone resolves the *anonymous baseline*
|
|
8
|
-
* (`context.authStatus = ""`); adding the
|
|
9
|
-
* as that
|
|
8
|
+
* (`context.authStatus = ""`); adding the user bearer resolves the request
|
|
9
|
+
* as that user (`context.authStatus = "authenticated"`, `user_id` set).
|
|
10
10
|
*
|
|
11
11
|
* Caching: an api-key-only read is identical for every anonymous visitor and is
|
|
12
|
-
* shared-cacheable; a bearer-bearing read resolves per-
|
|
13
|
-
* Because the bearer only exists once a
|
|
12
|
+
* shared-cacheable; a bearer-bearing read resolves per-user and is not.
|
|
13
|
+
* Because the bearer only exists once a user signs in, anonymous traffic
|
|
14
14
|
* keeps hitting the shared-cacheable baseline — only signed-in reads become
|
|
15
|
-
* per-
|
|
16
|
-
* apps, everywhere the
|
|
15
|
+
* per-user. Bind this client wherever content is personalized (for most
|
|
16
|
+
* apps, everywhere the user may be signed in) and use plain `foirRequest`
|
|
17
17
|
* for reads that must stay anonymous regardless of session.
|
|
18
18
|
*/
|
|
19
19
|
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
@@ -25,9 +25,9 @@ import type { FoirTokenProvider } from '../transport/foir-http';
|
|
|
25
25
|
export interface FoirContentClientConfig {
|
|
26
26
|
env: FoirEnv;
|
|
27
27
|
/**
|
|
28
|
-
* Resolves the current
|
|
28
|
+
* Resolves the current user bearer before every request. When it yields a
|
|
29
29
|
* token the token is forwarded, so `authStatus`/segment targeting and
|
|
30
|
-
*
|
|
30
|
+
* user-scoped records resolve as that user; when it yields
|
|
31
31
|
* null/undefined the read hits the anonymous baseline. Omit for a client that
|
|
32
32
|
* is always anonymous (equivalent to calling `foirRequest` directly).
|
|
33
33
|
*/
|
|
@@ -51,7 +51,7 @@ export interface FoirContentClientConfig {
|
|
|
51
51
|
}
|
|
52
52
|
export interface FoirContentClient {
|
|
53
53
|
/**
|
|
54
|
-
* Run a typed document, forwarding the bound
|
|
54
|
+
* Run a typed document, forwarding the bound user bearer when available.
|
|
55
55
|
* An explicit `options.bearerToken` still wins, so a single call can override
|
|
56
56
|
* the bound token (or force anonymous by passing `undefined` — the bound
|
|
57
57
|
* token only fills in when `options.bearerToken` is not supplied).
|
|
@@ -60,7 +60,7 @@ export interface FoirContentClient {
|
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Create a {@link FoirContentClient} bound to an env + token source. Every
|
|
63
|
-
* `request` forwards the
|
|
64
|
-
* that read from the anonymous baseline to the signed-in
|
|
63
|
+
* `request` forwards the user bearer when `getToken` yields one, upgrading
|
|
64
|
+
* that read from the anonymous baseline to the signed-in user.
|
|
65
65
|
*/
|
|
66
66
|
export declare function createFoirContentClient(config: FoirContentClientConfig): FoirContentClient;
|
package/dist/index.js
CHANGED
|
@@ -94,6 +94,27 @@ function isValidHostMessage(data) {
|
|
|
94
94
|
return true;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// src/transport/key-kind.ts
|
|
98
|
+
var KEY_PREFIXES = [
|
|
99
|
+
["foir_pub_", "publishable"],
|
|
100
|
+
["foir_srv_", "server"],
|
|
101
|
+
["foir_adm_", "admin"],
|
|
102
|
+
["pk_", "publishable"],
|
|
103
|
+
["sk_", "older"],
|
|
104
|
+
["tc_", "older"]
|
|
105
|
+
];
|
|
106
|
+
var FOIR_CREDENTIALS_DOC = "https://docs.foir.dev/credentials";
|
|
107
|
+
function foirKeyKind(apiKey) {
|
|
108
|
+
if (typeof apiKey !== "string") return void 0;
|
|
109
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
110
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
111
|
+
}
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
function adminKeyRefusal(client) {
|
|
115
|
+
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}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
97
118
|
// src/client/platform-client.ts
|
|
98
119
|
var PlatformClientError = class extends Error {
|
|
99
120
|
constructor(message, status, response) {
|
|
@@ -107,6 +128,9 @@ var PlatformClientError = class extends Error {
|
|
|
107
128
|
};
|
|
108
129
|
function createPlatformClient(options) {
|
|
109
130
|
const { apiUrl, apiKey } = options;
|
|
131
|
+
if (foirKeyKind(apiKey) === "admin") {
|
|
132
|
+
throw new PlatformClientError(adminKeyRefusal("createPlatformClient"));
|
|
133
|
+
}
|
|
110
134
|
return {
|
|
111
135
|
async query(query, variables) {
|
|
112
136
|
const res = await fetch(apiUrl, {
|
package/dist/live/drift.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { FoirSchemaDrift } from '../data/transport';
|
|
|
12
12
|
* Nothing else catches it. The dependency graph is about records, and no record
|
|
13
13
|
* changed. `seq` is about ordering, and nothing is out of order. The entry is
|
|
14
14
|
* fresh, correctly tagged, correctly stamped, and describes a schema that no
|
|
15
|
-
* longer exists — until the tab is reloaded, which on a long-lived
|
|
15
|
+
* longer exists — until the tab is reloaded, which on a long-lived console
|
|
16
16
|
* session can be days.
|
|
17
17
|
*
|
|
18
18
|
* The SDK already detects this: the data client compares the surface version
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* ---------------------------------------------------------------------------
|
|
7
7
|
*
|
|
8
8
|
* Two surfaces need the same picture: the console's own bug report, and the
|
|
9
|
-
* feedback widget a
|
|
9
|
+
* feedback widget a user's visitors use (`@foir/sdk/react`). One of those
|
|
10
10
|
* ships to npm and the other does not, so the shared half has to sit in the
|
|
11
11
|
* published package — a copy in each would be two answers to "how big is a
|
|
12
12
|
* screenshot", free to drift, and the drift would only show up as an upload the
|
package/dist/next/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@foir/sdk/next` — server-side Foir
|
|
2
|
+
* `@foir/sdk/next` — server-side Foir user auth for the Next.js App Router
|
|
3
3
|
* (the BFF pattern): an HttpOnly-cookie session adapter, server-side session
|
|
4
4
|
* helpers, a provider-callback exchange, and a route-protection middleware.
|
|
5
5
|
*
|
|
@@ -18,5 +18,5 @@ export { readFoirCacheTags, foirCacheTags, createFoirTaggingFetch, foirTagsForRe
|
|
|
18
18
|
export type { FoirHeaderSource, FoirCacheTags, FoirCacheTagsOptions, FoirTaggingReport, FoirTaggingFetchOptions, FoirRecordChange, } from './cacheTags';
|
|
19
19
|
export { createFoirMiddleware } from './middleware';
|
|
20
20
|
export type { FoirMiddlewareConfig, FoirMiddlewareRequest } from './middleware';
|
|
21
|
-
export type {
|
|
21
|
+
export type { FoirUser, SessionAdapter } from '../auth';
|
|
22
22
|
export type { FoirEnv } from '../graphql/foir-request';
|
package/dist/next/session.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FoirUser } from '../auth';
|
|
2
2
|
import type { FoirEnv } from '../graphql/foir-request';
|
|
3
3
|
import type { CookieAdapterOptions, FoirCookieStore } from './cookieAdapter';
|
|
4
4
|
/**
|
|
@@ -6,14 +6,14 @@ import type { CookieAdapterOptions, FoirCookieStore } from './cookieAdapter';
|
|
|
6
6
|
* cookie store (`await cookies()` in a route handler / server action, or the
|
|
7
7
|
* read-only `cookies()` in a Server Component for reads).
|
|
8
8
|
*
|
|
9
|
-
* Note on "verification": the
|
|
9
|
+
* Note on "verification": the user access token is opaque to your app and
|
|
10
10
|
* validated by the Foir API on use, so `getFoirSession` returns the identity
|
|
11
11
|
* stored in the cookies rather than locally verifying a JWT. A rejected/expired
|
|
12
12
|
* bearer surfaces when you call the API (and `@foir/sdk/auth`'s `authedRequest`
|
|
13
13
|
* transparently refreshes it).
|
|
14
14
|
*/
|
|
15
|
-
/** The signed-in
|
|
16
|
-
export declare function getFoirSession(store: FoirCookieStore): Promise<
|
|
15
|
+
/** The signed-in user from the session cookies, or null. */
|
|
16
|
+
export declare function getFoirSession(store: FoirCookieStore): Promise<FoirUser | null>;
|
|
17
17
|
/** The current access token from the session cookies, or null. */
|
|
18
18
|
export declare function getFoirToken(store: FoirCookieStore): Promise<string | null>;
|
|
19
19
|
/** Clear the session cookies (sign out). */
|
|
@@ -26,9 +26,9 @@ interface PasswordArgs {
|
|
|
26
26
|
cookieOptions?: CookieAdapterOptions['cookieOptions'];
|
|
27
27
|
}
|
|
28
28
|
/** Password sign-in in a route handler; persists the session to cookies. */
|
|
29
|
-
export declare function foirSignInWithPassword({ env, store, email, password, cookieOptions, }: PasswordArgs): Promise<
|
|
30
|
-
/** Register a
|
|
31
|
-
export declare function foirRegisterWithPassword({ env, store, email, password, cookieOptions, }: PasswordArgs): Promise<
|
|
29
|
+
export declare function foirSignInWithPassword({ env, store, email, password, cookieOptions, }: PasswordArgs): Promise<FoirUser>;
|
|
30
|
+
/** Register a user in a route handler; persists the session to cookies. */
|
|
31
|
+
export declare function foirRegisterWithPassword({ env, store, email, password, cookieOptions, }: PasswordArgs): Promise<FoirUser>;
|
|
32
32
|
/** Complete an OTP sign-in in a route handler; persists the session to cookies. */
|
|
33
33
|
export declare function foirSignInWithOtp({ env, store, email, otp, cookieOptions, }: {
|
|
34
34
|
env: FoirEnv;
|
|
@@ -36,11 +36,11 @@ export declare function foirSignInWithOtp({ env, store, email, otp, cookieOption
|
|
|
36
36
|
email: string;
|
|
37
37
|
otp: string;
|
|
38
38
|
cookieOptions?: CookieAdapterOptions['cookieOptions'];
|
|
39
|
-
}): Promise<
|
|
39
|
+
}): Promise<FoirUser>;
|
|
40
40
|
/**
|
|
41
41
|
* Complete a provider (OAuth/OIDC) login in your callback route handler:
|
|
42
42
|
* exchange the `code` + `state` for tokens and persist them to the session
|
|
43
|
-
* cookies. Returns the
|
|
43
|
+
* cookies. Returns the user.
|
|
44
44
|
*/
|
|
45
45
|
export declare function completeFoirProviderLogin(opts: {
|
|
46
46
|
env: FoirEnv;
|
|
@@ -49,5 +49,5 @@ export declare function completeFoirProviderLogin(opts: {
|
|
|
49
49
|
code?: string;
|
|
50
50
|
state?: string;
|
|
51
51
|
cookieOptions?: CookieAdapterOptions['cookieOptions'];
|
|
52
|
-
}): Promise<
|
|
52
|
+
}): Promise<FoirUser>;
|
|
53
53
|
export {};
|