@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.
- package/CHANGELOG.md +353 -2
- package/README.md +10 -2
- package/dist/app/client.d.ts +371 -0
- package/dist/app/contract/index.d.ts +23 -0
- package/dist/app/contract/proto/apps/v1/apps_pb.d.ts +1465 -0
- package/dist/app/contract/proto/apps/v1/discovery_pb.d.ts +678 -0
- package/dist/app/dispatch.d.ts +64 -0
- package/dist/app/errors.d.ts +50 -0
- package/dist/app/index.d.ts +40 -0
- package/dist/app/manifest.d.ts +56 -0
- package/dist/app/naming.d.ts +31 -0
- package/dist/app/record-key.d.ts +17 -0
- package/dist/app/ticket.d.ts +37 -0
- package/dist/app/vocabulary.generated.d.ts +20 -0
- package/dist/app-contract.js +124 -0
- package/dist/app.js +998 -0
- package/dist/auth/auth.d.ts +24 -6
- package/dist/auth/index.d.ts +2 -1
- package/dist/auth/ops.d.ts +8 -0
- package/dist/auth.js +230 -60
- package/dist/client.js +80 -15
- package/dist/codegen/preset.d.ts +3 -2
- package/dist/data/builtins.d.ts +17 -1
- package/dist/data/client.d.ts +10 -8
- package/dist/data/document.d.ts +13 -0
- package/dist/data/index.d.ts +15 -5
- package/dist/data/manifest.d.ts +31 -3
- package/dist/data/schema.d.ts +16 -9
- package/dist/data/selection.d.ts +15 -0
- package/dist/data/transport.d.ts +277 -23
- package/dist/data/ws-transport.d.ts +8 -2
- package/dist/data.js +313 -72
- package/dist/graphql/client.d.ts +1 -1
- package/dist/graphql/foir-request.d.ts +1 -1
- package/dist/index.js +0 -9
- package/dist/live/drift.d.ts +9 -10
- package/dist/live.js +1 -1
- package/dist/next.js +158 -37
- package/dist/placement/index.d.ts +129 -0
- package/dist/placement.js +144 -0
- package/dist/push.js +212 -50
- package/dist/react.js +158 -37
- package/dist/server/callback-client.d.ts +30 -9
- package/dist/server/continue-run.d.ts +56 -0
- package/dist/server/index.d.ts +6 -3
- package/dist/server/operation-types.d.ts +114 -65
- package/dist/server/read-dispatch.d.ts +53 -0
- package/dist/server/require-scoped-token.d.ts +71 -7
- package/dist/server/verify-scoped-token.d.ts +109 -3
- package/dist/server.js +566 -138
- package/dist/transport/foir-http.d.ts +103 -5
- package/package.json +24 -6
|
@@ -30,16 +30,17 @@ export interface FoirEnv {
|
|
|
30
30
|
* Returns true when `FOIR_PREVIEW=true` is set in the environment.
|
|
31
31
|
*
|
|
32
32
|
* Preview is selected at the environment level, not per-request: load a
|
|
33
|
-
* preview env file that sets `FOIR_PREVIEW=true` +
|
|
34
|
-
* `
|
|
33
|
+
* preview env file that sets `FOIR_PREVIEW=true` + the preview environment's
|
|
34
|
+
* own `pk_…` key holding `drafts:read` as `PLATFORM_PREVIEW_API_KEY`, instead
|
|
35
|
+
* of the default env.
|
|
35
36
|
* The site-wide mode keeps draft semantics consistent — every loader sees
|
|
36
37
|
* the same preview/published view of the world. Intended for non-production
|
|
37
38
|
* surfaces only.
|
|
38
39
|
*/
|
|
39
40
|
export declare function isFoirPreview(env: FoirEnv): boolean;
|
|
40
41
|
/**
|
|
41
|
-
* The api key a request should carry for this env: the
|
|
42
|
-
* `FOIR_PREVIEW=true` (it is the one with `drafts:read`), the
|
|
42
|
+
* The api key a request should carry for this env: the preview key when
|
|
43
|
+
* `FOIR_PREVIEW=true` (it is the one with `drafts:read`), the production key
|
|
43
44
|
* otherwise. Returns `undefined` when neither is configured, so the caller can
|
|
44
45
|
* raise the "add them to .env" error with its own wording.
|
|
45
46
|
*/
|
|
@@ -210,6 +211,12 @@ export interface FoirRequestOptions {
|
|
|
210
211
|
* (e.g. `myProfile`). Used by `@foir/sdk/auth`'s post-login profile fetch.
|
|
211
212
|
*/
|
|
212
213
|
bearerToken?: string;
|
|
214
|
+
/**
|
|
215
|
+
* Refuse an answer served without the bearer this request sent: throw
|
|
216
|
+
* {@link FoirAuthDowngradedError} instead of returning the anonymous data.
|
|
217
|
+
* `authedRequest` sets it, so it can refresh and ask again.
|
|
218
|
+
*/
|
|
219
|
+
rejectDowngrade?: boolean;
|
|
213
220
|
/**
|
|
214
221
|
* The organization this request acts in, sent as `X-Org-Id`. For a
|
|
215
222
|
* project-scoped key: the server resolves it to an active organization of
|
|
@@ -230,6 +237,12 @@ export interface FoirRequestOptions {
|
|
|
230
237
|
* covers what the request is about.
|
|
231
238
|
*/
|
|
232
239
|
orgId?: string;
|
|
240
|
+
/**
|
|
241
|
+
* The organization this request acts in, named by the address of the page it
|
|
242
|
+
* is for, sent as `X-Org-Url`. See `FoirClientConfig.orgUrl` for the full
|
|
243
|
+
* contract. With `orgId` as well, the two must name the same organization.
|
|
244
|
+
*/
|
|
245
|
+
orgUrl?: string;
|
|
233
246
|
/**
|
|
234
247
|
* Called with the `X-RateLimit-*` headers of every response, successful or
|
|
235
248
|
* not, and with `undefined` when a response carried none.
|
|
@@ -252,7 +265,8 @@ export interface FoirRequestOptions {
|
|
|
252
265
|
* they would end up accepting subtly different things.
|
|
253
266
|
*/
|
|
254
267
|
export type FoirTokenProvider = () => string | null | undefined | Promise<string | null | undefined>;
|
|
255
|
-
|
|
268
|
+
/** A request made with an api key, optionally for a signed-in customer. */
|
|
269
|
+
export interface FoirKeyHeaderInput {
|
|
256
270
|
apiKey: string;
|
|
257
271
|
/**
|
|
258
272
|
* Opt into the draft *schema* channel (`X-Foir-Schema-Channel: draft`) so
|
|
@@ -270,15 +284,75 @@ export interface FoirHeaderInput {
|
|
|
270
284
|
* spells the header in `extra` still wins, because `extra` is applied last.
|
|
271
285
|
*/
|
|
272
286
|
orgId?: string;
|
|
287
|
+
/**
|
|
288
|
+
* The organization the request acts in, named by a page URL (`X-Org-Url`).
|
|
289
|
+
* Set from `FoirClientConfig.orgUrl` / `FoirRequestOptions.orgUrl`.
|
|
290
|
+
*/
|
|
291
|
+
orgUrl?: string;
|
|
292
|
+
/**
|
|
293
|
+
* The address of the visitor a server rendering pages is calling for
|
|
294
|
+
* (`X-Foir-Visitor-IP`). Set from `FoirClientConfig.visitor`.
|
|
295
|
+
*/
|
|
296
|
+
visitorAddress?: string;
|
|
297
|
+
dispatchToken?: never;
|
|
298
|
+
dispatchContext?: never;
|
|
273
299
|
/** Caller-supplied extras, applied last so a caller can override. */
|
|
274
300
|
extra?: Readonly<Record<string, string>>;
|
|
275
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* A request made AS an operation, with the dispatch token its endpoint was
|
|
304
|
+
* handed.
|
|
305
|
+
*
|
|
306
|
+
* The token is the whole credential. api-public takes the scoped path when
|
|
307
|
+
* `X-Foir-Token` is present and never reads `x-api-key` on it, and the
|
|
308
|
+
* organization, the customer and the schema channel all come from the token's
|
|
309
|
+
* own claims, so none of the key-side inputs has anything to say here.
|
|
310
|
+
*/
|
|
311
|
+
export interface FoirDispatchHeaderInput {
|
|
312
|
+
/** Sent as `X-Foir-Token`. */
|
|
313
|
+
dispatchToken: string;
|
|
314
|
+
/** The dispatch's `X-Foir-Context`, forwarded as it arrived. */
|
|
315
|
+
dispatchContext?: string;
|
|
316
|
+
apiKey?: never;
|
|
317
|
+
draftChannel?: never;
|
|
318
|
+
bearerToken?: never;
|
|
319
|
+
orgId?: never;
|
|
320
|
+
orgUrl?: never;
|
|
321
|
+
/**
|
|
322
|
+
* Caller-supplied extras, applied last. They may not name a credential
|
|
323
|
+
* header: see {@link refuseCredentialHeaders}.
|
|
324
|
+
*/
|
|
325
|
+
extra?: Readonly<Record<string, string>>;
|
|
326
|
+
}
|
|
327
|
+
export type FoirHeaderInput = FoirKeyHeaderInput | FoirDispatchHeaderInput;
|
|
328
|
+
/**
|
|
329
|
+
* Refuse extra headers that would add a second credential to a request made as
|
|
330
|
+
* an operation. Throws naming the header; returns nothing when there is none.
|
|
331
|
+
*/
|
|
332
|
+
export declare function refuseCredentialHeaders(extra: Readonly<Record<string, string>> | undefined): void;
|
|
276
333
|
/**
|
|
277
334
|
* The exact header set every Foir data-plane request carries. Single
|
|
278
335
|
* definition on purpose: `/graphql` and `/cdn/graphql` authenticate identically,
|
|
279
336
|
* and a header that exists on one surface but not the other is a per-surface
|
|
280
337
|
* authorization difference nobody would notice until it mattered.
|
|
338
|
+
*
|
|
339
|
+
* Two credentials, and a request carries exactly one: an api key (with its
|
|
340
|
+
* optional customer bearer, organization and channel), or an operation's
|
|
341
|
+
* dispatch token. A dispatch request never writes `x-api-key`.
|
|
342
|
+
*/
|
|
343
|
+
/** The header a server names the visitor it calls for in. */
|
|
344
|
+
export declare const FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
345
|
+
/**
|
|
346
|
+
* The address of the visitor a server rendering pages is calling for, or a
|
|
347
|
+
* function that resolves it for each request.
|
|
348
|
+
*
|
|
349
|
+
* The SDK does not read it off a request, because which request header holds
|
|
350
|
+
* the visitor's real address, and whether a visitor can forge it, depends on
|
|
351
|
+
* what sits in front of your server. Your host says which one its edge sets and
|
|
352
|
+
* strips; that value, and only that, belongs here. A function that yields
|
|
353
|
+
* nothing sends nothing, and the API counts the request as your server's own.
|
|
281
354
|
*/
|
|
355
|
+
export type FoirVisitorSource = string | (() => string | null | undefined | Promise<string | null | undefined>);
|
|
282
356
|
export declare function buildFoirHeaders(input: FoirHeaderInput): Record<string, string>;
|
|
283
357
|
/**
|
|
284
358
|
* Build a `/v1/...` URL from whatever the caller put in `apiUrl`.
|
|
@@ -321,6 +395,29 @@ export interface FoirAuthDowngrade {
|
|
|
321
395
|
/** The authority the request actually ran under. */
|
|
322
396
|
servedAs: string;
|
|
323
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* A request that sent a customer token was served without it.
|
|
400
|
+
*
|
|
401
|
+
* Thrown only to a caller that asked (`rejectDowngrade`): one that sent the
|
|
402
|
+
* token because it wants the signed-in answer, and would otherwise be handed the
|
|
403
|
+
* anonymous one as though nothing had happened. `downgrade.reason` says whether
|
|
404
|
+
* refreshing could help: `expired` can be refreshed, `invalid` and
|
|
405
|
+
* `wrong_project` cannot.
|
|
406
|
+
*/
|
|
407
|
+
export declare class FoirAuthDowngradedError extends FoirRequestError {
|
|
408
|
+
readonly downgrade: FoirAuthDowngrade;
|
|
409
|
+
constructor(downgrade: FoirAuthDowngrade);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* The customer's session is over: its token was rejected and could not be
|
|
413
|
+
* refreshed. It is what a signed-in request gets in place of the anonymous
|
|
414
|
+
* answer. Send the customer to sign in again.
|
|
415
|
+
*/
|
|
416
|
+
export declare class FoirSessionExpiredError extends FoirRequestError {
|
|
417
|
+
constructor(options?: {
|
|
418
|
+
cause?: unknown;
|
|
419
|
+
});
|
|
420
|
+
}
|
|
324
421
|
export interface FoirResponseEnvelope<T> {
|
|
325
422
|
data?: T;
|
|
326
423
|
errors?: Array<FoirGraphQLErrorItem>;
|
|
@@ -334,4 +431,5 @@ export interface FoirResponseEnvelope<T> {
|
|
|
334
431
|
}
|
|
335
432
|
export declare function readFoirEnvelope<T>(json: FoirResponseEnvelope<T>, options?: {
|
|
336
433
|
strict?: boolean;
|
|
434
|
+
rejectDowngrade?: boolean;
|
|
337
435
|
}): T;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foir/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Official Foir SDK — editor iframe client + server-side platform helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -45,6 +45,18 @@
|
|
|
45
45
|
"types": "./dist/auth/index.d.ts",
|
|
46
46
|
"import": "./dist/auth.js"
|
|
47
47
|
},
|
|
48
|
+
"./placement": {
|
|
49
|
+
"types": "./dist/placement/index.d.ts",
|
|
50
|
+
"import": "./dist/placement.js"
|
|
51
|
+
},
|
|
52
|
+
"./app": {
|
|
53
|
+
"types": "./dist/app/index.d.ts",
|
|
54
|
+
"import": "./dist/app.js"
|
|
55
|
+
},
|
|
56
|
+
"./app/contract": {
|
|
57
|
+
"types": "./dist/app/contract/index.d.ts",
|
|
58
|
+
"import": "./dist/app-contract.js"
|
|
59
|
+
},
|
|
48
60
|
"./auth/session": {
|
|
49
61
|
"types": "./dist/auth/session-client.d.ts",
|
|
50
62
|
"import": "./dist/auth-session.js"
|
|
@@ -107,6 +119,7 @@
|
|
|
107
119
|
"@graphql-typed-document-node/core": "3.2.0"
|
|
108
120
|
},
|
|
109
121
|
"peerDependencies": {
|
|
122
|
+
"@bufbuild/protobuf": "^2.12.0",
|
|
110
123
|
"@graphql-codegen/cli": "^5.0.0",
|
|
111
124
|
"@graphql-codegen/client-preset": "^4.0.0",
|
|
112
125
|
"blurhash": "^2.0.0",
|
|
@@ -117,6 +130,9 @@
|
|
|
117
130
|
"yjs": "^13.6.0"
|
|
118
131
|
},
|
|
119
132
|
"peerDependenciesMeta": {
|
|
133
|
+
"@bufbuild/protobuf": {
|
|
134
|
+
"optional": true
|
|
135
|
+
},
|
|
120
136
|
"@graphql-codegen/cli": {
|
|
121
137
|
"optional": true
|
|
122
138
|
},
|
|
@@ -143,6 +159,7 @@
|
|
|
143
159
|
}
|
|
144
160
|
},
|
|
145
161
|
"devDependencies": {
|
|
162
|
+
"@bufbuild/protobuf": "^2.0.0",
|
|
146
163
|
"@graphql-codegen/cli": "^5.0.7",
|
|
147
164
|
"@testing-library/react": "^16.2.0",
|
|
148
165
|
"@types/react": "^19.2.0",
|
|
@@ -159,18 +176,19 @@
|
|
|
159
176
|
"vitest": "^3.2.4",
|
|
160
177
|
"ws": "^8.18.0",
|
|
161
178
|
"y-websocket": "^3.0.0",
|
|
162
|
-
"yjs": "^13.6.30"
|
|
179
|
+
"yjs": "^13.6.30",
|
|
180
|
+
"@foir/proto-ts": "0.109.0"
|
|
163
181
|
},
|
|
164
182
|
"engines": {
|
|
165
183
|
"node": ">=18.0.0"
|
|
166
184
|
},
|
|
167
185
|
"scripts": {
|
|
168
|
-
"build": "node scripts/clean-dist.mjs && tsup && tsc -p tsconfig.build.json && node scripts/assert-export-types.mjs && node scripts/assert-public-exports.mjs",
|
|
186
|
+
"build": "node scripts/generate-app-vocabulary.mjs && node scripts/clean-dist.mjs && tsup && tsc -p tsconfig.build.json && node scripts/bundle-app-contract-types.mjs && node scripts/assert-export-types.mjs && node scripts/assert-public-exports.mjs",
|
|
169
187
|
"snapshot:exports": "node scripts/assert-public-exports.mjs --update",
|
|
170
188
|
"dev": "node scripts/dev-watch.mjs",
|
|
171
|
-
"check-types": "tsc --noEmit",
|
|
172
|
-
"test": "vitest run",
|
|
173
|
-
"test:watch": "vitest watch",
|
|
189
|
+
"check-types": "node scripts/generate-app-vocabulary.mjs && tsc --noEmit",
|
|
190
|
+
"test": "node scripts/generate-app-vocabulary.mjs && vitest run",
|
|
191
|
+
"test:watch": "node scripts/generate-app-vocabulary.mjs && vitest watch",
|
|
174
192
|
"release:patch": "pnpm version patch",
|
|
175
193
|
"release:minor": "pnpm version minor",
|
|
176
194
|
"release:major": "pnpm version major"
|