@_linked/server 3.13.0 → 3.14.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 +33 -0
- package/lib/esm/backend.d.ts +23 -5
- package/lib/esm/backend.js +116 -25
- package/lib/esm/backend.js.map +1 -1
- package/lib/esm/shapes/LincdAPI.d.ts +1 -1
- package/lib/esm/shapes/LincdAPI.js +43 -7
- package/lib/esm/shapes/LincdAPI.js.map +1 -1
- package/lib/esm/shapes/LinkedServer.d.ts +50 -37
- package/lib/esm/shapes/LinkedServer.js +121 -76
- package/lib/esm/shapes/LinkedServer.js.map +1 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.d.ts +35 -6
- package/lib/esm/shapes/quadstores/RemoteDataset.js +77 -12
- package/lib/esm/shapes/quadstores/RemoteDataset.js.map +1 -1
- package/lib/esm/utils/adminChannel.d.ts +22 -51
- package/lib/esm/utils/adminChannel.js +48 -36
- package/lib/esm/utils/adminChannel.js.map +1 -1
- package/lib/esm/utils/effectiveRoutes.d.ts +6 -0
- package/lib/esm/utils/effectiveRoutes.js +2 -1
- package/lib/esm/utils/effectiveRoutes.js.map +1 -1
- package/lib/esm/utils/forwardedTransactions.d.ts +46 -0
- package/lib/esm/utils/forwardedTransactions.js +155 -0
- package/lib/esm/utils/forwardedTransactions.js.map +1 -0
- package/lib/esm/utils/graphTargets.d.ts +37 -20
- package/lib/esm/utils/graphTargets.js +61 -82
- package/lib/esm/utils/graphTargets.js.map +1 -1
- package/lib/esm/utils/ontologySync.d.ts +40 -23
- package/lib/esm/utils/ontologySync.js +59 -37
- package/lib/esm/utils/ontologySync.js.map +1 -1
- package/lib/esm/utils/queryPlane.d.ts +13 -0
- package/lib/esm/utils/queryPlane.js +49 -2
- package/lib/esm/utils/queryPlane.js.map +1 -1
- package/lib/esm/utils/reloadSource.js +1 -1
- package/lib/esm/utils/reloadSource.js.map +1 -1
- package/lib/esm/utils/serverTransactions.d.ts +22 -0
- package/lib/esm/utils/serverTransactions.js +81 -0
- package/lib/esm/utils/serverTransactions.js.map +1 -0
- package/lib/esm/utils/syncShapes.d.ts +120 -8
- package/lib/esm/utils/syncShapes.js +151 -32
- package/lib/esm/utils/syncShapes.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +134 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @\_linked/server
|
|
2
2
|
|
|
3
|
+
## 3.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#223](https://github.com/linked-fw/server/pull/223) [`47f7d72`](https://github.com/linked-fw/server/commit/47f7d7294ef433e46c1098576286a5311ad55fb5) Thanks [@flyon](https://github.com/flyon)! - `POST /admin/sync-shapes` is removed from the admin channel. A host that changed an app's shape
|
|
8
|
+
code asks `POST /admin/graph-content {reload: [packages]}`: it reloads those packages (in dev),
|
|
9
|
+
registers the ontologies again, runs the shape check and answers the content of the app's graphs,
|
|
10
|
+
writing nothing; the host writes them (`LINKED_GRAPH_WRITER=host`). The route answers 404, and
|
|
11
|
+
`ShapeSyncResult`, `parseSyncShapesBody`, the `syncShapes` admin handler and
|
|
12
|
+
`syncOntologiesThenShapes` are gone.
|
|
13
|
+
|
|
14
|
+
The env boot flags `LINKED_SYNC_SHAPES_ON_BOOT` and `LINKED_SYNC_ONTOLOGIES_ON_BOOT` are removed.
|
|
15
|
+
The boot syncs the shapes and the ontologies when the app writes its own graphs
|
|
16
|
+
(`LINKED_GRAPH_WRITER` unset or `app`) and `linked.config` does not set `syncShapesOnBoot: false`,
|
|
17
|
+
the one switch for both. `shapeSyncOn(configFlag)` takes the config switch only;
|
|
18
|
+
`bootSyncsOntologies` is gone.
|
|
19
|
+
|
|
20
|
+
- [#223](https://github.com/linked-fw/server/pull/223) [`c7e09cc`](https://github.com/linked-fw/server/commit/c7e09cc4e8c1d04d418e5d730890e517f1406c62) Thanks [@flyon](https://github.com/flyon)! - The boot's ontology and shape syncs follow the app's storage
|
|
21
|
+
routing through core's routed syncs: each package's graphs go into every dataset the routing needs
|
|
22
|
+
them in, a graph routing no longer sends to the default dataset is dropped there, and one left in
|
|
23
|
+
another dataset is reported. With pins the boot logs one line naming the target datasets; the
|
|
24
|
+
earlier warning that the syncs write into the default dataset only is gone.
|
|
25
|
+
|
|
26
|
+
New `LINKED_GRAPH_WRITER=app|host`: under `host` the app writes no shape or ontology graph, and
|
|
27
|
+
`GET /admin/routes` reports `graphWriter`. New `POST /admin/graph-content {packages?, reload?}` on
|
|
28
|
+
the admin channel answers, writing nothing, each package's shape and ontology graph and the app's
|
|
29
|
+
own shape graph as `{graph, mediaType, text, hash, sources}`: the exact bytes and hash the
|
|
30
|
+
`'package'`-mode sync writes.
|
|
31
|
+
|
|
32
|
+
- [#223](https://github.com/linked-fw/server/pull/223) [`34ac38d`](https://github.com/linked-fw/server/commit/34ac38d7f5f7a8c695504598bd4b7b82dca77485) Thanks [@flyon](https://github.com/flyon)! - One read method, for `@_linked/core`'s `IDataset.readQuery` (core 2.46, DSL-JSON 1.10; the `@_linked/core` range is raised to `^2.46.0`). The generic provider's `selectQuery` and `askQuery` endpoints are replaced by one, `POST /call/@_linked/server/readQuery` (`@callable('public')`), which answers a select, a count or an ask: the body's kind is read before it is decoded (`readKindOfJSON`) and picks the access rule (`select` for a select or a count, `ask` for an ask), and the decoded query must be of that kind. A triples or terms read, a graph query and a write posted to it answer 400 (and roll back the caller's transaction they name). `/api/select` checks its read by kind the same way (an ask posted there is now answered, under the ask rule, where it was refused with 400; a body naming a transaction that does not decode now rolls it back). A store's function `read` rule sees `operation: 'ask'` for an ask, as it did from `askQuery`. `RemoteDataset` implements `readQuery` (select, count and ask go to the `readQuery` endpoint; triples and terms reads are refused without being sent) and declares `supports: ['ask']`, plus `'transactions'` after a ping that lists them. `graphQuery` and the write endpoints are unchanged. A client on an older `@_linked/server` gets `501 No provider` for its reads from this server.
|
|
33
|
+
|
|
34
|
+
- [#223](https://github.com/linked-fw/server/pull/223) [`6538642`](https://github.com/linked-fw/server/commit/653864235823695adbc1a1a21739226bace334bc) Thanks [@flyon](https://github.com/flyon)! - Store transactions over `/call`: `beginTransaction`, `commit(txId)` and `rollback(txId)` on the generic provider, and queries that carry a `txId` run in the caller's transaction (checked against the store rules as if sent alone; a refusal rolls it back). The server holds transactions only when every dataset `LinkedStorage` routes to declares `'transactions'`, and its `ping` then answers `supports: ['transactions']`. A txId is bound to the session's user; another caller's is refused. `RemoteDataset` declares `'transactions'` after such a ping (the no-url form now pings its backend for this), and gains `beginTransaction`, `commit` and `rollback`. A query with a txId on `/api/*`, and a graph query with one, are refused; any refusal of a request naming the caller's own transaction rolls it back.
|
|
35
|
+
|
|
3
36
|
## 3.13.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
package/lib/esm/backend.d.ts
CHANGED
|
@@ -4,15 +4,33 @@ export default class LincdServerBackendProvider extends BackendProvider {
|
|
|
4
4
|
/**
|
|
5
5
|
* Reachability check for a `RemoteDataset` pointed at this server: reads nothing, changes
|
|
6
6
|
* nothing. A plain linked server has no token concept, so it always answers `{ok: true}`;
|
|
7
|
-
* a host that serves the wire with tokens answers 401 for a refused one.
|
|
7
|
+
* a host that serves the wire with tokens answers 401 for a refused one. `supports` lists
|
|
8
|
+
* `'transactions'` when this server holds store transactions (every store it routes to does);
|
|
9
|
+
* `RemoteDataset` declares them only then.
|
|
8
10
|
*/
|
|
9
11
|
ping(): Promise<{
|
|
10
12
|
ok: true;
|
|
13
|
+
supports: string[];
|
|
11
14
|
}>;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
beginTransaction(): Promise<string>;
|
|
16
|
+
commit(txId: unknown): Promise<void>;
|
|
17
|
+
rollback(txId: unknown): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Decode, check and run one generic query. `decode` turns the body into the builder the store
|
|
20
|
+
* runs and the access rule it is checked under (a write's rule is its endpoint's; a read's
|
|
21
|
+
* follows its kind, see `toReadQuery`). One that carries a txId runs in its transaction: the
|
|
22
|
+
* caller's own, checked against the store rules as if it were sent alone. Any refusal or
|
|
23
|
+
* failure of it (a body that does not decode included) rolls the transaction back.
|
|
24
|
+
*/
|
|
25
|
+
private genericQuery;
|
|
26
|
+
/**
|
|
27
|
+
* Every read: a select, a count or an ask (DSL-JSON, told apart by `op`). The body's kind picks
|
|
28
|
+
* the access rule (`select` for a select or a count, `ask` for an ask; see `toReadQuery`).
|
|
29
|
+
* Triples and terms reads, graph queries and writes are refused with 400.
|
|
30
|
+
*/
|
|
31
|
+
readQuery(json: any): Promise<number | boolean | string[] | import("@_linked/core/queries/IntermediateRepresentation.js").SelectResult | import("@_linked/core").Triple[]>;
|
|
32
|
+
updateQuery(json: any): Promise<import("@_linked/core/queries/IntermediateRepresentation.js").UpdateResult>;
|
|
33
|
+
createQuery(json: any): Promise<import("@_linked/core/queries/IntermediateRepresentation.js").ResultRow>;
|
|
16
34
|
deleteQuery(json: any): Promise<import("@_linked/core/queries/DeleteQuery.js").DeleteResponse>;
|
|
17
35
|
/**
|
|
18
36
|
* A whole-graph operation (`graph_create`, `graph_replace`, `graph_patch`, `graph_drop`), as
|
package/lib/esm/backend.js
CHANGED
|
@@ -12,10 +12,25 @@ import { callable } from '@_linked/server-utils/utils/callable';
|
|
|
12
12
|
import { LinkedFileStorage } from '@_linked/core/utils/LinkedFileStorage';
|
|
13
13
|
import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
|
|
14
14
|
import { LocalFileStore } from './shapes/filestores/LocalFileStore.js';
|
|
15
|
-
import { authorizeGenericQuery, authorizeGraphQuery, graphQueryCallError, resolveQueryCatalog, toGraphQuery, toQueryBuilder, } from './utils/queryPlane.js';
|
|
15
|
+
import { authorizeGenericQuery, authorizeGraphQuery, graphQueryCallError, resolveQueryCatalog, toGraphQuery, toQueryBuilder, toReadQuery, } from './utils/queryPlane.js';
|
|
16
16
|
import { ServerCallError } from '@_linked/server-utils/utils/ServerCallError';
|
|
17
|
+
import { txIdOf } from '@_linked/core/queries/transactionQuery';
|
|
18
|
+
import { UnresolvedContextError } from '@_linked/core/queries/QueryContext';
|
|
19
|
+
import { isLinkedError } from '@_linked/core/utils/linkedError';
|
|
20
|
+
import { abandonTransactionOf, assertStoresHoldTransactions, storesHoldTransactions, transactionCallError, transactionCaller, transactions, } from './utils/serverTransactions.js';
|
|
17
21
|
import path from 'path';
|
|
18
22
|
import fs from 'fs/promises';
|
|
23
|
+
/** A write's decoder for `genericQuery`: the body as its builder, checked under the endpoint's own rule. */
|
|
24
|
+
function writeOf(operation) {
|
|
25
|
+
return (json, catalog) => ({
|
|
26
|
+
query: toQueryBuilder(json, catalog),
|
|
27
|
+
operation,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function assertTxId(txId) {
|
|
31
|
+
if (typeof txId !== 'string' || !txId)
|
|
32
|
+
throw new ServerCallError(400, 'Expected a transaction id');
|
|
33
|
+
}
|
|
19
34
|
export default class LincdServerBackendProvider extends BackendProvider {
|
|
20
35
|
async setupBeforeControllers() {
|
|
21
36
|
if (!LinkedFileStorage.getDefaultStore()) {
|
|
@@ -34,10 +49,76 @@ export default class LincdServerBackendProvider extends BackendProvider {
|
|
|
34
49
|
/**
|
|
35
50
|
* Reachability check for a `RemoteDataset` pointed at this server: reads nothing, changes
|
|
36
51
|
* nothing. A plain linked server has no token concept, so it always answers `{ok: true}`;
|
|
37
|
-
* a host that serves the wire with tokens answers 401 for a refused one.
|
|
52
|
+
* a host that serves the wire with tokens answers 401 for a refused one. `supports` lists
|
|
53
|
+
* `'transactions'` when this server holds store transactions (every store it routes to does);
|
|
54
|
+
* `RemoteDataset` declares them only then.
|
|
38
55
|
*/
|
|
39
56
|
async ping() {
|
|
40
|
-
return { ok: true };
|
|
57
|
+
return { ok: true, supports: storesHoldTransactions() ? ['transactions'] : [] };
|
|
58
|
+
}
|
|
59
|
+
// ── Store transactions ───────────────────────────────────────────────────
|
|
60
|
+
//
|
|
61
|
+
// `RemoteDataset.beginTransaction()` / `commit(txId)` / `rollback(txId)` (core report 103). The
|
|
62
|
+
// server answers its own txId and holds a core `Transaction` for it, which opens a transaction
|
|
63
|
+
// in each store a query of it routes to. The txId is bound to the caller that began it.
|
|
64
|
+
// A server whose stores do not all hold transactions answers 501 `transactions-unsupported`;
|
|
65
|
+
// an unknown or expired txId 409 `transaction-unknown` (a rollback of one resolves); another
|
|
66
|
+
// caller's txId 403 `transaction-not-yours`.
|
|
67
|
+
async beginTransaction() {
|
|
68
|
+
try {
|
|
69
|
+
const caller = transactionCaller();
|
|
70
|
+
assertStoresHoldTransactions();
|
|
71
|
+
return transactions.begin(caller, LinkedStorage.begin());
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
throw transactionCallError(err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async commit(txId) {
|
|
78
|
+
assertTxId(txId);
|
|
79
|
+
try {
|
|
80
|
+
await transactions.commit(txId, transactionCaller());
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
throw transactionCallError(err);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async rollback(txId) {
|
|
87
|
+
assertTxId(txId);
|
|
88
|
+
try {
|
|
89
|
+
await transactions.rollback(txId, transactionCaller());
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
throw transactionCallError(err);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Decode, check and run one generic query. `decode` turns the body into the builder the store
|
|
97
|
+
* runs and the access rule it is checked under (a write's rule is its endpoint's; a read's
|
|
98
|
+
* follows its kind, see `toReadQuery`). One that carries a txId runs in its transaction: the
|
|
99
|
+
* caller's own, checked against the store rules as if it were sent alone. Any refusal or
|
|
100
|
+
* failure of it (a body that does not decode included) rolls the transaction back.
|
|
101
|
+
*/
|
|
102
|
+
async genericQuery(json, decode, endpoint, inTransaction, direct) {
|
|
103
|
+
try {
|
|
104
|
+
const { query, operation } = decode(json, await resolveQueryCatalog(json));
|
|
105
|
+
const authorize = () => authorizeGenericQuery(operation, json, query, endpoint);
|
|
106
|
+
if (!txIdOf(query)) {
|
|
107
|
+
await authorize();
|
|
108
|
+
return await direct(query);
|
|
109
|
+
}
|
|
110
|
+
assertStoresHoldTransactions();
|
|
111
|
+
return await transactions.query(query, transactionCaller(), async () => {
|
|
112
|
+
assertStoresHoldTransactions();
|
|
113
|
+
await authorize();
|
|
114
|
+
}, inTransaction);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
// A select whose query context is not ready did not run: it fails nothing (as in core).
|
|
118
|
+
if (!isLinkedError(err, UnresolvedContextError))
|
|
119
|
+
await abandonTransactionOf(json);
|
|
120
|
+
throw transactionCallError(err);
|
|
121
|
+
}
|
|
41
122
|
}
|
|
42
123
|
// ── Generic query execution ──────────────────────────────────────────────
|
|
43
124
|
//
|
|
@@ -49,8 +130,10 @@ export default class LincdServerBackendProvider extends BackendProvider {
|
|
|
49
130
|
// A query cannot cross the wire live, so the client ships `toJSON()` and `fromJSON()`
|
|
50
131
|
// rehydrates it here into a live, closed query. `LinkedStorage` then routes it by the
|
|
51
132
|
// QUERY's shape, which is what decides the target dataset; the store instance never
|
|
52
|
-
// participates.
|
|
53
|
-
//
|
|
133
|
+
// participates. A query that carries a txId (DSL-JSON 1.5) runs in the caller's
|
|
134
|
+
// transaction instead (see "Store transactions" above). The host's catalog resolver
|
|
135
|
+
// (`setQueryCatalogResolver`) picks the shape catalog the JSON is decoded in; the builder
|
|
136
|
+
// keeps it for both lowerings.
|
|
54
137
|
//
|
|
55
138
|
// Every query is checked first against the access rules of the stores it
|
|
56
139
|
// maps to, declared in the app's storage config (`withAccess`; a store with
|
|
@@ -58,30 +141,22 @@ export default class LincdServerBackendProvider extends BackendProvider {
|
|
|
58
141
|
// rethought. The store runs the builder that was checked. They are declared
|
|
59
142
|
// 'public' because that check, not the RPC layer, decides whether a session
|
|
60
143
|
// is required.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
async
|
|
67
|
-
|
|
68
|
-
await authorizeGenericQuery('ask', json, query, 'askQuery');
|
|
69
|
-
return LinkedStorage.askQuery(query);
|
|
144
|
+
/**
|
|
145
|
+
* Every read: a select, a count or an ask (DSL-JSON, told apart by `op`). The body's kind picks
|
|
146
|
+
* the access rule (`select` for a select or a count, `ask` for an ask; see `toReadQuery`).
|
|
147
|
+
* Triples and terms reads, graph queries and writes are refused with 400.
|
|
148
|
+
*/
|
|
149
|
+
async readQuery(json) {
|
|
150
|
+
return this.genericQuery(json, toReadQuery, 'readQuery', (tx, q) => tx.readQuery(q), (q) => LinkedStorage.readQuery(q));
|
|
70
151
|
}
|
|
71
152
|
async updateQuery(json) {
|
|
72
|
-
|
|
73
|
-
await authorizeGenericQuery('update', json, query, 'updateQuery');
|
|
74
|
-
return LinkedStorage.updateQuery(query);
|
|
153
|
+
return this.genericQuery(json, writeOf('update'), 'updateQuery', (tx, q) => tx.updateQuery(q), (q) => LinkedStorage.updateQuery(q));
|
|
75
154
|
}
|
|
76
155
|
async createQuery(json) {
|
|
77
|
-
|
|
78
|
-
await authorizeGenericQuery('create', json, query, 'createQuery');
|
|
79
|
-
return LinkedStorage.createQuery(query);
|
|
156
|
+
return this.genericQuery(json, writeOf('create'), 'createQuery', (tx, q) => tx.createQuery(q), (q) => LinkedStorage.createQuery(q));
|
|
80
157
|
}
|
|
81
158
|
async deleteQuery(json) {
|
|
82
|
-
|
|
83
|
-
await authorizeGenericQuery('delete', json, query, 'deleteQuery');
|
|
84
|
-
return LinkedStorage.deleteQuery(query);
|
|
159
|
+
return this.genericQuery(json, writeOf('delete'), 'deleteQuery', (tx, q) => tx.deleteQuery(q), (q) => LinkedStorage.deleteQuery(q));
|
|
85
160
|
}
|
|
86
161
|
/**
|
|
87
162
|
* A whole-graph operation (`graph_create`, `graph_replace`, `graph_patch`, `graph_drop`), as
|
|
@@ -92,6 +167,10 @@ export default class LincdServerBackendProvider extends BackendProvider {
|
|
|
92
167
|
* `graphQueryCallError`).
|
|
93
168
|
*/
|
|
94
169
|
async graphQuery(json) {
|
|
170
|
+
if (json && typeof json === 'object' && 'txId' in json) {
|
|
171
|
+
await abandonTransactionOf(json);
|
|
172
|
+
throw new ServerCallError(400, 'A graph query does not run in a transaction');
|
|
173
|
+
}
|
|
95
174
|
const query = toGraphQuery(json);
|
|
96
175
|
await authorizeGraphQuery(json, query, 'graphQuery');
|
|
97
176
|
if (!query)
|
|
@@ -110,18 +189,30 @@ __decorate([
|
|
|
110
189
|
__metadata("design:paramtypes", []),
|
|
111
190
|
__metadata("design:returntype", Promise)
|
|
112
191
|
], LincdServerBackendProvider.prototype, "ping", null);
|
|
192
|
+
__decorate([
|
|
193
|
+
callable('public'),
|
|
194
|
+
__metadata("design:type", Function),
|
|
195
|
+
__metadata("design:paramtypes", []),
|
|
196
|
+
__metadata("design:returntype", Promise)
|
|
197
|
+
], LincdServerBackendProvider.prototype, "beginTransaction", null);
|
|
198
|
+
__decorate([
|
|
199
|
+
callable('public'),
|
|
200
|
+
__metadata("design:type", Function),
|
|
201
|
+
__metadata("design:paramtypes", [Object]),
|
|
202
|
+
__metadata("design:returntype", Promise)
|
|
203
|
+
], LincdServerBackendProvider.prototype, "commit", null);
|
|
113
204
|
__decorate([
|
|
114
205
|
callable('public'),
|
|
115
206
|
__metadata("design:type", Function),
|
|
116
207
|
__metadata("design:paramtypes", [Object]),
|
|
117
208
|
__metadata("design:returntype", Promise)
|
|
118
|
-
], LincdServerBackendProvider.prototype, "
|
|
209
|
+
], LincdServerBackendProvider.prototype, "rollback", null);
|
|
119
210
|
__decorate([
|
|
120
211
|
callable('public'),
|
|
121
212
|
__metadata("design:type", Function),
|
|
122
213
|
__metadata("design:paramtypes", [Object]),
|
|
123
214
|
__metadata("design:returntype", Promise)
|
|
124
|
-
], LincdServerBackendProvider.prototype, "
|
|
215
|
+
], LincdServerBackendProvider.prototype, "readQuery", null);
|
|
125
216
|
__decorate([
|
|
126
217
|
callable('public'),
|
|
127
218
|
__metadata("design:type", Function),
|
package/lib/esm/backend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import { BackendProvider } from '@_linked/server-utils/utils/BackendProvider';\nimport { callable } from '@_linked/server-utils/utils/callable';\nimport { LinkedFileStorage } from '@_linked/core/utils/LinkedFileStorage';\nimport { LinkedStorage } from '@_linked/core/utils/LinkedStorage';\nimport { LocalFileStore } from './shapes/filestores/LocalFileStore.js';\nimport {\n authorizeGenericQuery,\n authorizeGraphQuery,\n graphQueryCallError,\n resolveQueryCatalog,\n toGraphQuery,\n toQueryBuilder,\n} from './utils/queryPlane.js';\nimport { ServerCallError } from '@_linked/server-utils/utils/ServerCallError';\nimport path from 'path';\nimport fs from 'fs/promises';\n\nexport default class LincdServerBackendProvider extends BackendProvider {\n async setupBeforeControllers() {\n if (!LinkedFileStorage.getDefaultStore()) {\n LinkedFileStorage.setDefaultStore(\n new LocalFileStore(process.env.NODE_ENV + '-filestore')\n );\n }\n let fileSystemUploadPath = path.join('data', 'filestores');\n try {\n await fs.access(fileSystemUploadPath);\n } catch (err) {\n await fs.mkdir(path.join(fileSystemUploadPath, 'filestores'), {\n recursive: true,\n });\n }\n }\n\n /**\n * Reachability check for a `RemoteDataset` pointed at this server: reads nothing, changes\n * nothing. A plain linked server has no token concept, so it always answers `{ok: true}`;\n * a host that serves the wire with tokens answers 401 for a refused one.\n */\n @callable('public')\n async ping(): Promise<{ ok: true }> {\n return { ok: true };\n }\n\n // ── Generic query execution ──────────────────────────────────────────────\n //\n // These answer `RemoteDataset`: a browser's store calls\n // `Server.call(packageName, {method}, json)` (the PACKAGE form), and a remote app's store\n // posts the same `{args}` body to `{url}/call/@_linked/server/<method>`. Either way the\n // request arrives here on the generic provider rather than through Shape resolution.\n //\n // A query cannot cross the wire live, so the client ships `toJSON()` and `fromJSON()`\n // rehydrates it here into a live, closed query. `LinkedStorage` then routes it by the\n // QUERY's shape, which is what decides the target dataset; the store instance never\n // participates. The host's catalog resolver (`setQueryCatalogResolver`) picks the shape\n // catalog the JSON is decoded in; the builder keeps it for both lowerings.\n //\n // Every query is checked first against the access rules of the stores it\n // maps to, declared in the app's storage config (`withAccess`; a store with\n // no rule requires a session): see utils/queryPlane. INTERIM model, to be\n // rethought. The store runs the builder that was checked. They are declared\n // 'public' because that check, not the RPC layer, decides whether a session\n // is required.\n\n @callable('public')\n async selectQuery(json: any) {\n const query = toQueryBuilder(json, await resolveQueryCatalog(json));\n await authorizeGenericQuery('select', json, query, 'selectQuery');\n return LinkedStorage.selectQuery(query);\n }\n\n @callable('public')\n async askQuery(json: any) {\n const query = toQueryBuilder(json, await resolveQueryCatalog(json));\n await authorizeGenericQuery('ask', json, query, 'askQuery');\n return LinkedStorage.askQuery(query);\n }\n\n @callable('public')\n async updateQuery(json: any) {\n const query = toQueryBuilder(json, await resolveQueryCatalog(json));\n await authorizeGenericQuery('update', json, query, 'updateQuery');\n return LinkedStorage.updateQuery(query);\n }\n\n @callable('public')\n async createQuery(json: any) {\n const query = toQueryBuilder(json, await resolveQueryCatalog(json));\n await authorizeGenericQuery('create', json, query, 'createQuery');\n return LinkedStorage.createQuery(query);\n }\n\n @callable('public')\n async deleteQuery(json: any) {\n const query = toQueryBuilder(json, await resolveQueryCatalog(json));\n await authorizeGenericQuery('delete', json, query, 'deleteQuery');\n return LinkedStorage.deleteQuery(query);\n }\n\n /**\n * A whole-graph operation (`graph_create`, `graph_replace`, `graph_patch`, `graph_drop`), as\n * `RemoteDataset.graphQuery` sends it. The query is plain data: core's `parseGraphQuery` checks\n * it, the `graph` rule of the store its record class routes to decides (no rule refuses), and\n * `LinkedStorage.graphQuery` runs it outside any shape catalog; no catalog is resolved for it,\n * so the check resolves the same store. A store's refusal answers with its code (see\n * `graphQueryCallError`).\n */\n @callable('public')\n async graphQuery(json: any) {\n const query = toGraphQuery(json);\n await authorizeGraphQuery(json, query, 'graphQuery');\n if (!query) throw new ServerCallError(400, 'Invalid graph query');\n try {\n return await LinkedStorage.graphQuery(query);\n } catch (err) {\n throw graphQueryCallError(err);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import { BackendProvider } from '@_linked/server-utils/utils/BackendProvider';\nimport { callable } from '@_linked/server-utils/utils/callable';\nimport { LinkedFileStorage } from '@_linked/core/utils/LinkedFileStorage';\nimport { LinkedStorage } from '@_linked/core/utils/LinkedStorage';\nimport { LocalFileStore } from './shapes/filestores/LocalFileStore.js';\nimport type { QueryOperation } from '@_linked/server-utils/utils/QueryAccess';\nimport {\n authorizeGenericQuery,\n authorizeGraphQuery,\n graphQueryCallError,\n resolveQueryCatalog,\n toGraphQuery,\n toQueryBuilder,\n toReadQuery,\n} from './utils/queryPlane.js';\nimport { ServerCallError } from '@_linked/server-utils/utils/ServerCallError';\nimport { txIdOf } from '@_linked/core/queries/transactionQuery';\nimport { UnresolvedContextError } from '@_linked/core/queries/QueryContext';\nimport { isLinkedError } from '@_linked/core/utils/linkedError';\nimport type { Transaction } from '@_linked/core/utils/Transaction';\nimport type { ShapeCatalog } from '@_linked/core/utils/ShapeCatalog';\nimport {\n abandonTransactionOf,\n assertStoresHoldTransactions,\n storesHoldTransactions,\n transactionCallError,\n transactionCaller,\n transactions,\n} from './utils/serverTransactions.js';\nimport path from 'path';\nimport fs from 'fs/promises';\n\n/** A write's decoder for `genericQuery`: the body as its builder, checked under the endpoint's own rule. */\nfunction writeOf(operation: Extract<QueryOperation, 'create' | 'update' | 'delete'>) {\n return (json: unknown, catalog: ShapeCatalog | undefined) => ({\n query: toQueryBuilder(json, catalog),\n operation,\n });\n}\n\nfunction assertTxId(txId: unknown): asserts txId is string {\n if (typeof txId !== 'string' || !txId) throw new ServerCallError(400, 'Expected a transaction id');\n}\n\nexport default class LincdServerBackendProvider extends BackendProvider {\n async setupBeforeControllers() {\n if (!LinkedFileStorage.getDefaultStore()) {\n LinkedFileStorage.setDefaultStore(\n new LocalFileStore(process.env.NODE_ENV + '-filestore')\n );\n }\n let fileSystemUploadPath = path.join('data', 'filestores');\n try {\n await fs.access(fileSystemUploadPath);\n } catch (err) {\n await fs.mkdir(path.join(fileSystemUploadPath, 'filestores'), {\n recursive: true,\n });\n }\n }\n\n /**\n * Reachability check for a `RemoteDataset` pointed at this server: reads nothing, changes\n * nothing. A plain linked server has no token concept, so it always answers `{ok: true}`;\n * a host that serves the wire with tokens answers 401 for a refused one. `supports` lists\n * `'transactions'` when this server holds store transactions (every store it routes to does);\n * `RemoteDataset` declares them only then.\n */\n @callable('public')\n async ping(): Promise<{ ok: true; supports: string[] }> {\n return { ok: true, supports: storesHoldTransactions() ? ['transactions'] : [] };\n }\n\n // ── Store transactions ───────────────────────────────────────────────────\n //\n // `RemoteDataset.beginTransaction()` / `commit(txId)` / `rollback(txId)` (core report 103). The\n // server answers its own txId and holds a core `Transaction` for it, which opens a transaction\n // in each store a query of it routes to. The txId is bound to the caller that began it.\n // A server whose stores do not all hold transactions answers 501 `transactions-unsupported`;\n // an unknown or expired txId 409 `transaction-unknown` (a rollback of one resolves); another\n // caller's txId 403 `transaction-not-yours`.\n\n @callable('public')\n async beginTransaction(): Promise<string> {\n try {\n const caller = transactionCaller();\n assertStoresHoldTransactions();\n return transactions.begin(caller, LinkedStorage.begin());\n } catch (err) {\n throw transactionCallError(err);\n }\n }\n\n @callable('public')\n async commit(txId: unknown): Promise<void> {\n assertTxId(txId);\n try {\n await transactions.commit(txId, transactionCaller());\n } catch (err) {\n throw transactionCallError(err);\n }\n }\n\n @callable('public')\n async rollback(txId: unknown): Promise<void> {\n assertTxId(txId);\n try {\n await transactions.rollback(txId, transactionCaller());\n } catch (err) {\n throw transactionCallError(err);\n }\n }\n\n /**\n * Decode, check and run one generic query. `decode` turns the body into the builder the store\n * runs and the access rule it is checked under (a write's rule is its endpoint's; a read's\n * follows its kind, see `toReadQuery`). One that carries a txId runs in its transaction: the\n * caller's own, checked against the store rules as if it were sent alone. Any refusal or\n * failure of it (a body that does not decode included) rolls the transaction back.\n */\n private async genericQuery<R>(\n json: any,\n decode: (json: unknown, catalog: ShapeCatalog | undefined) => { query: any; operation: QueryOperation },\n endpoint: string,\n inTransaction: (tx: Transaction, query: any) => Promise<R>,\n direct: (query: any) => Promise<R>\n ): Promise<R> {\n try {\n const { query, operation } = decode(json, await resolveQueryCatalog(json));\n const authorize = () => authorizeGenericQuery(operation, json, query, endpoint);\n if (!txIdOf(query)) {\n await authorize();\n return await direct(query);\n }\n assertStoresHoldTransactions();\n return await transactions.query(\n query,\n transactionCaller(),\n async () => {\n assertStoresHoldTransactions();\n await authorize();\n },\n inTransaction\n );\n } catch (err) {\n // A select whose query context is not ready did not run: it fails nothing (as in core).\n if (!isLinkedError(err, UnresolvedContextError)) await abandonTransactionOf(json);\n throw transactionCallError(err);\n }\n }\n\n // ── Generic query execution ──────────────────────────────────────────────\n //\n // These answer `RemoteDataset`: a browser's store calls\n // `Server.call(packageName, {method}, json)` (the PACKAGE form), and a remote app's store\n // posts the same `{args}` body to `{url}/call/@_linked/server/<method>`. Either way the\n // request arrives here on the generic provider rather than through Shape resolution.\n //\n // A query cannot cross the wire live, so the client ships `toJSON()` and `fromJSON()`\n // rehydrates it here into a live, closed query. `LinkedStorage` then routes it by the\n // QUERY's shape, which is what decides the target dataset; the store instance never\n // participates. A query that carries a txId (DSL-JSON 1.5) runs in the caller's\n // transaction instead (see \"Store transactions\" above). The host's catalog resolver\n // (`setQueryCatalogResolver`) picks the shape catalog the JSON is decoded in; the builder\n // keeps it for both lowerings.\n //\n // Every query is checked first against the access rules of the stores it\n // maps to, declared in the app's storage config (`withAccess`; a store with\n // no rule requires a session): see utils/queryPlane. INTERIM model, to be\n // rethought. The store runs the builder that was checked. They are declared\n // 'public' because that check, not the RPC layer, decides whether a session\n // is required.\n\n /**\n * Every read: a select, a count or an ask (DSL-JSON, told apart by `op`). The body's kind picks\n * the access rule (`select` for a select or a count, `ask` for an ask; see `toReadQuery`).\n * Triples and terms reads, graph queries and writes are refused with 400.\n */\n @callable('public')\n async readQuery(json: any) {\n return this.genericQuery(json, toReadQuery, 'readQuery', (tx, q) => tx.readQuery(q), (q) => LinkedStorage.readQuery(q));\n }\n\n @callable('public')\n async updateQuery(json: any) {\n return this.genericQuery(json, writeOf('update'), 'updateQuery', (tx, q) => tx.updateQuery(q), (q) => LinkedStorage.updateQuery(q));\n }\n\n @callable('public')\n async createQuery(json: any) {\n return this.genericQuery(json, writeOf('create'), 'createQuery', (tx, q) => tx.createQuery(q), (q) => LinkedStorage.createQuery(q));\n }\n\n @callable('public')\n async deleteQuery(json: any) {\n return this.genericQuery(json, writeOf('delete'), 'deleteQuery', (tx, q) => tx.deleteQuery(q), (q) => LinkedStorage.deleteQuery(q));\n }\n\n /**\n * A whole-graph operation (`graph_create`, `graph_replace`, `graph_patch`, `graph_drop`), as\n * `RemoteDataset.graphQuery` sends it. The query is plain data: core's `parseGraphQuery` checks\n * it, the `graph` rule of the store its record class routes to decides (no rule refuses), and\n * `LinkedStorage.graphQuery` runs it outside any shape catalog; no catalog is resolved for it,\n * so the check resolves the same store. A store's refusal answers with its code (see\n * `graphQueryCallError`).\n */\n @callable('public')\n async graphQuery(json: any) {\n if (json && typeof json === 'object' && 'txId' in json) {\n await abandonTransactionOf(json);\n throw new ServerCallError(400, 'A graph query does not run in a transaction');\n }\n const query = toGraphQuery(json);\n await authorizeGraphQuery(json, query, 'graphQuery');\n if (!query) throw new ServerCallError(400, 'Invalid graph query');\n try {\n return await LinkedStorage.graphQuery(query);\n } catch (err) {\n throw graphQueryCallError(err);\n }\n }\n}\n"]}
|
|
@@ -4,7 +4,7 @@ export declare class LincdAPI extends Shape {
|
|
|
4
4
|
process(request: any, response: any, httpMethod: 'get' | 'post' | 'put' | 'delete'): Promise<void>;
|
|
5
5
|
post_select({ query }: {
|
|
6
6
|
query: any;
|
|
7
|
-
}): Promise<
|
|
7
|
+
}): Promise<number | boolean | string[] | import("@_linked/core/queries/IntermediateRepresentation.js").SelectResult | import("@_linked/core").Triple[]>;
|
|
8
8
|
post_create({ query }: {
|
|
9
9
|
query: any;
|
|
10
10
|
}): Promise<unknown>;
|
|
@@ -10,8 +10,44 @@ import { server } from '../ontologies/server.js';
|
|
|
10
10
|
import { JSONParser } from '@_linked/server-utils/utils/JSONParser';
|
|
11
11
|
import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
|
|
12
12
|
import { JSONWriter } from '@_linked/server-utils/utils/JSONWriter';
|
|
13
|
-
import { authorizeGenericQuery, authorizeRawQuery, resolveQueryCatalog, toQueryBuilder, } from '../utils/queryPlane.js';
|
|
13
|
+
import { authorizeGenericQuery, authorizeRawQuery, resolveQueryCatalog, toQueryBuilder, toReadQuery, } from '../utils/queryPlane.js';
|
|
14
14
|
import { getRpcExposureMode } from '../utils/rpcExposure.js';
|
|
15
|
+
import { txIdOf } from '@_linked/core/queries/transactionQuery';
|
|
16
|
+
import { ServerCallError } from '@_linked/server-utils/utils/ServerCallError';
|
|
17
|
+
import { abandonTransactionOf } from '../utils/serverTransactions.js';
|
|
18
|
+
/**
|
|
19
|
+
* The builder of an `/api/*` query, wrapped (a query builder is a thenable: resolving an async
|
|
20
|
+
* function with one would run it). A query of a store transaction (a `txId`) is refused (400):
|
|
21
|
+
* transactions run over `/call/@_linked/server`, which holds them; here it would run outside.
|
|
22
|
+
* The caller's transaction it names is rolled back, as for any refused query of it.
|
|
23
|
+
*/
|
|
24
|
+
async function apiBuilder(query) {
|
|
25
|
+
const builder = toQueryBuilder(query, await resolveQueryCatalog(query));
|
|
26
|
+
await refuseTransactionQuery(query, builder);
|
|
27
|
+
return { builder };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The read of an `/api/select` query (a select, a count or an ask) with the access rule its kind
|
|
31
|
+
* is checked under (see `toReadQuery`); a query of a transaction is refused as in {@link apiBuilder}.
|
|
32
|
+
*/
|
|
33
|
+
async function apiRead(query) {
|
|
34
|
+
let read;
|
|
35
|
+
try {
|
|
36
|
+
read = toReadQuery(query, await resolveQueryCatalog(query));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
await abandonTransactionOf(query);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
await refuseTransactionQuery(query, read.query);
|
|
43
|
+
return { builder: read.query, operation: read.operation };
|
|
44
|
+
}
|
|
45
|
+
async function refuseTransactionQuery(query, builder) {
|
|
46
|
+
if (txIdOf(builder)) {
|
|
47
|
+
await abandonTransactionOf(query);
|
|
48
|
+
throw new ServerCallError(400, 'A query of a transaction is sent to /call/@_linked/server, not /api');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
15
51
|
/** An error carrying an HTTP status, such as a `ServerCallError` (401/403). */
|
|
16
52
|
function isStatusError(error) {
|
|
17
53
|
return (!!error &&
|
|
@@ -56,22 +92,22 @@ let LincdAPI = class LincdAPI extends Shape {
|
|
|
56
92
|
}
|
|
57
93
|
}
|
|
58
94
|
async post_select({ query }) {
|
|
59
|
-
const builder =
|
|
60
|
-
await authorizeGenericQuery(
|
|
61
|
-
return LinkedStorage.
|
|
95
|
+
const { builder, operation } = await apiRead(query);
|
|
96
|
+
await authorizeGenericQuery(operation, query, builder, 'api/select');
|
|
97
|
+
return LinkedStorage.readQuery(builder);
|
|
62
98
|
}
|
|
63
99
|
async post_create({ query }) {
|
|
64
|
-
const builder =
|
|
100
|
+
const { builder } = await apiBuilder(query);
|
|
65
101
|
await authorizeGenericQuery('create', query, builder, 'api/create');
|
|
66
102
|
return LinkedStorage.createQuery(builder);
|
|
67
103
|
}
|
|
68
104
|
async post_update({ query }) {
|
|
69
|
-
const builder =
|
|
105
|
+
const { builder } = await apiBuilder(query);
|
|
70
106
|
await authorizeGenericQuery('update', query, builder, 'api/update');
|
|
71
107
|
return LinkedStorage.updateQuery(builder);
|
|
72
108
|
}
|
|
73
109
|
async post_delete({ query }) {
|
|
74
|
-
const builder =
|
|
110
|
+
const { builder } = await apiBuilder(query);
|
|
75
111
|
await authorizeGenericQuery('delete', query, builder, 'api/delete');
|
|
76
112
|
return LinkedStorage.deleteQuery(builder);
|
|
77
113
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LincdAPI.js","sourceRoot":"","sources":["../../../src/shapes/LincdAPI.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAEpE,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,+EAA+E;AAC/E,SAAS,aAAa,CAAC,KAAU;IAC/B,OAAO,CACL,CAAC,CAAC,KAAK;QACP,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,KAAK,CAAC,MAAM,IAAI,GAAG;QACnB,KAAK,CAAC,MAAM,GAAG,GAAG;QAClB,KAAK,CAAC,IAAI,KAAK,iBAAiB,CACjC,CAAC;AACJ,CAAC;AAED,iGAAiG;AACjG,gGAAgG;AAChG,4EAA4E;AAErE,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,KAAK;IAGjC,KAAK,CAAC,OAAO,CACX,OAAO,EACP,QAAQ,EACR,UAA6C;QAE7C,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACxC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAM,OAAO,CAAC,IAAI,EAAE;YACrD,YAAY,EAAE,kBAAkB,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;SACrE,CAAC,CAAC;QAEH,kCAAkC;QAClC,IAAI,WAAW,GAAG,UAAU,GAAG,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC/D,IAAI,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,UAAU,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,WAAW,CAAC,CAAC;QACpD,CAAC;QAED,+DAA+D;QAC/D,oBAAoB;QACpB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YAChE,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC3D,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,QAAQ,CAAC,WAAW,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClD,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC7D,OAAO;YACT,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,0BAA0B,WAAW,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/D,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC1B,MAAM,OAAO,GACX,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;gBAClE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;QACxE,MAAM,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;QACxE,MAAM,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;QACxE,MAAM,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;QACxE,MAAM,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE;QAC7B,wEAAwE;QACxE,+DAA+D;QAC/D,MAAM,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;QACjD,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,OAAQ,aAAa,CAAC,iBAAiB,EAA6B,CAAC,QAAQ,CAC3E,KAAK,CACN,CAAC;IACJ,CAAC;IAEO,oBAAoB;QAC1B,IAAI,CAAE,aAAa,CAAC,iBAAiB,EAA6B,CAAC,QAAQ,EAAE,CAAC;YAC5E,MAAM,IAAI,KAAK,CACb,kBACE,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC,CAAC,WAAW;iBACjE,IACL,uCAAuC,CACxC,CAAC;QACJ,CAAC;IACH,CAAC;;AAhFM,oBAAW,GAAG,MAAM,CAAC,QAAQ,AAAlB,CAAmB;AAD1B,QAAQ;IADpB,WAAW,CAAC,EAAC,IAAI,EAAE,UAAU,EAAC,CAAC;GACnB,QAAQ,CAkFpB","sourcesContent":["import { Shape } from '@_linked/core/shapes/Shape';\nimport { linkedShape } from '../package.js';\nimport { server } from '../ontologies/server.js';\nimport { JSONParser } from '@_linked/server-utils/utils/JSONParser';\nimport { LinkedStorage } from '@_linked/core/utils/LinkedStorage';\nimport { JSONWriter } from '@_linked/server-utils/utils/JSONWriter';\nimport { SparqlDataset as SPARQLStore } from '@_linked/core/sparql/SparqlDataset';\nimport {\n authorizeGenericQuery,\n authorizeRawQuery,\n resolveQueryCatalog,\n toQueryBuilder,\n} from '../utils/queryPlane.js';\nimport { getRpcExposureMode } from '../utils/rpcExposure.js';\n\n/** An error carrying an HTTP status, such as a `ServerCallError` (401/403). */\nfunction isStatusError(error: any): error is { status: number; message: string } {\n return (\n !!error &&\n typeof error.status === 'number' &&\n error.status >= 400 &&\n error.status < 600 &&\n error.name === 'ServerCallError'\n );\n}\n\n// Named explicitly: tsc emits `let X = class X`, and any later esbuild pass over that JS (Vite's\n// SSR `define` replacement runs one on every file mentioning a defined `process.env.*`) renames\n// the inner binding to `X2`, which would otherwise become this shape's IRI.\n@linkedShape({name: 'LincdAPI'})\nexport class LincdAPI extends Shape {\n static targetClass = server.LincdAPI;\n\n async process(\n request,\n response,\n httpMethod: 'get' | 'post' | 'put' | 'delete'\n ) {\n let { method, action } = request.params;\n const body = JSONParser.parseObject<any>(request.body, {\n shapeClasses: getRpcExposureMode() === 'enforce' ? 'reject' : 'warn',\n });\n\n //replace - with _ in action names\n let localMethod = httpMethod + '_' + method.replace(/-/g, '_');\n if (typeof this[localMethod] !== 'function') {\n throw new Error('Unknown method: ' + localMethod);\n }\n\n // The query routes (`post_*`) check access themselves, through\n // utils/queryPlane.\n try {\n const result = await this[localMethod](body, request, response);\n if (typeof result !== 'undefined' || !response.headersSent) {\n const jsonObject = JSONWriter.toJsObject(result);\n response.json(jsonObject);\n }\n } catch (error) {\n if (!response.headersSent && isStatusError(error)) {\n response.status(error.status).json({ error: error.message });\n return;\n }\n console.error(`Error while processing ${localMethod}:`, error);\n if (!response.headersSent) {\n const message =\n error instanceof Error ? error.message : 'Unknown server error';\n response.status(500).json({ error: message });\n }\n }\n }\n\n async post_select({ query }) {\n const builder = toQueryBuilder(query, await resolveQueryCatalog(query));\n await authorizeGenericQuery('select', query, builder, 'api/select');\n return LinkedStorage.selectQuery(builder);\n }\n async post_create({ query }) {\n const builder = toQueryBuilder(query, await resolveQueryCatalog(query));\n await authorizeGenericQuery('create', query, builder, 'api/create');\n return LinkedStorage.createQuery(builder);\n }\n async post_update({ query }) {\n const builder = toQueryBuilder(query, await resolveQueryCatalog(query));\n await authorizeGenericQuery('update', query, builder, 'api/update');\n return LinkedStorage.updateQuery(builder);\n }\n async post_delete({ query }) {\n const builder = toQueryBuilder(query, await resolveQueryCatalog(query));\n await authorizeGenericQuery('delete', query, builder, 'api/delete');\n return LinkedStorage.deleteQuery(builder);\n }\n\n async post_select_raw({ query }) {\n // A raw SPARQL string cannot be analysed for the shapes it touches: the\n // `rawQueries` setting decides (a session by default, or off).\n await authorizeRawQuery(query, 'api/select-raw');\n this.checkRawQuerySupport();\n return (LinkedStorage.getDefaultDataset() as unknown as SPARQLStore).rawQuery(\n query\n );\n }\n\n private checkRawQuerySupport() {\n if (!(LinkedStorage.getDefaultDataset() as unknown as SPARQLStore).rawQuery) {\n throw new Error(\n `Default store (${\n Object.getPrototypeOf(LinkedStorage.getDefaultDataset()).constructor\n .name\n }) does not support raw SPARQL queries`\n );\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"LincdAPI.js","sourceRoot":"","sources":["../../../src/shapes/LincdAPI.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAEpE,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,WAAW,GAEZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,wCAAwC,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAEtE;;;;;GAKG;AACH,KAAK,UAAU,UAAU,CAAC,KAAc;IACtC,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,MAAM,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC7C,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,OAAO,CAAC,KAAc;IACnC,IAAI,IAA8C,CAAC;IACnD,IAAI,CAAC;QACH,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,KAAc,EAAE,OAAY;IAChE,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACpB,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,IAAI,eAAe,CAAC,GAAG,EAAE,qEAAqE,CAAC,CAAC;IACxG,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,aAAa,CAAC,KAAU;IAC/B,OAAO,CACL,CAAC,CAAC,KAAK;QACP,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,KAAK,CAAC,MAAM,IAAI,GAAG;QACnB,KAAK,CAAC,MAAM,GAAG,GAAG;QAClB,KAAK,CAAC,IAAI,KAAK,iBAAiB,CACjC,CAAC;AACJ,CAAC;AAED,iGAAiG;AACjG,gGAAgG;AAChG,4EAA4E;AAErE,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,KAAK;IAGjC,KAAK,CAAC,OAAO,CACX,OAAO,EACP,QAAQ,EACR,UAA6C;QAE7C,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACxC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAM,OAAO,CAAC,IAAI,EAAE;YACrD,YAAY,EAAE,kBAAkB,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;SACrE,CAAC,CAAC;QAEH,kCAAkC;QAClC,IAAI,WAAW,GAAG,UAAU,GAAG,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC/D,IAAI,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,UAAU,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,WAAW,CAAC,CAAC;QACpD,CAAC;QAED,+DAA+D;QAC/D,oBAAoB;QACpB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YAChE,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC3D,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,QAAQ,CAAC,WAAW,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClD,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC7D,OAAO;YACT,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,0BAA0B,WAAW,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/D,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAC1B,MAAM,OAAO,GACX,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;gBAClE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,qBAAqB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACrE,OAAO,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;QACzB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACpE,OAAO,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE;QAC7B,wEAAwE;QACxE,+DAA+D;QAC/D,MAAM,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;QACjD,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,OAAQ,aAAa,CAAC,iBAAiB,EAA6B,CAAC,QAAQ,CAC3E,KAAK,CACN,CAAC;IACJ,CAAC;IAEO,oBAAoB;QAC1B,IAAI,CAAE,aAAa,CAAC,iBAAiB,EAA6B,CAAC,QAAQ,EAAE,CAAC;YAC5E,MAAM,IAAI,KAAK,CACb,kBACE,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC,CAAC,WAAW;iBACjE,IACL,uCAAuC,CACxC,CAAC;QACJ,CAAC;IACH,CAAC;;AAhFM,oBAAW,GAAG,MAAM,CAAC,QAAQ,AAAlB,CAAmB;AAD1B,QAAQ;IADpB,WAAW,CAAC,EAAC,IAAI,EAAE,UAAU,EAAC,CAAC;GACnB,QAAQ,CAkFpB","sourcesContent":["import { Shape } from '@_linked/core/shapes/Shape';\nimport { linkedShape } from '../package.js';\nimport { server } from '../ontologies/server.js';\nimport { JSONParser } from '@_linked/server-utils/utils/JSONParser';\nimport { LinkedStorage } from '@_linked/core/utils/LinkedStorage';\nimport { JSONWriter } from '@_linked/server-utils/utils/JSONWriter';\nimport { SparqlDataset as SPARQLStore } from '@_linked/core/sparql/SparqlDataset';\nimport {\n authorizeGenericQuery,\n authorizeRawQuery,\n resolveQueryCatalog,\n toQueryBuilder,\n toReadQuery,\n type ReadOperation,\n} from '../utils/queryPlane.js';\nimport { getRpcExposureMode } from '../utils/rpcExposure.js';\nimport { txIdOf } from '@_linked/core/queries/transactionQuery';\nimport { ServerCallError } from '@_linked/server-utils/utils/ServerCallError';\nimport { abandonTransactionOf } from '../utils/serverTransactions.js';\n\n/**\n * The builder of an `/api/*` query, wrapped (a query builder is a thenable: resolving an async\n * function with one would run it). A query of a store transaction (a `txId`) is refused (400):\n * transactions run over `/call/@_linked/server`, which holds them; here it would run outside.\n * The caller's transaction it names is rolled back, as for any refused query of it.\n */\nasync function apiBuilder(query: unknown): Promise<{ builder: any }> {\n const builder = toQueryBuilder(query, await resolveQueryCatalog(query));\n await refuseTransactionQuery(query, builder);\n return { builder };\n}\n\n/**\n * The read of an `/api/select` query (a select, a count or an ask) with the access rule its kind\n * is checked under (see `toReadQuery`); a query of a transaction is refused as in {@link apiBuilder}.\n */\nasync function apiRead(query: unknown): Promise<{ builder: any; operation: ReadOperation }> {\n let read: { query: any; operation: ReadOperation };\n try {\n read = toReadQuery(query, await resolveQueryCatalog(query));\n } catch (error) {\n await abandonTransactionOf(query);\n throw error;\n }\n await refuseTransactionQuery(query, read.query);\n return { builder: read.query, operation: read.operation };\n}\n\nasync function refuseTransactionQuery(query: unknown, builder: any): Promise<void> {\n if (txIdOf(builder)) {\n await abandonTransactionOf(query);\n throw new ServerCallError(400, 'A query of a transaction is sent to /call/@_linked/server, not /api');\n }\n}\n\n/** An error carrying an HTTP status, such as a `ServerCallError` (401/403). */\nfunction isStatusError(error: any): error is { status: number; message: string } {\n return (\n !!error &&\n typeof error.status === 'number' &&\n error.status >= 400 &&\n error.status < 600 &&\n error.name === 'ServerCallError'\n );\n}\n\n// Named explicitly: tsc emits `let X = class X`, and any later esbuild pass over that JS (Vite's\n// SSR `define` replacement runs one on every file mentioning a defined `process.env.*`) renames\n// the inner binding to `X2`, which would otherwise become this shape's IRI.\n@linkedShape({name: 'LincdAPI'})\nexport class LincdAPI extends Shape {\n static targetClass = server.LincdAPI;\n\n async process(\n request,\n response,\n httpMethod: 'get' | 'post' | 'put' | 'delete'\n ) {\n let { method, action } = request.params;\n const body = JSONParser.parseObject<any>(request.body, {\n shapeClasses: getRpcExposureMode() === 'enforce' ? 'reject' : 'warn',\n });\n\n //replace - with _ in action names\n let localMethod = httpMethod + '_' + method.replace(/-/g, '_');\n if (typeof this[localMethod] !== 'function') {\n throw new Error('Unknown method: ' + localMethod);\n }\n\n // The query routes (`post_*`) check access themselves, through\n // utils/queryPlane.\n try {\n const result = await this[localMethod](body, request, response);\n if (typeof result !== 'undefined' || !response.headersSent) {\n const jsonObject = JSONWriter.toJsObject(result);\n response.json(jsonObject);\n }\n } catch (error) {\n if (!response.headersSent && isStatusError(error)) {\n response.status(error.status).json({ error: error.message });\n return;\n }\n console.error(`Error while processing ${localMethod}:`, error);\n if (!response.headersSent) {\n const message =\n error instanceof Error ? error.message : 'Unknown server error';\n response.status(500).json({ error: message });\n }\n }\n }\n\n async post_select({ query }) {\n const { builder, operation } = await apiRead(query);\n await authorizeGenericQuery(operation, query, builder, 'api/select');\n return LinkedStorage.readQuery(builder);\n }\n async post_create({ query }) {\n const { builder } = await apiBuilder(query);\n await authorizeGenericQuery('create', query, builder, 'api/create');\n return LinkedStorage.createQuery(builder);\n }\n async post_update({ query }) {\n const { builder } = await apiBuilder(query);\n await authorizeGenericQuery('update', query, builder, 'api/update');\n return LinkedStorage.updateQuery(builder);\n }\n async post_delete({ query }) {\n const { builder } = await apiBuilder(query);\n await authorizeGenericQuery('delete', query, builder, 'api/delete');\n return LinkedStorage.deleteQuery(builder);\n }\n\n async post_select_raw({ query }) {\n // A raw SPARQL string cannot be analysed for the shapes it touches: the\n // `rawQueries` setting decides (a session by default, or off).\n await authorizeRawQuery(query, 'api/select-raw');\n this.checkRawQuerySupport();\n return (LinkedStorage.getDefaultDataset() as unknown as SPARQLStore).rawQuery(\n query\n );\n }\n\n private checkRawQuerySupport() {\n if (!(LinkedStorage.getDefaultDataset() as unknown as SPARQLStore).rawQuery) {\n throw new Error(\n `Default store (${\n Object.getPrototypeOf(LinkedStorage.getDefaultDataset()).constructor\n .name\n }) does not support raw SPARQL queries`\n );\n }\n }\n}\n"]}
|