@foir/sdk 0.9.0 → 0.10.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 +497 -27
- package/README.md +111 -0
- package/dist/auth/auth.d.ts +72 -1
- package/dist/auth/hosted.d.ts +71 -4
- package/dist/auth/index.d.ts +3 -3
- package/dist/auth/operations.d.ts +84 -0
- package/dist/auth/ops.d.ts +10 -0
- package/dist/auth.js +127 -19
- package/dist/client.js +63 -6
- package/dist/codegen/block-registry.d.ts +1 -1
- package/dist/codegen/block-scaffold.d.ts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/codegen/item-registry.d.ts +1 -1
- package/dist/codegen/item-scaffold.d.ts +1 -1
- package/dist/content/content-renderer.d.ts +4 -5
- package/dist/content/index.d.ts +1 -1
- package/dist/content/richtext.d.ts +1 -1
- package/dist/data/builtins.d.ts +64 -12
- package/dist/data/client.d.ts +82 -1
- package/dist/data/decimal.d.ts +212 -0
- package/dist/data/document.d.ts +1 -1
- package/dist/data/index.d.ts +10 -8
- package/dist/data/manifest.d.ts +9 -1
- package/dist/data/schema.d.ts +2 -2
- package/dist/data/subscriptions.d.ts +36 -10
- package/dist/data/transport.d.ts +120 -3
- package/dist/data.js +377 -16
- package/dist/graphql/cdn-route.d.ts +1 -1
- package/dist/graphql/foir-request.d.ts +1 -1
- package/dist/live/coalesce.d.ts +57 -0
- package/dist/live/drift.d.ts +59 -0
- package/dist/live/events.d.ts +13 -0
- package/dist/live/headers.d.ts +53 -0
- package/dist/live/index.d.ts +54 -0
- package/dist/live/keys.d.ts +72 -0
- package/dist/live/manager.d.ts +77 -0
- package/dist/live/react/index.d.ts +143 -0
- package/dist/live/reconcile.d.ts +82 -0
- package/dist/live/resync.d.ts +59 -0
- package/dist/live/store.d.ts +134 -0
- package/dist/live/tagIndex.d.ts +29 -0
- package/dist/live/tanstack.d.ts +127 -0
- package/dist/live/writetoken.d.ts +86 -0
- package/dist/live-react.js +395 -0
- package/dist/live.js +845 -0
- package/dist/next/cacheTags.d.ts +245 -0
- package/dist/next/index.d.ts +7 -0
- package/dist/next.js +179 -6
- package/dist/push.js +81 -8
- package/dist/react.js +76 -5
- package/dist/realtime/index.d.ts +4 -5
- package/dist/server/events-client.d.ts +54 -4
- package/dist/server/index.d.ts +3 -2
- package/dist/server/require-scoped-token.d.ts +1 -1
- package/dist/server.js +454 -2
- package/dist/transport/foir-http.d.ts +113 -10
- package/package.json +10 -2
package/dist/data/client.d.ts
CHANGED
|
@@ -95,6 +95,74 @@ export type FoirNamespaceClient<N extends {
|
|
|
95
95
|
}> = {
|
|
96
96
|
[OK in keyof N['ops']]: N['ops'][OK] extends FoirNamespaceOpDefinition ? FoirNamespaceOperation<N['ops'][OK]> : never;
|
|
97
97
|
};
|
|
98
|
+
declare const foirDocumentResult: unique symbol;
|
|
99
|
+
declare const foirDocumentVariables: unique symbol;
|
|
100
|
+
/**
|
|
101
|
+
* A GraphQL document string that carries its own result and variable types.
|
|
102
|
+
*
|
|
103
|
+
* `raw` returns `unknown` because a hand-written document is opaque to the
|
|
104
|
+
* client, so every call site hand-writes a result type that nothing checks. That
|
|
105
|
+
* is the cost the escape hatch was never meant to have: the sites that most need
|
|
106
|
+
* type safety, the ones the typed client could not express, are the ones with
|
|
107
|
+
* none.
|
|
108
|
+
*
|
|
109
|
+
* A branded document moves that annotation from the call site to the document,
|
|
110
|
+
* once, next to the selection it describes. It is still an assertion rather than
|
|
111
|
+
* a proof, because nothing validates it against the live schema, but it is an
|
|
112
|
+
* assertion made in ONE place that every caller then inherits, and a wrong one
|
|
113
|
+
* is wrong somewhere a reader is looking at the query.
|
|
114
|
+
*
|
|
115
|
+
* The brands are phantom: they exist only in the type system, so a
|
|
116
|
+
* `TypedFoirDocument` is a plain string at runtime and can be logged, hashed or
|
|
117
|
+
* sent anywhere a string goes.
|
|
118
|
+
*/
|
|
119
|
+
export type TypedFoirDocument<TResult, TVariables = Record<string, unknown>> = string & {
|
|
120
|
+
readonly [foirDocumentResult]: TResult;
|
|
121
|
+
readonly [foirDocumentVariables]: TVariables;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Brand a GraphQL document with the shape it returns.
|
|
125
|
+
*
|
|
126
|
+
* ```ts
|
|
127
|
+
* const RECENT = graphql<{ pages: { totalCount: number } }, { n: number }>(`
|
|
128
|
+
* query Recent($n: Int) { pages(first: $n) { totalCount } }
|
|
129
|
+
* `);
|
|
130
|
+
*
|
|
131
|
+
* const { pages } = await foir.raw(RECENT, { n: 5 });
|
|
132
|
+
* // ^? { totalCount: number }
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* It does nothing at runtime beyond returning the string it was given. The value
|
|
136
|
+
* is entirely in the type, and in the fact that the type now lives beside the
|
|
137
|
+
* document rather than being retyped at each call.
|
|
138
|
+
*
|
|
139
|
+
* This does NOT make a bespoke selection type-checked against the schema. That
|
|
140
|
+
* is the larger half and wants generated documents rather than a helper. What it
|
|
141
|
+
* removes is the retyping, which is where the two real bugs came from: a result
|
|
142
|
+
* type that drifted from the selection it claimed to describe.
|
|
143
|
+
*/
|
|
144
|
+
export declare function graphql<TResult = unknown, TVariables = Record<string, unknown>>(document: string): TypedFoirDocument<TResult, TVariables>;
|
|
145
|
+
/**
|
|
146
|
+
* Per-call options for {@link FoirClient.raw}.
|
|
147
|
+
*/
|
|
148
|
+
export interface FoirRawOptions {
|
|
149
|
+
/**
|
|
150
|
+
* Treat a partial response as a failure.
|
|
151
|
+
*
|
|
152
|
+
* A GraphQL response can carry `errors` AND `data` together, and the default
|
|
153
|
+
* is to warn and return the data: a field the server could not resolve should
|
|
154
|
+
* not blank a whole page, and forward compatibility rests on it.
|
|
155
|
+
*
|
|
156
|
+
* On a hand-written document that default is the expensive one. There is no
|
|
157
|
+
* generated selection to have caught a wrong field name first, so selecting
|
|
158
|
+
* something that does not exist returns everything else and logs — a typo
|
|
159
|
+
* reads as a missing feature rather than a failure, and the hand-written
|
|
160
|
+
* result type asserts it was fine. `strict: true` turns that into a thrown
|
|
161
|
+
* `FoirRequestError`, which still carries the data on `partialData` so a
|
|
162
|
+
* caller can log what it declined to render.
|
|
163
|
+
*/
|
|
164
|
+
strict?: boolean;
|
|
165
|
+
}
|
|
98
166
|
export interface FoirClientCore<S extends FoirSchemaDefinition> {
|
|
99
167
|
/**
|
|
100
168
|
* Non-model root namespaces (`search`, `customers`, `notifications`).
|
|
@@ -126,7 +194,19 @@ export interface FoirClientCore<S extends FoirSchemaDefinition> {
|
|
|
126
194
|
* );
|
|
127
195
|
* ```
|
|
128
196
|
*/
|
|
129
|
-
raw<
|
|
197
|
+
raw<TResult, TVariables extends Record<string, unknown>>(document: TypedFoirDocument<TResult, TVariables>, variables?: TVariables, options?: FoirRawOptions): Promise<TResult>;
|
|
198
|
+
/**
|
|
199
|
+
* The untyped form, for a document with no brand.
|
|
200
|
+
*
|
|
201
|
+
* The document type excludes a branded one deliberately. Without that, a
|
|
202
|
+
* branded call whose VARIABLES do not match falls through to this overload,
|
|
203
|
+
* whose `Record<string, unknown>` accepts anything, and the mistake compiles.
|
|
204
|
+
* The result would still be typed, so the call site reads as checked while the
|
|
205
|
+
* variables are not, which is the failure mode a brand exists to prevent.
|
|
206
|
+
*/
|
|
207
|
+
raw<T = unknown>(document: string & {
|
|
208
|
+
readonly [foirDocumentResult]?: undefined;
|
|
209
|
+
}, variables?: Record<string, unknown>, options?: FoirRawOptions): Promise<T>;
|
|
130
210
|
/**
|
|
131
211
|
* Close the subscription socket and drop every live subscription.
|
|
132
212
|
*
|
|
@@ -164,3 +244,4 @@ export declare function createFoirClient<S extends FoirSchemaDefinition = FoirSc
|
|
|
164
244
|
export declare function createFoirClientFromEnv<S extends FoirSchemaDefinition = FoirSchemaDefinition>(env: FoirEnv, overrides?: Omit<FoirClientConfig, 'apiUrl' | 'apiKey' | 'channel' | 'generated'> & {
|
|
165
245
|
generated?: FoirGeneratedModule<S>;
|
|
166
246
|
}): FoirClient<S>;
|
|
247
|
+
export {};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `FoirDecimal` — the exact numeric type a `decimal` field arrives as.
|
|
3
|
+
*
|
|
4
|
+
* A `decimal` field travels as a STRING on the wire, because JSON has one
|
|
5
|
+
* number type and every JavaScript consumer parses it into an IEEE-754 double
|
|
6
|
+
* whatever the server meant. Supabase demonstrates the failure: Postgres stores
|
|
7
|
+
* `numeric` exactly, PostgREST serialises it as a JSON number, and precision
|
|
8
|
+
* dies in `JSON.parse` before any application code runs. Exact storage with a
|
|
9
|
+
* lossy wire format buys nothing.
|
|
10
|
+
*
|
|
11
|
+
* So the string is the transport. It is deliberately NOT what you are handed:
|
|
12
|
+
* `"19.99" + "2.00"` is `"19.992.00"`, silently. This class is the thing that
|
|
13
|
+
* makes that unrepresentable.
|
|
14
|
+
*
|
|
15
|
+
* ---------------------------------------------------------------------------
|
|
16
|
+
* Representation
|
|
17
|
+
* ---------------------------------------------------------------------------
|
|
18
|
+
*
|
|
19
|
+
* A scaled `bigint`: `value = units / 10 ** scale`. `19.99` is
|
|
20
|
+
* `{ units: 1999n, scale: 2 }`. All arithmetic is integer arithmetic on
|
|
21
|
+
* `units`, so it is exact by construction rather than exact by care.
|
|
22
|
+
*
|
|
23
|
+
* `bigint` is ES2020 and the package targets ES2022, so this has NO dependency.
|
|
24
|
+
* That is deliberate: Prisma ships `Decimal.js` instances and prisma/prisma#16397
|
|
25
|
+
* has run for years because callers cannot get a `Decimal.js` whose version
|
|
26
|
+
* matches Prisma's. A type that crosses a package boundary should not carry a
|
|
27
|
+
* third party's identity with it.
|
|
28
|
+
*
|
|
29
|
+
* ---------------------------------------------------------------------------
|
|
30
|
+
* Scale is preserved, equality is numeric
|
|
31
|
+
* ---------------------------------------------------------------------------
|
|
32
|
+
*
|
|
33
|
+
* `decimal('19.90')` renders as `"19.90"`, not `"19.9"`. Trailing zeros are
|
|
34
|
+
* significant to anyone displaying money, and dropping them would make the
|
|
35
|
+
* round trip lossy in the one way people actually notice.
|
|
36
|
+
*
|
|
37
|
+
* But `equals` compares VALUES: `decimal('1.0').equals(decimal('1.00'))` is
|
|
38
|
+
* `true`. Textual comparison would be a trap of a different shape.
|
|
39
|
+
*
|
|
40
|
+
* ---------------------------------------------------------------------------
|
|
41
|
+
* Why `+` throws instead of concatenating
|
|
42
|
+
* ---------------------------------------------------------------------------
|
|
43
|
+
*
|
|
44
|
+
* `Symbol.toPrimitive` answers the `'string'` hint and throws on every other
|
|
45
|
+
* one. So:
|
|
46
|
+
*
|
|
47
|
+
* ```ts
|
|
48
|
+
* `Total: ${price}` // "Total: 19.99" — string hint, fine
|
|
49
|
+
* price + tax // TypeError, naming .add()
|
|
50
|
+
* price > limit // TypeError, naming .cmp()
|
|
51
|
+
* Number(price) // TypeError, naming .toNumber()
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* TypeScript already rejects `price + tax` at compile time. The throw is for
|
|
55
|
+
* JavaScript callers and for anything that reaches this through `any`, where a
|
|
56
|
+
* silent `"19.992.00"` would otherwise reach a customer. Loud beats quiet: the
|
|
57
|
+
* whole reason this type exists is that the float bug was silent.
|
|
58
|
+
*
|
|
59
|
+
* `JSON.stringify` is answered by `toJSON`, so a decimal serialises back to the
|
|
60
|
+
* canonical string it arrived as — `{"price":"19.99"}`, never `{"price":{}}`.
|
|
61
|
+
* Prisma's most common support thread is that this behaviour was left implicit;
|
|
62
|
+
* it is pinned by a test here.
|
|
63
|
+
*
|
|
64
|
+
* ---------------------------------------------------------------------------
|
|
65
|
+
* If you are caching, cloning or comparing these, read this
|
|
66
|
+
* ---------------------------------------------------------------------------
|
|
67
|
+
*
|
|
68
|
+
* **Treat a decimal as an opaque leaf.** The state lives in `#`-private fields,
|
|
69
|
+
* which are invisible to every structural walk:
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* Object.keys(price) // []
|
|
73
|
+
* Object.keys(structuredClone(price)) // [] <- no throw. silently {}.
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* So a generic deep-clone, structural-share or deep-equal sees an EMPTY OBJECT.
|
|
77
|
+
* `structuredClone` does not throw, it just returns `{}`, and any equality that
|
|
78
|
+
* walks own enumerable keys calls two different decimals equal. This is the one
|
|
79
|
+
* that fails quietly, which on money is the worst place for it.
|
|
80
|
+
*
|
|
81
|
+
* Identity is `instanceof`. Equality is `.equals()`. Cloning is unnecessary —
|
|
82
|
+
* every operation returns a new instance and nothing mutates, so share the
|
|
83
|
+
* reference.
|
|
84
|
+
*
|
|
85
|
+
* **A JSON round trip returns a string, not a decimal.** Lossless in value,
|
|
86
|
+
* lossy in type:
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* JSON.parse(JSON.stringify(price)) // "19.99", a string
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* Rehydrate at the boundary — `decimal(value)` takes the string, and the
|
|
93
|
+
* generated types say which fields are decimals, so the revive is keyed on the
|
|
94
|
+
* schema rather than sniffed.
|
|
95
|
+
*
|
|
96
|
+
* **Scale is display, not identity — so normalise KEYS, never PAYLOADS.**
|
|
97
|
+
* `decimal('1.0').equals(decimal('1.00'))` is true while their `toString()`
|
|
98
|
+
* differs. Two consequences that pull in opposite directions:
|
|
99
|
+
*
|
|
100
|
+
* - Keying a cache on `toString()` splits one value into two entries. If you
|
|
101
|
+
* need a canonical key, `rescale()` to a fixed scale first.
|
|
102
|
+
* - But `rescale()` on a value you will SERVE rewrites what the author entered:
|
|
103
|
+
* `19.90` becomes `19.9`, and a price changes how it displays. Never
|
|
104
|
+
* normalise a stored payload.
|
|
105
|
+
*
|
|
106
|
+
* The cleanest way to hold both is to store the raw wire string and hydrate at
|
|
107
|
+
* the read boundary. The store then never holds two instances to choose
|
|
108
|
+
* between, and persistence is correct by construction.
|
|
109
|
+
*/
|
|
110
|
+
/**
|
|
111
|
+
* How to resolve a value sitting exactly halfway between two representable
|
|
112
|
+
* results. Only `div`, `toFixed` and `rescale` can produce one — `add`, `sub`
|
|
113
|
+
* and `mul` are always exact.
|
|
114
|
+
*
|
|
115
|
+
* - `half-even` — banker's rounding. Ties go to the even neighbour, so a long
|
|
116
|
+
* run of them does not drift upward. The default for `div`, where the result
|
|
117
|
+
* is usually an intermediate value that will be summed.
|
|
118
|
+
* - `half-up` — ties go away from zero. What a person means by "round to 2dp",
|
|
119
|
+
* and the default for `toFixed`, which is a display operation.
|
|
120
|
+
* - `down` — truncate toward zero. Never rounds up, for the cases where over-
|
|
121
|
+
* stating is the expensive direction.
|
|
122
|
+
*/
|
|
123
|
+
export type FoirDecimalRounding = 'half-even' | 'half-up' | 'down';
|
|
124
|
+
/** Options for {@link FoirDecimal.div}. */
|
|
125
|
+
export interface FoirDecimalDivOptions {
|
|
126
|
+
/**
|
|
127
|
+
* Decimal places in the result. Division is the one operation that cannot be
|
|
128
|
+
* exact — `1/3` has no finite decimal expansion — so a scale has to be named.
|
|
129
|
+
* Defaults to 20, which is wider than any currency and narrow enough to stay
|
|
130
|
+
* cheap.
|
|
131
|
+
*/
|
|
132
|
+
scale?: number;
|
|
133
|
+
/** Defaults to `half-even`. */
|
|
134
|
+
rounding?: FoirDecimalRounding;
|
|
135
|
+
}
|
|
136
|
+
/** Anything that can be widened to a decimal without losing precision. */
|
|
137
|
+
export type FoirDecimalInput = string | bigint | FoirDecimal;
|
|
138
|
+
/**
|
|
139
|
+
* An exact decimal number.
|
|
140
|
+
*
|
|
141
|
+
* Construct from a string (the wire form), a `bigint`, or another decimal.
|
|
142
|
+
* A `number` is refused — see {@link FoirDecimal.fromNumber}.
|
|
143
|
+
*/
|
|
144
|
+
export declare class FoirDecimal {
|
|
145
|
+
#private;
|
|
146
|
+
constructor(value: FoirDecimalInput);
|
|
147
|
+
/**
|
|
148
|
+
* Build a decimal from a JavaScript number, accepting the precision that
|
|
149
|
+
* number may already have lost.
|
|
150
|
+
*
|
|
151
|
+
* Deliberately not the constructor. It is for interop — a value read from a
|
|
152
|
+
* third-party JSON API that emitted a float — and naming it at every call
|
|
153
|
+
* site is the point. `fromNumber(0.1 + 0.2)` gives `0.30000000000000004`,
|
|
154
|
+
* because that is genuinely the value it was handed.
|
|
155
|
+
*/
|
|
156
|
+
static fromNumber(value: number): FoirDecimal;
|
|
157
|
+
/** `true` if `value` parses as a decimal. Does not throw. */
|
|
158
|
+
static isDecimalString(value: unknown): value is string;
|
|
159
|
+
/** Decimal places carried by this value. `19.90` has scale 2. */
|
|
160
|
+
get scale(): number;
|
|
161
|
+
/** Re-express at `scale`, rounding away any digits that no longer fit. */
|
|
162
|
+
rescale(scale: number, rounding?: FoirDecimalRounding): FoirDecimal;
|
|
163
|
+
add(other: FoirDecimalInput): FoirDecimal;
|
|
164
|
+
sub(other: FoirDecimalInput): FoirDecimal;
|
|
165
|
+
/** Exact: scales add, so `1.10 × 1.10` is `1.2100` rather than `1.21`. */
|
|
166
|
+
mul(other: FoirDecimalInput): FoirDecimal;
|
|
167
|
+
/** The only inexact operation — see {@link FoirDecimalDivOptions}. */
|
|
168
|
+
div(other: FoirDecimalInput, options?: FoirDecimalDivOptions): FoirDecimal;
|
|
169
|
+
abs(): FoirDecimal;
|
|
170
|
+
neg(): FoirDecimal;
|
|
171
|
+
isZero(): boolean;
|
|
172
|
+
isNegative(): boolean;
|
|
173
|
+
/** `-1`, `0` or `1`. Use this rather than `<`, which throws. */
|
|
174
|
+
cmp(other: FoirDecimalInput): -1 | 0 | 1;
|
|
175
|
+
/** Numeric equality: `1.0` equals `1.00`. Scale is display, not identity. */
|
|
176
|
+
equals(other: FoirDecimalInput): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Fixed-place string, rounding `half-up` by default because this is the
|
|
179
|
+
* display operation and that is what a person means by "to 2dp".
|
|
180
|
+
*/
|
|
181
|
+
toFixed(places: number, rounding?: FoirDecimalRounding): string;
|
|
182
|
+
/**
|
|
183
|
+
* The canonical wire form, preserving scale. Never exponent notation, so the
|
|
184
|
+
* output always parses back to an equal value.
|
|
185
|
+
*/
|
|
186
|
+
toString(): string;
|
|
187
|
+
/** `JSON.stringify` emits the canonical string, not an object. Pinned by test. */
|
|
188
|
+
toJSON(): string;
|
|
189
|
+
/**
|
|
190
|
+
* Explicitly lossy — this is the boundary back into float, and naming it is
|
|
191
|
+
* the point. Fine for a chart axis; never for money you will add up.
|
|
192
|
+
*/
|
|
193
|
+
toNumber(): number;
|
|
194
|
+
/**
|
|
195
|
+
* Answers the `'string'` hint so template literals work, and throws on every
|
|
196
|
+
* other hint so `+`, `<` and `Number()` fail loudly instead of silently
|
|
197
|
+
* concatenating or truncating.
|
|
198
|
+
*/
|
|
199
|
+
[Symbol.toPrimitive](hint: string): string;
|
|
200
|
+
get [Symbol.toStringTag](): string;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Shorthand for `new FoirDecimal(value)`.
|
|
204
|
+
*
|
|
205
|
+
* ```ts
|
|
206
|
+
* import { decimal } from '@foir/sdk/data';
|
|
207
|
+
*
|
|
208
|
+
* const total = decimal(product.price).mul('3').add(shipping);
|
|
209
|
+
* console.log(total.toFixed(2)); // "62.97"
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export declare function decimal(value: FoirDecimalInput): FoirDecimal;
|
package/dist/data/document.d.ts
CHANGED
|
@@ -215,7 +215,7 @@ export interface FoirNamespaceCall {
|
|
|
215
215
|
* stopped in front of. Each level above 1 spends one of those stopping points.
|
|
216
216
|
*
|
|
217
217
|
* It is a BUDGET rather than a setting, though a much larger one since
|
|
218
|
-
*
|
|
218
|
+
* On a real project the same op measures 318 fields at level 1, 468
|
|
219
219
|
* at level 2 and 618 at level 3, against a server ceiling of 10,000. Those
|
|
220
220
|
* were 837 / 3,513 / 14,217 while a union position was charged every member it
|
|
221
221
|
* could hold rather than the one a value can be.
|
package/dist/data/index.d.ts
CHANGED
|
@@ -37,21 +37,21 @@
|
|
|
37
37
|
* const anything = await foir.raw('{ designTokens { name value } }');
|
|
38
38
|
* ```
|
|
39
39
|
*
|
|
40
|
-
* `FoirSchema` comes from `foir types
|
|
40
|
+
* `FoirSchema` comes from `foir types`. The contract it generates
|
|
41
41
|
* against is written down in `schema.ts` — read that file before changing
|
|
42
42
|
* anything here, because a generator is written against it.
|
|
43
43
|
*/
|
|
44
|
-
export { createFoirClient, createFoirClientFromEnv } from './client';
|
|
45
|
-
export type { FoirClient, FoirClientCore, FoirModelClient, FoirNamespaceClient } from './client';
|
|
44
|
+
export { createFoirClient, createFoirClientFromEnv, graphql } from './client';
|
|
45
|
+
export type { FoirClient, FoirClientCore, FoirModelClient, FoirNamespaceClient, FoirRawOptions, TypedFoirDocument, } from './client';
|
|
46
46
|
export { fetchFoirManifest } from './manifest';
|
|
47
47
|
export type { FoirManifest, FoirManifestComposite, FoirManifestModel, FoirManifestNamespace, FoirManifestOp, FoirManifestOpPayload, FoirManifestRelation, FoirManifestScalar, FoirManifestSelection, FoirManifestSelectionField, FoirManifestSelectionMember, FoirManifestSelectionUnion, FoirManifestStructural, FoirManifestSubscription, FoirManifestSubscriptionArgScope, FoirManifestUnclassifiedField, } from './manifest';
|
|
48
48
|
export { planFoirModelCall, planFoirNamespaceCall } from './document';
|
|
49
49
|
export type { FoirCall, FoirDocumentPlan, FoirNamespaceCall, FoirNamespaceSelections, FoirRecordPath, FoirSelectionModel, FoirSelectionNamespace, FoirSelections, FoirSelectionSource, FoirSelectionUnions, } from './document';
|
|
50
50
|
export { createFoirSubjectRegistry, recordFoirSubjects } from './subjects';
|
|
51
51
|
export type { FoirSubject, FoirSubjectEntry, FoirSubjectOptions, FoirSubjectOrigin, FoirSubjectRegistry, } from './subjects';
|
|
52
|
-
export type { CollabInfo, FileValue, FoirAccessGrant, FoirAccessLevel, FoirAccessSource, FoirAccessor, FoirCreateMyNoteArgs, FoirCustomerNote, FoirCustomerNoteAttachment, FoirDegraded, FoirDepthBelow, FoirDepthLevel, FoirDeviceType, FoirGranteeKind, FoirJsonValue, FoirMyAccess, FoirMyNotesArgs, FoirMyNotesResult, FoirNoteConfig, FoirNoteSubjectType, FoirPlatformType, FoirResolveContext, FoirResourceAccess, FoirUnexpandedRecord, FoirUpdateManyPayload, FoirVisibility, GeoPoint, Image, ImageFormat, ImageVariant, ImageVariantSet, MediaValue, Passage, Prose, ProseSpan, RecordAncestor, Reference, Richtext, Video, } from './builtins';
|
|
52
|
+
export type { CollabInfo, FileValue, FoirAccessGrant, FoirAccessLevel, FoirAccessSource, FoirAccessor, FoirCreateMyNoteArgs, FoirSubmitAnonymousNoteArgs, FoirAnonymousNoteReceipt, FoirCustomerNote, FoirCustomerNoteAttachment, FoirDegraded, FoirDepthBelow, FoirDepthLevel, FoirDeviceType, FoirGranteeKind, FoirJsonValue, FoirMyAccess, FoirMyNotesArgs, FoirMyNotesResult, FoirNoteConfig, FoirNoteSubjectType, FoirPlatformType, FoirResolveContext, FoirResourceAccess, FoirUnexpandedRecord, FoirUpdateManyPayload, FoirVisibility, GeoPoint, Image, ImageFormat, ImageVariant, ImageVariantSet, MediaValue, Passage, Prose, ProseSpan, RecordAncestor, Reference, Richtext, Video, } from './builtins';
|
|
53
53
|
/**
|
|
54
|
-
* Pre-
|
|
54
|
+
* Pre-rename spellings. The `<Name>Block` wrappers and the `<Name>Value`
|
|
55
55
|
* split are the same thing as the types above — an item IS its value — so each
|
|
56
56
|
* of these is an alias, not a separate shape.
|
|
57
57
|
*
|
|
@@ -60,14 +60,16 @@ export type { CollabInfo, FileValue, FoirAccessGrant, FoirAccessLevel, FoirAcces
|
|
|
60
60
|
* emits, so there is nothing for them to alias. See `builtins.ts`.
|
|
61
61
|
*/
|
|
62
62
|
export type { FileBlock, GeoPointValue, ImageBlock, ImageValue, ReferenceBlock, ReferenceValue, RichtextBlock, RichtextValue, VideoBlock, VideoValue, } from './builtins';
|
|
63
|
-
export { foirDataEndpoint, foirRoute } from './transport';
|
|
63
|
+
export { FoirSchemaDriftError, foirDataEndpoint, foirRoute } from './transport';
|
|
64
64
|
export type { FoirClientConfig, FoirGeneratedModule, FoirLane, FoirLaneReason, FoirRouteDecision, FoirRouteInput, FoirSchemaDrift, } from './transport';
|
|
65
65
|
export { foirSocketEndpoint, FoirSocketTransport } from './ws-transport';
|
|
66
66
|
export type { FoirReconnectPolicy, FoirSubscribeRequest, FoirSubscription, FoirSubscriptionHandlers, FoirWebSocketFactory, FoirWebSocketLike, } from './ws-transport';
|
|
67
67
|
export { printSubscriptionDocument } from './subscription-document';
|
|
68
68
|
export type { FoirSubscriptionArgument, PrintSubscriptionDocumentInput, } from './subscription-document';
|
|
69
|
-
export type { FoirExecutionSubscribeOptions, FoirJobProgressEvent, FoirModelSubscriptions, FoirNamespaceSubscriptions, FoirNotificationEvent, FoirOperationExecutionEvent, FoirRecordChangedEvent, } from './subscriptions';
|
|
69
|
+
export type { FoirChangeSubscribeOptions, FoirExecutionSubscribeOptions, FoirJobProgressEvent, FoirModelSubscriptions, FoirNamespaceSubscriptions, FoirNotificationEvent, FoirOperationExecutionEvent, FoirRecordChangedEvent, } from './subscriptions';
|
|
70
70
|
export type { FoirConnection, FoirFieldsShape, FoirModelDefinition, FoirNamespaceDefinition, FoirNamespaceOpDefinition, FoirOpDefinition, FoirRelationDefinition, FoirReturnDefinition, FoirSchemaDefinition, } from './schema';
|
|
71
71
|
export type { FoirInclude, FoirModelOperation, FoirNamespaceOperation, FoirOmittableKey, FoirOpResult, FoirQuery, FoirRecordResult, } from './selection';
|
|
72
|
+
export { FoirDecimal, decimal } from './decimal';
|
|
73
|
+
export type { FoirDecimalDivOptions, FoirDecimalInput, FoirDecimalRounding, } from './decimal';
|
|
72
74
|
export { FoirRequestError } from '../transport/foir-http';
|
|
73
|
-
export type { FoirEnv, FoirGraphQLErrorItem, FoirTokenProvider, } from '../transport/foir-http';
|
|
75
|
+
export type { FoirEnv, FoirGraphQLErrorItem, FoirTokenProvider, FoirAuthDowngrade, } from '../transport/foir-http';
|
package/dist/data/manifest.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `GET /v1/manifest` — the per-caller surface descriptor, as a runtime type.
|
|
3
3
|
*
|
|
4
4
|
* This is the *runtime* mirror of `schema.ts`'s compile-time contract: what the
|
|
5
|
-
* server actually sends, verbatim, so `foir types`
|
|
5
|
+
* server actually sends, verbatim, so `foir types` has a typed thing
|
|
6
6
|
* to read and the drift check has a version to compare. The manifest is already
|
|
7
7
|
* the projection of `CachedSchema.SchemaForCaller(scopes)`, so what it lists is
|
|
8
8
|
* exactly what this credential may do — no client-side filtering anywhere.
|
|
@@ -275,6 +275,14 @@ export interface FoirManifestNamespace {
|
|
|
275
275
|
export interface FoirManifest {
|
|
276
276
|
schemaVersion: number;
|
|
277
277
|
channel: 'published' | 'draft';
|
|
278
|
+
/**
|
|
279
|
+
* The organisation whose composition this surface is, absent for the
|
|
280
|
+
* project's own. A credential confined to an organisation is served that
|
|
281
|
+
* organisation's surface without naming it, so this is how a reader of the
|
|
282
|
+
* manifest, or of a module generated from it, tells whose definitions it
|
|
283
|
+
* describes.
|
|
284
|
+
*/
|
|
285
|
+
composedFor?: string;
|
|
278
286
|
models: Record<string, FoirManifestModel>;
|
|
279
287
|
/**
|
|
280
288
|
* Keyed by namespace name (`auth`, `customers`, `files`, `notifications`,
|
package/dist/data/schema.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* THE `FoirSchema` CONTRACT.
|
|
3
3
|
*
|
|
4
4
|
* `createFoirClient<FoirSchema>()` is parameterised by a type that `foir types`
|
|
5
|
-
*
|
|
5
|
+
* emits from `GET /v1/manifest`. This file is that contract: the
|
|
6
6
|
* shape the generator writes and the shape the client reads. It is the only
|
|
7
7
|
* coupling between the two, so it is written down here rather than living in
|
|
8
8
|
* the generator's head.
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
* 8. **`raw` and `ns` are reserved top-level names.** They are the escape hatch
|
|
162
162
|
* and the namespace accessor on the client object. A model whose key is
|
|
163
163
|
* `raw` or `ns` would collide; the server should reject those model keys at
|
|
164
|
-
* create time (
|
|
164
|
+
* create time (a known limit).
|
|
165
165
|
*
|
|
166
166
|
* 9. **`models` and `namespaces` must be object type LITERALS, not named
|
|
167
167
|
* `interface`s.** TypeScript gives an implicit index signature to object
|
|
@@ -38,13 +38,17 @@
|
|
|
38
38
|
* carries a resolved selection tree, which `subscription-document.ts` prints
|
|
39
39
|
* with the same walk the query path prints models with.
|
|
40
40
|
*
|
|
41
|
-
* A
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
41
|
+
* A client built with `foirSubscriptions` makes no request at all: `client.ts`
|
|
42
|
+
* hands this an already-resolved descriptor instead of the fetcher, and the
|
|
43
|
+
* printer cannot tell the difference — it is the same shape either way, which is
|
|
44
|
+
* why nothing in this file changed when that landed.
|
|
45
|
+
*
|
|
46
|
+
* Without them it fetches once, which is what every client did before `foir
|
|
47
|
+
* types` emitted subscription trees and what a module generated before that
|
|
48
|
+
* still does. It is ONE fetch, memoised across every subscription and shared
|
|
49
|
+
* with the query path, sitting behind a long-lived socket. `client.ts` rejects a
|
|
50
|
+
* round trip in front of every query for good reason; in front of a socket that
|
|
51
|
+
* will live for minutes it is noise.
|
|
48
52
|
*/
|
|
49
53
|
import type { FoirManifest } from './manifest';
|
|
50
54
|
import type { FoirClientConfig } from './transport';
|
|
@@ -88,8 +92,12 @@ export interface FoirJobProgressEvent {
|
|
|
88
92
|
/** `operationExecution` / `operation{Op}Execution` — one execution's progress. */
|
|
89
93
|
export interface FoirOperationExecutionEvent {
|
|
90
94
|
id: string;
|
|
91
|
-
/**
|
|
92
|
-
|
|
95
|
+
/**
|
|
96
|
+
* The same spelling `operationExecution` and `operationExecutions` return for
|
|
97
|
+
* the run, so one comparison serves a read and an event. The socket carries
|
|
98
|
+
* the platform's lowercase value; this client hands it over as the enum name.
|
|
99
|
+
*/
|
|
100
|
+
status: 'PENDING' | 'DISPATCHED' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMED_OUT';
|
|
93
101
|
/** 0-100; null on terminal events. */
|
|
94
102
|
progressPct?: number | null;
|
|
95
103
|
progressMessage?: string | null;
|
|
@@ -117,6 +125,24 @@ export type FoirExecutionSubscribeOptions = FoirSubscriptionHandlers<FoirOperati
|
|
|
117
125
|
*/
|
|
118
126
|
operation?: string;
|
|
119
127
|
};
|
|
128
|
+
/**
|
|
129
|
+
* What `onChange` takes: the handlers, plus the optional resume cursor.
|
|
130
|
+
*
|
|
131
|
+
* `after` is the highest `seq` this consumer has already applied. Passing it
|
|
132
|
+
* makes the stream cursor-resumable — the server replays what came after it
|
|
133
|
+
* from the durable log before continuing live, so a message lost anywhere
|
|
134
|
+
* upstream is repaired by the next connect rather than left as a permanently
|
|
135
|
+
* stale cache entry. Omitting it is the stream as it has always been: live from
|
|
136
|
+
* the moment you connect.
|
|
137
|
+
*
|
|
138
|
+
* The two refusals it can produce — a cursor older than the retention window,
|
|
139
|
+
* or one further behind than the surface will page — arrive on `onError` with a
|
|
140
|
+
* machine-readable code, and both mean the cache must be thrown away rather
|
|
141
|
+
* than partially repaired. `@foir/sdk/live` classifies them.
|
|
142
|
+
*/
|
|
143
|
+
export type FoirChangeSubscribeOptions = FoirSubscriptionHandlers<FoirRecordChangedEvent> & {
|
|
144
|
+
after?: number;
|
|
145
|
+
};
|
|
120
146
|
/** The subscriptions a model accessor carries. */
|
|
121
147
|
export interface FoirModelSubscriptions {
|
|
122
148
|
/**
|
|
@@ -125,7 +151,7 @@ export interface FoirModelSubscriptions {
|
|
|
125
151
|
* The socket refuses a model this credential may not read, but the model is
|
|
126
152
|
* not in `FoirSchema` in that case, so the refusal is a type error first.
|
|
127
153
|
*/
|
|
128
|
-
onChange(
|
|
154
|
+
onChange(options: FoirChangeSubscribeOptions): FoirSubscription;
|
|
129
155
|
}
|
|
130
156
|
/**
|
|
131
157
|
* The subscriptions one namespace carries, by name.
|