@thomasfosterau/effect-sparql-node 0.0.0 → 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 +219 -1
- 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 +59 -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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Foster
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,221 @@
|
|
|
1
1
|
# @thomasfosterau/effect-sparql-node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Node side of [`@thomasfosterau/effect-rdf/sparql`](https://github.com/thomasfosterau/effect-rdf/tree/main/packages/core#sparql-thomasfosteraueffect-rdfsparql):
|
|
4
|
+
|
|
5
|
+
- **`NodeSparqlServer`** — the platform-neutral SPARQL 1.1 Protocol and Graph
|
|
6
|
+
Store HTTP Protocol server (`SparqlServer` in effect-rdf) on a `node:http`
|
|
7
|
+
socket, through `@effect/platform-node`'s `NodeHttpServer`: `layer(options)`
|
|
8
|
+
and `layerMemory(options)` (over an in-memory store).
|
|
9
|
+
- **`Cli`** and the `effect-sparql-server` executable — serve RDF files from
|
|
10
|
+
the command line.
|
|
11
|
+
- **`SparqlApi`**, **`SparqlApiBuilder`**, **`SparqlApiClient`** — the two
|
|
12
|
+
protocols as an Effect `HttpApi` definition with an OpenAPI document, the
|
|
13
|
+
routes that serve it with `SparqlServer`'s behaviour, and the client derived
|
|
14
|
+
from it.
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add effect @thomasfosterau/effect-parse @thomasfosterau/effect-rdf @thomasfosterau/effect-sparql-node
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Peers: `effect` (`^4.1.2`: its runtime dependency `@effect/platform-node`
|
|
21
|
+
needs it, and `SparqlApi` declares a list of response content types per
|
|
22
|
+
stream, a 4.1 form of `HttpApiSchema.StreamUint8Array`) and
|
|
23
|
+
`@thomasfosterau/effect-parse` (the SPARQL parser behind effect-rdf's
|
|
24
|
+
`/sparql`). `@thomasfosterau/effect-rdf` and `@effect/platform-node` are
|
|
25
|
+
dependencies.
|
|
26
|
+
|
|
27
|
+
## Serving data
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { Layer } from 'effect';
|
|
31
|
+
import { NodeRuntime } from '@effect/platform-node';
|
|
32
|
+
import { Dataset } from '@thomasfosterau/effect-rdf';
|
|
33
|
+
import { NodeSparqlServer } from '@thomasfosterau/effect-sparql-node';
|
|
34
|
+
|
|
35
|
+
const data: Dataset.Dataset = /* … */ Dataset.empty;
|
|
36
|
+
|
|
37
|
+
// SPARQL endpoint on /sparql, graph store on /rdf-graph-store.
|
|
38
|
+
const Server = NodeSparqlServer.layerMemory({
|
|
39
|
+
port: 3030,
|
|
40
|
+
data,
|
|
41
|
+
queryTimeout: '30 seconds',
|
|
42
|
+
maxResults: 100_000,
|
|
43
|
+
cors: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
Layer.launch(Server).pipe(NodeRuntime.runMain);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
With your own store or engine (e.g. a SQL backend), provide the
|
|
50
|
+
`SparqlEngine`, `QuadStore` and `Serialization.Formats` services:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { Layer } from 'effect';
|
|
54
|
+
import { Engine, ServerFormats, Store } from '@thomasfosterau/effect-rdf/sparql';
|
|
55
|
+
import { NodeSparqlServer } from '@thomasfosterau/effect-sparql-node';
|
|
56
|
+
|
|
57
|
+
const Server = NodeSparqlServer.layer({ port: 3030, readOnly: true }).pipe(
|
|
58
|
+
Layer.provide(Engine.layer({ unionDefaultGraph: true })),
|
|
59
|
+
Layer.provide(Store.layerMemory()),
|
|
60
|
+
Layer.provide(ServerFormats.layer),
|
|
61
|
+
);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`NodeSparqlServer` takes the options of `SparqlServer` (see effect-rdf's
|
|
65
|
+
README) plus `port` and `host`. To mount the routes into a server of your own,
|
|
66
|
+
or to serve them on another platform, use `SparqlServer.layerRoutes`,
|
|
67
|
+
`SparqlServer.handler` or `SparqlServer.layerServe` from
|
|
68
|
+
`@thomasfosterau/effect-rdf/sparql`.
|
|
69
|
+
|
|
70
|
+
## HttpApi definition and OpenAPI
|
|
71
|
+
|
|
72
|
+
`SparqlApi` describes both protocols as an Effect `HttpApi`, so the
|
|
73
|
+
endpoints, their parameters, request and response media types and error
|
|
74
|
+
statuses are typed and documented in one place:
|
|
75
|
+
|
|
76
|
+
- group `sparql`: `query` (`GET` with `query`, `default-graph-uri`,
|
|
77
|
+
`named-graph-uri`; without `query` it answers the service description) and
|
|
78
|
+
`post` (`POST` with an `application/x-www-form-urlencoded`,
|
|
79
|
+
`application/sparql-query` or `application/sparql-update` body; `200` with
|
|
80
|
+
results or `204` after an update);
|
|
81
|
+
- group `graphStore`: `get`, `head`, `put`, `post` and `delete` with
|
|
82
|
+
`graph=<iri>` or `default`, bodies under every RDF media type of
|
|
83
|
+
`ServerFormats.codecs`;
|
|
84
|
+
- responses are byte streams listing every format
|
|
85
|
+
(`SparqlApi.queryMediaTypes`, `SparqlApi.rdfMediaTypes`); errors are
|
|
86
|
+
`text/plain` messages decoded into `ProtocolErrors.ProtocolError` at `400`, `403`,
|
|
87
|
+
`404`, `405`, `406`, `415`, `500` and `503`.
|
|
88
|
+
|
|
89
|
+
`SparqlApi.make({ path, graphStorePath })` builds it at other paths and
|
|
90
|
+
`SparqlApi.openApi(api)` returns its OpenAPI document.
|
|
91
|
+
|
|
92
|
+
`SparqlApiBuilder.layerRoutes(options)` serves it with `HttpApiBuilder`. Its
|
|
93
|
+
handlers are raw (`handleRaw`) and run the same code as `SparqlServer`:
|
|
94
|
+
`Accept` is negotiated and bodies are read by the protocol internals, so
|
|
95
|
+
statuses, headers and messages are identical. Methods the API does not
|
|
96
|
+
declare (`405` with `Allow`), CORS preflights and graphs identified directly
|
|
97
|
+
by a URL below the graph store go to plain `HttpRouter` fallback routes, and
|
|
98
|
+
the `SparqlApi.SchemaFallback` middleware hands a request whose parameters
|
|
99
|
+
fail the API's schemas (e.g. a repeated `query`) to the protocol handler, so
|
|
100
|
+
it gets the protocol's own `400`. The conformance suites run against both
|
|
101
|
+
servers. `effect/http-api` is `@stability unstable` in Effect 4.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { Layer } from 'effect';
|
|
105
|
+
import { HttpRouter } from 'effect/http';
|
|
106
|
+
import { NodeHttpServer } from '@effect/platform-node';
|
|
107
|
+
import { createServer } from 'node:http';
|
|
108
|
+
import { SparqlServer } from '@thomasfosterau/effect-rdf/sparql';
|
|
109
|
+
import { SparqlApiBuilder } from '@thomasfosterau/effect-sparql-node';
|
|
110
|
+
|
|
111
|
+
const Main = HttpRouter.serve(SparqlApiBuilder.layerRoutes({ openapiPath: '/openapi.json' })).pipe(
|
|
112
|
+
Layer.provide(SparqlServer.layerMemoryServices()),
|
|
113
|
+
Layer.provide(NodeHttpServer.layer(createServer, { port: 8080 })),
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`SparqlApiBuilder.layerServe(options)` is the same with request logging, and
|
|
118
|
+
`layerHandlers` / `layerSchemaFallback` / `layerFallback` are the pieces, for
|
|
119
|
+
mounting the API next to other `HttpApiBuilder` groups. The API takes the
|
|
120
|
+
options of `SparqlServer` except `updatePath` (updates go to the query
|
|
121
|
+
endpoint) and `graphStorePath: false`. `SparqlApiBuilder.makeTestClient(options)` is an
|
|
122
|
+
in-process `HttpClient` for the API routes.
|
|
123
|
+
|
|
124
|
+
## Command line
|
|
125
|
+
|
|
126
|
+
This package installs an `effect-sparql-server` executable
|
|
127
|
+
(`bin/effect-sparql-server.mjs`):
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
effect-sparql-server --port 3030 --cors data.ttl more.trig
|
|
131
|
+
effect-sparql-server --read-only --timeout 10000 --graph-per-file a.ttl b.nt
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Files are parsed by extension with the default formats; run
|
|
135
|
+
`effect-sparql-server --help` for every option (the help, `--version` and
|
|
136
|
+
`--completions <shell>` come from `effect/cli`). (From the repository:
|
|
137
|
+
`pnpm --filter @thomasfosterau/effect-sparql-node start -- data.ttl`.)
|
|
138
|
+
|
|
139
|
+
Every flag can also be set by an environment variable — `SPARQL_SERVER_`
|
|
140
|
+
followed by the flag name in upper snake case — which suits containers. A flag
|
|
141
|
+
on the command line wins over the environment, which wins over the default:
|
|
142
|
+
|
|
143
|
+
| Flag | Environment variable | Default |
|
|
144
|
+
| ----------------------- | ----------------------------------- | ------------------ |
|
|
145
|
+
| `-p`, `--port` | `SPARQL_SERVER_PORT` | `3030` |
|
|
146
|
+
| `--host` | `SPARQL_SERVER_HOST` | all interfaces |
|
|
147
|
+
| `--path` | `SPARQL_SERVER_PATH` | `/sparql` |
|
|
148
|
+
| `--update-path` | `SPARQL_SERVER_UPDATE_PATH` | the query path |
|
|
149
|
+
| `--graph-store` | `SPARQL_SERVER_GRAPH_STORE` | `/rdf-graph-store` |
|
|
150
|
+
| `--no-graph-store` | `SPARQL_SERVER_NO_GRAPH_STORE` | `false` |
|
|
151
|
+
| `--read-only` | `SPARQL_SERVER_READ_ONLY` | `false` |
|
|
152
|
+
| `--timeout` | `SPARQL_SERVER_TIMEOUT` | none |
|
|
153
|
+
| `--max-results` | `SPARQL_SERVER_MAX_RESULTS` | none |
|
|
154
|
+
| `--cors` | `SPARQL_SERVER_CORS` | `false` |
|
|
155
|
+
| `--union-default-graph` | `SPARQL_SERVER_UNION_DEFAULT_GRAPH` | `false` |
|
|
156
|
+
| `--graph-per-file` | `SPARQL_SERVER_GRAPH_PER_FILE` | `false` |
|
|
157
|
+
| `--base-url` | `SPARQL_SERVER_BASE_URL` | the request origin |
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
SPARQL_SERVER_PORT=8080 SPARQL_SERVER_READ_ONLY=true effect-sparql-server data.ttl
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Programmatically, `Cli.command` is the `effect/cli` command, `Cli.parse(argv)`
|
|
164
|
+
validates arguments (and the environment) into `Cli.Arguments`, and
|
|
165
|
+
`Cli.run(argv, { version })` runs the server.
|
|
166
|
+
|
|
167
|
+
## Derived client
|
|
168
|
+
|
|
169
|
+
`SparqlApiClient.make(options)` derives a client with `HttpApiClient` from
|
|
170
|
+
the server's `SparqlApi` definition (above): one typed method
|
|
171
|
+
per endpoint (`client.sparql.query`, `client.sparql.post`,
|
|
172
|
+
`client.graphStore.get` / `head` / `put` / `post` / `delete`), returning
|
|
173
|
+
response bodies as byte streams and failing with the server's
|
|
174
|
+
`ProtocolErrors.ProtocolError` (with the status and message) on error statuses. The
|
|
175
|
+
helpers send the common requests and parse the bodies with `ResultsStream`
|
|
176
|
+
and `ClientFormats.defaults`:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { Effect, Stream } from 'effect';
|
|
180
|
+
import { FetchHttpClient } from 'effect/http';
|
|
181
|
+
import { SparqlApiClient } from '@thomasfosterau/effect-sparql-node';
|
|
182
|
+
|
|
183
|
+
const program = Effect.gen(function* () {
|
|
184
|
+
const client = yield* SparqlApiClient.SparqlApiClient;
|
|
185
|
+
// Raw: the generated method, with `responseMode` to see the response.
|
|
186
|
+
const [body, response] = yield* client.sparql.query({
|
|
187
|
+
query: { query: 'ASK {}' },
|
|
188
|
+
headers: { accept: 'application/sparql-results+json' },
|
|
189
|
+
responseMode: 'decoded-and-response',
|
|
190
|
+
});
|
|
191
|
+
// Parsed: SELECT / ASK events, CONSTRUCT graphs, updates, Graph Store.
|
|
192
|
+
const events = yield* Stream.runCollect(SparqlApiClient.results(client, 'SELECT * { ?s ?p ?o }'));
|
|
193
|
+
yield* SparqlApiClient.update(client, 'INSERT DATA { <urn:a> <urn:b> <urn:c> }');
|
|
194
|
+
yield* SparqlApiClient.writeGraph(client, { graph: 'urn:g' }, '<urn:a> <urn:b> "c" .');
|
|
195
|
+
return yield* SparqlApiClient.readGraph(client, { graph: 'urn:g' });
|
|
196
|
+
}).pipe(
|
|
197
|
+
Effect.provide(SparqlApiClient.layer({ baseUrl: 'https://example.org' })),
|
|
198
|
+
Effect.provide(FetchHttpClient.layer),
|
|
199
|
+
);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`options.paths` targets a server at other paths and `transformClient` adds
|
|
203
|
+
retries or authentication. Direct bodies are tagged payloads
|
|
204
|
+
(`{ _tag: 'DirectQuery', query }`, `{ _tag: 'DirectUpdate', update }`),
|
|
205
|
+
forms are plain records, and Graph Store bodies are `{ mediaType, document }`.
|
|
206
|
+
It works against `SparqlServer.handler` too: the definition describes the
|
|
207
|
+
same protocol. `effect/http-api` is `@stability unstable` in Effect 4.
|
|
208
|
+
|
|
209
|
+
## Conformance
|
|
210
|
+
|
|
211
|
+
The SPARQL 1.1 Protocol suites (`.test-suites/rdf-tests/sparql/sparql11`, or
|
|
212
|
+
`$RDF_TEST_SUITES`) run in-process (`test/ProtocolConformance.test.ts`), once
|
|
213
|
+
against `SparqlServer.handler` and once against the `SparqlApi` routes, with
|
|
214
|
+
the same results: Protocol 34 / 34, Graph Store Protocol 14 / 14, Service
|
|
215
|
+
Description 3 / 3 (the deprecated `http-rdf-update` suite is skipped). The
|
|
216
|
+
tests of `SparqlApi`, `SparqlApiClient` (against both servers), the CLI and
|
|
217
|
+
the Node server sit beside it in `test/`.
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
MIT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env -S node --no-warnings
|
|
2
|
+
// Serves RDF files over the SPARQL 1.1 Protocol and Graph Store Protocol.
|
|
3
|
+
// Uses the built `dist/` when available, otherwise the TypeScript sources
|
|
4
|
+
// (run directly with Node's built-in type stripping).
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
|
|
8
|
+
const dist = new URL('../dist/Cli.mjs', import.meta.url);
|
|
9
|
+
const Cli = await import(
|
|
10
|
+
existsSync(dist) ? dist.href : new URL('../src/Cli.ts', import.meta.url).href
|
|
11
|
+
);
|
|
12
|
+
const { NodeRuntime } = await import('@effect/platform-node');
|
|
13
|
+
const { Effect } = await import('effect');
|
|
14
|
+
const { version } = createRequire(import.meta.url)('../package.json');
|
|
15
|
+
|
|
16
|
+
NodeRuntime.runMain(
|
|
17
|
+
Cli.run(process.argv.slice(2), { version }).pipe(
|
|
18
|
+
// Usage errors: `effect/cli` has printed the help; say what was wrong.
|
|
19
|
+
Effect.catchTag('CliError', (e) =>
|
|
20
|
+
Effect.sync(() => {
|
|
21
|
+
if (e.message !== '') process.stderr.write(`${e.message}\n`);
|
|
22
|
+
process.exitCode = 2;
|
|
23
|
+
}),
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
);
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { n as ServeOptions } from "./NodeSparqlServer-1vSrPk3J.mjs";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as FileSystem from "effect/FileSystem";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
5
|
+
import * as Path from "effect/Path";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import { Argument, Command, Flag } from "effect/cli";
|
|
8
|
+
import * as Dataset from "@thomasfosterau/effect-rdf/Dataset";
|
|
9
|
+
import * as Serialization from "@thomasfosterau/effect-rdf/Serialization";
|
|
10
|
+
import * as HttpServerError from "effect/http/HttpServerError";
|
|
11
|
+
declare namespace Cli_d_exports {
|
|
12
|
+
export { Arguments, CliError, Flags, command, config, envPrefix, load, parse, run, toArguments, usage };
|
|
13
|
+
}
|
|
14
|
+
declare const CliError_base: Schema.Class<CliError, Schema.TaggedStruct<"CliError", {
|
|
15
|
+
readonly message: Schema.String;
|
|
16
|
+
}>, import("effect/Cause").YieldableError>;
|
|
17
|
+
/**
|
|
18
|
+
* A command line usage error.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
* @category errors
|
|
22
|
+
*/
|
|
23
|
+
declare class CliError extends CliError_base {}
|
|
24
|
+
/**
|
|
25
|
+
* The prefix of the environment variables that back the flags:
|
|
26
|
+
* `SPARQL_SERVER_PORT`, `SPARQL_SERVER_READ_ONLY`, …
|
|
27
|
+
*
|
|
28
|
+
* @since 0.1.0
|
|
29
|
+
* @category cli
|
|
30
|
+
*/
|
|
31
|
+
declare const envPrefix = "SPARQL_SERVER_";
|
|
32
|
+
/**
|
|
33
|
+
* A summary of the flags and their environment variables; `--help` prints
|
|
34
|
+
* the full, generated help.
|
|
35
|
+
*
|
|
36
|
+
* @since 0.1.0
|
|
37
|
+
* @category cli
|
|
38
|
+
*/
|
|
39
|
+
declare const usage = "Usage: effect-sparql-server [flags] [files...]\n\nServes the given RDF files from an in-memory store over the SPARQL 1.1\nProtocol and the Graph Store HTTP Protocol.\n\nFlags (environment variable in brackets; a flag wins over the environment):\n -p, --port <port> port to listen on (default 3030) [SPARQL_SERVER_PORT]\n --host <host> host to bind (default all interfaces) [SPARQL_SERVER_HOST]\n --path <path> SPARQL endpoint path (default /sparql) [SPARQL_SERVER_PATH]\n --update-path <path> separate update endpoint path [SPARQL_SERVER_UPDATE_PATH]\n --graph-store <path> Graph Store Protocol path (default /rdf-graph-store) [SPARQL_SERVER_GRAPH_STORE]\n --no-graph-store disable the Graph Store Protocol [SPARQL_SERVER_NO_GRAPH_STORE]\n --read-only reject updates and graph store writes [SPARQL_SERVER_READ_ONLY]\n --timeout <ms> query timeout in milliseconds [SPARQL_SERVER_TIMEOUT]\n --max-results <n> maximum number of results per query [SPARQL_SERVER_MAX_RESULTS]\n --cors enable permissive CORS [SPARQL_SERVER_CORS]\n --union-default-graph query the union of all graphs by default [SPARQL_SERVER_UNION_DEFAULT_GRAPH]\n --graph-per-file load each file into a named graph (its file URL) [SPARQL_SERVER_GRAPH_PER_FILE]\n --base-url <url> public origin of the server [SPARQL_SERVER_BASE_URL]\n -h, --help show the full help\n";
|
|
40
|
+
/**
|
|
41
|
+
* Parsed command line arguments.
|
|
42
|
+
*
|
|
43
|
+
* @since 0.1.0
|
|
44
|
+
* @category models
|
|
45
|
+
*/
|
|
46
|
+
interface Arguments {
|
|
47
|
+
readonly help: boolean;
|
|
48
|
+
readonly files: ReadonlyArray<string>;
|
|
49
|
+
readonly graphPerFile: boolean;
|
|
50
|
+
readonly unionDefaultGraph: boolean;
|
|
51
|
+
readonly serve: ServeOptions;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The command's flags and arguments.
|
|
55
|
+
*
|
|
56
|
+
* @since 0.1.0
|
|
57
|
+
* @category cli
|
|
58
|
+
*/
|
|
59
|
+
declare const config: {
|
|
60
|
+
readonly port: Flag.Flag<Option.Option<number>>;
|
|
61
|
+
readonly host: Flag.Flag<Option.Option<string>>;
|
|
62
|
+
readonly path: Flag.Flag<Option.Option<string>>;
|
|
63
|
+
readonly updatePath: Flag.Flag<Option.Option<string>>;
|
|
64
|
+
readonly graphStore: Flag.Flag<Option.Option<string>>;
|
|
65
|
+
readonly noGraphStore: Flag.Flag<boolean>;
|
|
66
|
+
readonly readOnly: Flag.Flag<boolean>;
|
|
67
|
+
readonly timeout: Flag.Flag<Option.Option<number>>;
|
|
68
|
+
readonly maxResults: Flag.Flag<Option.Option<number>>;
|
|
69
|
+
readonly cors: Flag.Flag<boolean>;
|
|
70
|
+
readonly unionDefaultGraph: Flag.Flag<boolean>;
|
|
71
|
+
readonly graphPerFile: Flag.Flag<boolean>;
|
|
72
|
+
readonly baseUrl: Flag.Flag<Option.Option<string>>;
|
|
73
|
+
readonly files: Argument.Argument<readonly string[]>;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The values of the flags and arguments, as `effect/cli` parses them.
|
|
77
|
+
*
|
|
78
|
+
* @since 0.1.0
|
|
79
|
+
* @category models
|
|
80
|
+
*/
|
|
81
|
+
type Flags = Command.Command.Config.Infer<typeof config>;
|
|
82
|
+
/**
|
|
83
|
+
* Validates parsed flags into `Arguments`: integers must be non-negative,
|
|
84
|
+
* paths must start with `/`, and `--base-url` must be an absolute IRI.
|
|
85
|
+
*
|
|
86
|
+
* @since 0.1.0
|
|
87
|
+
* @category cli
|
|
88
|
+
*/
|
|
89
|
+
declare const toArguments: (flags: Flags) => Effect.Effect<Arguments, CliError>;
|
|
90
|
+
/**
|
|
91
|
+
* Parses command line arguments, reading unset flags from the environment
|
|
92
|
+
* (`SPARQL_SERVER_*`). `-h` / `--help` sets `help` instead.
|
|
93
|
+
*
|
|
94
|
+
* @since 0.1.0
|
|
95
|
+
* @category cli
|
|
96
|
+
*/
|
|
97
|
+
declare const parse: (argv: ReadonlyArray<string>) => Effect.Effect<Arguments, CliError>;
|
|
98
|
+
/**
|
|
99
|
+
* Loads RDF files into a dataset through Effect's `FileSystem`, choosing
|
|
100
|
+
* parsers by file extension from the `Formats` service. Relative IRIs
|
|
101
|
+
* resolve against each file's `file:` URL; with `graphPerFile`, a file's
|
|
102
|
+
* default-graph triples go into a graph named by that URL.
|
|
103
|
+
*
|
|
104
|
+
* @since 0.1.0
|
|
105
|
+
* @category cli
|
|
106
|
+
*/
|
|
107
|
+
declare const load: (files: ReadonlyArray<string>, graphPerFile: boolean) => Effect.Effect<Dataset.Dataset, CliError | Serialization.RdfParseError, Serialization.Formats | FileSystem.FileSystem | Path.Path>;
|
|
108
|
+
/**
|
|
109
|
+
* The `effect/cli` command: parses the flags (and their environment
|
|
110
|
+
* variables), loads the files and serves them until interrupted.
|
|
111
|
+
*
|
|
112
|
+
* @since 0.1.0
|
|
113
|
+
* @category cli
|
|
114
|
+
*/
|
|
115
|
+
declare const command: Command.Command<"effect-sparql-server", {
|
|
116
|
+
readonly port: Option.Option<number>;
|
|
117
|
+
readonly host: Option.Option<string>;
|
|
118
|
+
readonly path: Option.Option<string>;
|
|
119
|
+
readonly updatePath: Option.Option<string>;
|
|
120
|
+
readonly graphStore: Option.Option<string>;
|
|
121
|
+
readonly noGraphStore: boolean;
|
|
122
|
+
readonly readOnly: boolean;
|
|
123
|
+
readonly timeout: Option.Option<number>;
|
|
124
|
+
readonly maxResults: Option.Option<number>;
|
|
125
|
+
readonly cors: boolean;
|
|
126
|
+
readonly unionDefaultGraph: boolean;
|
|
127
|
+
readonly graphPerFile: boolean;
|
|
128
|
+
readonly baseUrl: Option.Option<string>;
|
|
129
|
+
readonly files: readonly string[];
|
|
130
|
+
}, {}, CliError | Serialization.RdfParseError | HttpServerError.ServeError, never>;
|
|
131
|
+
/**
|
|
132
|
+
* Runs the command line program: parses arguments (printing the generated
|
|
133
|
+
* help for `--help` or a usage error), loads the files and serves them
|
|
134
|
+
* until interrupted. `version` is what `--version` prints.
|
|
135
|
+
*
|
|
136
|
+
* @since 0.1.0
|
|
137
|
+
* @category cli
|
|
138
|
+
*/
|
|
139
|
+
declare const run: (argv: ReadonlyArray<string>, options?: {
|
|
140
|
+
readonly version?: string | undefined;
|
|
141
|
+
}) => Effect.Effect<void, CliError | Serialization.RdfParseError | HttpServerError.ServeError>;
|
|
142
|
+
export { command as a, load as c, toArguments as d, usage as f, Flags as i, parse as l, CliError as n, config as o, Cli_d_exports as r, envPrefix as s, Arguments as t, run as u };
|
|
143
|
+
//# sourceMappingURL=Cli-DE0eXMJr.d.mts.map
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.mjs";
|
|
2
|
+
import { r as layerMemory } from "./NodeSparqlServer-CYAd_6BG.mjs";
|
|
3
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
4
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
5
|
+
import * as NodePath from "@effect/platform-node/NodePath";
|
|
6
|
+
import * as Config from "effect/Config";
|
|
7
|
+
import * as Console from "effect/Console";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import "effect/FileSystem";
|
|
10
|
+
import * as Layer from "effect/Layer";
|
|
11
|
+
import * as Match from "effect/Match";
|
|
12
|
+
import * as Option from "effect/Option";
|
|
13
|
+
import * as Path from "effect/Path";
|
|
14
|
+
import * as PlatformError from "effect/PlatformError";
|
|
15
|
+
import * as Ref from "effect/Ref";
|
|
16
|
+
import * as Schema from "effect/Schema";
|
|
17
|
+
import { Argument, Command, Flag } from "effect/cli";
|
|
18
|
+
import * as CliErrors from "effect/cli/CliError";
|
|
19
|
+
import * as Iri from "@thomasfosterau/effect-rdf/Iri";
|
|
20
|
+
import * as Dataset from "@thomasfosterau/effect-rdf/Dataset";
|
|
21
|
+
import * as Serialization from "@thomasfosterau/effect-rdf/Serialization";
|
|
22
|
+
import * as Term from "@thomasfosterau/effect-rdf/Term";
|
|
23
|
+
import * as ServerFormats from "@thomasfosterau/effect-rdf/sparql/ServerFormats";
|
|
24
|
+
// src/Cli.ts
|
|
25
|
+
/**
|
|
26
|
+
* The command line entry point: serves RDF files loaded into an in-memory
|
|
27
|
+
* store.
|
|
28
|
+
*
|
|
29
|
+
* ```sh
|
|
30
|
+
* effect-sparql-server --port 3030 data.ttl more.trig
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Files are parsed with the default formats, chosen by file extension.
|
|
34
|
+
* Triples go to the default graph, or with `--graph-per-file` to a named
|
|
35
|
+
* graph per file (named by the file URL); quads keep their graph.
|
|
36
|
+
*
|
|
37
|
+
* The command is built with `effect/cli` (generated `--help`, `--version`
|
|
38
|
+
* and shell completions), and every flag also reads an environment variable
|
|
39
|
+
* through `Config`: `SPARQL_SERVER_` followed by the flag name in upper
|
|
40
|
+
* snake case (`--max-results` is `SPARQL_SERVER_MAX_RESULTS`). A flag on the
|
|
41
|
+
* command line wins over the environment, which wins over the default.
|
|
42
|
+
*
|
|
43
|
+
* @since 0.1.0
|
|
44
|
+
*/
|
|
45
|
+
var Cli_exports = /* @__PURE__ */ __exportAll({
|
|
46
|
+
CliError: () => CliError,
|
|
47
|
+
command: () => command,
|
|
48
|
+
config: () => config,
|
|
49
|
+
envPrefix: () => envPrefix,
|
|
50
|
+
load: () => load,
|
|
51
|
+
parse: () => parse,
|
|
52
|
+
run: () => run,
|
|
53
|
+
toArguments: () => toArguments,
|
|
54
|
+
usage: () => usage
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* A command line usage error.
|
|
58
|
+
*
|
|
59
|
+
* @since 0.1.0
|
|
60
|
+
* @category errors
|
|
61
|
+
*/
|
|
62
|
+
var CliError = class extends Schema.TaggedError()("CliError", { message: Schema.String }) {};
|
|
63
|
+
/**
|
|
64
|
+
* The prefix of the environment variables that back the flags:
|
|
65
|
+
* `SPARQL_SERVER_PORT`, `SPARQL_SERVER_READ_ONLY`, …
|
|
66
|
+
*
|
|
67
|
+
* @since 0.1.0
|
|
68
|
+
* @category cli
|
|
69
|
+
*/
|
|
70
|
+
const envPrefix = "SPARQL_SERVER_";
|
|
71
|
+
const envName = (flag) => `${envPrefix}${flag.toUpperCase().replaceAll("-", "_")}`;
|
|
72
|
+
/**
|
|
73
|
+
* A summary of the flags and their environment variables; `--help` prints
|
|
74
|
+
* the full, generated help.
|
|
75
|
+
*
|
|
76
|
+
* @since 0.1.0
|
|
77
|
+
* @category cli
|
|
78
|
+
*/
|
|
79
|
+
const usage = `Usage: effect-sparql-server [flags] [files...]
|
|
80
|
+
|
|
81
|
+
Serves the given RDF files from an in-memory store over the SPARQL 1.1
|
|
82
|
+
Protocol and the Graph Store HTTP Protocol.
|
|
83
|
+
|
|
84
|
+
Flags (environment variable in brackets; a flag wins over the environment):
|
|
85
|
+
-p, --port <port> port to listen on (default 3030) [SPARQL_SERVER_PORT]
|
|
86
|
+
--host <host> host to bind (default all interfaces) [SPARQL_SERVER_HOST]
|
|
87
|
+
--path <path> SPARQL endpoint path (default /sparql) [SPARQL_SERVER_PATH]
|
|
88
|
+
--update-path <path> separate update endpoint path [SPARQL_SERVER_UPDATE_PATH]
|
|
89
|
+
--graph-store <path> Graph Store Protocol path (default /rdf-graph-store) [SPARQL_SERVER_GRAPH_STORE]
|
|
90
|
+
--no-graph-store disable the Graph Store Protocol [SPARQL_SERVER_NO_GRAPH_STORE]
|
|
91
|
+
--read-only reject updates and graph store writes [SPARQL_SERVER_READ_ONLY]
|
|
92
|
+
--timeout <ms> query timeout in milliseconds [SPARQL_SERVER_TIMEOUT]
|
|
93
|
+
--max-results <n> maximum number of results per query [SPARQL_SERVER_MAX_RESULTS]
|
|
94
|
+
--cors enable permissive CORS [SPARQL_SERVER_CORS]
|
|
95
|
+
--union-default-graph query the union of all graphs by default [SPARQL_SERVER_UNION_DEFAULT_GRAPH]
|
|
96
|
+
--graph-per-file load each file into a named graph (its file URL) [SPARQL_SERVER_GRAPH_PER_FILE]
|
|
97
|
+
--base-url <url> public origin of the server [SPARQL_SERVER_BASE_URL]
|
|
98
|
+
-h, --help show the full help
|
|
99
|
+
`;
|
|
100
|
+
const stringFlag = (name, description) => Flag.String(name).pipe(Flag.withDescription(`${description} [${envName(name)}]`), Flag.withFallbackConfig(Config.String(envName(name))), Flag.optional);
|
|
101
|
+
const intFlag = (name, description) => Flag.Int(name).pipe(Flag.withDescription(`${description} [${envName(name)}]`), Flag.withFallbackConfig(Config.Int(envName(name))), Flag.optional);
|
|
102
|
+
const booleanFlag = (name, description) => Flag.Boolean(name).pipe(Flag.withDescription(`${description} [${envName(name)}]`), Flag.withFallbackConfig(Config.Boolean(envName(name))), Flag.withDefault(false));
|
|
103
|
+
/**
|
|
104
|
+
* The command's flags and arguments.
|
|
105
|
+
*
|
|
106
|
+
* @since 0.1.0
|
|
107
|
+
* @category cli
|
|
108
|
+
*/
|
|
109
|
+
const config = {
|
|
110
|
+
port: intFlag("port", "port to listen on (default 3030)").pipe(Flag.withAlias("p")),
|
|
111
|
+
host: stringFlag("host", "host to bind (default all interfaces)"),
|
|
112
|
+
path: stringFlag("path", "SPARQL endpoint path (default /sparql)"),
|
|
113
|
+
updatePath: stringFlag("update-path", "separate update endpoint path"),
|
|
114
|
+
graphStore: stringFlag("graph-store", "Graph Store Protocol path (default /rdf-graph-store)"),
|
|
115
|
+
noGraphStore: booleanFlag("no-graph-store", "disable the Graph Store Protocol"),
|
|
116
|
+
readOnly: booleanFlag("read-only", "reject updates and graph store writes"),
|
|
117
|
+
timeout: intFlag("timeout", "query timeout in milliseconds"),
|
|
118
|
+
maxResults: intFlag("max-results", "maximum number of results per query"),
|
|
119
|
+
cors: booleanFlag("cors", "enable permissive CORS"),
|
|
120
|
+
unionDefaultGraph: booleanFlag("union-default-graph", "query the union of all graphs by default"),
|
|
121
|
+
graphPerFile: booleanFlag("graph-per-file", "load each file into a named graph (its file URL)"),
|
|
122
|
+
baseUrl: stringFlag("base-url", "public origin of the server"),
|
|
123
|
+
files: Argument.String("files").pipe(Argument.withDescription("RDF files to load, in any syntax the server reads"), Argument.variadic())
|
|
124
|
+
};
|
|
125
|
+
const nonNegative = (name, value) => Option.match(value, {
|
|
126
|
+
onNone: () => Effect.succeed(void 0),
|
|
127
|
+
onSome: (n) => Number.isInteger(n) && n >= 0 ? Effect.succeed(n) : Effect.fail(new CliError({ message: `--${name} expects a non-negative integer` }))
|
|
128
|
+
});
|
|
129
|
+
const endpointPath = (name, value) => Option.match(value, {
|
|
130
|
+
onNone: () => Effect.succeed(void 0),
|
|
131
|
+
onSome: (path) => Schema.is(EndpointPath)(path) ? Effect.succeed(path) : Effect.fail(new CliError({ message: `--${name} must start with '/'` }))
|
|
132
|
+
});
|
|
133
|
+
const EndpointPath = Schema.TemplateLiteral(["/", Schema.String]);
|
|
134
|
+
/**
|
|
135
|
+
* Validates parsed flags into `Arguments`: integers must be non-negative,
|
|
136
|
+
* paths must start with `/`, and `--base-url` must be an absolute IRI.
|
|
137
|
+
*
|
|
138
|
+
* @since 0.1.0
|
|
139
|
+
* @category cli
|
|
140
|
+
*/
|
|
141
|
+
const toArguments = (flags) => Effect.gen(function* () {
|
|
142
|
+
const baseUrl = Option.getOrUndefined(flags.baseUrl);
|
|
143
|
+
if (baseUrl !== void 0 && !Iri.isValid(baseUrl)) return yield* new CliError({ message: `--base-url must be an absolute IRI: ${baseUrl}` });
|
|
144
|
+
const graphStore = yield* endpointPath("graph-store", flags.graphStore);
|
|
145
|
+
const options = {
|
|
146
|
+
path: yield* endpointPath("path", flags.path),
|
|
147
|
+
updatePath: yield* endpointPath("update-path", flags.updatePath),
|
|
148
|
+
graphStorePath: flags.noGraphStore ? false : graphStore,
|
|
149
|
+
readOnly: flags.readOnly,
|
|
150
|
+
queryTimeout: yield* nonNegative("timeout", flags.timeout),
|
|
151
|
+
maxResults: yield* nonNegative("max-results", flags.maxResults),
|
|
152
|
+
cors: flags.cors,
|
|
153
|
+
logging: true,
|
|
154
|
+
baseUrl,
|
|
155
|
+
unionDefaultGraph: flags.unionDefaultGraph
|
|
156
|
+
};
|
|
157
|
+
return {
|
|
158
|
+
help: false,
|
|
159
|
+
files: flags.files,
|
|
160
|
+
graphPerFile: flags.graphPerFile,
|
|
161
|
+
unionDefaultGraph: flags.unionDefaultGraph,
|
|
162
|
+
serve: {
|
|
163
|
+
...options,
|
|
164
|
+
port: (yield* nonNegative("port", flags.port)) ?? 3030,
|
|
165
|
+
host: Option.getOrUndefined(flags.host)
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
const fromCliError = (error) => new CliError({ message: Match.value(error).pipe(Match.tag("ShowHelp", (help) => help.errors.map((e) => e.message).join("\n")), Match.orElse((e) => e.message)) });
|
|
170
|
+
const quiet = () => void 0;
|
|
171
|
+
const silentConsole = Object.assign(Object.create(globalThis.console), {
|
|
172
|
+
log: quiet,
|
|
173
|
+
error: quiet,
|
|
174
|
+
info: quiet,
|
|
175
|
+
warn: quiet
|
|
176
|
+
});
|
|
177
|
+
const isHelp = (argv) => argv.includes("--help") || argv.includes("-h");
|
|
178
|
+
/**
|
|
179
|
+
* Parses command line arguments, reading unset flags from the environment
|
|
180
|
+
* (`SPARQL_SERVER_*`). `-h` / `--help` sets `help` instead.
|
|
181
|
+
*
|
|
182
|
+
* @since 0.1.0
|
|
183
|
+
* @category cli
|
|
184
|
+
*/
|
|
185
|
+
const parse = (argv) => isHelp(argv) ? Effect.succeed({
|
|
186
|
+
help: true,
|
|
187
|
+
files: [],
|
|
188
|
+
graphPerFile: false,
|
|
189
|
+
unionDefaultGraph: false,
|
|
190
|
+
serve: { port: 3030 }
|
|
191
|
+
}) : Effect.gen(function* () {
|
|
192
|
+
const parsed = yield* Ref.make(Option.none());
|
|
193
|
+
const capture = Command.make("effect-sparql-server", config, (flags) => Effect.flatMap(toArguments(flags), (args) => Ref.set(parsed, Option.some(args))));
|
|
194
|
+
yield* Command.runWith(capture, {
|
|
195
|
+
version: "",
|
|
196
|
+
renderErrors: false
|
|
197
|
+
})(argv).pipe(Effect.catchIf(CliErrors.isCliError, (e) => Effect.fail(fromCliError(e))), Effect.provideService(Console.Console, silentConsole), Effect.provide(NodeServices.layer));
|
|
198
|
+
return yield* Effect.flatMap(Ref.get(parsed), Effect.fromOption).pipe(Effect.mapError(() => new CliError({ message: "no arguments were parsed" })));
|
|
199
|
+
});
|
|
200
|
+
/**
|
|
201
|
+
* Loads RDF files into a dataset through Effect's `FileSystem`, choosing
|
|
202
|
+
* parsers by file extension from the `Formats` service. Relative IRIs
|
|
203
|
+
* resolve against each file's `file:` URL; with `graphPerFile`, a file's
|
|
204
|
+
* default-graph triples go into a graph named by that URL.
|
|
205
|
+
*
|
|
206
|
+
* @since 0.1.0
|
|
207
|
+
* @category cli
|
|
208
|
+
*/
|
|
209
|
+
const load = (files, graphPerFile) => Effect.gen(function* () {
|
|
210
|
+
const formats = yield* Serialization.Formats;
|
|
211
|
+
const paths = yield* Path.Path;
|
|
212
|
+
const perFile = yield* Effect.forEach(files, Effect.fnUntraced(function* (file) {
|
|
213
|
+
const parser = Serialization.parserForExtension(formats, paths.extname(file));
|
|
214
|
+
if (Option.isNone(parser)) return yield* new CliError({ message: `Unknown RDF file extension: ${file}` });
|
|
215
|
+
const cannotRead = (e) => new CliError({ message: `Cannot read ${file}: ${e.message}` });
|
|
216
|
+
const url = (yield* paths.toFileUrl(paths.resolve(file)).pipe(Effect.mapError((e) => cannotRead(new PlatformError.PlatformError(e))))).href;
|
|
217
|
+
const result = yield* Serialization.readFile(file, {
|
|
218
|
+
parser: parser.value,
|
|
219
|
+
baseIri: url
|
|
220
|
+
}).pipe(Effect.catchTag("PlatformError", (e) => Effect.fail(cannotRead(e))));
|
|
221
|
+
const graph = graphPerFile ? Term.namedNode(url) : void 0;
|
|
222
|
+
const quads = Array.from(result.dataset, (q) => graph !== void 0 && q.graph._tag === "DefaultGraph" ? new Term.Quad(q.subject, q.predicate, q.object, graph) : q);
|
|
223
|
+
yield* Effect.logInfo(`Loaded ${result.dataset.size} quads from ${file}`);
|
|
224
|
+
return quads;
|
|
225
|
+
}), { concurrency: 1 });
|
|
226
|
+
return Dataset.fromIterable(perFile.flat());
|
|
227
|
+
});
|
|
228
|
+
/**
|
|
229
|
+
* The `effect/cli` command: parses the flags (and their environment
|
|
230
|
+
* variables), loads the files and serves them until interrupted.
|
|
231
|
+
*
|
|
232
|
+
* @since 0.1.0
|
|
233
|
+
* @category cli
|
|
234
|
+
*/
|
|
235
|
+
const command = Command.make("effect-sparql-server", config, (flags) => Effect.gen(function* () {
|
|
236
|
+
const args = yield* toArguments(flags);
|
|
237
|
+
const data = yield* Effect.provide(load(args.files, args.graphPerFile), Layer.mergeAll(ServerFormats.layer, NodeFileSystem.layer, NodePath.layer));
|
|
238
|
+
const server = layerMemory({
|
|
239
|
+
...args.serve,
|
|
240
|
+
data,
|
|
241
|
+
engine: { unionDefaultGraph: args.unionDefaultGraph }
|
|
242
|
+
});
|
|
243
|
+
return yield* Layer.launch(server);
|
|
244
|
+
})).pipe(Command.withDescription("Serves the given RDF files from an in-memory store over the SPARQL 1.1 Protocol and the Graph Store HTTP Protocol."));
|
|
245
|
+
/**
|
|
246
|
+
* Runs the command line program: parses arguments (printing the generated
|
|
247
|
+
* help for `--help` or a usage error), loads the files and serves them
|
|
248
|
+
* until interrupted. `version` is what `--version` prints.
|
|
249
|
+
*
|
|
250
|
+
* @since 0.1.0
|
|
251
|
+
* @category cli
|
|
252
|
+
*/
|
|
253
|
+
const run = (argv, options) => Command.runWith(command, {
|
|
254
|
+
version: options?.version ?? "unknown",
|
|
255
|
+
renderErrors: false
|
|
256
|
+
})(argv).pipe(Effect.catchIf(CliErrors.isCliError, (e) => Effect.fail(fromCliError(e))), Effect.provide(NodeServices.layer));
|
|
257
|
+
export { envPrefix as a, run as c, config as i, toArguments as l, Cli_exports as n, load as o, command as r, parse as s, CliError as t, usage as u };
|
|
258
|
+
|
|
259
|
+
//# sourceMappingURL=Cli-DFvng1N8.mjs.map
|