@thomasfosterau/effect-sparql-node 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/LICENSE +21 -0
- package/README.md +220 -2
- package/bin/effect-sparql-server.mjs +26 -0
- package/dist/Cli-DE0eXMJr.d.mts +143 -0
- package/dist/Cli-DFvng1N8.mjs +259 -0
- package/dist/Cli-DFvng1N8.mjs.map +1 -0
- package/dist/Cli.d.mts +2 -0
- package/dist/Cli.mjs +1 -0
- package/dist/NodeSparqlServer-1vSrPk3J.d.mts +41 -0
- package/dist/NodeSparqlServer-CYAd_6BG.mjs +50 -0
- package/dist/NodeSparqlServer-CYAd_6BG.mjs.map +1 -0
- package/dist/NodeSparqlServer.d.mts +2 -0
- package/dist/NodeSparqlServer.mjs +1 -0
- package/dist/SparqlApi-BKE9nIf4.d.mts +488 -0
- package/dist/SparqlApi-BuycDkWu.mjs +371 -0
- package/dist/SparqlApi-BuycDkWu.mjs.map +1 -0
- package/dist/SparqlApi.d.mts +2 -0
- package/dist/SparqlApi.mjs +1 -0
- package/dist/SparqlApiBuilder-BAMpMy-Z.d.mts +83 -0
- package/dist/SparqlApiBuilder-GT7EopBq.mjs +136 -0
- package/dist/SparqlApiBuilder-GT7EopBq.mjs.map +1 -0
- package/dist/SparqlApiBuilder.d.mts +2 -0
- package/dist/SparqlApiBuilder.mjs +1 -0
- package/dist/SparqlApiClient-C82RGMEF.d.mts +386 -0
- package/dist/SparqlApiClient-CJcoFy5b.mjs +203 -0
- package/dist/SparqlApiClient-CJcoFy5b.mjs.map +1 -0
- package/dist/SparqlApiClient.d.mts +2 -0
- package/dist/SparqlApiClient.mjs +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +6 -0
- package/dist/rolldown-runtime-D8jQpDG7.mjs +12 -0
- package/package.json +67 -3
- package/src/Cli.ts +373 -0
- package/src/NodeSparqlServer.ts +71 -0
- package/src/SparqlApi.ts +506 -0
- package/src/SparqlApiBuilder.ts +196 -0
- package/src/SparqlApiClient.ts +383 -0
- package/src/index.ts +20 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.mjs";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import * as ServerFormats from "@thomasfosterau/effect-rdf/sparql/ServerFormats";
|
|
4
|
+
import * as Arr from "effect/Array";
|
|
5
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
6
|
+
import * as HttpApi from "effect/http-api/HttpApi";
|
|
7
|
+
import * as HttpApiEndpoint from "effect/http-api/HttpApiEndpoint";
|
|
8
|
+
import * as HttpApiGroup from "effect/http-api/HttpApiGroup";
|
|
9
|
+
import * as HttpApiMiddleware from "effect/http-api/HttpApiMiddleware";
|
|
10
|
+
import * as HttpApiSchema from "effect/http-api/HttpApiSchema";
|
|
11
|
+
import * as OpenApi from "effect/http-api/OpenApi";
|
|
12
|
+
import * as Results from "@thomasfosterau/effect-rdf/sparql/Results";
|
|
13
|
+
import * as ProtocolErrors from "@thomasfosterau/effect-rdf/sparql/ProtocolErrors";
|
|
14
|
+
import * as Protocol from "@thomasfosterau/effect-rdf/sparql/Protocol";
|
|
15
|
+
// src/SparqlApi.ts
|
|
16
|
+
/**
|
|
17
|
+
* The SPARQL 1.1 Protocol and Graph Store HTTP Protocol as an Effect
|
|
18
|
+
* `HttpApi` definition: the endpoints, their parameters, request media types,
|
|
19
|
+
* response media types and error statuses, shared by the server
|
|
20
|
+
* (`SparqlApiBuilder`), the derived client
|
|
21
|
+
* (`SparqlApiClient`) and the OpenAPI document
|
|
22
|
+
* (`openApi`).
|
|
23
|
+
*
|
|
24
|
+
* The API has two groups:
|
|
25
|
+
*
|
|
26
|
+
* - `sparql`: `query` (`GET` with `?query=`, or without it for the service
|
|
27
|
+
* description) and `post` (`POST` with an
|
|
28
|
+
* `application/x-www-form-urlencoded`, `application/sparql-query` or
|
|
29
|
+
* `application/sparql-update` body);
|
|
30
|
+
* - `graphStore`: `get`, `head`, `put`, `post` and `delete` on the graph store
|
|
31
|
+
* with `?graph=<iri>` or `?default`, with RDF bodies under every media type
|
|
32
|
+
* of the default `Formats`.
|
|
33
|
+
*
|
|
34
|
+
* Content negotiation (`Accept`) and `405 Method Not Allowed` are not part of
|
|
35
|
+
* the definition: the server's handlers negotiate with the same code as
|
|
36
|
+
* `SparqlServer`, and `SparqlApiBuilder` answers the methods the API does not
|
|
37
|
+
* declare. A request whose parameters the API's schemas reject (a repeated
|
|
38
|
+
* `query` or `graph`, say) is handed to the protocol handler too, by the
|
|
39
|
+
* `SchemaFallback` middleware, so it gets the protocol's own `400` and
|
|
40
|
+
* message. Error bodies are `text/plain` messages decoded into
|
|
41
|
+
* `ProtocolErrors.ProtocolError`.
|
|
42
|
+
*
|
|
43
|
+
* This module only describes the API, so a client can import it without the
|
|
44
|
+
* server. `effect/http-api` is `@stability unstable` in Effect 4, and this
|
|
45
|
+
* definition follows it.
|
|
46
|
+
*
|
|
47
|
+
* @since 0.1.0
|
|
48
|
+
*/
|
|
49
|
+
var SparqlApi_exports = /* @__PURE__ */ __exportAll({
|
|
50
|
+
AcceptHeader: () => AcceptHeader,
|
|
51
|
+
DatasetParams: () => DatasetParams,
|
|
52
|
+
DirectQueryBody: () => DirectQueryBody,
|
|
53
|
+
DirectUpdateBody: () => DirectUpdateBody,
|
|
54
|
+
FormBody: () => FormBody,
|
|
55
|
+
GraphParams: () => GraphParams,
|
|
56
|
+
ProtocolErrorResponse: () => ProtocolErrorResponse,
|
|
57
|
+
QueryParams: () => QueryParams,
|
|
58
|
+
QueryResponse: () => QueryResponse,
|
|
59
|
+
RdfDocumentBody: () => RdfDocumentBody,
|
|
60
|
+
RdfResponse: () => RdfResponse,
|
|
61
|
+
SchemaFallback: () => SchemaFallback,
|
|
62
|
+
SparqlApi: () => SparqlApi,
|
|
63
|
+
make: () => make,
|
|
64
|
+
openApi: () => openApi,
|
|
65
|
+
queryMediaTypes: () => queryMediaTypes,
|
|
66
|
+
rdfMediaTypes: () => rdfMediaTypes,
|
|
67
|
+
resultsMediaTypes: () => resultsMediaTypes
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* The media types of the default RDF formats (`ServerFormats.codecs`), in order of
|
|
71
|
+
* preference: Graph Store bodies and `CONSTRUCT` / `DESCRIBE` / service
|
|
72
|
+
* description responses.
|
|
73
|
+
*
|
|
74
|
+
* @since 0.1.0
|
|
75
|
+
* @category media types
|
|
76
|
+
*/
|
|
77
|
+
const rdfMediaTypes = Arr.dedupe(Arr.flatMap(ServerFormats.codecs, (codec) => codec.format.mediaTypes));
|
|
78
|
+
/**
|
|
79
|
+
* The media types of SPARQL results (`Results.codecs`): JSON, XML, CSV and
|
|
80
|
+
* TSV, in order of preference.
|
|
81
|
+
*
|
|
82
|
+
* @since 0.1.0
|
|
83
|
+
* @category media types
|
|
84
|
+
*/
|
|
85
|
+
const resultsMediaTypes = Arr.dedupe(Arr.flatMap(Results.codecs, (codec) => codec.format.mediaTypes));
|
|
86
|
+
/**
|
|
87
|
+
* Every media type a query response can have: the results formats for
|
|
88
|
+
* `SELECT` / `ASK`, then the RDF formats for `CONSTRUCT` / `DESCRIBE` and the
|
|
89
|
+
* service description.
|
|
90
|
+
*
|
|
91
|
+
* @since 0.1.0
|
|
92
|
+
* @category media types
|
|
93
|
+
*/
|
|
94
|
+
const queryMediaTypes = Arr.dedupe(Arr.appendAll(resultsMediaTypes, rdfMediaTypes));
|
|
95
|
+
/**
|
|
96
|
+
* A query response body: a byte stream under any of `queryMediaTypes`.
|
|
97
|
+
*
|
|
98
|
+
* @since 0.1.0
|
|
99
|
+
* @category schemas
|
|
100
|
+
*/
|
|
101
|
+
const QueryResponse = HttpApiSchema.StreamUint8Array({ contentType: queryMediaTypes });
|
|
102
|
+
/**
|
|
103
|
+
* An RDF document response body: a byte stream under any of `rdfMediaTypes`.
|
|
104
|
+
*
|
|
105
|
+
* @since 0.1.0
|
|
106
|
+
* @category schemas
|
|
107
|
+
*/
|
|
108
|
+
const RdfResponse = HttpApiSchema.StreamUint8Array({ contentType: rdfMediaTypes });
|
|
109
|
+
const iris = Schema.optionalKey(Schema.Array(Schema.String));
|
|
110
|
+
/**
|
|
111
|
+
* The `Accept` request header, which selects the response format.
|
|
112
|
+
*
|
|
113
|
+
* @since 0.1.0
|
|
114
|
+
* @category schemas
|
|
115
|
+
*/
|
|
116
|
+
const AcceptHeader = Schema.Struct({ accept: Schema.optionalKey(Schema.String) });
|
|
117
|
+
/**
|
|
118
|
+
* The query string of a query: the query and its protocol dataset.
|
|
119
|
+
*
|
|
120
|
+
* @since 0.1.0
|
|
121
|
+
* @category schemas
|
|
122
|
+
*/
|
|
123
|
+
const QueryParams = Schema.Struct({
|
|
124
|
+
query: Schema.optionalKey(Schema.String),
|
|
125
|
+
"default-graph-uri": iris,
|
|
126
|
+
"named-graph-uri": iris
|
|
127
|
+
});
|
|
128
|
+
/**
|
|
129
|
+
* The query string of a `POST`: the protocol dataset of a query
|
|
130
|
+
* (`default-graph-uri` / `named-graph-uri`) or of an update
|
|
131
|
+
* (`using-graph-uri` / `using-named-graph-uri`).
|
|
132
|
+
*
|
|
133
|
+
* @since 0.1.0
|
|
134
|
+
* @category schemas
|
|
135
|
+
*/
|
|
136
|
+
const DatasetParams = Schema.Struct({
|
|
137
|
+
"default-graph-uri": iris,
|
|
138
|
+
"named-graph-uri": iris,
|
|
139
|
+
"using-graph-uri": iris,
|
|
140
|
+
"using-named-graph-uri": iris
|
|
141
|
+
});
|
|
142
|
+
/**
|
|
143
|
+
* An `application/x-www-form-urlencoded` `POST` body holding a `query` or an
|
|
144
|
+
* `update` and its protocol dataset.
|
|
145
|
+
*
|
|
146
|
+
* @since 0.1.0
|
|
147
|
+
* @category schemas
|
|
148
|
+
*/
|
|
149
|
+
const FormBody = Schema.Struct({
|
|
150
|
+
query: Schema.optionalKey(Schema.String),
|
|
151
|
+
update: Schema.optionalKey(Schema.String),
|
|
152
|
+
"default-graph-uri": iris,
|
|
153
|
+
"named-graph-uri": iris,
|
|
154
|
+
"using-graph-uri": iris,
|
|
155
|
+
"using-named-graph-uri": iris
|
|
156
|
+
}).pipe(HttpApiSchema.asFormUrlEncoded());
|
|
157
|
+
/**
|
|
158
|
+
* An `application/sparql-query` `POST` body, decoded as
|
|
159
|
+
* `{ _tag: 'DirectQuery', query }`.
|
|
160
|
+
*
|
|
161
|
+
* @since 0.1.0
|
|
162
|
+
* @category schemas
|
|
163
|
+
*/
|
|
164
|
+
const DirectQueryBody = Schema.String.pipe(Schema.decodeTo(Schema.TaggedStruct("DirectQuery", { query: Schema.String }), SchemaTransformation.transform({
|
|
165
|
+
decode: (query) => ({
|
|
166
|
+
_tag: "DirectQuery",
|
|
167
|
+
query
|
|
168
|
+
}),
|
|
169
|
+
encode: (body) => body.query
|
|
170
|
+
})), HttpApiSchema.asText({ contentType: Protocol.SPARQL_QUERY }));
|
|
171
|
+
/**
|
|
172
|
+
* An `application/sparql-update` `POST` body, decoded as
|
|
173
|
+
* `{ _tag: 'DirectUpdate', update }`.
|
|
174
|
+
*
|
|
175
|
+
* @since 0.1.0
|
|
176
|
+
* @category schemas
|
|
177
|
+
*/
|
|
178
|
+
const DirectUpdateBody = Schema.String.pipe(Schema.decodeTo(Schema.TaggedStruct("DirectUpdate", { update: Schema.String }), SchemaTransformation.transform({
|
|
179
|
+
decode: (update) => ({
|
|
180
|
+
_tag: "DirectUpdate",
|
|
181
|
+
update
|
|
182
|
+
}),
|
|
183
|
+
encode: (body) => body.update
|
|
184
|
+
})), HttpApiSchema.asText({ contentType: Protocol.SPARQL_UPDATE }));
|
|
185
|
+
const rdfDocument = (mediaType) => Schema.String.pipe(Schema.decodeTo(Schema.Struct({
|
|
186
|
+
mediaType: Schema.Literal(mediaType),
|
|
187
|
+
document: Schema.String
|
|
188
|
+
}), SchemaTransformation.transform({
|
|
189
|
+
decode: (document) => ({
|
|
190
|
+
mediaType,
|
|
191
|
+
document
|
|
192
|
+
}),
|
|
193
|
+
encode: (body) => body.document
|
|
194
|
+
})), HttpApiSchema.asText({ contentType: mediaType }));
|
|
195
|
+
/**
|
|
196
|
+
* Graph Store request bodies: an RDF document under one of `rdfMediaTypes`,
|
|
197
|
+
* decoded as `{ mediaType, document }` (the media type selects the request's
|
|
198
|
+
* `Content-Type`).
|
|
199
|
+
*
|
|
200
|
+
* @since 0.1.0
|
|
201
|
+
* @category schemas
|
|
202
|
+
*/
|
|
203
|
+
const RdfDocumentBody = Arr.map(rdfMediaTypes, rdfDocument);
|
|
204
|
+
/**
|
|
205
|
+
* The query string of a Graph Store request: `graph=<iri>` (indirect
|
|
206
|
+
* identification) or `default` (the default graph).
|
|
207
|
+
*
|
|
208
|
+
* @since 0.1.0
|
|
209
|
+
* @category schemas
|
|
210
|
+
*/
|
|
211
|
+
const GraphParams = Schema.Struct({
|
|
212
|
+
graph: Schema.optionalKey(Schema.String),
|
|
213
|
+
default: Schema.optionalKey(Schema.String)
|
|
214
|
+
});
|
|
215
|
+
/**
|
|
216
|
+
* The error response of a status: a `text/plain` message, decoded as an
|
|
217
|
+
* `ProtocolErrors.ProtocolError` with that status.
|
|
218
|
+
*
|
|
219
|
+
* @since 0.1.0
|
|
220
|
+
* @category schemas
|
|
221
|
+
*/
|
|
222
|
+
const ProtocolErrorResponse = (status) => Schema.String.pipe(Schema.decodeTo(ProtocolErrors.ProtocolError.pipe(Schema.check(Schema.makeFilter((error) => error.status === status, {
|
|
223
|
+
identifier: `ProtocolError${status}`,
|
|
224
|
+
title: `ProtocolError (${status})`,
|
|
225
|
+
description: `A protocol error with status ${status}`
|
|
226
|
+
}))), SchemaTransformation.transform({
|
|
227
|
+
decode: (message) => ({
|
|
228
|
+
_tag: "ProtocolError",
|
|
229
|
+
status,
|
|
230
|
+
message: message.trimEnd()
|
|
231
|
+
}),
|
|
232
|
+
encode: (error) => `${error.message}\n`
|
|
233
|
+
})), HttpApiSchema.asText({ contentType: "text/plain" }), HttpApiSchema.status(status));
|
|
234
|
+
const BadRequest = ProtocolErrorResponse(400);
|
|
235
|
+
const Forbidden = ProtocolErrorResponse(403);
|
|
236
|
+
const NotFound = ProtocolErrorResponse(404);
|
|
237
|
+
const MethodNotAllowed = ProtocolErrorResponse(405);
|
|
238
|
+
const NotAcceptable = ProtocolErrorResponse(406);
|
|
239
|
+
const UnsupportedMediaType = ProtocolErrorResponse(415);
|
|
240
|
+
const InternalServerError = ProtocolErrorResponse(500);
|
|
241
|
+
const ServiceUnavailable = ProtocolErrorResponse(503);
|
|
242
|
+
/**
|
|
243
|
+
* Middleware on every endpoint that answers a request whose parameters or
|
|
244
|
+
* headers fail the API's schemas with the protocol handler instead of an
|
|
245
|
+
* empty `400` (see `SparqlApiBuilder.layerSchemaFallback`). It needs the
|
|
246
|
+
* server's services and nothing on the client side.
|
|
247
|
+
*
|
|
248
|
+
* @since 0.1.0
|
|
249
|
+
* @category middleware
|
|
250
|
+
*/
|
|
251
|
+
var SchemaFallback = class extends HttpApiMiddleware.Service()("@thomasfosterau/effect-sparql-node/SparqlApi/SchemaFallback") {};
|
|
252
|
+
const sparqlGroup = (path) => HttpApiGroup.make("sparql").add(HttpApiEndpoint.get("query", path, {
|
|
253
|
+
query: QueryParams,
|
|
254
|
+
headers: AcceptHeader,
|
|
255
|
+
success: QueryResponse,
|
|
256
|
+
error: [
|
|
257
|
+
BadRequest,
|
|
258
|
+
NotAcceptable,
|
|
259
|
+
InternalServerError,
|
|
260
|
+
ServiceUnavailable
|
|
261
|
+
]
|
|
262
|
+
}).annotateMerge(OpenApi.annotations({
|
|
263
|
+
summary: "Query, or read the service description",
|
|
264
|
+
description: "A SPARQL query in `query`, with its dataset in `default-graph-uri` / `named-graph-uri`. Without `query`, answers the service description. The response format is negotiated from `Accept`."
|
|
265
|
+
})), HttpApiEndpoint.post("post", path, {
|
|
266
|
+
query: DatasetParams,
|
|
267
|
+
headers: AcceptHeader,
|
|
268
|
+
payload: [
|
|
269
|
+
DirectQueryBody,
|
|
270
|
+
DirectUpdateBody,
|
|
271
|
+
FormBody
|
|
272
|
+
],
|
|
273
|
+
success: [QueryResponse, HttpApiSchema.NoContent],
|
|
274
|
+
error: [
|
|
275
|
+
BadRequest,
|
|
276
|
+
Forbidden,
|
|
277
|
+
NotAcceptable,
|
|
278
|
+
UnsupportedMediaType,
|
|
279
|
+
InternalServerError,
|
|
280
|
+
ServiceUnavailable
|
|
281
|
+
]
|
|
282
|
+
}).annotateMerge(OpenApi.annotations({
|
|
283
|
+
summary: "Query or update",
|
|
284
|
+
description: "A query (`200` with the results) or an update (`204`), as a form or as an `application/sparql-query` / `application/sparql-update` body. A read-only endpoint answers updates with `403`."
|
|
285
|
+
}))).annotateMerge(OpenApi.annotations({
|
|
286
|
+
title: "SPARQL",
|
|
287
|
+
description: "The SPARQL 1.1 Protocol"
|
|
288
|
+
}));
|
|
289
|
+
const writeErrors = [
|
|
290
|
+
BadRequest,
|
|
291
|
+
NotFound,
|
|
292
|
+
MethodNotAllowed,
|
|
293
|
+
UnsupportedMediaType,
|
|
294
|
+
InternalServerError
|
|
295
|
+
];
|
|
296
|
+
const graphStoreGroup = (path) => HttpApiGroup.make("graphStore").add(HttpApiEndpoint.get("get", path, {
|
|
297
|
+
query: GraphParams,
|
|
298
|
+
headers: AcceptHeader,
|
|
299
|
+
success: RdfResponse,
|
|
300
|
+
error: [
|
|
301
|
+
BadRequest,
|
|
302
|
+
NotFound,
|
|
303
|
+
NotAcceptable,
|
|
304
|
+
InternalServerError
|
|
305
|
+
]
|
|
306
|
+
}).annotateMerge(OpenApi.annotations({ summary: "Read a graph" })), HttpApiEndpoint.head("head", path, {
|
|
307
|
+
query: GraphParams,
|
|
308
|
+
headers: AcceptHeader,
|
|
309
|
+
success: HttpApiSchema.Empty(200),
|
|
310
|
+
error: [
|
|
311
|
+
BadRequest,
|
|
312
|
+
NotFound,
|
|
313
|
+
NotAcceptable,
|
|
314
|
+
InternalServerError
|
|
315
|
+
]
|
|
316
|
+
}).annotateMerge(OpenApi.annotations({ summary: "Check that a graph exists" })), HttpApiEndpoint.put("put", path, {
|
|
317
|
+
query: GraphParams,
|
|
318
|
+
payload: RdfDocumentBody,
|
|
319
|
+
success: [HttpApiSchema.NoContent, HttpApiSchema.Created],
|
|
320
|
+
error: writeErrors
|
|
321
|
+
}).annotateMerge(OpenApi.annotations({
|
|
322
|
+
summary: "Replace a graph",
|
|
323
|
+
description: "`201` when the graph is new, `204` when it existed."
|
|
324
|
+
})), HttpApiEndpoint.post("post", path, {
|
|
325
|
+
query: GraphParams,
|
|
326
|
+
payload: RdfDocumentBody,
|
|
327
|
+
success: [HttpApiSchema.NoContent, HttpApiSchema.Created],
|
|
328
|
+
error: writeErrors
|
|
329
|
+
}).annotateMerge(OpenApi.annotations({
|
|
330
|
+
summary: "Add to a graph, or create one",
|
|
331
|
+
description: "Merges the document into the graph (`201` when it is new). Without `graph` or `default`, creates a new graph and answers `201` with its IRI in `Location`."
|
|
332
|
+
})), HttpApiEndpoint.delete("delete", path, {
|
|
333
|
+
query: GraphParams,
|
|
334
|
+
success: HttpApiSchema.NoContent,
|
|
335
|
+
error: [
|
|
336
|
+
BadRequest,
|
|
337
|
+
NotFound,
|
|
338
|
+
MethodNotAllowed,
|
|
339
|
+
InternalServerError
|
|
340
|
+
]
|
|
341
|
+
}).annotateMerge(OpenApi.annotations({ summary: "Delete a graph" }))).annotateMerge(OpenApi.annotations({
|
|
342
|
+
title: "Graph Store",
|
|
343
|
+
description: "The SPARQL 1.1 Graph Store HTTP Protocol (indirect identification; URLs below the graph store identify graphs directly)"
|
|
344
|
+
}));
|
|
345
|
+
/**
|
|
346
|
+
* Builds the API at the given paths.
|
|
347
|
+
*
|
|
348
|
+
* @since 0.1.0
|
|
349
|
+
* @category constructors
|
|
350
|
+
*/
|
|
351
|
+
const make = (paths) => HttpApi.make("effect-sparql-node").add(sparqlGroup(paths?.path ?? "/sparql")).add(graphStoreGroup(paths?.graphStorePath ?? "/rdf-graph-store")).middleware(SchemaFallback).annotateMerge(OpenApi.annotations({
|
|
352
|
+
title: "SPARQL 1.1 Protocol and Graph Store HTTP Protocol",
|
|
353
|
+
description: "Every response format is negotiated from `Accept`; methods an endpoint does not support are answered with `405` and `Allow`."
|
|
354
|
+
}));
|
|
355
|
+
/**
|
|
356
|
+
* The API at the default paths, `/sparql` and `/rdf-graph-store`.
|
|
357
|
+
*
|
|
358
|
+
* @since 0.1.0
|
|
359
|
+
* @category api
|
|
360
|
+
*/
|
|
361
|
+
const SparqlApi = make();
|
|
362
|
+
/**
|
|
363
|
+
* The OpenAPI document of an API (the default one when omitted).
|
|
364
|
+
*
|
|
365
|
+
* @since 0.1.0
|
|
366
|
+
* @category openapi
|
|
367
|
+
*/
|
|
368
|
+
const openApi = (api = SparqlApi) => OpenApi.fromApi(api);
|
|
369
|
+
export { queryMediaTypes as _, FormBody as a, QueryParams as c, RdfResponse as d, SchemaFallback as f, openApi as g, make as h, DirectUpdateBody as i, QueryResponse as l, SparqlApi_exports as m, DatasetParams as n, GraphParams as o, SparqlApi as p, DirectQueryBody as r, ProtocolErrorResponse as s, AcceptHeader as t, RdfDocumentBody as u, rdfMediaTypes as v, resultsMediaTypes as y };
|
|
370
|
+
|
|
371
|
+
//# sourceMappingURL=SparqlApi-BuycDkWu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SparqlApi-BuycDkWu.mjs","names":[],"sources":["../src/SparqlApi.ts"],"sourcesContent":["/**\n * The SPARQL 1.1 Protocol and Graph Store HTTP Protocol as an Effect\n * `HttpApi` definition: the endpoints, their parameters, request media types,\n * response media types and error statuses, shared by the server\n * (`SparqlApiBuilder`), the derived client\n * (`SparqlApiClient`) and the OpenAPI document\n * (`openApi`).\n *\n * The API has two groups:\n *\n * - `sparql`: `query` (`GET` with `?query=`, or without it for the service\n * description) and `post` (`POST` with an\n * `application/x-www-form-urlencoded`, `application/sparql-query` or\n * `application/sparql-update` body);\n * - `graphStore`: `get`, `head`, `put`, `post` and `delete` on the graph store\n * with `?graph=<iri>` or `?default`, with RDF bodies under every media type\n * of the default `Formats`.\n *\n * Content negotiation (`Accept`) and `405 Method Not Allowed` are not part of\n * the definition: the server's handlers negotiate with the same code as\n * `SparqlServer`, and `SparqlApiBuilder` answers the methods the API does not\n * declare. A request whose parameters the API's schemas reject (a repeated\n * `query` or `graph`, say) is handed to the protocol handler too, by the\n * `SchemaFallback` middleware, so it gets the protocol's own `400` and\n * message. Error bodies are `text/plain` messages decoded into\n * `ProtocolErrors.ProtocolError`.\n *\n * This module only describes the API, so a client can import it without the\n * server. `effect/http-api` is `@stability unstable` in Effect 4, and this\n * definition follows it.\n *\n * @since 0.1.0\n */\nimport * as Arr from 'effect/Array';\nimport * as Schema from 'effect/Schema';\nimport * as SchemaTransformation from 'effect/SchemaTransformation';\nimport * as HttpApi from 'effect/http-api/HttpApi';\nimport * as HttpApiEndpoint from 'effect/http-api/HttpApiEndpoint';\nimport * as HttpApiGroup from 'effect/http-api/HttpApiGroup';\nimport * as HttpApiMiddleware from 'effect/http-api/HttpApiMiddleware';\nimport * as HttpApiSchema from 'effect/http-api/HttpApiSchema';\nimport * as OpenApi from 'effect/http-api/OpenApi';\nimport type * as Serialization from '@thomasfosterau/effect-rdf/Serialization';\nimport type * as Engine from '@thomasfosterau/effect-rdf/sparql/Engine';\nimport * as Results from '@thomasfosterau/effect-rdf/sparql/Results';\nimport type * as Store from '@thomasfosterau/effect-rdf/sparql/Store';\nimport * as ProtocolErrors from '@thomasfosterau/effect-rdf/sparql/ProtocolErrors';\nimport * as ServerFormats from '@thomasfosterau/effect-rdf/sparql/ServerFormats';\nimport * as Protocol from '@thomasfosterau/effect-rdf/sparql/Protocol';\n\n// -----------------------------------------------------------------------------\n// Media types\n// -----------------------------------------------------------------------------\n\n/**\n * The media types of the default RDF formats (`ServerFormats.codecs`), in order of\n * preference: Graph Store bodies and `CONSTRUCT` / `DESCRIBE` / service\n * description responses.\n *\n * @since 0.1.0\n * @category media types\n */\nexport const rdfMediaTypes: ReadonlyArray<string> = Arr.dedupe(\n Arr.flatMap(ServerFormats.codecs, (codec) => codec.format.mediaTypes),\n);\n\n/**\n * The media types of SPARQL results (`Results.codecs`): JSON, XML, CSV and\n * TSV, in order of preference.\n *\n * @since 0.1.0\n * @category media types\n */\nexport const resultsMediaTypes: ReadonlyArray<string> = Arr.dedupe(\n Arr.flatMap(Results.codecs, (codec) => codec.format.mediaTypes),\n);\n\n/**\n * Every media type a query response can have: the results formats for\n * `SELECT` / `ASK`, then the RDF formats for `CONSTRUCT` / `DESCRIBE` and the\n * service description.\n *\n * @since 0.1.0\n * @category media types\n */\nexport const queryMediaTypes: ReadonlyArray<string> = Arr.dedupe(\n Arr.appendAll(resultsMediaTypes, rdfMediaTypes),\n);\n\n// -----------------------------------------------------------------------------\n// Schemas\n// -----------------------------------------------------------------------------\n\n/**\n * A query response body: a byte stream under any of `queryMediaTypes`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const QueryResponse: HttpApiSchema.StreamUint8Array = HttpApiSchema.StreamUint8Array({\n contentType: queryMediaTypes,\n});\n\n/**\n * An RDF document response body: a byte stream under any of `rdfMediaTypes`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const RdfResponse: HttpApiSchema.StreamUint8Array = HttpApiSchema.StreamUint8Array({\n contentType: rdfMediaTypes,\n});\n\nconst iris = Schema.optionalKey(Schema.Array(Schema.String));\n\n/**\n * The `Accept` request header, which selects the response format.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const AcceptHeader = Schema.Struct({ accept: Schema.optionalKey(Schema.String) });\n\n/**\n * The decoded type of `AcceptHeader`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport type AcceptHeader = typeof AcceptHeader.Type;\n\n/**\n * The query string of a query: the query and its protocol dataset.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const QueryParams = Schema.Struct({\n query: Schema.optionalKey(Schema.String),\n 'default-graph-uri': iris,\n 'named-graph-uri': iris,\n});\n\n/**\n * The decoded type of `QueryParams`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport type QueryParams = typeof QueryParams.Type;\n\n/**\n * The query string of a `POST`: the protocol dataset of a query\n * (`default-graph-uri` / `named-graph-uri`) or of an update\n * (`using-graph-uri` / `using-named-graph-uri`).\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const DatasetParams = Schema.Struct({\n 'default-graph-uri': iris,\n 'named-graph-uri': iris,\n 'using-graph-uri': iris,\n 'using-named-graph-uri': iris,\n});\n\n/**\n * The decoded type of `DatasetParams`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport type DatasetParams = typeof DatasetParams.Type;\n\n/**\n * An `application/x-www-form-urlencoded` `POST` body holding a `query` or an\n * `update` and its protocol dataset.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const FormBody = Schema.Struct({\n query: Schema.optionalKey(Schema.String),\n update: Schema.optionalKey(Schema.String),\n 'default-graph-uri': iris,\n 'named-graph-uri': iris,\n 'using-graph-uri': iris,\n 'using-named-graph-uri': iris,\n}).pipe(HttpApiSchema.asFormUrlEncoded());\n\n/**\n * The decoded type of `FormBody`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport type FormBody = typeof FormBody.Type;\n\n/**\n * An `application/sparql-query` `POST` body, decoded as\n * `{ _tag: 'DirectQuery', query }`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const DirectQueryBody = Schema.String.pipe(\n Schema.decodeTo(\n Schema.TaggedStruct('DirectQuery', { query: Schema.String }),\n SchemaTransformation.transform({\n decode: (query) => ({ _tag: 'DirectQuery' as const, query }),\n encode: (body) => body.query,\n }),\n ),\n HttpApiSchema.asText({ contentType: Protocol.SPARQL_QUERY }),\n);\n\n/**\n * The decoded type of `DirectQueryBody`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport type DirectQueryBody = typeof DirectQueryBody.Type;\n\n/**\n * An `application/sparql-update` `POST` body, decoded as\n * `{ _tag: 'DirectUpdate', update }`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const DirectUpdateBody = Schema.String.pipe(\n Schema.decodeTo(\n Schema.TaggedStruct('DirectUpdate', { update: Schema.String }),\n SchemaTransformation.transform({\n decode: (update) => ({ _tag: 'DirectUpdate' as const, update }),\n encode: (body) => body.update,\n }),\n ),\n HttpApiSchema.asText({ contentType: Protocol.SPARQL_UPDATE }),\n);\n\n/**\n * The decoded type of `DirectUpdateBody`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport type DirectUpdateBody = typeof DirectUpdateBody.Type;\n\nconst rdfDocument = (mediaType: string) =>\n Schema.String.pipe(\n Schema.decodeTo(\n Schema.Struct({ mediaType: Schema.Literal(mediaType), document: Schema.String }),\n SchemaTransformation.transform({\n decode: (document) => ({ mediaType, document }),\n encode: (body) => body.document,\n }),\n ),\n HttpApiSchema.asText({ contentType: mediaType }),\n );\n\n/**\n * Graph Store request bodies: an RDF document under one of `rdfMediaTypes`,\n * decoded as `{ mediaType, document }` (the media type selects the request's\n * `Content-Type`).\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const RdfDocumentBody = Arr.map(rdfMediaTypes, rdfDocument);\n\n/**\n * The query string of a Graph Store request: `graph=<iri>` (indirect\n * identification) or `default` (the default graph).\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const GraphParams = Schema.Struct({\n graph: Schema.optionalKey(Schema.String),\n default: Schema.optionalKey(Schema.String),\n});\n\n/**\n * The decoded type of `GraphParams`.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport type GraphParams = typeof GraphParams.Type;\n\n/**\n * The error response of a status: a `text/plain` message, decoded as an\n * `ProtocolErrors.ProtocolError` with that status.\n *\n * @since 0.1.0\n * @category schemas\n */\nexport const ProtocolErrorResponse = (status: number) =>\n Schema.String.pipe(\n Schema.decodeTo(\n ProtocolErrors.ProtocolError.pipe(\n Schema.check(\n Schema.makeFilter((error: ProtocolErrors.ProtocolError) => error.status === status, {\n identifier: `ProtocolError${status}`,\n title: `ProtocolError (${status})`,\n description: `A protocol error with status ${status}`,\n }),\n ),\n ),\n SchemaTransformation.transform({\n decode: (message) => ({\n _tag: 'ProtocolError' as const,\n status,\n message: message.trimEnd(),\n }),\n encode: (error) => `${error.message}\\n`,\n }),\n ),\n HttpApiSchema.asText({ contentType: 'text/plain' }),\n HttpApiSchema.status(status),\n );\n\nconst BadRequest = ProtocolErrorResponse(400);\nconst Forbidden = ProtocolErrorResponse(403);\nconst NotFound = ProtocolErrorResponse(404);\nconst MethodNotAllowed = ProtocolErrorResponse(405);\nconst NotAcceptable = ProtocolErrorResponse(406);\nconst UnsupportedMediaType = ProtocolErrorResponse(415);\nconst InternalServerError = ProtocolErrorResponse(500);\nconst ServiceUnavailable = ProtocolErrorResponse(503);\n\n// -----------------------------------------------------------------------------\n// The API\n// -----------------------------------------------------------------------------\n\n/**\n * Middleware on every endpoint that answers a request whose parameters or\n * headers fail the API's schemas with the protocol handler instead of an\n * empty `400` (see `SparqlApiBuilder.layerSchemaFallback`). It needs the\n * server's services and nothing on the client side.\n *\n * @since 0.1.0\n * @category middleware\n */\nexport class SchemaFallback extends HttpApiMiddleware.Service<\n SchemaFallback,\n { requires: Engine.SparqlEngine | Store.QuadStore | Serialization.Formats }\n>()('@thomasfosterau/effect-sparql-node/SparqlApi/SchemaFallback') {}\n\n/**\n * The paths an API is served at.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface Paths {\n /** The SPARQL endpoint, for queries and updates (default `/sparql`). */\n readonly path?: `/${string}` | undefined;\n /** The Graph Store endpoint (default `/rdf-graph-store`). */\n readonly graphStorePath?: `/${string}` | undefined;\n}\n\nconst sparqlGroup = (path: `/${string}`) =>\n HttpApiGroup.make('sparql')\n .add(\n HttpApiEndpoint.get('query', path, {\n query: QueryParams,\n headers: AcceptHeader,\n success: QueryResponse,\n error: [BadRequest, NotAcceptable, InternalServerError, ServiceUnavailable],\n }).annotateMerge(\n OpenApi.annotations({\n summary: 'Query, or read the service description',\n description:\n 'A SPARQL query in `query`, with its dataset in `default-graph-uri` / `named-graph-uri`. Without `query`, answers the service description. The response format is negotiated from `Accept`.',\n }),\n ),\n HttpApiEndpoint.post('post', path, {\n query: DatasetParams,\n headers: AcceptHeader,\n payload: [DirectQueryBody, DirectUpdateBody, FormBody],\n success: [QueryResponse, HttpApiSchema.NoContent],\n error: [\n BadRequest,\n Forbidden,\n NotAcceptable,\n UnsupportedMediaType,\n InternalServerError,\n ServiceUnavailable,\n ],\n }).annotateMerge(\n OpenApi.annotations({\n summary: 'Query or update',\n description:\n 'A query (`200` with the results) or an update (`204`), as a form or as an `application/sparql-query` / `application/sparql-update` body. A read-only endpoint answers updates with `403`.',\n }),\n ),\n )\n .annotateMerge(\n OpenApi.annotations({ title: 'SPARQL', description: 'The SPARQL 1.1 Protocol' }),\n );\n\nconst writeErrors = [\n BadRequest,\n NotFound,\n MethodNotAllowed,\n UnsupportedMediaType,\n InternalServerError,\n] as const;\n\nconst graphStoreGroup = (path: `/${string}`) =>\n HttpApiGroup.make('graphStore')\n .add(\n HttpApiEndpoint.get('get', path, {\n query: GraphParams,\n headers: AcceptHeader,\n success: RdfResponse,\n error: [BadRequest, NotFound, NotAcceptable, InternalServerError],\n }).annotateMerge(OpenApi.annotations({ summary: 'Read a graph' })),\n HttpApiEndpoint.head('head', path, {\n query: GraphParams,\n headers: AcceptHeader,\n success: HttpApiSchema.Empty(200),\n error: [BadRequest, NotFound, NotAcceptable, InternalServerError],\n }).annotateMerge(OpenApi.annotations({ summary: 'Check that a graph exists' })),\n HttpApiEndpoint.put('put', path, {\n query: GraphParams,\n payload: RdfDocumentBody,\n success: [HttpApiSchema.NoContent, HttpApiSchema.Created],\n error: writeErrors,\n }).annotateMerge(\n OpenApi.annotations({\n summary: 'Replace a graph',\n description: '`201` when the graph is new, `204` when it existed.',\n }),\n ),\n HttpApiEndpoint.post('post', path, {\n query: GraphParams,\n payload: RdfDocumentBody,\n success: [HttpApiSchema.NoContent, HttpApiSchema.Created],\n error: writeErrors,\n }).annotateMerge(\n OpenApi.annotations({\n summary: 'Add to a graph, or create one',\n description:\n 'Merges the document into the graph (`201` when it is new). Without `graph` or `default`, creates a new graph and answers `201` with its IRI in `Location`.',\n }),\n ),\n HttpApiEndpoint.delete('delete', path, {\n query: GraphParams,\n success: HttpApiSchema.NoContent,\n error: [BadRequest, NotFound, MethodNotAllowed, InternalServerError],\n }).annotateMerge(OpenApi.annotations({ summary: 'Delete a graph' })),\n )\n .annotateMerge(\n OpenApi.annotations({\n title: 'Graph Store',\n description:\n 'The SPARQL 1.1 Graph Store HTTP Protocol (indirect identification; URLs below the graph store identify graphs directly)',\n }),\n );\n\n/**\n * Builds the API at the given paths.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const make = (paths?: Paths) =>\n HttpApi.make('effect-sparql-node')\n .add(sparqlGroup(paths?.path ?? '/sparql'))\n .add(graphStoreGroup(paths?.graphStorePath ?? '/rdf-graph-store'))\n .middleware(SchemaFallback)\n .annotateMerge(\n OpenApi.annotations({\n title: 'SPARQL 1.1 Protocol and Graph Store HTTP Protocol',\n description:\n 'Every response format is negotiated from `Accept`; methods an endpoint does not support are answered with `405` and `Allow`.',\n }),\n );\n\n/**\n * The type of an API built by `make`.\n *\n * @since 0.1.0\n * @category models\n */\nexport type Api = ReturnType<typeof make>;\n\n/**\n * The API at the default paths, `/sparql` and `/rdf-graph-store`.\n *\n * @since 0.1.0\n * @category api\n */\nexport const SparqlApi: Api = make();\n\n/**\n * The OpenAPI document of an API (the default one when omitted).\n *\n * @since 0.1.0\n * @category openapi\n */\nexport const openApi = (api: Api = SparqlApi): OpenApi.OpenAPISpec => OpenApi.fromApi(api);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as make, a as DirectUpdateBody, b as rdfMediaTypes, c as Paths, d as QueryResponse, f as RdfDocumentBody, h as SparqlApi, i as DirectQueryBody, l as ProtocolErrorResponse, m as SchemaFallback, n as Api, o as FormBody, p as RdfResponse, r as DatasetParams, s as GraphParams, t as AcceptHeader, u as QueryParams, v as openApi, x as resultsMediaTypes, y as queryMediaTypes } from "./SparqlApi-BKE9nIf4.mjs";
|
|
2
|
+
export { AcceptHeader, Api, DatasetParams, DirectQueryBody, DirectUpdateBody, FormBody, GraphParams, Paths, ProtocolErrorResponse, QueryParams, QueryResponse, RdfDocumentBody, RdfResponse, SchemaFallback, SparqlApi, make, openApi, queryMediaTypes, rdfMediaTypes, resultsMediaTypes };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as AcceptHeader, n as DatasetParams, r as DirectQueryBody, i as DirectUpdateBody, a as FormBody, o as GraphParams, s as ProtocolErrorResponse, c as QueryParams, l as QueryResponse, u as RdfDocumentBody, d as RdfResponse, f as SchemaFallback, p as SparqlApi, h as make, g as openApi, _ as queryMediaTypes, v as rdfMediaTypes, y as resultsMediaTypes } from "./SparqlApi-BuycDkWu.mjs";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { c as Paths, m as SchemaFallback } from "./SparqlApi-BKE9nIf4.mjs";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as SparqlServer from "@thomasfosterau/effect-rdf/sparql/SparqlServer";
|
|
5
|
+
import * as HttpApiMiddleware from "effect/http-api/HttpApiMiddleware";
|
|
6
|
+
import * as Scope from "effect/Scope";
|
|
7
|
+
import * as HttpRouter from "effect/http/HttpRouter";
|
|
8
|
+
import * as HttpServer from "effect/http/HttpServer";
|
|
9
|
+
import * as Options from "@thomasfosterau/effect-rdf/sparql/Options";
|
|
10
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
11
|
+
declare namespace SparqlApiBuilder_d_exports {
|
|
12
|
+
export { ApiOptions, layerFallback, layerHandlers, layerRoutes, layerSchemaFallback, layerServe, makeTestClient };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options of the API server: the server options, with the endpoint paths of
|
|
16
|
+
* the API (the update endpoint is the query endpoint) and where to serve the
|
|
17
|
+
* OpenAPI document.
|
|
18
|
+
*
|
|
19
|
+
* @since 0.1.0
|
|
20
|
+
* @category models
|
|
21
|
+
*/
|
|
22
|
+
interface ApiOptions extends Omit<Options.Options, 'path' | 'updatePath' | 'graphStorePath'>, Paths {
|
|
23
|
+
/** Serves the OpenAPI document (`SparqlApi.openApi`) as JSON at this path. */
|
|
24
|
+
readonly openapiPath?: `/${string}` | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The handlers of both API groups (`sparql` and `graphStore`) for the API at
|
|
28
|
+
* the options' paths, using the server's services from the context.
|
|
29
|
+
*
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
* @category layers
|
|
32
|
+
*/
|
|
33
|
+
declare const layerHandlers: (options?: ApiOptions) => Layer.Layer<import("effect/http-api/HttpApiGroup").Service<"effect-sparql-node", "sparql"> | import("effect/http-api/HttpApiGroup").Service<"effect-sparql-node", "graphStore">, never, HttpRouter.Request<"Requires", import("@thomasfosterau/effect-rdf/sparql/Engine").SparqlEngine> | (SchemaFallback | HttpApiMiddleware.ApplyServices<SchemaFallback, never> | HttpRouter.Request<"Requires", import("@thomasfosterau/effect-rdf/sparql/Store").QuadStore> | HttpRouter.Request<"Requires", import("@thomasfosterau/effect-rdf/Serialization").Formats>)>;
|
|
34
|
+
/**
|
|
35
|
+
* The `SparqlApi.SchemaFallback` middleware: a request whose parameters or
|
|
36
|
+
* headers fail the API's schemas is answered by `SparqlServer.handler`, so
|
|
37
|
+
* it gets the protocol's own status and message (e.g. `400` "Exactly one
|
|
38
|
+
* 'query' parameter is allowed"). Response encoding failures stay `500`.
|
|
39
|
+
*
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
* @category layers
|
|
42
|
+
*/
|
|
43
|
+
declare const layerSchemaFallback: (options?: ApiOptions) => Layer.Layer<SchemaFallback>;
|
|
44
|
+
/**
|
|
45
|
+
* The fallback routes: every method on `<path>*` and `<graphStorePath>*`
|
|
46
|
+
* that the API does not declare, answered by `SparqlServer.handler` (`405`
|
|
47
|
+
* with `Allow`, CORS preflights, direct graph identification, `404` for
|
|
48
|
+
* other paths).
|
|
49
|
+
*
|
|
50
|
+
* @since 0.1.0
|
|
51
|
+
* @category layers
|
|
52
|
+
*/
|
|
53
|
+
declare const layerFallback: (options?: ApiOptions) => Layer.Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", SparqlServer.Requirements>>;
|
|
54
|
+
/**
|
|
55
|
+
* Registers the API on the `HttpRouter`: the `HttpApiBuilder` routes with
|
|
56
|
+
* their handlers and the `SchemaFallback` middleware, the fallback routes and, with `openapiPath`, the OpenAPI
|
|
57
|
+
* document.
|
|
58
|
+
*
|
|
59
|
+
* @since 0.1.0
|
|
60
|
+
* @category layers
|
|
61
|
+
*/
|
|
62
|
+
declare const layerRoutes: (options?: ApiOptions) => Layer.Layer<never, never, HttpApiMiddleware.ApplyServices<SchemaFallback, never> | HttpRouter.Request<"Requires", import("@thomasfosterau/effect-rdf/sparql/Engine").SparqlEngine> | HttpRouter.Request<"Requires", import("@thomasfosterau/effect-rdf/sparql/Store").QuadStore> | HttpRouter.Request<"Requires", import("@thomasfosterau/effect-rdf/Serialization").Formats> | HttpRouter.HttpRouter | import("effect/http/Etag").Generator | import("effect/FileSystem").FileSystem | import("effect/http/HttpPlatform").HttpPlatform | import("effect/Path").Path>;
|
|
63
|
+
/**
|
|
64
|
+
* Serves the API routes with `HttpRouter.serve` on an `HttpServer` from the
|
|
65
|
+
* context (logging requests unless `logging: false`); the server layer (such
|
|
66
|
+
* as `NodeHttpServer.layer`) also provides the platform services
|
|
67
|
+
* `HttpApiBuilder` needs.
|
|
68
|
+
*
|
|
69
|
+
* @since 0.1.0
|
|
70
|
+
* @category layers
|
|
71
|
+
*/
|
|
72
|
+
declare const layerServe: (options?: ApiOptions) => Layer.Layer<never, never, HttpApiMiddleware.ApplyServices<SchemaFallback, never> | import("effect/http/Etag").Generator | import("effect/FileSystem").FileSystem | import("effect/http/HttpPlatform").HttpPlatform | import("effect/Path").Path | HttpServer.HttpServer>;
|
|
73
|
+
/**
|
|
74
|
+
* In-process testing: builds a client for the `SparqlApi` routes
|
|
75
|
+
* (`layerRoutes(options)`) using the server's services from
|
|
76
|
+
* the context. The routes live as long as the surrounding scope.
|
|
77
|
+
*
|
|
78
|
+
* @since 0.1.0
|
|
79
|
+
* @category constructors
|
|
80
|
+
*/
|
|
81
|
+
declare const makeTestClient: (options?: ApiOptions) => Effect.Effect<HttpClient.HttpClient, never, SparqlServer.Requirements | Scope.Scope>;
|
|
82
|
+
export { layerRoutes as a, makeTestClient as c, layerHandlers as i, SparqlApiBuilder_d_exports as n, layerSchemaFallback as o, layerFallback as r, layerServe as s, ApiOptions as t };
|
|
83
|
+
//# sourceMappingURL=SparqlApiBuilder-BAMpMy-Z.d.mts.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.mjs";
|
|
2
|
+
import { f as SchemaFallback, h as make } from "./SparqlApi-BuycDkWu.mjs";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as SparqlServer from "@thomasfosterau/effect-rdf/sparql/SparqlServer";
|
|
6
|
+
import * as HttpApiMiddleware from "effect/http-api/HttpApiMiddleware";
|
|
7
|
+
import * as Scope from "effect/Scope";
|
|
8
|
+
import * as HttpRouter from "effect/http/HttpRouter";
|
|
9
|
+
import * as HttpServer from "effect/http/HttpServer";
|
|
10
|
+
import * as HttpServerError from "effect/http/HttpServerError";
|
|
11
|
+
import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
|
|
12
|
+
import * as Testing from "@thomasfosterau/effect-rdf/sparql/Testing";
|
|
13
|
+
// src/SparqlApiBuilder.ts
|
|
14
|
+
/**
|
|
15
|
+
* Serves the `SparqlApi` definition with `HttpApiBuilder`.
|
|
16
|
+
*
|
|
17
|
+
* Every endpoint is implemented with a raw handler (`handleRaw`) that runs
|
|
18
|
+
* the same code as `SparqlServer`: the request body, `Accept` and
|
|
19
|
+
* `Content-Type` are read by the protocol internals, so responses, statuses
|
|
20
|
+
* and error messages are those of `SparqlServer.handler`. Routes the API does
|
|
21
|
+
* not declare (other methods on the endpoints, which get `405` with
|
|
22
|
+
* `Allow`; `OPTIONS` preflights when CORS is on; graphs identified directly
|
|
23
|
+
* by a URL below the graph store) are registered as plain `HttpRouter`
|
|
24
|
+
* fallback routes to `SparqlServer.handler`.
|
|
25
|
+
*
|
|
26
|
+
* `effect/http-api` is `@stability unstable` in Effect 4.
|
|
27
|
+
*
|
|
28
|
+
* @since 0.1.0
|
|
29
|
+
*/
|
|
30
|
+
var SparqlApiBuilder_exports = /* @__PURE__ */ __exportAll({
|
|
31
|
+
layerFallback: () => layerFallback,
|
|
32
|
+
layerHandlers: () => layerHandlers,
|
|
33
|
+
layerRoutes: () => layerRoutes,
|
|
34
|
+
layerSchemaFallback: () => layerSchemaFallback,
|
|
35
|
+
layerServe: () => layerServe,
|
|
36
|
+
makeTestClient: () => makeTestClient
|
|
37
|
+
});
|
|
38
|
+
const serverOptions = (options) => {
|
|
39
|
+
const path = options?.path ?? "/sparql";
|
|
40
|
+
return {
|
|
41
|
+
...options,
|
|
42
|
+
path,
|
|
43
|
+
updatePath: path,
|
|
44
|
+
graphStorePath: options?.graphStorePath ?? "/rdf-graph-store"
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const handlersFor = (api, options) => {
|
|
48
|
+
const sparql = SparqlServer.sparqlHandler(options);
|
|
49
|
+
const graphStore = SparqlServer.graphStoreHandler(options);
|
|
50
|
+
return Layer.mergeAll(HttpApiBuilder.group(api, "sparql", (handlers) => handlers.handleRaw("query", () => sparql).handleRaw("post", () => sparql)), HttpApiBuilder.group(api, "graphStore", (handlers) => handlers.handleRaw("get", () => graphStore).handleRaw("head", () => graphStore).handleRaw("put", () => graphStore).handleRaw("post", () => graphStore).handleRaw("delete", () => graphStore)));
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The handlers of both API groups (`sparql` and `graphStore`) for the API at
|
|
54
|
+
* the options' paths, using the server's services from the context.
|
|
55
|
+
*
|
|
56
|
+
* @since 0.1.0
|
|
57
|
+
* @category layers
|
|
58
|
+
*/
|
|
59
|
+
const layerHandlers = (options) => {
|
|
60
|
+
const resolved = serverOptions(options);
|
|
61
|
+
return handlersFor(make(resolved), resolved);
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The `SparqlApi.SchemaFallback` middleware: a request whose parameters or
|
|
65
|
+
* headers fail the API's schemas is answered by `SparqlServer.handler`, so
|
|
66
|
+
* it gets the protocol's own status and message (e.g. `400` "Exactly one
|
|
67
|
+
* 'query' parameter is allowed"). Response encoding failures stay `500`.
|
|
68
|
+
*
|
|
69
|
+
* @since 0.1.0
|
|
70
|
+
* @category layers
|
|
71
|
+
*/
|
|
72
|
+
const layerSchemaFallback = (options) => {
|
|
73
|
+
const app = SparqlServer.handler(serverOptions(options));
|
|
74
|
+
return HttpApiMiddleware.layerSchemaErrorTransform(SchemaFallback, (error) => error.kind === "Body" || error.kind === "ResponseHeaders" ? Effect.fail(error) : app);
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* The fallback routes: every method on `<path>*` and `<graphStorePath>*`
|
|
78
|
+
* that the API does not declare, answered by `SparqlServer.handler` (`405`
|
|
79
|
+
* with `Allow`, CORS preflights, direct graph identification, `404` for
|
|
80
|
+
* other paths).
|
|
81
|
+
*
|
|
82
|
+
* @since 0.1.0
|
|
83
|
+
* @category layers
|
|
84
|
+
*/
|
|
85
|
+
const layerFallback = (options) => {
|
|
86
|
+
const resolved = serverOptions(options);
|
|
87
|
+
const app = SparqlServer.handler(resolved);
|
|
88
|
+
return HttpRouter.use((router) => Effect.gen(function* () {
|
|
89
|
+
yield* router.add("*", `${resolved.path}*`, app);
|
|
90
|
+
yield* router.add("*", `${resolved.graphStorePath}*`, app);
|
|
91
|
+
}));
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Registers the API on the `HttpRouter`: the `HttpApiBuilder` routes with
|
|
95
|
+
* their handlers and the `SchemaFallback` middleware, the fallback routes and, with `openapiPath`, the OpenAPI
|
|
96
|
+
* document.
|
|
97
|
+
*
|
|
98
|
+
* @since 0.1.0
|
|
99
|
+
* @category layers
|
|
100
|
+
*/
|
|
101
|
+
const layerRoutes = (options) => {
|
|
102
|
+
const resolved = serverOptions(options);
|
|
103
|
+
const api = make(resolved);
|
|
104
|
+
const schemaFallback = layerSchemaFallback(options);
|
|
105
|
+
return Layer.mergeAll(HttpApiBuilder.layer(api, { openapiPath: options?.openapiPath }).pipe(Layer.provide(handlersFor(api, resolved).pipe(Layer.provide(schemaFallback)))), layerFallback(options));
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Serves the API routes with `HttpRouter.serve` on an `HttpServer` from the
|
|
109
|
+
* context (logging requests unless `logging: false`); the server layer (such
|
|
110
|
+
* as `NodeHttpServer.layer`) also provides the platform services
|
|
111
|
+
* `HttpApiBuilder` needs.
|
|
112
|
+
*
|
|
113
|
+
* @since 0.1.0
|
|
114
|
+
* @category layers
|
|
115
|
+
*/
|
|
116
|
+
const layerServe = (options) => HttpRouter.serve(layerRoutes({
|
|
117
|
+
...options,
|
|
118
|
+
logging: false
|
|
119
|
+
}), { disableLogger: options?.logging === false });
|
|
120
|
+
/**
|
|
121
|
+
* In-process testing: builds a client for the `SparqlApi` routes
|
|
122
|
+
* (`layerRoutes(options)`) using the server's services from
|
|
123
|
+
* the context. The routes live as long as the surrounding scope.
|
|
124
|
+
*
|
|
125
|
+
* @since 0.1.0
|
|
126
|
+
* @category constructors
|
|
127
|
+
*/
|
|
128
|
+
const makeTestClient = (options) => Effect.gen(function* () {
|
|
129
|
+
const scope = yield* Scope.Scope;
|
|
130
|
+
const context = yield* Effect.context();
|
|
131
|
+
const app = yield* HttpRouter.toHttpEffect(layerRoutes(options).pipe(Layer.provide(HttpServer.layerServices)));
|
|
132
|
+
return Testing.fromApp(app.pipe(Effect.catchCause((cause) => Effect.map(HttpServerError.causeResponse(cause), ([response]) => response)), Effect.provideService(Scope.Scope, scope), Effect.provideContext(context)));
|
|
133
|
+
});
|
|
134
|
+
export { layerSchemaFallback as a, layerRoutes as i, layerFallback as n, layerServe as o, layerHandlers as r, makeTestClient as s, SparqlApiBuilder_exports as t };
|
|
135
|
+
|
|
136
|
+
//# sourceMappingURL=SparqlApiBuilder-GT7EopBq.mjs.map
|