@thomasfosterau/legation-jsonapi 0.0.0-stage → 0.1.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 +973 -0
- package/LICENSE +21 -0
- package/README.md +755 -2
- package/dist/index.d.ts +1708 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4304 -0
- package/dist/index.js.map +1 -0
- package/dist/rolldown-runtime-D8jQpDG7.js +12 -0
- package/package.json +53 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1708 @@
|
|
|
1
|
+
import { ApiError as ApiError$1, Document, Filter, Handlers, Resource } from "@thomasfosterau/effect-jsonapi";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
5
|
+
import * as Result from "effect/Result";
|
|
6
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
import { Authorization, AuthorizationError, Backend, BackendError, Client, ClientStore, Collection, CrudMutation, FilterPolicy, Mutator, MutatorError, Node, Predicate, Query as Query$1, QueryError, StoreError } from "@thomasfosterau/legation";
|
|
9
|
+
import * as HttpApi from "effect/http-api/HttpApi";
|
|
10
|
+
import { ClientEngine, ClientView, Coverage, MutationState, Related, Subset } from "@thomasfosterau/legation/internal";
|
|
11
|
+
import * as Duration from "effect/Duration";
|
|
12
|
+
import * as Stream from "effect/Stream";
|
|
13
|
+
import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
14
|
+
import * as Layer from "effect/Layer";
|
|
15
|
+
import { KeyValueStore } from "effect/persistence";
|
|
16
|
+
import * as HttpClientError from "effect/http/HttpClientError";
|
|
17
|
+
import * as HttpClientResponse from "effect/http/HttpClientResponse";
|
|
18
|
+
import * as HttpApiEndpoint from "effect/http-api/HttpApiEndpoint";
|
|
19
|
+
import * as HttpApiGroup from "effect/http-api/HttpApiGroup";
|
|
20
|
+
import { HttpServerRequest } from "effect/http/HttpServerRequest";
|
|
21
|
+
import * as Scope from "effect/Scope";
|
|
22
|
+
declare const SyncError_base: Schema.Class<SyncError, Schema.TaggedStruct<"SyncError", {
|
|
23
|
+
readonly message: Schema.String;
|
|
24
|
+
readonly cause: Schema.optional<Schema.Defect>;
|
|
25
|
+
}>, import("effect/Cause").YieldableError & {
|
|
26
|
+
readonly brand: unique symbol;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Base error for synchronizer operations.
|
|
30
|
+
*/
|
|
31
|
+
export declare class SyncError extends SyncError_base {}
|
|
32
|
+
declare const NetworkError_base: Schema.Class<NetworkError, Schema.TaggedStruct<"NetworkError", {
|
|
33
|
+
readonly message: Schema.String;
|
|
34
|
+
readonly cause: Schema.optional<Schema.Defect>;
|
|
35
|
+
}>, import("effect/Cause").YieldableError & {
|
|
36
|
+
readonly brand: unique symbol;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Network-related error (connection failure, timeout, etc.).
|
|
40
|
+
*/
|
|
41
|
+
export declare class NetworkError extends NetworkError_base {}
|
|
42
|
+
declare const ParseError_base: Schema.Class<ParseError, Schema.TaggedStruct<"ParseError", {
|
|
43
|
+
readonly message: Schema.String;
|
|
44
|
+
readonly cause: Schema.optional<Schema.Defect>;
|
|
45
|
+
}>, import("effect/Cause").YieldableError & {
|
|
46
|
+
readonly brand: unique symbol;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Parsing error for JSON:API documents.
|
|
50
|
+
*/
|
|
51
|
+
export declare class ParseError extends ParseError_base {}
|
|
52
|
+
declare const ApiError_base: Schema.Class<ApiError, Schema.TaggedStruct<"ApiError", {
|
|
53
|
+
readonly message: Schema.String;
|
|
54
|
+
readonly errors: Schema.$Array<Schema.Struct<{
|
|
55
|
+
readonly id: Schema.optionalKey<Schema.String>;
|
|
56
|
+
readonly links: Schema.optionalKey<Schema.Struct<{
|
|
57
|
+
readonly about: Schema.optionalKey<Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
58
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
59
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
60
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
61
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
62
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
63
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
64
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
65
|
+
}>]>>;
|
|
66
|
+
readonly type: Schema.optionalKey<Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
67
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
68
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
69
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
70
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
71
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
72
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
73
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
74
|
+
}>]>>;
|
|
75
|
+
}>>;
|
|
76
|
+
readonly status: Schema.optionalKey<Schema.String>;
|
|
77
|
+
readonly code: Schema.optionalKey<Schema.String>;
|
|
78
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
79
|
+
readonly detail: Schema.optionalKey<Schema.String>;
|
|
80
|
+
readonly source: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
|
|
81
|
+
readonly pointer: Schema.String;
|
|
82
|
+
}>, Schema.Struct<{
|
|
83
|
+
readonly parameter: Schema.String;
|
|
84
|
+
}>, Schema.Struct<{
|
|
85
|
+
readonly header: Schema.String;
|
|
86
|
+
}>]>>;
|
|
87
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
88
|
+
}>>;
|
|
89
|
+
}>, import("effect/Cause").YieldableError & {
|
|
90
|
+
readonly brand: unique symbol;
|
|
91
|
+
}>;
|
|
92
|
+
/**
|
|
93
|
+
* API error carrying the JSON:API error objects returned by the server.
|
|
94
|
+
*/
|
|
95
|
+
export declare class ApiError extends ApiError_base {}
|
|
96
|
+
declare const ConflictError_base: Schema.Class<ConflictError, Schema.TaggedStruct<"ConflictError", {
|
|
97
|
+
readonly message: Schema.String;
|
|
98
|
+
readonly localVersion: Schema.optional<Schema.String>;
|
|
99
|
+
readonly remoteVersion: Schema.optional<Schema.String>;
|
|
100
|
+
}>, import("effect/Cause").YieldableError & {
|
|
101
|
+
readonly brand: unique symbol;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Conflict error raised when local and remote resource versions mismatch.
|
|
105
|
+
*/
|
|
106
|
+
export declare class ConflictError extends ConflictError_base {}
|
|
107
|
+
declare namespace schema_d_exports {
|
|
108
|
+
export { JsonApiDocument, JsonApiError, LinkObject, Links, Relationship, ResourceIdentifier, ResourceObject, decodeDocument };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Schema for a JSON:API link object (`{ href, ... }`), adopted from the
|
|
112
|
+
* library's spec-compliant definition.
|
|
113
|
+
*/
|
|
114
|
+
declare const LinkObject: Schema.Struct<{
|
|
115
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
116
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
117
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
118
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
119
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
120
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
121
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
122
|
+
}>;
|
|
123
|
+
/** A JSON:API link object. */
|
|
124
|
+
type LinkObject = typeof LinkObject.Type;
|
|
125
|
+
/**
|
|
126
|
+
* Schema for a JSON:API `links` object. The spec allows arbitrary link names,
|
|
127
|
+
* so this is a record of link values; `self` / `related` are simply well-known
|
|
128
|
+
* keys within it.
|
|
129
|
+
*/
|
|
130
|
+
declare const Links: Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
131
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
132
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
133
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
134
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
135
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
136
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
137
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
138
|
+
}>]>>;
|
|
139
|
+
/** A JSON:API `links` object: arbitrary link names → string | link object. */
|
|
140
|
+
type Links = typeof Links.Type;
|
|
141
|
+
/**
|
|
142
|
+
* Schema for a JSON:API resource identifier (`{ type, id, meta? }`).
|
|
143
|
+
*/
|
|
144
|
+
declare const ResourceIdentifier: Schema.Struct<{
|
|
145
|
+
readonly type: Schema.String;
|
|
146
|
+
readonly id: Schema.String;
|
|
147
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
148
|
+
}>;
|
|
149
|
+
/** A JSON:API resource identifier (`{ type, id, meta? }`). */
|
|
150
|
+
type ResourceIdentifier = typeof ResourceIdentifier.Type;
|
|
151
|
+
/**
|
|
152
|
+
* Schema for a JSON:API relationship (`{ data?, links?, meta? }`). The
|
|
153
|
+
* `data` member may be a single identifier, an array of identifiers, or
|
|
154
|
+
* `null`.
|
|
155
|
+
*/
|
|
156
|
+
declare const Relationship: Schema.Struct<{
|
|
157
|
+
readonly data: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.Struct<{
|
|
158
|
+
readonly type: Schema.String;
|
|
159
|
+
readonly id: Schema.String;
|
|
160
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
161
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
162
|
+
readonly type: Schema.String;
|
|
163
|
+
readonly id: Schema.String;
|
|
164
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
165
|
+
}>>]>>>;
|
|
166
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
167
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
168
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
169
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
170
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
171
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
172
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
173
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
174
|
+
}>]>>>;
|
|
175
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
176
|
+
}>;
|
|
177
|
+
/** A JSON:API relationship (`{ data?, links?, meta? }`). */
|
|
178
|
+
type Relationship = typeof Relationship.Type;
|
|
179
|
+
/**
|
|
180
|
+
* Schema for a JSON:API resource object (a resource identifier plus
|
|
181
|
+
* optional `attributes`, `relationships`, and `links`).
|
|
182
|
+
*/
|
|
183
|
+
declare const ResourceObject: Schema.Struct<{
|
|
184
|
+
readonly type: Schema.String;
|
|
185
|
+
readonly id: Schema.String;
|
|
186
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
187
|
+
readonly attributes: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
188
|
+
readonly relationships: Schema.optional<Schema.$Record<Schema.String, Schema.Struct<{
|
|
189
|
+
readonly data: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.Struct<{
|
|
190
|
+
readonly type: Schema.String;
|
|
191
|
+
readonly id: Schema.String;
|
|
192
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
193
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
194
|
+
readonly type: Schema.String;
|
|
195
|
+
readonly id: Schema.String;
|
|
196
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
197
|
+
}>>]>>>;
|
|
198
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
199
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
200
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
201
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
202
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
203
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
204
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
205
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
206
|
+
}>]>>>;
|
|
207
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
208
|
+
}>>>;
|
|
209
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
210
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
211
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
212
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
213
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
214
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
215
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
216
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
217
|
+
}>]>>>;
|
|
218
|
+
}>;
|
|
219
|
+
/** A JSON:API resource object (identifier + optional attributes/relationships/links). */
|
|
220
|
+
type ResourceObject = typeof ResourceObject.Type;
|
|
221
|
+
/**
|
|
222
|
+
* Schema for a JSON:API error object, adopted from the library's
|
|
223
|
+
* spec-compliant definition.
|
|
224
|
+
*/
|
|
225
|
+
declare const JsonApiError: Schema.Struct<{
|
|
226
|
+
readonly id: Schema.optionalKey<Schema.String>;
|
|
227
|
+
readonly links: Schema.optionalKey<Schema.Struct<{
|
|
228
|
+
readonly about: Schema.optionalKey<Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
229
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
230
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
231
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
232
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
233
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
234
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
235
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
236
|
+
}>]>>;
|
|
237
|
+
readonly type: Schema.optionalKey<Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
238
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
239
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
240
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
241
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
242
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
243
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
244
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
245
|
+
}>]>>;
|
|
246
|
+
}>>;
|
|
247
|
+
readonly status: Schema.optionalKey<Schema.String>;
|
|
248
|
+
readonly code: Schema.optionalKey<Schema.String>;
|
|
249
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
250
|
+
readonly detail: Schema.optionalKey<Schema.String>;
|
|
251
|
+
readonly source: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
|
|
252
|
+
readonly pointer: Schema.String;
|
|
253
|
+
}>, Schema.Struct<{
|
|
254
|
+
readonly parameter: Schema.String;
|
|
255
|
+
}>, Schema.Struct<{
|
|
256
|
+
readonly header: Schema.String;
|
|
257
|
+
}>]>>;
|
|
258
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
259
|
+
}>;
|
|
260
|
+
/** A JSON:API error object. */
|
|
261
|
+
type JsonApiError = typeof JsonApiError.Type;
|
|
262
|
+
/**
|
|
263
|
+
* Schema for a top-level JSON:API document.
|
|
264
|
+
*
|
|
265
|
+
* The `data` member may be a single resource, a collection of resources, a
|
|
266
|
+
* resource identifier (or collection thereof), or `null`. The other
|
|
267
|
+
* top-level members (`errors`, `meta`, `included`, `links`, `jsonapi`) are
|
|
268
|
+
* all optional. The `jsonapi` member reuses the library's `JsonApiObject`.
|
|
269
|
+
*/
|
|
270
|
+
declare const JsonApiDocument: Schema.Struct<{
|
|
271
|
+
readonly data: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.Struct<{
|
|
272
|
+
readonly type: Schema.String;
|
|
273
|
+
readonly id: Schema.String;
|
|
274
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
275
|
+
readonly attributes: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
276
|
+
readonly relationships: Schema.optional<Schema.$Record<Schema.String, Schema.Struct<{
|
|
277
|
+
readonly data: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.Struct<{
|
|
278
|
+
readonly type: Schema.String;
|
|
279
|
+
readonly id: Schema.String;
|
|
280
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
281
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
282
|
+
readonly type: Schema.String;
|
|
283
|
+
readonly id: Schema.String;
|
|
284
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
285
|
+
}>>]>>>;
|
|
286
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
287
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
288
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
289
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
290
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
291
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
292
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
293
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
294
|
+
}>]>>>;
|
|
295
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
296
|
+
}>>>;
|
|
297
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
298
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
299
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
300
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
301
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
302
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
303
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
304
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
305
|
+
}>]>>>;
|
|
306
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
307
|
+
readonly type: Schema.String;
|
|
308
|
+
readonly id: Schema.String;
|
|
309
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
310
|
+
readonly attributes: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
311
|
+
readonly relationships: Schema.optional<Schema.$Record<Schema.String, Schema.Struct<{
|
|
312
|
+
readonly data: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.Struct<{
|
|
313
|
+
readonly type: Schema.String;
|
|
314
|
+
readonly id: Schema.String;
|
|
315
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
316
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
317
|
+
readonly type: Schema.String;
|
|
318
|
+
readonly id: Schema.String;
|
|
319
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
320
|
+
}>>]>>>;
|
|
321
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
322
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
323
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
324
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
325
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
326
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
327
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
328
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
329
|
+
}>]>>>;
|
|
330
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
331
|
+
}>>>;
|
|
332
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
333
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
334
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
335
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
336
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
337
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
338
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
339
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
340
|
+
}>]>>>;
|
|
341
|
+
}>>, Schema.Struct<{
|
|
342
|
+
readonly type: Schema.String;
|
|
343
|
+
readonly id: Schema.String;
|
|
344
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
345
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
346
|
+
readonly type: Schema.String;
|
|
347
|
+
readonly id: Schema.String;
|
|
348
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
349
|
+
}>>]>>>;
|
|
350
|
+
readonly errors: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
351
|
+
readonly id: Schema.optionalKey<Schema.String>;
|
|
352
|
+
readonly links: Schema.optionalKey<Schema.Struct<{
|
|
353
|
+
readonly about: Schema.optionalKey<Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
354
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
355
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
356
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
357
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
358
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
359
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
360
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
361
|
+
}>]>>;
|
|
362
|
+
readonly type: Schema.optionalKey<Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
363
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
364
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
365
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
366
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
367
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
368
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
369
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
370
|
+
}>]>>;
|
|
371
|
+
}>>;
|
|
372
|
+
readonly status: Schema.optionalKey<Schema.String>;
|
|
373
|
+
readonly code: Schema.optionalKey<Schema.String>;
|
|
374
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
375
|
+
readonly detail: Schema.optionalKey<Schema.String>;
|
|
376
|
+
readonly source: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
|
|
377
|
+
readonly pointer: Schema.String;
|
|
378
|
+
}>, Schema.Struct<{
|
|
379
|
+
readonly parameter: Schema.String;
|
|
380
|
+
}>, Schema.Struct<{
|
|
381
|
+
readonly header: Schema.String;
|
|
382
|
+
}>]>>;
|
|
383
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
384
|
+
}>>>;
|
|
385
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
386
|
+
readonly included: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
387
|
+
readonly type: Schema.String;
|
|
388
|
+
readonly id: Schema.String;
|
|
389
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
390
|
+
readonly attributes: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
391
|
+
readonly relationships: Schema.optional<Schema.$Record<Schema.String, Schema.Struct<{
|
|
392
|
+
readonly data: Schema.optional<Schema.NullOr<Schema.Union<readonly [Schema.Struct<{
|
|
393
|
+
readonly type: Schema.String;
|
|
394
|
+
readonly id: Schema.String;
|
|
395
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
396
|
+
}>, Schema.$Array<Schema.Struct<{
|
|
397
|
+
readonly type: Schema.String;
|
|
398
|
+
readonly id: Schema.String;
|
|
399
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
400
|
+
}>>]>>>;
|
|
401
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
402
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
403
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
404
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
405
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
406
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
407
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
408
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
409
|
+
}>]>>>;
|
|
410
|
+
readonly meta: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
411
|
+
}>>>;
|
|
412
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
413
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
414
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
415
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
416
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
417
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
418
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
419
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
420
|
+
}>]>>>;
|
|
421
|
+
}>>>;
|
|
422
|
+
readonly links: Schema.optional<Schema.$Record<Schema.String, Schema.Union<readonly [Schema.Union<readonly [Schema.URLFromString, Schema.String]>, Schema.Struct<{
|
|
423
|
+
readonly href: Schema.Union<readonly [Schema.URLFromString, Schema.String]>;
|
|
424
|
+
readonly rel: Schema.optionalKey<Schema.String>;
|
|
425
|
+
readonly describedby: Schema.optionalKey<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>;
|
|
426
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
427
|
+
readonly type: Schema.optionalKey<Schema.String>;
|
|
428
|
+
readonly hreflang: Schema.optionalKey<Schema.String>;
|
|
429
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
430
|
+
}>]>>>;
|
|
431
|
+
readonly jsonapi: Schema.optional<Schema.Struct<{
|
|
432
|
+
readonly version: Schema.optionalKey<Schema.Literals<readonly ["1.0", "1.1"]>>;
|
|
433
|
+
readonly ext: Schema.optionalKey<Schema.$Array<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>>;
|
|
434
|
+
readonly profile: Schema.optionalKey<Schema.$Array<Schema.Union<readonly [Schema.URLFromString, Schema.String]>>>;
|
|
435
|
+
readonly meta: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
436
|
+
}>>;
|
|
437
|
+
}>;
|
|
438
|
+
/**
|
|
439
|
+
* A top-level JSON:API document. Derived from {@link JsonApiDocument}
|
|
440
|
+
* so the type and the validator cannot drift.
|
|
441
|
+
*/
|
|
442
|
+
type JsonApiDocument = typeof JsonApiDocument.Type;
|
|
443
|
+
/**
|
|
444
|
+
* Decode an unknown value (e.g. a parsed HTTP response body) into a
|
|
445
|
+
* `JsonApiDocument`. Schema failures become `ParseError`, with the schema
|
|
446
|
+
* issue attached as `cause`.
|
|
447
|
+
*/
|
|
448
|
+
declare const decodeDocument: (input: unknown) => Effect.Effect<JsonApiDocument, ParseError>;
|
|
449
|
+
/**
|
|
450
|
+
* A single JSON:API `sort` term: a field and a direction, matching the decoded
|
|
451
|
+
* shape of the library's `Query.Sort` codec.
|
|
452
|
+
*/
|
|
453
|
+
interface SortTerm {
|
|
454
|
+
readonly field: string;
|
|
455
|
+
readonly direction: "asc" | "desc";
|
|
456
|
+
}
|
|
457
|
+
/** The pagination strategies the library models, as a decoded value. */
|
|
458
|
+
declare const PageParams: Schema.Union<readonly [Schema.Struct<{
|
|
459
|
+
readonly offset: Schema.optionalKey<Schema.FiniteFromString>;
|
|
460
|
+
readonly limit: Schema.optionalKey<Schema.FiniteFromString>;
|
|
461
|
+
}>, Schema.Struct<{
|
|
462
|
+
readonly number: Schema.optionalKey<Schema.FiniteFromString>;
|
|
463
|
+
readonly size: Schema.optionalKey<Schema.FiniteFromString>;
|
|
464
|
+
}>, Schema.Struct<{
|
|
465
|
+
readonly cursor: Schema.optionalKey<Schema.String>;
|
|
466
|
+
readonly size: Schema.optionalKey<Schema.FiniteFromString>;
|
|
467
|
+
}>]>;
|
|
468
|
+
/**
|
|
469
|
+
* JSON:API `page` parameters: offset/limit, number/size, or cursor/size — the
|
|
470
|
+
* strategies exposed by the library's `Query.Page`.
|
|
471
|
+
*/
|
|
472
|
+
type PageParams = typeof PageParams.Type;
|
|
473
|
+
/**
|
|
474
|
+
* JSON:API Request Options. `filter` is a filter-grammar AST (ADR-0028;
|
|
475
|
+
* `Filter.eq("status", "open")`, `Filter.and(...)`, …), encoded exactly
|
|
476
|
+
* through the grammar's canonical `filter[...]` form by
|
|
477
|
+
* `JsonApiQuery.buildQueryString`.
|
|
478
|
+
*/
|
|
479
|
+
interface JsonApiRequestOptions {
|
|
480
|
+
include?: ReadonlyArray<string>;
|
|
481
|
+
fields?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
482
|
+
sort?: ReadonlyArray<SortTerm>;
|
|
483
|
+
filter?: Filter.Node;
|
|
484
|
+
page?: PageParams;
|
|
485
|
+
}
|
|
486
|
+
declare namespace JsonApiQuery_d_exports {
|
|
487
|
+
export { DecodedQuery, PageWindow, ParsedRead, Projection, QueryEncoder, SingleRowQuery, buildQueryString, canonical, defaultEncoder, flatFilter, foldConstants, fromFilterAst, invalidParameter, parseParams, parseRead, querySchema, resourcesOf, singleRowParams, singleRowSchema, toFilterAst, toRequestOptions, urlReads };
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* The engine form of a filter AST, or a typed `QueryError` naming the field
|
|
491
|
+
* whose literal {@link literalOf} rejects: `Compare` / `In` / `NotIn` /
|
|
492
|
+
* `IsNull` map node for node, `And` / `Or` / `Not` recurse, and the empty
|
|
493
|
+
* conjunction (the grammar's "true") is `Predicate.always`. The map is
|
|
494
|
+
* structural — a singleton group, which the grammar's normal form keeps
|
|
495
|
+
* after deduplicating `Or(x, x)`, stays a singleton group rather than
|
|
496
|
+
* collapsing through `Predicate.or` — so {@link canonical} is idempotent.
|
|
497
|
+
* Literals are the codec's decoded scalars; a `Date` (a `DateFromString`
|
|
498
|
+
* attribute) becomes its ISO-8601 UTC text (see the module doc's encode
|
|
499
|
+
* note). Field names are copied verbatim — the module doc's
|
|
500
|
+
* field-namespace precondition.
|
|
501
|
+
*
|
|
502
|
+
* This module's own callers ({@link readOf}, {@link filterOf}) never see
|
|
503
|
+
* this fail: their ASTs are decoded through {@link resourcesOf}'s synthetic
|
|
504
|
+
* resource, whose filterable attributes admit only wire scalars, or are
|
|
505
|
+
* built by {@link toFilterAst} from a `Predicate.Literal`, which is one
|
|
506
|
+
* already. The typed failure exists for this function's public contract —
|
|
507
|
+
* any caller bridging an AST built over a resource with a richer decoded
|
|
508
|
+
* attribute type (a `Date`-valued one aside, which is handled) — not for
|
|
509
|
+
* either call site here.
|
|
510
|
+
*/
|
|
511
|
+
declare const fromFilterAst: (ast: Filter.Node) => Result.Result<Predicate.Predicate, QueryError>;
|
|
512
|
+
/**
|
|
513
|
+
* The grammar form of a predicate, or `None` where it has no URL form:
|
|
514
|
+
* `Exists` (server-only, ADR-0028 §5), an empty `In` / `NotIn` list, or a
|
|
515
|
+
* `null` literal (the deprecated wire form of `isnull`; it evaluates to
|
|
516
|
+
* unknown and cannot be folded). `True` is the empty conjunction.
|
|
517
|
+
* `fromFilterAst ∘ toFilterAst` is the identity on every constructor-built
|
|
518
|
+
* predicate it accepts.
|
|
519
|
+
*/
|
|
520
|
+
declare const toFilterAst: (predicate: Predicate.Predicate) => Option.Option<Filter.Node>;
|
|
521
|
+
/**
|
|
522
|
+
* Fold the definite constants out of a predicate, exactly under ADR-0028's
|
|
523
|
+
* three-valued semantics: an empty `In` is false and an empty `NotIn` is
|
|
524
|
+
* true for every row (the compilers render them `1 = 0` / `1 = 1`), a
|
|
525
|
+
* false conjunct or a true disjunct decides its connective, and true
|
|
526
|
+
* conjuncts / false disjuncts vanish. Unknown is never folded — a `null`
|
|
527
|
+
* literal stays where it is. The result is `Predicate.always`, the empty
|
|
528
|
+
* `Or` (false), or a predicate free of empty lists.
|
|
529
|
+
*/
|
|
530
|
+
declare const foldConstants: (predicate: Predicate.Predicate) => Predicate.Predicate;
|
|
531
|
+
/**
|
|
532
|
+
* The node graph as effect-jsonapi resource definitions, one per node —
|
|
533
|
+
* the declaration the URL codecs are derived from. Attributes are typed by
|
|
534
|
+
* the node's filterable set (kind and operators), relationships are its
|
|
535
|
+
* edges; nothing else about the resource is faithful (a document is never
|
|
536
|
+
* serialised through it). Memoised per registry.
|
|
537
|
+
*/
|
|
538
|
+
declare const resourcesOf: (nodes: Node.NodeRegistry) => ReadonlyMap<string, Resource.Any>;
|
|
539
|
+
/**
|
|
540
|
+
* The root window's page parameters: `page[limit]` is the read's `first`
|
|
541
|
+
* (a positive integer), `page[after]` its `after` cursor (a row id,
|
|
542
|
+
* opaque on the wire), `page[offset]` its `offset` (a non-negative
|
|
543
|
+
* integer, #209 — every audited app's pagination idiom). `page[offset]`
|
|
544
|
+
* and `page[after]` are different pagination idioms; {@link parseParams}
|
|
545
|
+
* rejects a request naming both, and {@link decodedOf} emits `limit` then
|
|
546
|
+
* whichever of `after` / `offset` the read carries — the order the
|
|
547
|
+
* canonical string keeps.
|
|
548
|
+
*/
|
|
549
|
+
declare const PageWindow: {
|
|
550
|
+
limit: Schema.optionalKey<Schema.FiniteFromString>;
|
|
551
|
+
after: Schema.optionalKey<Schema.String>;
|
|
552
|
+
offset: Schema.optionalKey<Schema.FiniteFromString>;
|
|
553
|
+
};
|
|
554
|
+
/** The decoded shape of a collection's query, as the node-derived codec produces it. */
|
|
555
|
+
interface DecodedQuery {
|
|
556
|
+
readonly include?: ReadonlyArray<string>;
|
|
557
|
+
readonly fields?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
558
|
+
readonly sort?: ReadonlyArray<SortTerm>;
|
|
559
|
+
readonly page?: {
|
|
560
|
+
readonly limit?: number;
|
|
561
|
+
readonly after?: string;
|
|
562
|
+
readonly offset?: number;
|
|
563
|
+
};
|
|
564
|
+
readonly filter?: Filter.Node;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* The query codec of a collection's `list` endpoint — `include`, `fields[…]`,
|
|
568
|
+
* `sort`, `page[…]` and `filter[…]` exactly as the node graph declares them:
|
|
569
|
+
* the codec {@link parseParams} decodes with, so an effect-jsonapi endpoint
|
|
570
|
+
* served by a derived handler (`RestHandlers.list`) declares the grammar the
|
|
571
|
+
* handler parses when it is passed as `Endpoint.list(R, { query })`.
|
|
572
|
+
* effect-jsonapi's own `filter: true` derives a grammar from the *resource*
|
|
573
|
+
* instead, and the two spell a to-one relationship's filter differently: the
|
|
574
|
+
* resource grammar names the relationship (`filter[category]=c-audio`), the
|
|
575
|
+
* node graph — and every URL the engine emits ({@link canonical}) — names the
|
|
576
|
+
* key field (`filter[categoryId]=c-audio`, as ADR-0029's URLs do). One codec
|
|
577
|
+
* on both sides keeps the endpoint's contract and the handler's parse the
|
|
578
|
+
* same language. Every edge is includable under it, so a resource served
|
|
579
|
+
* with it declares its collection-valued edges `Relationship.many`, never
|
|
580
|
+
* `paginated` (whose `include=` only the resource grammar refuses). Throws a
|
|
581
|
+
* `QueryError` for an unknown collection — a definition-time defect.
|
|
582
|
+
*/
|
|
583
|
+
declare const querySchema: (nodes: Node.NodeRegistry, collection: string) => Schema.Codec<DecodedQuery, Readonly<Record<string, unknown>>>;
|
|
584
|
+
/** The decoded shape of a single-resource request's query: `include` and `fields[…]` only. */
|
|
585
|
+
interface SingleRowQuery {
|
|
586
|
+
readonly include?: ReadonlyArray<string>;
|
|
587
|
+
readonly fields?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* The query codec of a collection's `get` endpoint — `include` and
|
|
591
|
+
* `fields[…]` exactly as the node graph declares them, the two parameters a
|
|
592
|
+
* single-resource request takes ({@link singleRowParams}) — for
|
|
593
|
+
* `Endpoint.get(R, { query })`, the counterpart of {@link querySchema}: the
|
|
594
|
+
* endpoint's contract and the derived handler's parse are one grammar on
|
|
595
|
+
* the single-row route too. Throws a `QueryError` for an unknown collection.
|
|
596
|
+
*/
|
|
597
|
+
declare const singleRowSchema: (nodes: Node.NodeRegistry, collection: string) => Schema.Codec<SingleRowQuery, Readonly<Record<string, unknown>>>;
|
|
598
|
+
/** A sparse-fieldset projection: attribute names by resource type (ADR-0029 §5). */
|
|
599
|
+
type Projection = Readonly<Record<string, ReadonlyArray<string>>>;
|
|
600
|
+
/**
|
|
601
|
+
* A parsed URL: the declarative read, the `fields[]` projection beside it,
|
|
602
|
+
* and — from {@link parseRead} of a `/{type}/{id}` path — the row id the
|
|
603
|
+
* path addressed, so the caller knows to write a single-resource document
|
|
604
|
+
* (the read already carries it as `filter[id]`, first 1).
|
|
605
|
+
*/
|
|
606
|
+
interface ParsedRead {
|
|
607
|
+
readonly read: Query$1.Read;
|
|
608
|
+
readonly fields?: Projection;
|
|
609
|
+
readonly id?: string;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* The rejection of one query parameter, in the shape every check here
|
|
613
|
+
* raises (`parameter` names the offending key, so a caller answers 400 with
|
|
614
|
+
* `source.parameter`). Exported for a caller that checks a parsed URL
|
|
615
|
+
* against a declaration of its own — the replica-side writer against the
|
|
616
|
+
* resource it is bound to — and must reject the way the codec does.
|
|
617
|
+
*/
|
|
618
|
+
declare const invalidParameter: (collection: string, parameter: string, detail: string) => QueryError;
|
|
619
|
+
/**
|
|
620
|
+
* Parse a collection's query parameters into a read. `include`, `fields`,
|
|
621
|
+
* `sort`, `page` and `filter` are decoded with the node-derived query codec
|
|
622
|
+
* (unknown filter fields, undeclared operators, literals of the wrong kind,
|
|
623
|
+
* unsortable fields, unknown sparse fields and nested include paths all
|
|
624
|
+
* fail with a `QueryError` naming the parameter) and mapped onto the read;
|
|
625
|
+
* `fields[]` rides beside it as the projection.
|
|
626
|
+
*/
|
|
627
|
+
declare const parseParams: (nodes: Node.NodeRegistry, collection: string, params: URLSearchParams | Readonly<Record<string, string | ReadonlyArray<string>>>) => Effect.Effect<ParsedRead, QueryError>;
|
|
628
|
+
/**
|
|
629
|
+
* The query of a single-row read — `filter[id]=…`, first 1 (ADR-0029 §3) —
|
|
630
|
+
* from a `/{type}/{id}` request's own query, which takes `include` and
|
|
631
|
+
* `fields[…]` only. Shared by {@link parseRead} and the derived `get`
|
|
632
|
+
* handler, which has the id from the route instead of the path.
|
|
633
|
+
*/
|
|
634
|
+
declare const singleRowParams: (collection: string, id: string, params: URLSearchParams) => Effect.Effect<URLSearchParams, QueryError>;
|
|
635
|
+
/**
|
|
636
|
+
* Parse a request URL into a read. The collection is the path's last
|
|
637
|
+
* segment that names a node (`/api/products?…`, any prefix), or the
|
|
638
|
+
* segment before a row id (`/api/products/p-1?…`, which reads as
|
|
639
|
+
* `filter[id]=p-1` with `first` 1). The query is then {@link parseParams}.
|
|
640
|
+
*/
|
|
641
|
+
declare const parseRead: (nodes: Node.NodeRegistry, url: string | URL) => Effect.Effect<ParsedRead, QueryError>;
|
|
642
|
+
/**
|
|
643
|
+
* The canonical URL of a read — `/{collection}?{query}` (no API prefix;
|
|
644
|
+
* `parseRead` accepts it back) — through effect-jsonapi's canonical encoder
|
|
645
|
+
* over the compiled plan: one string per plan, whatever spelling the read
|
|
646
|
+
* used (`where` as a map or a predicate, conjuncts in any order, `True`
|
|
647
|
+
* conjuncts). It is the subscription identity of ADR-0029 §2. Throws a
|
|
648
|
+
* `QueryError` for a read the plan or the grammar refuses: an unknown
|
|
649
|
+
* collection or edge, a field outside the filterable set, an `Exists`, a
|
|
650
|
+
* `null` literal, or a per-edge window. A `fields[]` projection rides
|
|
651
|
+
* along when given.
|
|
652
|
+
*/
|
|
653
|
+
declare const canonical: (nodes: Node.NodeRegistry, read: Query$1.Read, fields?: Projection) => string;
|
|
654
|
+
/**
|
|
655
|
+
* The `Client.UrlReads` seam over this bridge (#159, ADR-0029 §2): pass as
|
|
656
|
+
* `urls` to `Client.make` / `Client.connect` / `Client.fromView` so
|
|
657
|
+
* `client.subscribe(url)` parses through {@link parseRead} and keys the pin
|
|
658
|
+
* by {@link canonical}, and to an engine host (`ServiceWorkerHost.run`) so
|
|
659
|
+
* a page with no Client can register by URL. The `fields[]` projection a
|
|
660
|
+
* URL carries is dropped: it never changes what a subscription syncs
|
|
661
|
+
* (ADR-0029 §5), so two URLs differing only in `fields[]` are one
|
|
662
|
+
* subscription.
|
|
663
|
+
*/
|
|
664
|
+
declare const urlReads: (nodes: Node.NodeRegistry) => Client.UrlReads;
|
|
665
|
+
/**
|
|
666
|
+
* The canonical flat `filter[...]` record of a filter AST — the grammar's
|
|
667
|
+
* own encoder (`Query.Filter`), so the shorthand, the group form, escaping
|
|
668
|
+
* and member order are effect-jsonapi's byte for byte.
|
|
669
|
+
*/
|
|
670
|
+
declare const flatFilter: (ast: Filter.Node) => Readonly<Record<string, string>>;
|
|
671
|
+
/**
|
|
672
|
+
* Build the JSON:API query string (with its leading `?`, or `` when empty)
|
|
673
|
+
* for `include` / `fields` / `filter` / `sort` / `page`, in the canonical
|
|
674
|
+
* family order and percent-encoding (`Query.serialise`). `filter` is a
|
|
675
|
+
* filter AST, encoded exactly through {@link flatFilter}.
|
|
676
|
+
*/
|
|
677
|
+
declare const buildQueryString: (options?: JsonApiRequestOptions) => string;
|
|
678
|
+
/**
|
|
679
|
+
* Encode a subset into a JSON:API query string (incl. leading `?`, or ``),
|
|
680
|
+
* or `None` when no row can match it (the request is skipped). A predicate
|
|
681
|
+
* without a URL form is a `QueryError`: the backend fails closed.
|
|
682
|
+
*/
|
|
683
|
+
interface QueryEncoder {
|
|
684
|
+
readonly encode: (subset: Subset.Subset) => Effect.Effect<Option.Option<string>, QueryError>;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* The request options a subset encodes to, or `None` when its predicate
|
|
688
|
+
* folds to false. The filter is the whole predicate, constants folded;
|
|
689
|
+
* `orderBy` becomes `sort`. Limit and cursor are not pushed: the window is
|
|
690
|
+
* applied locally over the exact filter. Throws a `QueryError` for a
|
|
691
|
+
* predicate with no URL form (`Exists`, a `null` literal).
|
|
692
|
+
*/
|
|
693
|
+
declare const toRequestOptions: (subset: Subset.Subset) => Option.Option<JsonApiRequestOptions>;
|
|
694
|
+
/** The shipped encoder: exact under ADR-0028 (see the module doc). */
|
|
695
|
+
declare const defaultEncoder: QueryEncoder;
|
|
696
|
+
declare namespace CollectionLoader_d_exports {
|
|
697
|
+
export { CollectionRow, JsonApiSource, LoadedRows, RelationshipRef, ResourceValue$1 as ResourceValue, buildQueryString, documentToRows, load, loadOne, parseResponse, resourceToRow };
|
|
698
|
+
}
|
|
699
|
+
/** A relationship as it lands in a row value: linkage only, no documents. */
|
|
700
|
+
type RelationshipRef = {
|
|
701
|
+
readonly type: string;
|
|
702
|
+
readonly id: string;
|
|
703
|
+
} | ReadonlyArray<{
|
|
704
|
+
readonly type: string;
|
|
705
|
+
readonly id: string;
|
|
706
|
+
}> | null;
|
|
707
|
+
/** The value a JSON:API resource takes as a collection row. */
|
|
708
|
+
interface ResourceValue$1 {
|
|
709
|
+
readonly type: string;
|
|
710
|
+
readonly id: string;
|
|
711
|
+
readonly attributes: Record<string, unknown>;
|
|
712
|
+
readonly relationships: Record<string, RelationshipRef>;
|
|
713
|
+
}
|
|
714
|
+
/** A collection row materialized from a resource: (collection, id, value). */
|
|
715
|
+
interface CollectionRow {
|
|
716
|
+
readonly collection: string;
|
|
717
|
+
readonly id: string;
|
|
718
|
+
readonly value: ResourceValue$1;
|
|
719
|
+
}
|
|
720
|
+
/** The rows a document yields: its primary data and its compound `included`. */
|
|
721
|
+
interface LoadedRows {
|
|
722
|
+
readonly rows: ReadonlyArray<CollectionRow>;
|
|
723
|
+
readonly included: ReadonlyArray<CollectionRow>;
|
|
724
|
+
}
|
|
725
|
+
/** Map a resource (or bare identifier) to a collection row. */
|
|
726
|
+
declare const resourceToRow: (resource: ResourceObject | ResourceIdentifier) => CollectionRow;
|
|
727
|
+
/** Map a whole document to its primary rows and its `included` rows. */
|
|
728
|
+
declare const documentToRows: (document: JsonApiDocument) => LoadedRows;
|
|
729
|
+
/** The JSON:API endpoint a loader reads from. */
|
|
730
|
+
interface JsonApiSource {
|
|
731
|
+
readonly baseUrl: string;
|
|
732
|
+
}
|
|
733
|
+
/** The {@link JsonApiSource} service loaders read from. */
|
|
734
|
+
declare const JsonApiSource: Context.Service<JsonApiSource, JsonApiSource>;
|
|
735
|
+
/** Validate a JSON:API response into a document (non-2xx / `errors` → ApiError). */
|
|
736
|
+
declare const parseResponse: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<JsonApiDocument, ParseError | ApiError | HttpClientError.HttpClientError>;
|
|
737
|
+
/** Load a collection (`GET /{type}`) into rows. */
|
|
738
|
+
declare const load: (type: string, options?: JsonApiRequestOptions) => Effect.Effect<LoadedRows, ParseError | ApiError | HttpClientError.HttpClientError, HttpClient.HttpClient | JsonApiSource>;
|
|
739
|
+
/** Load a single resource (`GET /{type}/{id}`); `undefined` if absent. */
|
|
740
|
+
declare const loadOne: (type: string, id: string, options?: JsonApiRequestOptions) => Effect.Effect<CollectionRow | undefined, ParseError | ApiError | HttpClientError.HttpClientError, HttpClient.HttpClient | JsonApiSource>;
|
|
741
|
+
declare namespace Convention_d_exports {
|
|
742
|
+
export { EndpointRef, Operation, ResourceEndpoints, ResourceMap, classify, isAtomicEndpoint, isParam, reflectAtomic, reflectResources, segmentsOf };
|
|
743
|
+
}
|
|
744
|
+
/** A JSON:API role a CRUD endpoint plays for one resource type. */
|
|
745
|
+
type Operation = "list" | "getOne" | "create" | "update" | "delete";
|
|
746
|
+
/** A reflected endpoint we can address on the app's HttpApi. */
|
|
747
|
+
interface EndpointRef {
|
|
748
|
+
readonly groupId: string;
|
|
749
|
+
readonly name: string;
|
|
750
|
+
readonly method: string;
|
|
751
|
+
/** Route template as declared, e.g. `/todos/:id`. */
|
|
752
|
+
readonly path: string;
|
|
753
|
+
}
|
|
754
|
+
/** The CRUD endpoints discovered for one resource type (collection). */
|
|
755
|
+
interface ResourceEndpoints {
|
|
756
|
+
readonly type: string;
|
|
757
|
+
readonly list?: EndpointRef;
|
|
758
|
+
readonly getOne?: EndpointRef;
|
|
759
|
+
readonly create?: EndpointRef;
|
|
760
|
+
readonly update?: EndpointRef;
|
|
761
|
+
readonly delete?: EndpointRef;
|
|
762
|
+
}
|
|
763
|
+
/** Resource endpoints keyed by type (collection name). */
|
|
764
|
+
type ResourceMap = ReadonlyMap<string, ResourceEndpoints>;
|
|
765
|
+
/** A route template's segments, empty ones dropped (`/todos/:id` is `["todos", ":id"]`). */
|
|
766
|
+
declare const segmentsOf: (path: string) => ReadonlyArray<string>;
|
|
767
|
+
/** Whether a template segment is a path parameter (`:id`). */
|
|
768
|
+
declare const isParam: (segment: string) => boolean;
|
|
769
|
+
/**
|
|
770
|
+
* Classify a (method, path) into a resource type + role, or `undefined` if
|
|
771
|
+
* it isn't a conventional JSON:API CRUD route — `/{type}` or `/{type}/:id`,
|
|
772
|
+
* where the type is a static segment and the only param is a trailing id
|
|
773
|
+
* (nested routes like `/todos/:id/comments` are not CRUD on a single type).
|
|
774
|
+
*/
|
|
775
|
+
declare const classify: (method: string, path: string) => {
|
|
776
|
+
readonly type: string;
|
|
777
|
+
readonly operation: Operation;
|
|
778
|
+
} | undefined;
|
|
779
|
+
/**
|
|
780
|
+
* Whether an endpoint takes an `atomic:operations` request document: some
|
|
781
|
+
* payload schema (any content type) is an object with a top-level
|
|
782
|
+
* `atomic:operations` member, possibly one arm of a union. This is what
|
|
783
|
+
* makes an endpoint the atomic operations endpoint — not its path, which is
|
|
784
|
+
* configurable.
|
|
785
|
+
*/
|
|
786
|
+
declare const isAtomicEndpoint: (endpoint: HttpApiEndpoint.Top) => boolean;
|
|
787
|
+
/**
|
|
788
|
+
* Reflect an `HttpApi` into its JSON:API resource surface. The first endpoint
|
|
789
|
+
* matching each (type, role) wins; later duplicates are ignored. An atomic
|
|
790
|
+
* operations endpoint is never a resource (see `reflectAtomic`).
|
|
791
|
+
*/
|
|
792
|
+
declare const reflectResources: <Id extends string, Groups extends HttpApiGroup.Constraint>(api: HttpApi.HttpApi<Id, Groups>) => ResourceMap;
|
|
793
|
+
/**
|
|
794
|
+
* Reflect an `HttpApi`'s atomic operations endpoint, if it declares one: the
|
|
795
|
+
* first endpoint whose request payload is an `atomic:operations` document.
|
|
796
|
+
* `JsonApiSync` uses it to push a batch as one all-or-nothing request.
|
|
797
|
+
*/
|
|
798
|
+
declare const reflectAtomic: <Id extends string, Groups extends HttpApiGroup.Constraint>(api: HttpApi.HttpApi<Id, Groups>) => EndpointRef | undefined;
|
|
799
|
+
declare namespace Derive_d_exports {
|
|
800
|
+
export { CreateArgs, DeleteArgs, Derived, JsonApiRow, OperationInfo, RowSchema, UpdateArgs, collectionFor, derive, mutatorName, toRow };
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* The engine row for a JSON:API resource: attributes flattened to the top
|
|
804
|
+
* level so predicates and orderBy address them directly (e.g.
|
|
805
|
+
* `Predicate.eq("status", "open")`), exactly as JSON:API `filter[status]`
|
|
806
|
+
* does. The keys `id` and `relationships` are reserved.
|
|
807
|
+
*/
|
|
808
|
+
interface JsonApiRow {
|
|
809
|
+
readonly id: string;
|
|
810
|
+
readonly relationships: Record<string, RelationshipRef>;
|
|
811
|
+
readonly [field: string]: unknown;
|
|
812
|
+
}
|
|
813
|
+
/** Flatten a loaded resource into an engine row (attributes at top level). */
|
|
814
|
+
declare const toRow: (value: ResourceValue$1) => JsonApiRow;
|
|
815
|
+
/** Permissive codec for a row (the engine never decodes rows through it). */
|
|
816
|
+
declare const RowSchema: Schema.Codec<JsonApiRow>;
|
|
817
|
+
/** The collection for a resource type: rows keyed by resource id. */
|
|
818
|
+
declare const collectionFor: (type: string) => Collection.Collection<JsonApiRow>;
|
|
819
|
+
/** The mutator name for a (type, operation): `"{type}/{operation}"`. */
|
|
820
|
+
declare const mutatorName: (type: string, operation: Operation) => string;
|
|
821
|
+
/** Create: the full resource to insert (client supplies the id). */
|
|
822
|
+
declare const CreateArgs: Schema.Struct<{
|
|
823
|
+
readonly type: Schema.String;
|
|
824
|
+
readonly id: Schema.String;
|
|
825
|
+
readonly attributes: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
826
|
+
readonly relationships: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
|
|
827
|
+
}>;
|
|
828
|
+
/** The type {@link CreateArgs} decodes to. */
|
|
829
|
+
type CreateArgs = typeof CreateArgs.Type;
|
|
830
|
+
/** Update: a partial attribute patch, merged over the current row. */
|
|
831
|
+
declare const UpdateArgs: Schema.Struct<{
|
|
832
|
+
readonly type: Schema.String;
|
|
833
|
+
readonly id: Schema.String;
|
|
834
|
+
readonly attributes: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
835
|
+
}>;
|
|
836
|
+
/** The type {@link UpdateArgs} decodes to. */
|
|
837
|
+
type UpdateArgs = typeof UpdateArgs.Type;
|
|
838
|
+
/** Delete: address one row. */
|
|
839
|
+
declare const DeleteArgs: Schema.Struct<{
|
|
840
|
+
readonly type: Schema.String;
|
|
841
|
+
readonly id: Schema.String;
|
|
842
|
+
}>;
|
|
843
|
+
/** The type {@link DeleteArgs} decodes to. */
|
|
844
|
+
type DeleteArgs = typeof DeleteArgs.Type;
|
|
845
|
+
/** Which CRUD operation a derived mutator performs, and on what type. */
|
|
846
|
+
interface OperationInfo {
|
|
847
|
+
readonly operation: Operation;
|
|
848
|
+
readonly type: string;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* What {@link derive} produces from a resource map: collections, mutators and their CRUD mapping.
|
|
852
|
+
*/
|
|
853
|
+
interface Derived {
|
|
854
|
+
readonly collections: ReadonlyArray<Collection.AnyCollection>;
|
|
855
|
+
/** Mutator definitions + client (optimistic) handlers. */
|
|
856
|
+
readonly registry: Mutator.MutatorRegistry;
|
|
857
|
+
/** Mutator name → CRUD operation + type, for the authoritative backend. */
|
|
858
|
+
readonly operations: ReadonlyMap<string, OperationInfo>;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Derive collections, CRUD mutators, and operation metadata from a resource
|
|
862
|
+
* map. A mutator is only derived when the resource exposes the corresponding
|
|
863
|
+
* write endpoint (no create endpoint ⇒ no `create` mutator).
|
|
864
|
+
*/
|
|
865
|
+
declare const derive: (resources: ResourceMap) => Derived;
|
|
866
|
+
declare namespace DocumentWriter_d_exports {
|
|
867
|
+
export { CollectionDocument, DataDocument, Identifier, Input, Linkage, RelationshipValue, ResourceValue, collection, data, idOf, relatedValuesOf };
|
|
868
|
+
}
|
|
869
|
+
/** A resource identifier: `{ type, id }`. */
|
|
870
|
+
interface Identifier {
|
|
871
|
+
readonly type: string;
|
|
872
|
+
readonly id: string;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* A relationship object as written: inline linkage for `one` / `many`
|
|
876
|
+
* edges; links for a paginated edge, with its rows' linkage when resolved.
|
|
877
|
+
*/
|
|
878
|
+
type RelationshipValue = {
|
|
879
|
+
readonly data: Identifier | ReadonlyArray<Identifier> | null;
|
|
880
|
+
} | {
|
|
881
|
+
readonly links: {
|
|
882
|
+
readonly self: string;
|
|
883
|
+
readonly related: string;
|
|
884
|
+
};
|
|
885
|
+
readonly data?: ReadonlyArray<Identifier>;
|
|
886
|
+
};
|
|
887
|
+
/** A resource object as written: the node's attributes and its edges' relationships. */
|
|
888
|
+
interface ResourceValue {
|
|
889
|
+
readonly type: string;
|
|
890
|
+
readonly id: string;
|
|
891
|
+
readonly attributes: Readonly<Record<string, unknown>>;
|
|
892
|
+
readonly relationships?: Readonly<Record<string, RelationshipValue>>;
|
|
893
|
+
}
|
|
894
|
+
/** A collection document: `{ data: [...], included?, links }`, with `included` exactly when the read has an `include`. */
|
|
895
|
+
type CollectionDocument = Handlers.DocumentValue<ReadonlyArray<ResourceValue>, ResourceValue>;
|
|
896
|
+
/** A single-resource document: `{ data: resource | null, included?, links }`, with `included` exactly when the read has an `include`. */
|
|
897
|
+
type DataDocument = Handlers.DocumentValue<ResourceValue | null, ResourceValue>;
|
|
898
|
+
/** Edge rows by collection, then row id, then edge name (see {@link Input.linkage}). */
|
|
899
|
+
type Linkage = ReadonlyMap<string, ReadonlyMap<string, Readonly<Record<string, ReadonlyArray<unknown>>>>>;
|
|
900
|
+
/** What a document is a function of (ADR-0029 §6). */
|
|
901
|
+
interface Input {
|
|
902
|
+
readonly nodes: Node.NodeRegistry;
|
|
903
|
+
/** The read as parsed from the URL — its `include` selects `included`, its plan is `links.self`. */
|
|
904
|
+
readonly read: Query$1.Read;
|
|
905
|
+
/**
|
|
906
|
+
* The read's resolved rows (`Query.runVia` / `Query.run`): root rows in
|
|
907
|
+
* window order, each with the edge rows resolved onto `related`. Edges
|
|
908
|
+
* beyond `read.include` may be present (the linkage plan resolves every
|
|
909
|
+
* declared `many` edge); they contribute linkage, not `included`.
|
|
910
|
+
*/
|
|
911
|
+
readonly rows: ReadonlyArray<Related.RelatedRow>;
|
|
912
|
+
/**
|
|
913
|
+
* Collection-valued edge rows resolved for resources beyond the root rows
|
|
914
|
+
* — the included resources' own `many` edges — by collection, then row
|
|
915
|
+
* id, then edge name. Consulted where a row has no `related` entry for
|
|
916
|
+
* the edge; a parent listed with an empty array gets `data: []`.
|
|
917
|
+
*/
|
|
918
|
+
readonly linkage?: Linkage;
|
|
919
|
+
/** The `fields[]` projection, applied to attributes at the end. */
|
|
920
|
+
readonly fields?: Projection;
|
|
921
|
+
/** Prepended to every link (the api's mount path, e.g. `/jsonapi`); default `""`. */
|
|
922
|
+
readonly prefix?: string;
|
|
923
|
+
}
|
|
924
|
+
/** The row id a row value carries (node rows carry `id` in the value). */
|
|
925
|
+
declare const idOf: (value: unknown) => string;
|
|
926
|
+
/** A resolved edge's value as its rows: a list as it is, a single row alone, `null` / `undefined` as none. */
|
|
927
|
+
declare const relatedValuesOf: (value: Related.RelatedValue) => ReadonlyArray<unknown>;
|
|
928
|
+
/**
|
|
929
|
+
* The collection document of a read: every row of its page as primary
|
|
930
|
+
* data, the included edges compounded, `links.self` canonical and
|
|
931
|
+
* `links.next` (and, for an offset read past the first page, `links.prev`)
|
|
932
|
+
* when the page is full. Throws for a read the canonical encoder refuses
|
|
933
|
+
* (a per-edge window, an `Exists`) — such a read has no URL, so it is
|
|
934
|
+
* never one a URL produced.
|
|
935
|
+
*/
|
|
936
|
+
declare const collection: (input: Input) => CollectionDocument;
|
|
937
|
+
/**
|
|
938
|
+
* The single-resource document of a read: the first row as primary data
|
|
939
|
+
* (`null` when the read matched nothing — the caller decides the 404), the
|
|
940
|
+
* included edges compounded, `links.self` canonical.
|
|
941
|
+
*/
|
|
942
|
+
declare const data: (input: Input) => DataDocument;
|
|
943
|
+
declare namespace Ledger_d_exports {
|
|
944
|
+
export { GroupLedger, LedgerStore, Rejection, emptyGroup, keyValue, memory };
|
|
945
|
+
}
|
|
946
|
+
/** A permanent rejection the ledger remembers: the mutation and the reason it was refused. */
|
|
947
|
+
interface Rejection {
|
|
948
|
+
readonly clientId: string;
|
|
949
|
+
readonly mutationId: number;
|
|
950
|
+
readonly reason: string;
|
|
951
|
+
}
|
|
952
|
+
/** One client group's ledger, as a store holds it. */
|
|
953
|
+
interface GroupLedger {
|
|
954
|
+
/** Highest acknowledged mutation id per client. */
|
|
955
|
+
readonly lastMutationIds: Readonly<Record<string, number>>;
|
|
956
|
+
/** The rejections still remembered, for replayed copies and pulls. */
|
|
957
|
+
readonly rejections: ReadonlyArray<Rejection>;
|
|
958
|
+
}
|
|
959
|
+
/** An empty group ledger: nothing acknowledged, nothing remembered. */
|
|
960
|
+
declare const emptyGroup: GroupLedger;
|
|
961
|
+
/**
|
|
962
|
+
* Where a `JsonApiBackend` reads and writes each client group's ledger (#468).
|
|
963
|
+
* `read` is called once per group when the backend first needs it; `write`
|
|
964
|
+
* replaces the group's stored ledger after each change, and only after the
|
|
965
|
+
* server has applied the writes the change acknowledges.
|
|
966
|
+
*/
|
|
967
|
+
interface LedgerStore {
|
|
968
|
+
/** The stored ledger of a client group, or {@link emptyGroup} when it has none. */
|
|
969
|
+
readonly read: (clientGroupId: string) => Effect.Effect<GroupLedger, BackendError>;
|
|
970
|
+
/** Replace a client group's stored ledger with `ledger`. */
|
|
971
|
+
readonly write: (clientGroupId: string, ledger: GroupLedger) => Effect.Effect<void, BackendError>;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* The in-memory ledger store: it persists nothing, so the backend keeps each
|
|
975
|
+
* group's ledger in its own memory for the life of the instance. A restart of
|
|
976
|
+
* the backend forgets every acknowledged id and remembered rejection.
|
|
977
|
+
*/
|
|
978
|
+
declare const memory: LedgerStore;
|
|
979
|
+
/**
|
|
980
|
+
* A ledger store over an Effect `KeyValueStore`: each client group's ledger is
|
|
981
|
+
* one JSON value under `${prefix}${clientGroupId}`. Use a durable key/value
|
|
982
|
+
* store (a file system, SQL or browser storage layer) for it to survive a
|
|
983
|
+
* restart. A stored value that does not decode is a `BackendError`, not an
|
|
984
|
+
* empty ledger, so a corrupt store is never mistaken for a fresh one.
|
|
985
|
+
*/
|
|
986
|
+
declare const keyValue: (store: KeyValueStore.KeyValueStore, options?: {
|
|
987
|
+
readonly prefix?: string | undefined;
|
|
988
|
+
}) => LedgerStore;
|
|
989
|
+
declare namespace JsonApiBackend_d_exports {
|
|
990
|
+
export { JsonApiBackendConfig, Write, make$2 as make, versionOf };
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* One JSON:API write — the unit both delivery paths speak: a create (`POST`,
|
|
994
|
+
* or atomic `add`), update (`PATCH` / `update`) or delete (`DELETE` /
|
|
995
|
+
* `remove`) of one resource. Creates carry the client-generated id.
|
|
996
|
+
*/
|
|
997
|
+
interface Write {
|
|
998
|
+
readonly operation: "create" | "update" | "delete";
|
|
999
|
+
readonly type: string;
|
|
1000
|
+
readonly id: string;
|
|
1001
|
+
readonly attributes?: Record<string, unknown>;
|
|
1002
|
+
readonly relationships?: Record<string, unknown>;
|
|
1003
|
+
}
|
|
1004
|
+
/** Configuration for {@link make}. */
|
|
1005
|
+
interface JsonApiBackendConfig {
|
|
1006
|
+
/** Base URL the resource paths are resolved against (no trailing slash). */
|
|
1007
|
+
readonly baseUrl: string;
|
|
1008
|
+
readonly resources: ResourceMap;
|
|
1009
|
+
/** Mutator definitions, for decoding/validating pushed args. */
|
|
1010
|
+
readonly mutators: ReadonlyMap<string, Mutator.AnyMutator>;
|
|
1011
|
+
/** Mutator name → CRUD operation + type. */
|
|
1012
|
+
readonly operations: ReadonlyMap<string, OperationInfo>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Mutators beyond the derived CRUD ones, and how to express each one's
|
|
1015
|
+
* decoded args as a single JSON:API `Write`. Consulted before `operations`,
|
|
1016
|
+
* so an app can push its own named intents through this backend as long
|
|
1017
|
+
* as each is one create/update/delete of one resource.
|
|
1018
|
+
*/
|
|
1019
|
+
readonly writes?: ReadonlyMap<string, (args: unknown) => Write> | undefined;
|
|
1020
|
+
/**
|
|
1021
|
+
* The server's JSON:API Atomic Operations endpoint (`POST {path}`), when it
|
|
1022
|
+
* has one. With it set, `applyMutations` pushes a whole batch as one
|
|
1023
|
+
* all-or-nothing `atomic:operations` request; without it, each mutation is
|
|
1024
|
+
* its own request. `JsonApiSync` fills this in from the reflected api
|
|
1025
|
+
* (`Convention.reflectAtomic`).
|
|
1026
|
+
*/
|
|
1027
|
+
readonly atomic?: {
|
|
1028
|
+
readonly path: string;
|
|
1029
|
+
} | undefined;
|
|
1030
|
+
/** Subset → JSON:API query string. Defaults to the exact filter-grammar encoder. */
|
|
1031
|
+
readonly query?: QueryEncoder | undefined;
|
|
1032
|
+
/**
|
|
1033
|
+
* Relationship names to compound-load per collection (`?include=...`). When a
|
|
1034
|
+
* listing pulls in related resources, they are harvested so an auto-subscribed
|
|
1035
|
+
* `id IN (...)` subset over those collections is served from the same response
|
|
1036
|
+
* — no extra round trip. See `Relationships.related`.
|
|
1037
|
+
*/
|
|
1038
|
+
readonly includes?: Readonly<Record<string, ReadonlyArray<string>>> | undefined;
|
|
1039
|
+
/**
|
|
1040
|
+
* Where each client group's ledger (its acknowledged mutation ids and
|
|
1041
|
+
* remembered rejections) is kept across restarts (#468). Unset, the ledger
|
|
1042
|
+
* lives in this instance's memory (`Ledger.memory`), so a restart forgets it.
|
|
1043
|
+
* `Ledger.keyValue` over a durable Effect `KeyValueStore` keeps it.
|
|
1044
|
+
*/
|
|
1045
|
+
readonly ledger?: LedgerStore | undefined;
|
|
1046
|
+
}
|
|
1047
|
+
/** A content-hash row version: a plain REST API exposes no version token. */
|
|
1048
|
+
declare const versionOf: (value: unknown) => string;
|
|
1049
|
+
/** Build the JsonApiBackend from its config; requires an `HttpClient`. */
|
|
1050
|
+
declare const make$2: (config: JsonApiBackendConfig) => Effect.Effect<Backend.Backend, never, HttpClient.HttpClient>;
|
|
1051
|
+
declare namespace FacadeWrites_d_exports {
|
|
1052
|
+
export { Intent, IntentOf, InvalidWriteArgs, Preferences, Refusal, Target, decodeWrite, graphIntentOf, preferencesOf, writesOf };
|
|
1053
|
+
}
|
|
1054
|
+
/** The preferences a façade write reads from `Prefer` (RFC 7240 §4); others are forwarded untouched. */
|
|
1055
|
+
interface Preferences {
|
|
1056
|
+
/** `Prefer: respond-async`: journal and answer a receipt even when online. */
|
|
1057
|
+
readonly respondAsync: boolean;
|
|
1058
|
+
/** `Prefer: handling=strict`: never journal. */
|
|
1059
|
+
readonly strict: boolean;
|
|
1060
|
+
/** `Prefer: wait=N`, in seconds, when present and a non-negative integer. */
|
|
1061
|
+
readonly wait?: number;
|
|
1062
|
+
}
|
|
1063
|
+
/** Read the `Prefer` headers of a request (several headers and comma-separated lists alike). */
|
|
1064
|
+
declare const preferencesOf: (headers: Headers) => Preferences;
|
|
1065
|
+
/**
|
|
1066
|
+
* Why a write's body cannot be journaled, as the error object an offline
|
|
1067
|
+
* façade answers with (ADR-0030 §3): `400 invalid_document`, `409
|
|
1068
|
+
* type_mismatch` / `id_mismatch`, `422 unsupported_relationship`, or the
|
|
1069
|
+
* `client_id_required` a `POST` without an id meets.
|
|
1070
|
+
*/
|
|
1071
|
+
interface Refusal {
|
|
1072
|
+
readonly status: 400 | 409 | 422 | 503;
|
|
1073
|
+
readonly code: "invalid_document" | "type_mismatch" | "id_mismatch" | "unsupported_relationship" | "client_id_required";
|
|
1074
|
+
readonly title: string;
|
|
1075
|
+
readonly detail: string;
|
|
1076
|
+
readonly pointer?: string;
|
|
1077
|
+
}
|
|
1078
|
+
/** What a write route matched: its operation, the route's type, and the path's id for `PATCH` / `DELETE`. */
|
|
1079
|
+
type Target = {
|
|
1080
|
+
readonly operation: "create";
|
|
1081
|
+
readonly type: string;
|
|
1082
|
+
} | {
|
|
1083
|
+
readonly operation: "update" | "delete";
|
|
1084
|
+
readonly type: string;
|
|
1085
|
+
readonly id: string;
|
|
1086
|
+
};
|
|
1087
|
+
/**
|
|
1088
|
+
* Decode a write's body structurally into a `Write` whose `relationships`
|
|
1089
|
+
* are flattened into `one`-edge ids by relationship name (`null` clears).
|
|
1090
|
+
* A `DELETE` reads no body. Fails with the {@link Refusal} the offline façade
|
|
1091
|
+
* answers with; a create without a client id is refused only once the rest
|
|
1092
|
+
* of its body has passed.
|
|
1093
|
+
*/
|
|
1094
|
+
declare const decodeWrite: (node: Node.AnyNode, target: Target, body: Effect.Effect<string, unknown>) => Effect.Effect<Write, Refusal>;
|
|
1095
|
+
/** The mutation a write is journaled as: a registered mutator name and its encoded args. */
|
|
1096
|
+
interface Intent {
|
|
1097
|
+
readonly mutator: string;
|
|
1098
|
+
readonly args: unknown;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* The intent mapping (ADR-0030 §5): a decoded `Write` to the mutation the
|
|
1102
|
+
* engine journals, or `undefined` when the write is not interceptable.
|
|
1103
|
+
* `relationships` arrive flattened by {@link decodeWrite}: relationship name
|
|
1104
|
+
* to related id (or `null`).
|
|
1105
|
+
*/
|
|
1106
|
+
type IntentOf = (write: Write) => Intent | undefined;
|
|
1107
|
+
/**
|
|
1108
|
+
* The default intent mapping: the graph-derived CRUD mutations
|
|
1109
|
+
* (`CrudMutation`), args in the graph row form — the attributes plus, per
|
|
1110
|
+
* `one` edge the payload links, its `via` key.
|
|
1111
|
+
*/
|
|
1112
|
+
declare const graphIntentOf: (nodes: Node.NodeRegistry) => IntentOf;
|
|
1113
|
+
declare const InvalidWriteArgs_base: Schema.Class<InvalidWriteArgs, Schema.TaggedStruct<"InvalidWriteArgs", {
|
|
1114
|
+
readonly message: Schema.String;
|
|
1115
|
+
}>, import("effect/Cause").YieldableError & {
|
|
1116
|
+
readonly brand: unique symbol;
|
|
1117
|
+
}>;
|
|
1118
|
+
/** Graph-derived CRUD args that do not fit the mutation they were journaled under. */
|
|
1119
|
+
declare class InvalidWriteArgs extends InvalidWriteArgs_base {}
|
|
1120
|
+
/**
|
|
1121
|
+
* The inverse of {@link graphIntentOf} (ADR-0030 §5): for every graph-derived
|
|
1122
|
+
* CRUD mutation (`CrudMutation`, filtered by `options.writes` as the registry
|
|
1123
|
+
* is), how its args replay as one JSON:API write. Pass it as a
|
|
1124
|
+
* `JsonApiBackend`'s `writes`, so a node-graph app's journaled `{type}/create`,
|
|
1125
|
+
* `{type}/update` and `{type}/delete` reach the server as `POST`, `PATCH` and
|
|
1126
|
+
* `DELETE` (or atomic operations), with each `one` edge's `via` key rebuilt as
|
|
1127
|
+
* the relationship's linkage. The write's `type` is always the mutation's own:
|
|
1128
|
+
* args naming another type are refused ({@link InvalidWriteArgs}).
|
|
1129
|
+
*/
|
|
1130
|
+
declare const writesOf: (nodes: Node.NodeRegistry, options?: CrudMutation.Options) => NonNullable<JsonApiBackendConfig["writes"]>;
|
|
1131
|
+
declare namespace RestHandlers_d_exports {
|
|
1132
|
+
export { CollectionDocumentOf, DataDocumentOf, ErrorDocument, GetInput, GetOptions, ListInput, Options$2 as Options, RestHandlers, make$1 as make, queryErrorDocument };
|
|
1133
|
+
}
|
|
1134
|
+
/** What the derived handlers run over: the graph, the read path's seams, and the request's identity. */
|
|
1135
|
+
interface Options$2 {
|
|
1136
|
+
/** The node graph the URLs are parsed against and the rows are written by. */
|
|
1137
|
+
readonly nodes: Node.NodeRegistry;
|
|
1138
|
+
/** The storage contract the read evaluates through. */
|
|
1139
|
+
readonly backend: Backend.Backend;
|
|
1140
|
+
/** The deny-by-default auth policy, with the filterable declarations (#154). */
|
|
1141
|
+
readonly policy: Authorization.AuthPolicy;
|
|
1142
|
+
/**
|
|
1143
|
+
* The identity seam: an HTTP request to the `SyncContext` the auth filters
|
|
1144
|
+
* see — the HTTP analogue of `IdentityResolver`, which resolves a client
|
|
1145
|
+
* group. Derive `identity` from the verified session (a forwarded header
|
|
1146
|
+
* the app trusts, a session token); never from anything a browser can
|
|
1147
|
+
* forge. `clientGroupId` names the carrier (`"http"`) or the session.
|
|
1148
|
+
*/
|
|
1149
|
+
readonly context: (request: HttpServerRequest) => Effect.Effect<Backend.SyncContext, AuthorizationError>;
|
|
1150
|
+
/** The interception seam around reads (`onRead`); absent means passthrough. */
|
|
1151
|
+
readonly middleware?: Authorization.SyncMiddleware;
|
|
1152
|
+
/** The api's mount path, prepended to every link (`/jsonapi`); default `""`. */
|
|
1153
|
+
readonly prefix?: string;
|
|
1154
|
+
}
|
|
1155
|
+
/** The handler input a `list` endpoint gives (the rest of the request is not needed). */
|
|
1156
|
+
interface ListInput {
|
|
1157
|
+
readonly request: HttpServerRequest;
|
|
1158
|
+
}
|
|
1159
|
+
/** The handler input a `get` endpoint gives: the `:id` path parameter and the request. */
|
|
1160
|
+
interface GetInput {
|
|
1161
|
+
readonly params: {
|
|
1162
|
+
readonly id: string;
|
|
1163
|
+
};
|
|
1164
|
+
readonly request: HttpServerRequest;
|
|
1165
|
+
}
|
|
1166
|
+
/** The collection document type of a resource's default `Endpoint.list` success. */
|
|
1167
|
+
type CollectionDocumentOf<R extends Resource.Any> = Document.CollectionDocument<R, Resource.DefaultIncluded<R["relationships"]>, typeof Document.AnyMeta>["Type"];
|
|
1168
|
+
/** The single-resource document type of a resource's default `Endpoint.get` success. */
|
|
1169
|
+
type DataDocumentOf<R extends Resource.Any> = Document.DataDocument<R, Resource.DefaultIncluded<R["relationships"]>, typeof Document.AnyMeta>["Type"];
|
|
1170
|
+
/** Options of a derived `get`: the endpoint's own 404. */
|
|
1171
|
+
interface GetOptions<E> {
|
|
1172
|
+
/** The error to fail with when no row matches — the `ApiError` class the endpoint declares. */
|
|
1173
|
+
readonly notFound: () => E;
|
|
1174
|
+
}
|
|
1175
|
+
/** The derived handler factories, bound to one graph and one read path. */
|
|
1176
|
+
interface RestHandlers {
|
|
1177
|
+
/**
|
|
1178
|
+
* The `list` handler of a resource: `GET /{type}?…` as a read over the
|
|
1179
|
+
* node of the same type. Typed as the resource's default collection
|
|
1180
|
+
* document; the endpoint's success encoder checks the document at
|
|
1181
|
+
* runtime, so a resource whose declaration disagrees with the node graph
|
|
1182
|
+
* (an attribute the node calls an edge) fails there, not silently.
|
|
1183
|
+
*/
|
|
1184
|
+
readonly list: <R extends Resource.Any>(resource: R) => (input: ListInput) => Effect.Effect<CollectionDocumentOf<R> | HttpServerResponse.HttpServerResponse>;
|
|
1185
|
+
/**
|
|
1186
|
+
* The `get` handler of a resource: `GET /{type}/{id}?…` as the read
|
|
1187
|
+
* `filter[id]={id}`, first 1, taking `include` and `fields[…]` only.
|
|
1188
|
+
*/
|
|
1189
|
+
readonly get: <R extends Resource.Any, E>(resource: R, options: GetOptions<E>) => (input: GetInput) => Effect.Effect<DataDocumentOf<R> | HttpServerResponse.HttpServerResponse, E>;
|
|
1190
|
+
}
|
|
1191
|
+
/** A JSON:API error document value (the shape `ApiError.toDocument` produces). */
|
|
1192
|
+
type ErrorDocument = typeof ApiError$1.WireDocument.Type;
|
|
1193
|
+
/**
|
|
1194
|
+
* The 400 error document of a URL the grammar rejects: the codec's own
|
|
1195
|
+
* document (`Middleware.schemaErrorDocument`, one error object per issue,
|
|
1196
|
+
* each with `source.parameter`) when the rejection came from decoding; the
|
|
1197
|
+
* same shape around the `QueryError`'s message and `parameter` when it came
|
|
1198
|
+
* from the graph checks that run before decoding (an undeclared include, a
|
|
1199
|
+
* `fields[]` type the read cannot carry). The façade answers a parse failure
|
|
1200
|
+
* with this document too (ADR-0029 §1).
|
|
1201
|
+
*/
|
|
1202
|
+
declare const queryErrorDocument: (error: QueryError) => ErrorDocument;
|
|
1203
|
+
/**
|
|
1204
|
+
* Bind the derived handlers to a graph and a read path. The node of a
|
|
1205
|
+
* resource is looked up by its `type` when the handler is built and the
|
|
1206
|
+
* resource's relationships are checked against the node's edges then, so a
|
|
1207
|
+
* declaration the graph cannot serve is a definition-time defect, never a
|
|
1208
|
+
* 500.
|
|
1209
|
+
*/
|
|
1210
|
+
declare const make$1: (options: Options$2) => RestHandlers;
|
|
1211
|
+
declare namespace JsonApiFacade_d_exports {
|
|
1212
|
+
export { FetchHandler, Options$1 as Options, fetchHandler, offlineDocument, offlineResponse, servedAtHeader };
|
|
1213
|
+
}
|
|
1214
|
+
/** What the façade is built from: the api's routes, the graph, the replica and its ledger. */
|
|
1215
|
+
interface Options$1<Id extends string, Groups extends HttpApiGroup.Constraint> {
|
|
1216
|
+
/** The node graph URLs are parsed against and documents are written by. */
|
|
1217
|
+
readonly nodes: Node.NodeRegistry;
|
|
1218
|
+
/**
|
|
1219
|
+
* The app's api; its `list` / `getOne` routes (the api's own prefix
|
|
1220
|
+
* included) whose type is a node of the graph are what the façade
|
|
1221
|
+
* recognises. Its read endpoints must be declared with the graph's codec
|
|
1222
|
+
* (the module doc's parity assumption).
|
|
1223
|
+
*/
|
|
1224
|
+
readonly api: HttpApi.HttpApi<Id, Groups>;
|
|
1225
|
+
/**
|
|
1226
|
+
* Where the api is served as the browser sees it, in front of the routes
|
|
1227
|
+
* the api declares: an origin (`https://api.example.com`), an
|
|
1228
|
+
* origin-relative path (`/proxy`), or `""` (default) for the page's own
|
|
1229
|
+
* origin with no extra path.
|
|
1230
|
+
*/
|
|
1231
|
+
readonly baseUrl?: string;
|
|
1232
|
+
/** The replica a covered read runs over: `ClientView.fromEngine(engine)` in a worker, `client.view` over a Client. */
|
|
1233
|
+
readonly view: ClientView.Reader;
|
|
1234
|
+
/**
|
|
1235
|
+
* The ledger's verdict for a read — `client.coverageOf`, or
|
|
1236
|
+
* `Coverage.coverageOf(nodes, view.query, host.registrations)` over the registrations
|
|
1237
|
+
* an `EngineHost` holds on mirrors' behalf. A request is answered locally
|
|
1238
|
+
* only when every read it needs is covered. `options.snapshot` is the root
|
|
1239
|
+
* rows the façade read for the request, with their subset: the verdict is
|
|
1240
|
+
* judged over them for that subset (#426), and any other read is judged as usual.
|
|
1241
|
+
*/
|
|
1242
|
+
readonly coverageOf: (read: Query$1.Read, options?: Coverage.JudgeOptions) => Effect.Effect<Coverage.Verdict>;
|
|
1243
|
+
/**
|
|
1244
|
+
* The replica's CVR cookie, for `x-legation-served-at` — the store's
|
|
1245
|
+
* `getCookie`, read only when a response is synthesised. Absent, or
|
|
1246
|
+
* failing, the header is empty.
|
|
1247
|
+
*/
|
|
1248
|
+
readonly cookie?: Effect.Effect<string | undefined, unknown>;
|
|
1249
|
+
/**
|
|
1250
|
+
* The resource declarations linkage follows — those the api's endpoints
|
|
1251
|
+
* are declared with, so the replica writes the relationships the server
|
|
1252
|
+
* writes. Default: the graph's synthetic resources (see `ReplicaDocuments`).
|
|
1253
|
+
*/
|
|
1254
|
+
readonly resources?: ReadonlyMap<string, Resource.Any>;
|
|
1255
|
+
/**
|
|
1256
|
+
* The prefix of every link in a document. Default: the matched route's
|
|
1257
|
+
* own prefix (`/jsonapi` for a route `/jsonapi/products`), which is what
|
|
1258
|
+
* a server mounting the api at its declared prefix writes.
|
|
1259
|
+
*/
|
|
1260
|
+
readonly prefix?: string;
|
|
1261
|
+
/** Whether the network is reachable; default `navigator.onLine !== false` (reachable where there is no `navigator`). */
|
|
1262
|
+
readonly online?: () => boolean;
|
|
1263
|
+
/**
|
|
1264
|
+
* The engine's journal seam for façade writes (ADR-0030 §5): in a worker,
|
|
1265
|
+
* `host.engine.mutateWithAck`. Absent, the façade intercepts no write: a
|
|
1266
|
+
* `POST` / `PATCH` / `DELETE` is never its business, as before ADR-0030.
|
|
1267
|
+
*/
|
|
1268
|
+
readonly mutate?: (mutator: string, args: unknown) => Effect.Effect<ClientEngine.MutationHandle, MutatorError | StoreError>;
|
|
1269
|
+
/**
|
|
1270
|
+
* The intent mapping: a decoded write to the mutation the engine journals,
|
|
1271
|
+
* or `undefined` when it is not interceptable. Default: the graph-derived
|
|
1272
|
+
* CRUD mutations (`CrudMutation`, `FacadeWrites.graphIntentOf`), which the
|
|
1273
|
+
* engine's registry must then carry (`CrudMutation.entries(nodes)`), with a
|
|
1274
|
+
* server handler under each name.
|
|
1275
|
+
*/
|
|
1276
|
+
readonly intentOf?: IntentOf;
|
|
1277
|
+
/**
|
|
1278
|
+
* Which reflected write routes may be intercepted, per type: owning this
|
|
1279
|
+
* declaration means owning the server's side of it (handlers under the
|
|
1280
|
+
* mutation names, creates that honour the client id, ADR-0030 §4).
|
|
1281
|
+
* Default: every reflected `create` / `update` / `delete` route of a type
|
|
1282
|
+
* the graph declares.
|
|
1283
|
+
*/
|
|
1284
|
+
readonly writes?: CrudMutation.Writes;
|
|
1285
|
+
/**
|
|
1286
|
+
* The pull to signal after a write forwarded online succeeded
|
|
1287
|
+
* ({@link FetchHandler.forwarded}): in a worker, `host.engine.pull`. Its
|
|
1288
|
+
* failure is ignored; the sync loop pulls again on its own.
|
|
1289
|
+
*/
|
|
1290
|
+
readonly pull?: Effect.Effect<unknown, unknown>;
|
|
1291
|
+
/**
|
|
1292
|
+
* The engine's mutation-state map (`engine.mutationStates`, #116), which
|
|
1293
|
+
* the mutation status URL `{prefix}/_legation/mutations/{mutationId}` is
|
|
1294
|
+
* answered from (ADR-0030 §2, `MutationStatus`). Absent, that route is not
|
|
1295
|
+
* the façade's. A worker composing the façade should raise the engine's
|
|
1296
|
+
* `mutationStateRetention.maxTerminal`, so a settled phase outlives a busy
|
|
1297
|
+
* session.
|
|
1298
|
+
*/
|
|
1299
|
+
readonly mutationStates?: Stream.Stream<MutationState.MutationStateMap>;
|
|
1300
|
+
/** The cap on a status request's `Prefer: wait`; default 30 seconds. */
|
|
1301
|
+
readonly maxWait?: Duration.Input;
|
|
1302
|
+
}
|
|
1303
|
+
/** The built façade. */
|
|
1304
|
+
interface FetchHandler {
|
|
1305
|
+
/**
|
|
1306
|
+
* Whether a request is the façade's business: a `GET` accepting
|
|
1307
|
+
* `application/vnd.api+json` on a reflected read route under `baseUrl`,
|
|
1308
|
+
* or — with {@link Options.mutate} set — a `POST` / `PATCH` / `DELETE` on a
|
|
1309
|
+
* reflected write route the façade may intercept, accepting the media
|
|
1310
|
+
* type and (for `POST` / `PATCH`) sending it without the atomic extension
|
|
1311
|
+
* (ADR-0030 §3). A worker asks it for every method, clones a matched
|
|
1312
|
+
* write before forwarding it, and turns a forward that rejected at the
|
|
1313
|
+
* transport into {@link FetchHandler.offline}.
|
|
1314
|
+
*/
|
|
1315
|
+
readonly matches: (request: Request) => boolean;
|
|
1316
|
+
/**
|
|
1317
|
+
* The façade's answer: a document (200), the codec's 400 for a URL the
|
|
1318
|
+
* grammar rejects, the 503 `offline` document for an uncovered request
|
|
1319
|
+
* while offline, a `202` write receipt for a write it journals (offline,
|
|
1320
|
+
* or on `Prefer: respond-async`) — or `undefined` when the request is not
|
|
1321
|
+
* the façade's business, is uncovered and online, or is a write to
|
|
1322
|
+
* forward, for the caller to forward unchanged. A write's body is read
|
|
1323
|
+
* from a clone, so the request itself can still be forwarded. A store
|
|
1324
|
+
* failure while reading the replica is a defect, as `Query.run` treats it.
|
|
1325
|
+
*/
|
|
1326
|
+
readonly handle: (request: Request) => Effect.Effect<Response | undefined>;
|
|
1327
|
+
/**
|
|
1328
|
+
* What a worker answers when the forward of a request {@link matches} held
|
|
1329
|
+
* for rejected at the transport, the other half of ADR-0029 §4's
|
|
1330
|
+
* definition: for a write, the request is journaled and answered with a
|
|
1331
|
+
* `202` receipt (pass the clone taken before forwarding, whose body is
|
|
1332
|
+
* unread); otherwise, or when the write cannot be journaled, the 503
|
|
1333
|
+
* `offline` response with the replica's current cookie.
|
|
1334
|
+
*/
|
|
1335
|
+
readonly offline: (request: Request) => Effect.Effect<Response>;
|
|
1336
|
+
/**
|
|
1337
|
+
* Tell the façade what a forwarded request came back with: after a `2xx`
|
|
1338
|
+
* to a write it {@link matches}, it signals {@link Options.pull} so a
|
|
1339
|
+
* covered read soon shows the server's row (ADR-0030 §3). A worker runs it
|
|
1340
|
+
* in the background (`event.waitUntil`); it never fails.
|
|
1341
|
+
*/
|
|
1342
|
+
readonly forwarded: (request: Request, response: Response) => Effect.Effect<void>;
|
|
1343
|
+
}
|
|
1344
|
+
/** The header a locally served response carries: the replica's CVR cookie at the time of the answer. */
|
|
1345
|
+
declare const servedAtHeader = "x-legation-served-at";
|
|
1346
|
+
/** The 503 error document of an uncovered request while offline (ADR-0029 §4). */
|
|
1347
|
+
declare const offlineDocument: ErrorDocument;
|
|
1348
|
+
/** The 503 response of an uncovered request while offline, marked with the cookie when known. */
|
|
1349
|
+
declare const offlineResponse: (cookie?: string) => Response;
|
|
1350
|
+
/**
|
|
1351
|
+
* Build the façade over an api and a replica. Route reflection and the
|
|
1352
|
+
* writer's declaration checks run here, so a declaration the graph cannot
|
|
1353
|
+
* serve is a definition-time defect, never a response.
|
|
1354
|
+
*/
|
|
1355
|
+
declare const fetchHandler: <Id extends string, Groups extends HttpApiGroup.Constraint>(options: Options$1<Id, Groups>) => FetchHandler;
|
|
1356
|
+
declare namespace JsonApiSync_d_exports {
|
|
1357
|
+
export { JsonApi, JsonApiSyncOptions, inspect, layer, start };
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* An app's JSON:API surface defined with Effect `HttpApi`, with its identifier
|
|
1361
|
+
* and group types erased. `HttpApi`'s group parameter is invariant, so the
|
|
1362
|
+
* entry points below are generic over the concrete api rather than accepting
|
|
1363
|
+
* this alias directly.
|
|
1364
|
+
*/
|
|
1365
|
+
type JsonApi = HttpApi.Constraint;
|
|
1366
|
+
/** Options for {@link layer}. */
|
|
1367
|
+
interface JsonApiSyncOptions {
|
|
1368
|
+
/** Base URL the resource paths resolve against (no trailing slash). */
|
|
1369
|
+
readonly baseUrl: string;
|
|
1370
|
+
/** This client's id (stable per device/tab). */
|
|
1371
|
+
readonly clientId: string;
|
|
1372
|
+
/** The client group this client belongs to (shared journal scope). */
|
|
1373
|
+
readonly clientGroupId: string;
|
|
1374
|
+
/** Override the Subset → JSON:API query encoding (e.g. your own package). */
|
|
1375
|
+
readonly queryEncoder?: QueryEncoder | undefined;
|
|
1376
|
+
/**
|
|
1377
|
+
* Relationship names to compound-load per collection (`?include=...`), so
|
|
1378
|
+
* `Relationships.related` resolves them without extra round trips.
|
|
1379
|
+
*/
|
|
1380
|
+
readonly includes?: Readonly<Record<string, ReadonlyArray<string>>> | undefined;
|
|
1381
|
+
/**
|
|
1382
|
+
* Where the backend keeps its ledger (acknowledged mutation ids and remembered
|
|
1383
|
+
* rejections) across restarts (#468). Unset, it is `Ledger.keyValue` over
|
|
1384
|
+
* `keyValueStore` when that is given, and otherwise in memory, where a restart
|
|
1385
|
+
* of the backend forgets it. A durable ledger needs a durable client store too
|
|
1386
|
+
* (`keyValueStore` or `store`): with the in-memory store, a reload restarts the
|
|
1387
|
+
* client's mutation ids at 1, the ledger already acknowledges them, and the
|
|
1388
|
+
* backend skips the new writes as already applied, with no error.
|
|
1389
|
+
*/
|
|
1390
|
+
readonly ledger?: LedgerStore | undefined;
|
|
1391
|
+
/**
|
|
1392
|
+
* The durable Effect `KeyValueStore` the client store is built over (#468). When
|
|
1393
|
+
* it is set, the client store defaults to `ClientStore.layerKeyValue()` over it
|
|
1394
|
+
* (unless `store` is given), and the ledger defaults to `Ledger.keyValue` over it
|
|
1395
|
+
* (unless `ledger` is given), so one key/value store keeps the journal and the
|
|
1396
|
+
* ledger across a reload. A `store` given with it must be built over this same
|
|
1397
|
+
* key/value store: `store` is an opaque layer, so the layer cannot check that.
|
|
1398
|
+
*/
|
|
1399
|
+
readonly keyValueStore?: KeyValueStore.KeyValueStore | undefined;
|
|
1400
|
+
/**
|
|
1401
|
+
* Atomic operations. When the api advertises the JSON:API Atomic Operations
|
|
1402
|
+
* extension (an endpoint taking an `atomic:operations` document, e.g.
|
|
1403
|
+
* effect-jsonapi's `Endpoint.operations`), the backend pushes each batch as
|
|
1404
|
+
* one all-or-nothing request to it — detected from the api by default.
|
|
1405
|
+
* Pass `false` to keep one request per mutation, or `{ path }` to name the
|
|
1406
|
+
* endpoint explicitly (an api that serves it outside the reflected
|
|
1407
|
+
* `HttpApi`).
|
|
1408
|
+
*/
|
|
1409
|
+
readonly atomic?: false | {
|
|
1410
|
+
readonly path: string;
|
|
1411
|
+
} | undefined;
|
|
1412
|
+
/**
|
|
1413
|
+
* Periodic pull interval — the correctness backstop that picks up changes
|
|
1414
|
+
* made elsewhere ("realtime" is periodic pull for client-direct deployment).
|
|
1415
|
+
* Defaults to 5 seconds; set to `null` to disable interval pulling.
|
|
1416
|
+
*/
|
|
1417
|
+
readonly pullInterval?: Duration.Input | null | undefined;
|
|
1418
|
+
/**
|
|
1419
|
+
* Auto-push debounce window. When set, mutations are pushed automatically
|
|
1420
|
+
* (coalesced) without an app-level `push` call.
|
|
1421
|
+
*/
|
|
1422
|
+
readonly pushDebounce?: Duration.Input | undefined;
|
|
1423
|
+
/**
|
|
1424
|
+
* The client store backing the engine. Defaults to `ClientStore.layerKeyValue()`
|
|
1425
|
+
* over `keyValueStore` when that is given, and otherwise to the in-memory store.
|
|
1426
|
+
* A durable store (e.g. `IndexedDbClientStore.layerWindow()` from
|
|
1427
|
+
* `@thomasfosterau/legation-browser`, or `ClientStore.layerKeyValue` over localStorage)
|
|
1428
|
+
* keeps the journal and confirmed rows across reloads (offline-first). It needs
|
|
1429
|
+
* a durable ledger too: pass `keyValueStore` (the key/value store it is built
|
|
1430
|
+
* over) or `ledger`. With the in-memory ledger, a reload forgets the
|
|
1431
|
+
* acknowledged ids, and the backend refuses every later push with a
|
|
1432
|
+
* mutation-id gap.
|
|
1433
|
+
*/
|
|
1434
|
+
readonly store?: Layer.Layer<ClientStore.ClientStore> | undefined;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Reflect + derive an api's resources, collections, mutators, operations —
|
|
1438
|
+
* and its atomic operations endpoint, when it declares one.
|
|
1439
|
+
*/
|
|
1440
|
+
declare const inspect: <Id extends string, Groups extends HttpApiGroup.Constraint>(api: HttpApi.HttpApi<Id, Groups>) => {
|
|
1441
|
+
readonly resources: ResourceMap;
|
|
1442
|
+
readonly atomic?: EndpointRef;
|
|
1443
|
+
} & Derived;
|
|
1444
|
+
/**
|
|
1445
|
+
* The full client-direct stack as a layer: provide an `HttpClient`, get a
|
|
1446
|
+
* ready `ClientEngine`. CVR store and invalidation bus are in-process; the
|
|
1447
|
+
* client store defaults to in-memory, or to the durable store over
|
|
1448
|
+
* `keyValueStore` (pass it for offline persistence and a durable ledger across
|
|
1449
|
+
* reloads, or pass `store` and `ledger` for another durable store).
|
|
1450
|
+
*/
|
|
1451
|
+
declare const layer: <Id extends string, Groups extends HttpApiGroup.Constraint>(api: HttpApi.HttpApi<Id, Groups>, options: JsonApiSyncOptions) => Layer.Layer<ClientEngine.ClientEngine, never, HttpClient.HttpClient>;
|
|
1452
|
+
/**
|
|
1453
|
+
* Start syncing: run the engine's sync loop. Interval pull, push-on-mutate,
|
|
1454
|
+
* pull-on-poke and push+pull-on-reconnect are all scheduled by the core loop
|
|
1455
|
+
* (configure the interval/debounce on `layer`'s options). Requires a `Scope` —
|
|
1456
|
+
* fork it into the app's runtime and the fibers live until the scope closes.
|
|
1457
|
+
*/
|
|
1458
|
+
declare const start: Effect.Effect<void, never, ClientEngine.ClientEngine | Scope.Scope>;
|
|
1459
|
+
declare namespace MutationStatus_d_exports {
|
|
1460
|
+
export { StatusDocument, StatusResource, current, defaultMaxWait, document, parseId, resourceType, segment, settled, unknownDocument };
|
|
1461
|
+
}
|
|
1462
|
+
/** The JSON:API type of a status resource; `_legation` and this type are never an app's. */
|
|
1463
|
+
declare const resourceType = "legation-mutations";
|
|
1464
|
+
/** The path segments of the status route after the api prefix: `/_legation/mutations/{id}`. */
|
|
1465
|
+
declare const segment = "_legation";
|
|
1466
|
+
/** The default cap on `Prefer: wait`: a `respondWith` promise must not outlive the worker's patience. */
|
|
1467
|
+
declare const defaultMaxWait: Duration.Input;
|
|
1468
|
+
/** The status resource of one tracked mutation. */
|
|
1469
|
+
interface StatusResource {
|
|
1470
|
+
readonly type: typeof resourceType;
|
|
1471
|
+
readonly id: string;
|
|
1472
|
+
readonly attributes: {
|
|
1473
|
+
readonly phase: MutationState.MutationPhase;
|
|
1474
|
+
readonly mutator: string;
|
|
1475
|
+
readonly journaledAt: number;
|
|
1476
|
+
readonly settledAt?: number;
|
|
1477
|
+
readonly reason?: string;
|
|
1478
|
+
};
|
|
1479
|
+
readonly links: {
|
|
1480
|
+
readonly self: string;
|
|
1481
|
+
readonly resource?: string;
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
/** The `200` document of the status URL: one status resource. */
|
|
1485
|
+
interface StatusDocument {
|
|
1486
|
+
readonly data: StatusResource;
|
|
1487
|
+
}
|
|
1488
|
+
/** The status document of a tracked mutation; `prefix` is where the api is mounted, as links see it. */
|
|
1489
|
+
declare const document: (nodes: Node.NodeRegistry, prefix: string, state: MutationState.MutationState) => StatusDocument;
|
|
1490
|
+
/** The `404 unknown_mutation` error document. */
|
|
1491
|
+
declare const unknownDocument: (id: string) => ErrorDocument;
|
|
1492
|
+
/** A mutation id as the route carries it: a positive integer, or none. */
|
|
1493
|
+
declare const parseId: (raw: string) => Option.Option<number>;
|
|
1494
|
+
/** One mutation's state now, from the engine's map stream (it emits the current map first). */
|
|
1495
|
+
declare const current: (states: Stream.Stream<MutationState.MutationStateMap>, mutationId: number) => Effect.Effect<Option.Option<MutationState.MutationState>>;
|
|
1496
|
+
/**
|
|
1497
|
+
* The mutation's state once it leaves `pending` (or is no longer tracked),
|
|
1498
|
+
* or as it is when `wait` elapses first.
|
|
1499
|
+
*/
|
|
1500
|
+
declare const settled: (states: Stream.Stream<MutationState.MutationStateMap>, mutationId: number, wait: Duration.Duration) => Effect.Effect<Option.Option<MutationState.MutationState>>;
|
|
1501
|
+
declare namespace NodeBridge_d_exports {
|
|
1502
|
+
export { FromResourceOptions, InversesAnnotation, KindsAnnotation, ResourceBridgeError, SyncAnnotationRecord, SyncAnnotations, fromResource, registry, syncAnnotations };
|
|
1503
|
+
}
|
|
1504
|
+
declare const ResourceBridgeError_base: Schema.Class<ResourceBridgeError, Schema.TaggedStruct<"ResourceBridgeError", {
|
|
1505
|
+
readonly resource: Schema.String;
|
|
1506
|
+
readonly relationship: Schema.String;
|
|
1507
|
+
readonly message: Schema.String;
|
|
1508
|
+
}>, import("effect/Cause").YieldableError & {
|
|
1509
|
+
readonly brand: unique symbol;
|
|
1510
|
+
}>;
|
|
1511
|
+
/** A definition-time mismatch between a resource graph and the node model. */
|
|
1512
|
+
declare class ResourceBridgeError extends ResourceBridgeError_base {}
|
|
1513
|
+
/** The annotation key naming the inverse edge of collection relationships, by relationship name. */
|
|
1514
|
+
declare const InversesAnnotation = "@legation/inverses";
|
|
1515
|
+
/** The annotation key naming the literal kind of filterable fields, by row field. */
|
|
1516
|
+
declare const KindsAnnotation = "@legation/kinds";
|
|
1517
|
+
/** The sync metadata a resource can declare about itself (#220). */
|
|
1518
|
+
interface SyncAnnotations {
|
|
1519
|
+
/**
|
|
1520
|
+
* The inverse edge name on the target, by `many` / `paginated` relationship
|
|
1521
|
+
* name — needed only where discovery is ambiguous or impossible.
|
|
1522
|
+
*/
|
|
1523
|
+
readonly inverses?: Readonly<Record<string, string>>;
|
|
1524
|
+
/**
|
|
1525
|
+
* The literal kind of a filterable field, by row field, where its schema
|
|
1526
|
+
* cannot tell it: an `instant` stored as text. The field must be declared
|
|
1527
|
+
* filterable on the resource.
|
|
1528
|
+
*/
|
|
1529
|
+
readonly kinds?: Readonly<Record<string, FilterPolicy.LiteralKind>>;
|
|
1530
|
+
}
|
|
1531
|
+
/** {@link SyncAnnotations} as resource annotations, under the bridge's keys. */
|
|
1532
|
+
type SyncAnnotationRecord = {
|
|
1533
|
+
readonly [InversesAnnotation]?: SyncAnnotations["inverses"];
|
|
1534
|
+
readonly [KindsAnnotation]?: SyncAnnotations["kinds"];
|
|
1535
|
+
};
|
|
1536
|
+
/**
|
|
1537
|
+
* The resource annotations that declare `sync`, to pass as (or merge into)
|
|
1538
|
+
* `Resource.make`'s `annotations` or `Resource.annotate`'s.
|
|
1539
|
+
*/
|
|
1540
|
+
declare const syncAnnotations: (sync: SyncAnnotations) => SyncAnnotationRecord;
|
|
1541
|
+
/** Per-resource overrides for {@link fromResource} / {@link registry}. */
|
|
1542
|
+
interface FromResourceOptions {
|
|
1543
|
+
/**
|
|
1544
|
+
* The foreign-key field a `one`/`optional` relationship's edge stores its
|
|
1545
|
+
* id in, by relationship name. Defaults to `` `${name}Id` ``.
|
|
1546
|
+
*/
|
|
1547
|
+
readonly via?: Record<string, string>;
|
|
1548
|
+
/**
|
|
1549
|
+
* One-edges beyond the resource's relationships, by edge name — promote
|
|
1550
|
+
* a foreign-key **attribute** to an edge when the resource graph is
|
|
1551
|
+
* acyclic and could not declare the relationship itself (see the module
|
|
1552
|
+
* doc). The attribute named by `via` moves out of the node's attributes;
|
|
1553
|
+
* the edge's key field replaces it.
|
|
1554
|
+
*/
|
|
1555
|
+
readonly edges?: Record<string, {
|
|
1556
|
+
readonly target: string;
|
|
1557
|
+
readonly via: string;
|
|
1558
|
+
}>;
|
|
1559
|
+
/**
|
|
1560
|
+
* The inverse edge name on the target, by relationship name — required
|
|
1561
|
+
* only when discovery is ambiguous (the target has several one-edges
|
|
1562
|
+
* back) or impossible (it has none).
|
|
1563
|
+
*/
|
|
1564
|
+
readonly inverses?: Record<string, string>;
|
|
1565
|
+
/**
|
|
1566
|
+
* The edge order for `paginated` relationships, by relationship name
|
|
1567
|
+
* (field names, `-` prefixed for descending). Required for every
|
|
1568
|
+
* `paginated` relationship — order is part of the edge's identity.
|
|
1569
|
+
*/
|
|
1570
|
+
readonly order?: Record<string, ReadonlyArray<string>>;
|
|
1571
|
+
/** Passed through to the node (fine fan-out topics — ADR-0023). */
|
|
1572
|
+
readonly hintKeys?: ReadonlyArray<string>;
|
|
1573
|
+
/**
|
|
1574
|
+
* Overrides and additions to the resource's own filterable declaration
|
|
1575
|
+
* (ADR-0028, #154; see the module doc), by row field: the operators
|
|
1576
|
+
* alone (the literal kind is derived from the attribute schema), or
|
|
1577
|
+
* `{ operators, kind }` where the schema cannot tell the kind (an
|
|
1578
|
+
* `instant` stored as text). An entry replaces the resource's declaration
|
|
1579
|
+
* for that field. `id` and every bridged one-edge's key field are
|
|
1580
|
+
* filterable by `eq` / `in` regardless.
|
|
1581
|
+
*/
|
|
1582
|
+
readonly filterable?: Record<string, FilterPolicy.FilterableInput>;
|
|
1583
|
+
/** Sortable fields beyond the resource's `Sort.able` declaration; `id` is always sortable. */
|
|
1584
|
+
readonly sortable?: ReadonlyArray<string>;
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Derive a {@link Node} definition from a single JSON:API resource
|
|
1588
|
+
* definition. Inverse discovery here sees only the target resource's own
|
|
1589
|
+
* relationships — bridge a whole graph with {@link registry} to discover
|
|
1590
|
+
* across option-declared edges too.
|
|
1591
|
+
*/
|
|
1592
|
+
declare const fromResource: (resource: Resource.Any, options?: FromResourceOptions) => Node.AnyNode;
|
|
1593
|
+
/**
|
|
1594
|
+
* Bridge a whole resource graph and close it as a validated
|
|
1595
|
+
* {@link Node.NodeRegistry} — nodes plus the derived engine
|
|
1596
|
+
* `CollectionRegistry`, ready for `Client.make` / `Server.make`.
|
|
1597
|
+
* Options are keyed by resource type.
|
|
1598
|
+
*/
|
|
1599
|
+
declare const registry: (resources: ReadonlyArray<Resource.Any>, options?: Record<string, FromResourceOptions>) => Node.NodeRegistry;
|
|
1600
|
+
declare namespace Relationships_d_exports {
|
|
1601
|
+
export { Lookup, RelatedRow, RelatedValue, hydrate, observe, query, related, resolve };
|
|
1602
|
+
}
|
|
1603
|
+
/** A resolved relationship: a row, a list of rows, `null`, or `undefined`. */
|
|
1604
|
+
type RelatedValue = JsonApiRow | ReadonlyArray<JsonApiRow> | null | undefined;
|
|
1605
|
+
/** A base row with its named relationships resolved against the mirror. */
|
|
1606
|
+
interface RelatedRow {
|
|
1607
|
+
readonly id: string;
|
|
1608
|
+
readonly value: JsonApiRow;
|
|
1609
|
+
readonly related: Record<string, RelatedValue>;
|
|
1610
|
+
}
|
|
1611
|
+
/** Look up a row by (collection, id) from the local mirror. */
|
|
1612
|
+
type Lookup = (collection: string, id: string) => JsonApiRow | undefined;
|
|
1613
|
+
/** Resolve one relationship of a row against a lookup (linkage → value[s]). */
|
|
1614
|
+
declare const resolve: (lookup: Lookup, value: JsonApiRow, name: string) => RelatedValue;
|
|
1615
|
+
/** Resolve several named relationships of a row. */
|
|
1616
|
+
declare const hydrate: (lookup: Lookup, value: JsonApiRow, names: ReadonlyArray<string>) => Record<string, RelatedValue>;
|
|
1617
|
+
/**
|
|
1618
|
+
* Resolve a subset's rows together with their named relationships, reading the
|
|
1619
|
+
* referenced collections from the engine's local view. Delegates to
|
|
1620
|
+
* {@link Related.queryWith} with the linkage strategy.
|
|
1621
|
+
*/
|
|
1622
|
+
declare const query: (engine: ClientEngine.ClientEngine, subset: Subset.Subset, names: ReadonlyArray<string>) => Effect.Effect<ReadonlyArray<RelatedRow>, never, never>;
|
|
1623
|
+
/**
|
|
1624
|
+
* Live version of {@link query} as an incrementally-maintained join. Delegates
|
|
1625
|
+
* to {@link Related.observeWith}: a write to a referenced row re-hydrates the
|
|
1626
|
+
* join without re-querying the base; a base change that shifts referenced
|
|
1627
|
+
* collections re-establishes the observers.
|
|
1628
|
+
*/
|
|
1629
|
+
declare const observe: (engine: ClientEngine.ClientEngine, subset: Subset.Subset, names: ReadonlyArray<string>) => Stream.Stream<ReadonlyArray<RelatedRow>, never, never>;
|
|
1630
|
+
/**
|
|
1631
|
+
* Auto-subscribing relational read: subscribes the base subset and the narrowed
|
|
1632
|
+
* `id IN (...)` subsets of each referenced collection, tracks the live linkage,
|
|
1633
|
+
* and tears the subscriptions down when the scope closes. Delegates to
|
|
1634
|
+
* {@link Related.relatedWith}. Pair with the backend's `includes` config to
|
|
1635
|
+
* deliver the related rows in the parent's response (no extra round trip).
|
|
1636
|
+
*/
|
|
1637
|
+
declare const related: (engine: ClientEngine.ClientEngine, subset: Subset.Subset, names: ReadonlyArray<string>) => Effect.Effect<Stream.Stream<ReadonlyArray<RelatedRow>, never, never>, never, Scope.Scope>;
|
|
1638
|
+
/** A batch of subsets evaluated to rows — the row source a read runs over. */
|
|
1639
|
+
type Fetch<E> = (subsets: ReadonlyArray<Subset.Subset>) => Effect.Effect<ReadonlyArray<Related.SourcedRow>, E>;
|
|
1640
|
+
declare namespace ReplicaDocuments_d_exports {
|
|
1641
|
+
export { Document$1 as Document, Options, ReplicaDocuments, fetchOf, make };
|
|
1642
|
+
}
|
|
1643
|
+
/** What the replica-side writer is bound to: the graph, the declarations linkage follows, the link prefix. */
|
|
1644
|
+
interface Options {
|
|
1645
|
+
/** The node graph URLs are parsed against, reads compile through and rows are written by. */
|
|
1646
|
+
readonly nodes: Node.NodeRegistry;
|
|
1647
|
+
/**
|
|
1648
|
+
* The resource declarations linkage follows, by type — the resources the
|
|
1649
|
+
* app's REST endpoints are declared with, so the replica emits exactly
|
|
1650
|
+
* the relationships the derived REST handler emits. Default: the node
|
|
1651
|
+
* graph's synthetic resources (`JsonApiQuery.resourcesOf(nodes)`), every
|
|
1652
|
+
* collection edge resolved. Every resource is checked against its node
|
|
1653
|
+
* when the writer is built; a read on a type no resource declares fails
|
|
1654
|
+
* with a `QueryError`.
|
|
1655
|
+
*/
|
|
1656
|
+
readonly resources?: ReadonlyMap<string, Resource.Any>;
|
|
1657
|
+
/** The api's mount path, prepended to every link (`/jsonapi`); default `""`. */
|
|
1658
|
+
readonly prefix?: string;
|
|
1659
|
+
}
|
|
1660
|
+
/** A document a URL yields: a collection document for `/{type}`, a single-resource document for `/{type}/{id}`. */
|
|
1661
|
+
type Document$1 = CollectionDocument | DataDocument;
|
|
1662
|
+
/** The replica-side writer, bound to one graph and one set of declarations. */
|
|
1663
|
+
interface ReplicaDocuments {
|
|
1664
|
+
/**
|
|
1665
|
+
* Run a parsed read over the view and write its collection document.
|
|
1666
|
+
* Fails with a `QueryError` for a collection no bound resource declares,
|
|
1667
|
+
* or an `include` / `fields[type]` the bound resource does not declare.
|
|
1668
|
+
*/
|
|
1669
|
+
readonly collection: (view: ClientView.Reader, parsed: ParsedRead) => Effect.Effect<CollectionDocument, QueryError>;
|
|
1670
|
+
/**
|
|
1671
|
+
* Run a parsed read over the view and write its single-resource document:
|
|
1672
|
+
* the first row, or `data: null` when the replica holds no matching row
|
|
1673
|
+
* (the caller decides the 404). Fails as {@link ReplicaDocuments.collection}.
|
|
1674
|
+
*/
|
|
1675
|
+
readonly data: (view: ClientView.Reader, parsed: ParsedRead) => Effect.Effect<DataDocument, QueryError>;
|
|
1676
|
+
/**
|
|
1677
|
+
* Parse a URL (`JsonApiQuery.parseRead`: the read and its `fields[]`
|
|
1678
|
+
* projection together) and write the document it addresses — a
|
|
1679
|
+
* single-resource document for `/{type}/{id}`, a collection document
|
|
1680
|
+
* otherwise. Fails with the `QueryError` the grammar gives, naming the
|
|
1681
|
+
* parameter, for a URL the server would reject the same way; and as
|
|
1682
|
+
* {@link ReplicaDocuments.collection} for what the bound resource does
|
|
1683
|
+
* not declare.
|
|
1684
|
+
*/
|
|
1685
|
+
readonly fromUrl: (view: ClientView.Reader, url: string | URL) => Effect.Effect<Document$1, QueryError>;
|
|
1686
|
+
/**
|
|
1687
|
+
* The reads a request needs covered for its document to be faithful: the
|
|
1688
|
+
* read as stated, then one whole-collection read per collection the
|
|
1689
|
+
* declared linkage resolves from beyond the read's includes (see the
|
|
1690
|
+
* module doc). The caller checks each with `Client.coverageOf`. Fails
|
|
1691
|
+
* with a `QueryError` for a collection no bound resource declares — a
|
|
1692
|
+
* request the façade sends to the network.
|
|
1693
|
+
*/
|
|
1694
|
+
readonly toCover: (read: Query$1.Read) => Effect.Effect<ReadonlyArray<Query$1.Read>, QueryError>;
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* The local view as a row source: each subset evaluated over the replica
|
|
1698
|
+
* (confirmed rows with the optimistic overlay), rows tagged with their
|
|
1699
|
+
* collection, batches concatenated in subset order — the shape
|
|
1700
|
+
* `SyncServer.readSubsets` gives the REST tier. The subsets of a batch are
|
|
1701
|
+
* independent, so they run concurrently. A store failure is a defect, as
|
|
1702
|
+
* `Query.run` treats it.
|
|
1703
|
+
*/
|
|
1704
|
+
declare const fetchOf: (view: ClientView.Reader) => Fetch<never>;
|
|
1705
|
+
/** Bind the replica-side writer to a graph and its declarations (a definition-time check of every declaration). */
|
|
1706
|
+
declare const make: (options: Options) => ReplicaDocuments;
|
|
1707
|
+
export { CollectionLoader_d_exports as CollectionLoader, Convention_d_exports as Convention, Derive_d_exports as Derive, DocumentWriter_d_exports as DocumentWriter, FacadeWrites_d_exports as FacadeWrites, JsonApiBackend_d_exports as JsonApiBackend, type JsonApiDocument, type JsonApiError, JsonApiFacade_d_exports as JsonApiFacade, JsonApiQuery_d_exports as JsonApiQuery, type JsonApiRequestOptions, schema_d_exports as JsonApiSchema, JsonApiSync_d_exports as JsonApiSync, Ledger_d_exports as Ledger, type LinkObject, type Links, MutationStatus_d_exports as MutationStatus, NodeBridge_d_exports as NodeBridge, type Relationship, Relationships_d_exports as Relationships, ReplicaDocuments_d_exports as ReplicaDocuments, type ResourceIdentifier, type ResourceObject, RestHandlers_d_exports as RestHandlers };
|
|
1708
|
+
//# sourceMappingURL=index.d.ts.map
|