@foir/sdk 0.5.0 → 0.8.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 +1126 -0
- package/README.md +94 -0
- package/dist/auth/auth.d.ts +243 -0
- package/dist/auth/hosted.d.ts +40 -0
- package/dist/auth/index.d.ts +21 -0
- package/dist/auth/operations.d.ts +144 -0
- package/dist/auth/ops.d.ts +99 -0
- package/dist/auth/session-client.d.ts +76 -0
- package/dist/auth-session.js +137 -0
- package/dist/auth.js +973 -92
- package/dist/chunk-IGNLWT3H.js +35 -0
- package/dist/chunk-OHYVKTGB.js +45 -0
- package/dist/chunk-TAL5H6VA.js +39 -0
- package/dist/client/platform-client.d.ts +29 -0
- package/dist/client.js +275 -37
- package/dist/codegen/block-registry.d.ts +13 -23
- package/dist/codegen/block-registry.js +3 -31
- package/dist/codegen/block-scaffold.d.ts +12 -25
- package/dist/codegen/block-scaffold.js +3 -41
- package/dist/codegen/index.d.ts +12 -61
- package/dist/codegen/index.js +7 -11
- package/dist/codegen/item-registry.d.ts +28 -0
- package/dist/codegen/item-registry.js +7 -0
- package/dist/codegen/item-scaffold.d.ts +29 -0
- package/dist/codegen/item-scaffold.js +7 -0
- package/dist/codegen/preset.d.ts +67 -0
- package/dist/codegen/shared.d.ts +36 -0
- package/dist/components/EditorProvider.d.ts +36 -0
- package/dist/content/content-renderer.d.ts +113 -0
- package/dist/content/index.d.ts +18 -0
- package/dist/content/richtext.d.ts +118 -0
- package/dist/content.js +227 -80
- package/dist/data/builtins.d.ts +506 -0
- package/dist/data/client.d.ts +166 -0
- package/dist/data/document.d.ts +251 -0
- package/dist/data/index.d.ts +73 -0
- package/dist/data/manifest.d.ts +424 -0
- package/dist/data/schema.d.ts +313 -0
- package/dist/data/selection.d.ts +524 -0
- package/dist/data/subjects.d.ts +221 -0
- package/dist/data/subscription-document.d.ts +75 -0
- package/dist/data/subscriptions.d.ts +181 -0
- package/dist/data/transport.d.ts +325 -0
- package/dist/data/ws-transport.d.ts +197 -0
- package/dist/data.js +1683 -0
- package/dist/graphql/cdn-route.d.ts +26 -0
- package/dist/graphql/client.d.ts +30 -0
- package/dist/graphql/content-client.d.ts +66 -0
- package/dist/graphql/foir-request.d.ts +47 -0
- package/dist/hooks/useAutoResize.d.ts +22 -0
- package/dist/hooks/useEditor.d.ts +16 -0
- package/dist/index.d.ts +10 -394
- package/dist/media/BlurhashCanvas.d.ts +10 -0
- package/dist/media/ProgressiveMedia.d.ts +56 -0
- package/dist/media/index.d.ts +18 -0
- package/dist/media/media.d.ts +139 -0
- package/dist/media/screen-capture.d.ts +81 -0
- package/dist/media/useProgressiveLoad.d.ts +23 -0
- package/dist/media.js +102 -0
- package/dist/next/cookieAdapter.d.ts +26 -0
- package/dist/next/index.d.ts +15 -0
- package/dist/next/middleware.d.ts +49 -0
- package/dist/next/session.d.ts +53 -0
- package/dist/next.js +763 -78
- package/dist/protocol/constants.d.ts +6 -0
- package/dist/protocol/messages.d.ts +155 -0
- package/dist/protocol/validation.d.ts +8 -0
- package/dist/push/index.d.ts +14 -0
- package/dist/push/operations.d.ts +22 -0
- package/dist/push/web-push.d.ts +39 -0
- package/dist/push.js +892 -78
- package/dist/react/FoirFeedback.d.ts +62 -0
- package/dist/react/FoirProvider.d.ts +116 -0
- package/dist/react/OAuthButtons.d.ts +20 -0
- package/dist/react/SignIn.d.ts +22 -0
- package/dist/react/UserButton.d.ts +13 -0
- package/dist/react/feedback-notes.d.ts +101 -0
- package/dist/react/index.d.ts +25 -0
- package/dist/react/localStorageAdapter.d.ts +10 -0
- package/dist/react.js +1373 -91
- package/dist/{realtime.d.ts → realtime/index.d.ts} +35 -16
- package/dist/realtime.js +1 -1
- package/dist/server/callback-client.d.ts +92 -0
- package/dist/server/events-client.d.ts +279 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/operation-types.d.ts +134 -0
- package/dist/server/require-scoped-token.d.ts +76 -0
- package/dist/server/server-client.d.ts +34 -0
- package/dist/server/types.d.ts +54 -0
- package/dist/server/verify-scoped-token.d.ts +100 -0
- package/dist/server/webhook-helpers.d.ts +36 -0
- package/dist/server.js +298 -0
- package/dist/transport/foir-http.d.ts +234 -0
- package/dist/transport/persisted-query.d.ts +68 -0
- package/dist/transport/retry.d.ts +61 -0
- package/dist/types/index.d.ts +124 -0
- package/package.json +35 -13
- package/dist/auth.d.ts +0 -382
- package/dist/chunk-ADO5FJNM.js +0 -30
- package/dist/client.d.ts +0 -135
- package/dist/content.d.ts +0 -96
- package/dist/media.d.ts +0 -234
- package/dist/next.d.ts +0 -178
- package/dist/push.d.ts +0 -109
- package/dist/react.d.ts +0 -319
- package/dist/server.d.ts +0 -534
- package/dist/shared-DkWlEd2Z.d.ts +0 -18
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform-owned types `foir types` does NOT generate.
|
|
3
|
+
*
|
|
4
|
+
* Two closed sets live here, and they are here rather than in the generated
|
|
5
|
+
* file for the same reason: they are identical in every project, so generating
|
|
6
|
+
* them per project would emit the same 200 lines into every consumer's repo and
|
|
7
|
+
* give four projects four chances to disagree about `ImageValue`.
|
|
8
|
+
*
|
|
9
|
+
* 1. **Value objects.** `GET /v1/manifest` reports a scalar's `type` as a
|
|
10
|
+
* GraphQL named type, and several of the names it can use are
|
|
11
|
+
* platform-owned objects rather than leaves: `Image`, `Video`, `FileValue`,
|
|
12
|
+
* `Richtext`, `GeoPoint`, `Reference`, `CollabInfo`. They are structurally
|
|
13
|
+
* nested but behave as leaves — always present, never lazily hydrated,
|
|
14
|
+
* never `include`-able.
|
|
15
|
+
*
|
|
16
|
+
* 2. **Primitive members.** A flexible field's union members are *model keys*,
|
|
17
|
+
* except for six lowercase tokens — `richtext`, `image`, `video`, `file`,
|
|
18
|
+
* `reference`, `geopoint` — which are members of the real GraphQL union but
|
|
19
|
+
* have **no entry in `models`**. A generator that resolved every member to
|
|
20
|
+
* `models[key]` would skip them (an inexhaustive union, silently wrong at
|
|
21
|
+
* runtime) or emit `never`.
|
|
22
|
+
*
|
|
23
|
+
* # These are ONE set, not two (TTS-1391)
|
|
24
|
+
*
|
|
25
|
+
* They used to be two: a value object for a plain field, and a separate
|
|
26
|
+
* `<Name>Block` wrapper for the same thing inside a flexible field. TTS-1391
|
|
27
|
+
* removed the wrapper — an item IS its value — so a plain `image` field and an
|
|
28
|
+
* `image` inside a flexible field are the SAME GraphQL type, `Image`. There is
|
|
29
|
+
* no `ImageValue` in a real schema and never will be again; the only difference
|
|
30
|
+
* between the two positions is whether `__typename` was selected.
|
|
31
|
+
*
|
|
32
|
+
* ---------------------------------------------------------------------------
|
|
33
|
+
* The obligation that comes with a static table
|
|
34
|
+
* ---------------------------------------------------------------------------
|
|
35
|
+
*
|
|
36
|
+
* This IS a mirror of a platform-side table, and mirrors drift. What keeps it
|
|
37
|
+
* honest is that the generator refuses to guess: a union member key that is
|
|
38
|
+
* neither in `models` nor in this table makes `foir types` fail loudly naming
|
|
39
|
+
* the member, rather than emitting a union that silently lacks a branch. So the
|
|
40
|
+
* failure mode of drift is a red build with the missing name in it.
|
|
41
|
+
*
|
|
42
|
+
* `apps/cli/src/lib/__tests__/types-codegen.test.ts` asserts that against both
|
|
43
|
+
* committed live-captured manifests.
|
|
44
|
+
*
|
|
45
|
+
* Field shapes verified against deployed dev's introspection on 2026-07-28 and
|
|
46
|
+
* against `api-public/internal/schema/field_mapping.go`.
|
|
47
|
+
*/
|
|
48
|
+
import type { FoirConnection } from './schema';
|
|
49
|
+
/** A `JSON` scalar. Deliberately not `any`: reading through it needs a check. */
|
|
50
|
+
export type FoirJsonValue = string | number | boolean | null | FoirJsonValue[] | {
|
|
51
|
+
[key: string]: FoirJsonValue;
|
|
52
|
+
};
|
|
53
|
+
export type ImageFormat = 'WEBP' | 'AVIF' | 'JPEG' | 'PNG';
|
|
54
|
+
/** One rendition of an image at a named tier, in a single format. */
|
|
55
|
+
export interface ImageVariant {
|
|
56
|
+
size: string;
|
|
57
|
+
format: ImageFormat;
|
|
58
|
+
url: string;
|
|
59
|
+
width: number | null;
|
|
60
|
+
height: number | null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The curated tier set for one format. `_2xl`/`_3xl`/`_4xl` carry the
|
|
64
|
+
* underscore because a GraphQL identifier cannot start with a digit.
|
|
65
|
+
*/
|
|
66
|
+
export interface ImageVariantSet {
|
|
67
|
+
thumbnail: ImageVariant | null;
|
|
68
|
+
small: ImageVariant | null;
|
|
69
|
+
medium: ImageVariant | null;
|
|
70
|
+
large: ImageVariant | null;
|
|
71
|
+
xlarge: ImageVariant | null;
|
|
72
|
+
_2xl: ImageVariant | null;
|
|
73
|
+
_3xl: ImageVariant | null;
|
|
74
|
+
_4xl: ImageVariant | null;
|
|
75
|
+
}
|
|
76
|
+
export interface Image {
|
|
77
|
+
/**
|
|
78
|
+
* `String` on the wire, not `String!`.
|
|
79
|
+
*
|
|
80
|
+
* It was non-null here and nullable on the flexible-field member until
|
|
81
|
+
* TTS-1391, which is a §5.3.2 response-shape conflict the moment both are one
|
|
82
|
+
* type. The platform relaxed rather than tightened, because nullable is the
|
|
83
|
+
* honest form — its own public `File` asset type already declares
|
|
84
|
+
* `url: String` — so this follows.
|
|
85
|
+
*/
|
|
86
|
+
__typename?: 'Image';
|
|
87
|
+
url: string | null;
|
|
88
|
+
width: number | null;
|
|
89
|
+
height: number | null;
|
|
90
|
+
alt: string | null;
|
|
91
|
+
blurhash: string | null;
|
|
92
|
+
dominantColor: string | null;
|
|
93
|
+
mimeType: string | null;
|
|
94
|
+
fileSize: number | null;
|
|
95
|
+
focalPoint: FoirJsonValue | null;
|
|
96
|
+
crop: FoirJsonValue | null;
|
|
97
|
+
variants: ImageVariantSet | null;
|
|
98
|
+
/** WebP tier set (universal support). Pair with `avif` in a `<picture>`. */
|
|
99
|
+
webp: ImageVariantSet | null;
|
|
100
|
+
/** AVIF tier set (smaller, narrower support). The preferred `<picture>` source. */
|
|
101
|
+
avif: ImageVariantSet | null;
|
|
102
|
+
}
|
|
103
|
+
export interface Video {
|
|
104
|
+
__typename?: 'Video';
|
|
105
|
+
url: string | null;
|
|
106
|
+
thumbnailUrl: string | null;
|
|
107
|
+
previewUrl: string | null;
|
|
108
|
+
hlsManifestUrl: string | null;
|
|
109
|
+
width: number | null;
|
|
110
|
+
height: number | null;
|
|
111
|
+
duration: number | null;
|
|
112
|
+
blurhash: string | null;
|
|
113
|
+
dominantColor: string | null;
|
|
114
|
+
mimeType: string | null;
|
|
115
|
+
fileSize: number | null;
|
|
116
|
+
}
|
|
117
|
+
export interface FileValue {
|
|
118
|
+
__typename?: 'FileValue';
|
|
119
|
+
url: string | null;
|
|
120
|
+
filename: string | null;
|
|
121
|
+
mimeType: string | null;
|
|
122
|
+
size: number | null;
|
|
123
|
+
}
|
|
124
|
+
export interface GeoPoint {
|
|
125
|
+
__typename?: 'GeoPoint';
|
|
126
|
+
lat: number | null;
|
|
127
|
+
lng: number | null;
|
|
128
|
+
}
|
|
129
|
+
export interface Richtext {
|
|
130
|
+
__typename?: 'Richtext';
|
|
131
|
+
html: string | null;
|
|
132
|
+
json: FoirJsonValue | null;
|
|
133
|
+
markdown: string | null;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* A reference whose target model is not pinned by the field's declaration.
|
|
137
|
+
*
|
|
138
|
+
* A reference field that declares ONE `referenceTypes` entry resolves to that
|
|
139
|
+
* model's own type, and one declaring several resolves to a `<Members>Ref`
|
|
140
|
+
* union — so this type is what an undeclared reference resolves to, and what a
|
|
141
|
+
* flexible field's `reference` member is.
|
|
142
|
+
*
|
|
143
|
+
* # What makes it followable
|
|
144
|
+
*
|
|
145
|
+
* `_targetModelKey` names the model and `_targetId` OR `_targetNaturalKey`
|
|
146
|
+
* identifies the row within it: `foir[_targetModelKey].findUnique({ where: { _id:
|
|
147
|
+
* _targetId } })`. Both identifier shapes are live — on the dev database 101 of
|
|
148
|
+
* 193 stored reference values carry an id and 92 carry a natural key and no id
|
|
149
|
+
* — so a consumer must handle either.
|
|
150
|
+
*
|
|
151
|
+
* `_id` and `_type` are NOT here, and their absence is the TTS-1391 correction
|
|
152
|
+
* rather than an omission. They were the wrapper's own metadata: `_id` was the
|
|
153
|
+
* ITEM's id (never the target's, which is why `_targetId` exists separately) and
|
|
154
|
+
* `_type` was the union discriminator, reading `"reference"` on every reference
|
|
155
|
+
* ever stored. The wrapper is gone, so neither field is in the schema, and a
|
|
156
|
+
* consumer selecting them got `undefined` while the types promised a string.
|
|
157
|
+
*/
|
|
158
|
+
export interface Reference<TRecord = FoirUnexpandedRecord> {
|
|
159
|
+
__typename?: 'Reference';
|
|
160
|
+
/** The model to fetch the target from. Null when the target no longer exists. */
|
|
161
|
+
_targetModelKey: string | null;
|
|
162
|
+
/** The target's id, when the reference stored one. */
|
|
163
|
+
_targetId: string | null;
|
|
164
|
+
/** The target's natural key, when the reference names its target that way. */
|
|
165
|
+
_targetNaturalKey: string | null;
|
|
166
|
+
/**
|
|
167
|
+
* The resolved target, over the project's `Record` union — every model in the
|
|
168
|
+
* project. Left as a type parameter so the generated file can name that
|
|
169
|
+
* union; the default is the honest answer for a consumer who has not narrowed
|
|
170
|
+
* it.
|
|
171
|
+
*/
|
|
172
|
+
record: TRecord | null;
|
|
173
|
+
/** A stored SPELLING of the model. Read as an input to `_targetModelKey`. */
|
|
174
|
+
_schema: string | null;
|
|
175
|
+
naturalKey: string | null;
|
|
176
|
+
_preview: FoirJsonValue | null;
|
|
177
|
+
}
|
|
178
|
+
/** Inline media embedded in a content field — polymorphic per `mediaType`. */
|
|
179
|
+
export interface MediaValue {
|
|
180
|
+
mediaType: string | null;
|
|
181
|
+
url: string | null;
|
|
182
|
+
mimeType: string | null;
|
|
183
|
+
fileId: string | null;
|
|
184
|
+
fileSize: number | null;
|
|
185
|
+
filename: string | null;
|
|
186
|
+
width: number | null;
|
|
187
|
+
height: number | null;
|
|
188
|
+
blurhash: string | null;
|
|
189
|
+
dominantColor: string | null;
|
|
190
|
+
duration: number | null;
|
|
191
|
+
hlsManifestUrl: string | null;
|
|
192
|
+
previewUrl: string | null;
|
|
193
|
+
thumbnailUrl: string | null;
|
|
194
|
+
variants: FoirJsonValue | null;
|
|
195
|
+
crop: FoirJsonValue | null;
|
|
196
|
+
focalPoint: FoirJsonValue | null;
|
|
197
|
+
}
|
|
198
|
+
/** Collaborative-editing discovery. */
|
|
199
|
+
export interface CollabInfo {
|
|
200
|
+
enabled: boolean;
|
|
201
|
+
wsUrl: string;
|
|
202
|
+
roomName: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* `Image`, `Video`, `FileValue`, `Richtext`, `GeoPoint` and `Reference` above
|
|
206
|
+
* ARE the members. There is no separate item type, because an item is its
|
|
207
|
+
* value: `... on Image { url }` means the same thing inside a flexible field
|
|
208
|
+
* and at a plain `image` field, and one fragment reads it in both positions.
|
|
209
|
+
*
|
|
210
|
+
* `__typename` is optional on each, matching what graphql-codegen emits: it is
|
|
211
|
+
* present whenever selected, and the op compiler always selects it on a union's
|
|
212
|
+
* own selection set — it is the one field no `omit` path can name. Narrowing on
|
|
213
|
+
* it is exact regardless, because the literal types are disjoint.
|
|
214
|
+
*
|
|
215
|
+
* `_type` was the second discriminant and is gone with the envelope. It was
|
|
216
|
+
* never protected from `omit` anyway, so `__typename` was always the one to
|
|
217
|
+
* prefer.
|
|
218
|
+
*
|
|
219
|
+
* The three wrappers below have no replacement, because the things they
|
|
220
|
+
* described no longer exist:
|
|
221
|
+
*
|
|
222
|
+
* - `MediaBlock` — `media` stopped being a primitive at TTS-1391. It was
|
|
223
|
+
* never in the proto FieldType enum, and it is the one primitive name a
|
|
224
|
+
* project had also used as a model key.
|
|
225
|
+
* - `TextBlock` — TTS-1406 removed it. GraphQL requires a union member to be
|
|
226
|
+
* an object type and a text item's value is a bare scalar, which is the
|
|
227
|
+
* whole reason a wrapper existed. An inline model with a text field is how
|
|
228
|
+
* a text slot is authored.
|
|
229
|
+
* - `FlexibleBlock` — there is no fallback member. An item whose `_type`
|
|
230
|
+
* names nothing is nulled out of its list and reported, which is what a
|
|
231
|
+
* stored null already read as.
|
|
232
|
+
*/
|
|
233
|
+
/** @deprecated Renamed to {@link Image}. The wrapper is gone; this IS the image. */
|
|
234
|
+
export type ImageBlock = Image;
|
|
235
|
+
/** @deprecated Renamed to {@link Video}. */
|
|
236
|
+
export type VideoBlock = Video;
|
|
237
|
+
/** @deprecated Renamed to {@link FileValue}. */
|
|
238
|
+
export type FileBlock = FileValue;
|
|
239
|
+
/** @deprecated Renamed to {@link Richtext}. */
|
|
240
|
+
export type RichtextBlock = Richtext;
|
|
241
|
+
/** @deprecated Renamed to {@link GeoPoint}. */
|
|
242
|
+
export type GeoPointValue = GeoPoint;
|
|
243
|
+
/** @deprecated Renamed to {@link Image}. */
|
|
244
|
+
export type ImageValue = Image;
|
|
245
|
+
/** @deprecated Renamed to {@link Video}. */
|
|
246
|
+
export type VideoValue = Video;
|
|
247
|
+
/** @deprecated Renamed to {@link Richtext}. */
|
|
248
|
+
export type RichtextValue = Richtext;
|
|
249
|
+
/** @deprecated Renamed to {@link Reference}. */
|
|
250
|
+
export type ReferenceValue<TRecord = FoirUnexpandedRecord> = Reference<TRecord>;
|
|
251
|
+
/** @deprecated Renamed to {@link Reference}. */
|
|
252
|
+
export type ReferenceBlock<TRecord = FoirUnexpandedRecord> = Reference<TRecord>;
|
|
253
|
+
/**
|
|
254
|
+
* What a nested union or a re-entered object resolves to.
|
|
255
|
+
*
|
|
256
|
+
* The op compiler enforces a termination rule — *never expand a type, object or
|
|
257
|
+
* union, that is already on the current path; degrade it to `__typename`
|
|
258
|
+
* instead* — because `Block` contains members that contain `Block`, and a
|
|
259
|
+
* `tilly_page.blocks → TillyPageBlock.blocks` cycle is reachable in two hops
|
|
260
|
+
* with no `include` and no caller input at all. At a degraded position the
|
|
261
|
+
* response carries `__typename` and nothing else, so that is what the generated
|
|
262
|
+
* type says.
|
|
263
|
+
*
|
|
264
|
+
* # Reading past one
|
|
265
|
+
*
|
|
266
|
+
* A stopping point in front of a UNION can be spent a level on:
|
|
267
|
+
* `foir.product.findMany({ depth: 2 })` expands one more, and the response then
|
|
268
|
+
* carries that union's members here rather than `__typename` alone. A level
|
|
269
|
+
* costs one more expansion at each stopping point inside the expansion — so it
|
|
270
|
+
* compounds where there are several, and costs nothing per union MEMBER — and
|
|
271
|
+
* the call is refused client-side, with the arithmetic, when it would cross the
|
|
272
|
+
* server's field ceiling. See `FoirNamespaceCall.depth`.
|
|
273
|
+
*
|
|
274
|
+
* The TYPE follows the level. A generated composite on the path to a stopping
|
|
275
|
+
* point takes a `<D>` level parameter (see `FoirDepthLevel`), so at `depth: 2`
|
|
276
|
+
* the position resolves to the expanded union — `<Field>Blocks`, the type
|
|
277
|
+
* exported beside the `<Field>BlocksDegraded` named here — rather than to this
|
|
278
|
+
* one. The caller narrows on `__typename` and reads the member's own field; no
|
|
279
|
+
* cast, and no hand-written widening.
|
|
280
|
+
*
|
|
281
|
+
* `D` defaults to `1`, so a call passing no `depth` reaches THIS type at the
|
|
282
|
+
* stopping point exactly as it always did.
|
|
283
|
+
*
|
|
284
|
+
* A stopping point that is a composite CYCLE rather than a union — `menu.items
|
|
285
|
+
* .children` — has no union to expand and stays `__typename` at every depth.
|
|
286
|
+
*/
|
|
287
|
+
export interface FoirDegraded<TTypename extends string = string> {
|
|
288
|
+
__typename: TTypename;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* The union levels the generated types describe — the type-level half of
|
|
292
|
+
* `depth`.
|
|
293
|
+
*
|
|
294
|
+
* A generated composite on the path to a stopping point takes one of these as
|
|
295
|
+
* a parameter, defaulted to `1`, and the model's shape at each level is named
|
|
296
|
+
* under `FoirModelDefinition.atDepth`. `1` is the complete-by-default document
|
|
297
|
+
* and is what every existing reference to a generated interface resolves to,
|
|
298
|
+
* which is why adding the parameter changed no result type.
|
|
299
|
+
*
|
|
300
|
+
* Four because depth is a budget against the server's 10,000-field ceiling and
|
|
301
|
+
* four is past what a generated module usefully describes: on the live dev
|
|
302
|
+
* project `product.findUnique` measures 318 fields at the default, 468 at 2 and
|
|
303
|
+
* 618 at 3. Those levels all fit since TTS-1448 — they read 1,203 / 4,308 /
|
|
304
|
+
* 13,623 when a union position was charged for every member — so the bound here
|
|
305
|
+
* is about how much generated type surface is worth emitting, not about what
|
|
306
|
+
* the server will run. A call
|
|
307
|
+
* asking for more is not an error: `atDepth` simply has no entry, the result
|
|
308
|
+
* falls back to the default shape, and the caller narrows by hand exactly as
|
|
309
|
+
* they did before. Under-promising is the safe direction, and the wire is
|
|
310
|
+
* unaffected — the printer's budget is a runtime number with no cap here.
|
|
311
|
+
*/
|
|
312
|
+
export type FoirDepthLevel = 1 | 2 | 3 | 4;
|
|
313
|
+
/**
|
|
314
|
+
* `FoirDepthBelow[3] === 2` — one union level spent.
|
|
315
|
+
*
|
|
316
|
+
* Mirrors the printer's `depth - 1` at an expanded stopping point
|
|
317
|
+
* (`document.ts`, `printFields`), and lives beside `FoirDepthLevel` so the two
|
|
318
|
+
* cannot disagree about how many levels exist.
|
|
319
|
+
*/
|
|
320
|
+
export type FoirDepthBelow = [never, never, 1, 2, 3];
|
|
321
|
+
/** A `Record`-union value the compiler did not expand. See `FoirDegraded`. */
|
|
322
|
+
export type FoirUnexpandedRecord = FoirDegraded;
|
|
323
|
+
/**
|
|
324
|
+
* The results of the ops that do NOT return the model.
|
|
325
|
+
*
|
|
326
|
+
* `foir types` classifies these as `opaque`, so the client offers no
|
|
327
|
+
* `include` / `omit` for them and uses the type below verbatim. They are here
|
|
328
|
+
* for the same reason the value objects are: platform-owned, fixed, and
|
|
329
|
+
* identical in every project.
|
|
330
|
+
*/
|
|
331
|
+
export type FoirVisibility = 'PRIVATE' | 'PUBLIC';
|
|
332
|
+
export type FoirAccessLevel = 'READ' | 'WRITE' | 'DELETE';
|
|
333
|
+
export type FoirAccessSource = 'PUBLIC' | 'OWNER' | 'GRANT' | 'ROLE';
|
|
334
|
+
export type FoirGranteeKind = 'CUSTOMER' | 'ADMIN';
|
|
335
|
+
/** `myAccess` — this principal's effective access to one record. */
|
|
336
|
+
export interface FoirMyAccess {
|
|
337
|
+
access: FoirAccessLevel;
|
|
338
|
+
source: FoirAccessSource;
|
|
339
|
+
}
|
|
340
|
+
export interface FoirAccessGrant {
|
|
341
|
+
access: FoirAccessLevel;
|
|
342
|
+
createdAt: string;
|
|
343
|
+
granteeId: string;
|
|
344
|
+
granteeKind: FoirGranteeKind;
|
|
345
|
+
}
|
|
346
|
+
export interface FoirAccessor {
|
|
347
|
+
access: FoirAccessLevel;
|
|
348
|
+
granteeId: string;
|
|
349
|
+
granteeKind: FoirGranteeKind;
|
|
350
|
+
source: FoirAccessSource;
|
|
351
|
+
}
|
|
352
|
+
/** `access` — the full ACL of one record. */
|
|
353
|
+
export interface FoirResourceAccess {
|
|
354
|
+
accessors: FoirAccessor[];
|
|
355
|
+
grants: FoirAccessGrant[];
|
|
356
|
+
public: boolean;
|
|
357
|
+
visibility: FoirVisibility;
|
|
358
|
+
}
|
|
359
|
+
/** `updateMany` — how many rows moved, and which. */
|
|
360
|
+
export interface FoirUpdateManyPayload {
|
|
361
|
+
count: number;
|
|
362
|
+
ids: string[];
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* What a note is ABOUT — the closed set the platform's write path enforces.
|
|
366
|
+
*
|
|
367
|
+
* On the wire it is `String!`, not a GraphQL enum, so the manifest publishes no
|
|
368
|
+
* `enumValues` for it and `foir types` cannot generate this union. It is here
|
|
369
|
+
* for the same reason the value objects are: platform-owned, identical in every
|
|
370
|
+
* project, and otherwise hand-written once per consumer.
|
|
371
|
+
*
|
|
372
|
+
* It is never a MODEL KEY. Model keys are the project's to choose, so a project
|
|
373
|
+
* with a model keyed `file` would make one value mean two things; a note on a
|
|
374
|
+
* record is `'record'` plus the model key on `subjectModelKey`.
|
|
375
|
+
*
|
|
376
|
+
* This mirrors a platform-side set, and the obligation that comes with a mirror
|
|
377
|
+
* (see the file header) applies: a kind added there widens this union in a
|
|
378
|
+
* minor release, and until then a value outside it is a row no writer could
|
|
379
|
+
* have produced.
|
|
380
|
+
*/
|
|
381
|
+
export type FoirNoteSubjectType = 'record' | 'file' | 'model' | 'operation' | 'schedule' | 'customer';
|
|
382
|
+
/**
|
|
383
|
+
* One file the customer submitted with their note.
|
|
384
|
+
*
|
|
385
|
+
* The file's own facts, and nothing about the platform's handling of it: when
|
|
386
|
+
* the link row was written and which retention clock it starts are not the
|
|
387
|
+
* author's business. What is here is enough to render "you sent this".
|
|
388
|
+
*/
|
|
389
|
+
export interface FoirCustomerNoteAttachment {
|
|
390
|
+
/** The file's id — the same one `createMyNote` was given. */
|
|
391
|
+
fileId: string;
|
|
392
|
+
filename: string;
|
|
393
|
+
mimeType: string;
|
|
394
|
+
/** Size in bytes, as stored. Authoritative; not the number the uploader declared. */
|
|
395
|
+
size: number;
|
|
396
|
+
url: string;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* One note on the customer plane — `foir.ns.notes.myNotes` and
|
|
400
|
+
* `foir.ns.notes.createMyNote`.
|
|
401
|
+
*
|
|
402
|
+
* Deliberately narrower than the note your team sees in the dashboard. There is
|
|
403
|
+
* no visibility, no resolution note, no mentions and no metadata: those are the
|
|
404
|
+
* project's view of a thread, not the author's, and the customer-facing type
|
|
405
|
+
* does not merely omit them from the document — the surface does not carry them.
|
|
406
|
+
*/
|
|
407
|
+
export interface FoirCustomerNote {
|
|
408
|
+
id: string;
|
|
409
|
+
/** The note's prose. */
|
|
410
|
+
body: string | null;
|
|
411
|
+
subjectType: FoirNoteSubjectType;
|
|
412
|
+
subjectId: string;
|
|
413
|
+
/** The record's model key. Present only on a `record` subject. */
|
|
414
|
+
subjectModelKey: string | null;
|
|
415
|
+
/** Set when this note is a reply within a thread. */
|
|
416
|
+
parentNoteId: string | null;
|
|
417
|
+
threadRootId: string | null;
|
|
418
|
+
isResolved: boolean;
|
|
419
|
+
/**
|
|
420
|
+
* The files submitted with this note. Empty when there are none.
|
|
421
|
+
*
|
|
422
|
+
* The one place the customer surface is WIDER than "the author's own prose",
|
|
423
|
+
* and deliberately: an author who attached a screenshot has to be able to see
|
|
424
|
+
* that it arrived, or the only readings left to them are that it failed
|
|
425
|
+
* silently or that it was taken away.
|
|
426
|
+
*/
|
|
427
|
+
attachments: FoirCustomerNoteAttachment[];
|
|
428
|
+
/** ISO-8601. */
|
|
429
|
+
createdAt: string;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* What a note surface has to know about the project before it renders anything
|
|
433
|
+
* — `foir.ns.notes.noteConfig`.
|
|
434
|
+
*
|
|
435
|
+
* Charged `notes:write` rather than `notes:read`, and refused to a caller with
|
|
436
|
+
* no customer session, because the only caller it is useful to is one that can
|
|
437
|
+
* submit. So a composer can read it as its single readiness probe: an answer
|
|
438
|
+
* means this visitor may write, and a refusal names which half is missing.
|
|
439
|
+
*/
|
|
440
|
+
export interface FoirNoteConfig {
|
|
441
|
+
/**
|
|
442
|
+
* Whether this project's note surfaces may OFFER to capture a screenshot.
|
|
443
|
+
*
|
|
444
|
+
* It gates the offer, not the storing. Capture stays opt-in per submission —
|
|
445
|
+
* the browser's own picker is the consent gate and it cannot be bypassed.
|
|
446
|
+
*/
|
|
447
|
+
screenshotsEnabled: boolean;
|
|
448
|
+
}
|
|
449
|
+
/** `myNotes` arguments. Forward-only cursor pagination, like every list read. */
|
|
450
|
+
export interface FoirMyNotesArgs {
|
|
451
|
+
first?: number;
|
|
452
|
+
after?: string;
|
|
453
|
+
/** Narrow to one kind of subject. */
|
|
454
|
+
subjectType?: FoirNoteSubjectType;
|
|
455
|
+
/** Narrow to one subject — a record id, a file id, a model key. */
|
|
456
|
+
subjectId?: string;
|
|
457
|
+
/** Read one thread. Returns only the notes in it this caller can see. */
|
|
458
|
+
threadRootId?: string;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* `myNotes` — the notes this customer wrote, plus the replies your team
|
|
462
|
+
* deliberately sent them. Never another customer's, and never an operator's
|
|
463
|
+
* internal ones.
|
|
464
|
+
*/
|
|
465
|
+
export type FoirMyNotesResult = FoirConnection<FoirCustomerNote>;
|
|
466
|
+
/** `createMyNote` arguments. */
|
|
467
|
+
export interface FoirCreateMyNoteArgs {
|
|
468
|
+
subjectType: FoirNoteSubjectType;
|
|
469
|
+
subjectId: string;
|
|
470
|
+
/** Required when `subjectType` is `'record'`, refused otherwise. */
|
|
471
|
+
subjectModelKey?: string;
|
|
472
|
+
body: string;
|
|
473
|
+
/** Reply within an existing thread. */
|
|
474
|
+
parentNoteId?: string;
|
|
475
|
+
/**
|
|
476
|
+
* Files to submit with this note, already uploaded through
|
|
477
|
+
* `createFileUpload` / `confirmFileUpload`.
|
|
478
|
+
*
|
|
479
|
+
* A file this caller cannot read is REFUSED and the note is not written. That
|
|
480
|
+
* is not a rough edge to code around: a create that accepted the note and
|
|
481
|
+
* dropped the attachment would leave the author believing they sent a
|
|
482
|
+
* screenshot nobody has.
|
|
483
|
+
*/
|
|
484
|
+
attachmentFileIds?: string[];
|
|
485
|
+
}
|
|
486
|
+
export type FoirDeviceType = 'DESKTOP' | 'MOBILE' | 'TABLET';
|
|
487
|
+
export type FoirPlatformType = 'IOS' | 'ANDROID' | 'WEB';
|
|
488
|
+
/**
|
|
489
|
+
* The `contexts` argument on every typed read.
|
|
490
|
+
*
|
|
491
|
+
* **It is an object, not a list**, despite the plural name — `ResolveContextInput`
|
|
492
|
+
* on the wire, verified against deployed dev's introspection on both projects:
|
|
493
|
+
* `contexts: ResolveContextInput` with no LIST wrapper, four optional fields.
|
|
494
|
+
*
|
|
495
|
+
* Worth the paragraph because the plural reads like `string[]` and typing it
|
|
496
|
+
* that way compiles fine and then sends a body the server rejects. The
|
|
497
|
+
* `FoirSchemaDefinition` sketch in `schema.ts` had exactly that error, and it
|
|
498
|
+
* was found by the docs lane running the call rather than by anyone reading the
|
|
499
|
+
* contract.
|
|
500
|
+
*/
|
|
501
|
+
export interface FoirResolveContext {
|
|
502
|
+
device?: FoirDeviceType | null;
|
|
503
|
+
locale?: string | null;
|
|
504
|
+
platform?: FoirPlatformType | null;
|
|
505
|
+
preview?: boolean | null;
|
|
506
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createFoirClient` — the typed data client.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { createFoirClient } from '@foir/sdk/data';
|
|
6
|
+
* import * as generated from './foir.generated';
|
|
7
|
+
*
|
|
8
|
+
* const foir = createFoirClient({
|
|
9
|
+
* apiUrl: process.env.PLATFORM_API_URL!,
|
|
10
|
+
* apiKey: process.env.PLATFORM_API_KEY!,
|
|
11
|
+
* generated,
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* const block = await foir.tilly_block.findUnique({
|
|
15
|
+
* id: 'a3pr8x0j10rigx8u6t1xthbr',
|
|
16
|
+
* include: { noteId: true },
|
|
17
|
+
* omit: ['aiSummary'],
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* One import and one call. `FoirSchema` is inferred from the module rather than
|
|
22
|
+
* restated, and the four runtime exports the client reads are read off it rather
|
|
23
|
+
* than named one at a time at every call site — see `resolveGeneratedConfig`
|
|
24
|
+
* below for why that indirection is worth having.
|
|
25
|
+
*
|
|
26
|
+
* The individual fields (`schemaVersion`, `selections`, `namespaceSelections`,
|
|
27
|
+
* `selectionUnions`) still work, and still win where both are given.
|
|
28
|
+
*
|
|
29
|
+
* The call is the same call it always was. What changed underneath is what goes
|
|
30
|
+
* on the wire: the client loads that op's selection tree, applies
|
|
31
|
+
* `omit`/`pick`/`include`, prints a GraphQL document, hashes it and sends the
|
|
32
|
+
* hash. See `document.ts` for the printer and `transport.ts` for the routing.
|
|
33
|
+
*
|
|
34
|
+
* ---------------------------------------------------------------------------
|
|
35
|
+
* Why this is a Proxy
|
|
36
|
+
* ---------------------------------------------------------------------------
|
|
37
|
+
*
|
|
38
|
+
* `FoirSchema` is a TYPE. `foir types` emits interfaces, and interfaces do not
|
|
39
|
+
* exist at runtime, so the client is handed the full shape of the API at
|
|
40
|
+
* compile time and nothing at all at run time. There is no model list to build
|
|
41
|
+
* an object from — `foir.tilly_block.findMany` has to be resolved when it is
|
|
42
|
+
* reached. A two-level `Proxy` does that in a few lines and costs one property
|
|
43
|
+
* lookup per call.
|
|
44
|
+
*
|
|
45
|
+
* ---------------------------------------------------------------------------
|
|
46
|
+
* Why `selections` is a config field and not a fetch
|
|
47
|
+
* ---------------------------------------------------------------------------
|
|
48
|
+
*
|
|
49
|
+
* The trees ARE runtime data — that is the one thing the generated module has to
|
|
50
|
+
* carry across the type/value boundary, alongside `foirSchemaVersion`. Passing
|
|
51
|
+
* them in costs nothing and is the intended path.
|
|
52
|
+
*
|
|
53
|
+
* When they are absent the client fetches `GET /v1/manifest` once, at first use,
|
|
54
|
+
* and reads the same trees off it. That is a FALLBACK and never the default,
|
|
55
|
+
* for the reason this file has always given: it puts a round trip in front of
|
|
56
|
+
* the first query on every cold edge invocation, to learn something the
|
|
57
|
+
* generated file already knows. What it buys is that `@foir/sdk/data` still
|
|
58
|
+
* works with zero codegen, which keeps the getting-started path honest.
|
|
59
|
+
*
|
|
60
|
+
* It has to stay reachable, and it must not be reachable by ACCIDENT. Those
|
|
61
|
+
* pull in opposite directions and the split is: a client built with no generated
|
|
62
|
+
* module and no `selections` has chosen the fallback, and says so once; a client
|
|
63
|
+
* built with a `generated` module that cannot serve the printer is refused where
|
|
64
|
+
* it was constructed. There is no input that quietly produces the round trip.
|
|
65
|
+
*
|
|
66
|
+
* ---------------------------------------------------------------------------
|
|
67
|
+
* Why namespaces live under `.ns`
|
|
68
|
+
* ---------------------------------------------------------------------------
|
|
69
|
+
*
|
|
70
|
+
* Since there is no runtime schema (above), the client cannot tell whether
|
|
71
|
+
* `foir.search` means a model or a namespace. So the addressing carries it:
|
|
72
|
+
* `foir.<model>.<op>()` addresses a model, `foir.ns.<namespace>.<op>()` a
|
|
73
|
+
* namespace. `ns` and `raw` are therefore reserved top-level names.
|
|
74
|
+
*/
|
|
75
|
+
import type { FoirModelDefinition, FoirNamespaceOpDefinition, FoirOpDefinition, FoirSchemaDefinition } from './schema';
|
|
76
|
+
import type { FoirModelOperation, FoirNamespaceOperation } from './selection';
|
|
77
|
+
import { type FoirEnv } from '../transport/foir-http';
|
|
78
|
+
import { type FoirClientConfig, type FoirGeneratedModule } from './transport';
|
|
79
|
+
import { type FoirModelSubscriptions, type FoirNamespaceSubscriptions } from './subscriptions';
|
|
80
|
+
/**
|
|
81
|
+
* The ops available on one model for this credential, plus its subscriptions.
|
|
82
|
+
*
|
|
83
|
+
* `onChange` is on every model rather than gated on a generated flag, and that
|
|
84
|
+
* is correct rather than lazy: `recordChanged` charges
|
|
85
|
+
* `records:read:<modelKey>`, which is the SAME scope that put the model in
|
|
86
|
+
* `FoirSchema` at all. A model this credential cannot watch is a model it
|
|
87
|
+
* cannot read, so it is already absent from `S['models']` and the accessor
|
|
88
|
+
* cannot be reached.
|
|
89
|
+
*/
|
|
90
|
+
export type FoirModelClient<S extends FoirSchemaDefinition, M extends FoirModelDefinition> = {
|
|
91
|
+
[OK in keyof M['ops']]: M['ops'][OK] extends FoirOpDefinition ? FoirModelOperation<S, M, M['ops'][OK]> : never;
|
|
92
|
+
} & FoirModelSubscriptions;
|
|
93
|
+
export type FoirNamespaceClient<N extends {
|
|
94
|
+
ops: object;
|
|
95
|
+
}> = {
|
|
96
|
+
[OK in keyof N['ops']]: N['ops'][OK] extends FoirNamespaceOpDefinition ? FoirNamespaceOperation<N['ops'][OK]> : never;
|
|
97
|
+
};
|
|
98
|
+
export interface FoirClientCore<S extends FoirSchemaDefinition> {
|
|
99
|
+
/**
|
|
100
|
+
* Non-model root namespaces (`search`, `customers`, `notifications`).
|
|
101
|
+
* Separate accessor because the addressing has to distinguish a model from a
|
|
102
|
+
* namespace and the client has no runtime schema to disambiguate with.
|
|
103
|
+
*/
|
|
104
|
+
readonly ns: {
|
|
105
|
+
[NK in keyof S['namespaces']]: FoirNamespaceClient<S['namespaces'][NK]> & FoirNamespaceSubscriptions<NK>;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Untyped passthrough — a raw GraphQL document, executed on this credential.
|
|
109
|
+
*
|
|
110
|
+
* It changed meaning with the transport, and deliberately so. It used to take
|
|
111
|
+
* a JSON op because that was the wire; now the wire is GraphQL, and an escape
|
|
112
|
+
* hatch that could only say what the JSON op could say would be an escape from
|
|
113
|
+
* nothing. As a document it reaches everything the credential can reach —
|
|
114
|
+
* including the root fields no model or namespace claims (`designTokens`,
|
|
115
|
+
* `record`, `locales`, `resolvedLocale`, the whole customer-auth mutation
|
|
116
|
+
* set), which the JSON op could not address at all.
|
|
117
|
+
*
|
|
118
|
+
* Returns the whole `data` object rather than a single root field, because a
|
|
119
|
+
* hand-written document may legitimately have several. Routing, the persisted
|
|
120
|
+
* -query handshake and `FoirRequestError` are exactly as on the typed path.
|
|
121
|
+
*
|
|
122
|
+
* ```ts
|
|
123
|
+
* const { pages } = await foir.raw<{ pages: { totalCount: number } }>(
|
|
124
|
+
* 'query Recent($n: Int) { pages(first: $n, orderBy: { createdAt: DESC }) { totalCount } }',
|
|
125
|
+
* { n: 5 },
|
|
126
|
+
* );
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
raw<T = unknown>(document: string, variables?: Record<string, unknown>): Promise<T>;
|
|
130
|
+
/**
|
|
131
|
+
* Close the subscription socket and drop every live subscription.
|
|
132
|
+
*
|
|
133
|
+
* Only needed by a long-lived process that discards a client — a browser tab
|
|
134
|
+
* or an edge invocation just goes away. The socket is lazy and self-closing
|
|
135
|
+
* (it opens on the first subscription and closes when the last is cancelled),
|
|
136
|
+
* so a client that only queries never has one to dispose of.
|
|
137
|
+
*/
|
|
138
|
+
dispose(): void;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The client, shaped by the credential's manifest. An op that is not in
|
|
142
|
+
* `FoirSchema` is not on this type, so `foir.tilly_block.create(...)` under a
|
|
143
|
+
* read-only `pk_` key is a compile error rather than a 400.
|
|
144
|
+
*/
|
|
145
|
+
export type FoirClient<S extends FoirSchemaDefinition> = FoirClientCore<S> & {
|
|
146
|
+
[MK in keyof S['models']]: FoirModelClient<S, S['models'][MK]>;
|
|
147
|
+
};
|
|
148
|
+
export declare function createFoirClient<S extends FoirSchemaDefinition = FoirSchemaDefinition>(clientConfig: FoirClientConfig<S>): FoirClient<S>;
|
|
149
|
+
/**
|
|
150
|
+
* `createFoirClient` from the `PLATFORM_API_URL` / `PLATFORM_API_KEY` /
|
|
151
|
+
* `FOIR_PREVIEW` convention `@foir/sdk/client` already uses, so a storefront
|
|
152
|
+
* that has an env wired for GraphQL can add the data client without a second
|
|
153
|
+
* set of variables. Preview picks the `sk_` `PLATFORM_PREVIEW_API_KEY` and the
|
|
154
|
+
* draft schema channel, exactly as `foirRequest` does.
|
|
155
|
+
*
|
|
156
|
+
* ```ts
|
|
157
|
+
* import * as generated from './foir.generated';
|
|
158
|
+
* const foir = createFoirClientFromEnv(env, { generated });
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* `generated` is spelled out in the overrides type rather than left inside the
|
|
162
|
+
* `Omit<…>` so `S` is inferred from it here exactly as it is on `createFoirClient`.
|
|
163
|
+
*/
|
|
164
|
+
export declare function createFoirClientFromEnv<S extends FoirSchemaDefinition = FoirSchemaDefinition>(env: FoirEnv, overrides?: Omit<FoirClientConfig, 'apiUrl' | 'apiKey' | 'channel' | 'generated'> & {
|
|
165
|
+
generated?: FoirGeneratedModule<S>;
|
|
166
|
+
}): FoirClient<S>;
|