@_linked/server 3.7.0 → 3.9.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 +99 -0
- package/lib/esm/backend.d.ts +9 -0
- package/lib/esm/backend.js +28 -1
- package/lib/esm/backend.js.map +1 -1
- package/lib/esm/data/shapes.ttl +26 -0
- package/lib/esm/shapes/LinkedServer.d.ts +23 -19
- package/lib/esm/shapes/LinkedServer.js +63 -33
- package/lib/esm/shapes/LinkedServer.js.map +1 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.d.ts +41 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.js +152 -4
- package/lib/esm/shapes/quadstores/RemoteDataset.js.map +1 -1
- package/lib/esm/utils/adminChannel.d.ts +57 -4
- package/lib/esm/utils/adminChannel.js +125 -15
- package/lib/esm/utils/adminChannel.js.map +1 -1
- package/lib/esm/utils/effectiveRoutes.d.ts +26 -0
- package/lib/esm/utils/effectiveRoutes.js +40 -0
- package/lib/esm/utils/effectiveRoutes.js.map +1 -0
- package/lib/esm/utils/ontologySync.d.ts +24 -6
- package/lib/esm/utils/ontologySync.js +30 -9
- package/lib/esm/utils/ontologySync.js.map +1 -1
- package/lib/esm/utils/queryPlane.d.ts +21 -0
- package/lib/esm/utils/queryPlane.js +60 -0
- package/lib/esm/utils/queryPlane.js.map +1 -1
- package/lib/esm/utils/syncShapes.d.ts +136 -26
- package/lib/esm/utils/syncShapes.js +175 -26
- package/lib/esm/utils/syncShapes.js.map +1 -1
- package/package.json +3 -3
- package/readme.md +40 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,104 @@
|
|
|
1
1
|
# @\_linked/server
|
|
2
2
|
|
|
3
|
+
## 3.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#206](https://github.com/linked-fw/server/pull/206) [`dc25a49`](https://github.com/linked-fw/server/commit/dc25a490a9a115e366d99f8db644d236043d609e) Thanks [@flyon](https://github.com/flyon)! - The host control channel reads its secret from `APP_ADMIN_SECRET` and its requests carry it in the
|
|
8
|
+
`x-app-admin-secret` header. New route `GET /admin/routes` answers the graph routes the process
|
|
9
|
+
runs with: `{datasets: [{store, id?, default, routes, defaultGraphShapes}]}`, one entry per dataset
|
|
10
|
+
the router knows, `routes` as `{<shape IRI>: <graph IRI>}` as core's routing holds them once the
|
|
11
|
+
app's storage file has run, and the shapes each dataset writes to its default graph. Like every
|
|
12
|
+
`/admin/*` route it answers 404 when the process has no admin secret.
|
|
13
|
+
|
|
14
|
+
Renamed, with a fallback (each old name logs one deprecation warning per process):
|
|
15
|
+
|
|
16
|
+
- `CN_APP_ADMIN_SECRET` → `APP_ADMIN_SECRET`; the old variable is read when the new one is unset.
|
|
17
|
+
- The `x-cn-admin-secret` header → `x-app-admin-secret`; the old header is read when the new one
|
|
18
|
+
is absent.
|
|
19
|
+
|
|
20
|
+
The secret is compared through sha256 digests of both sides, so the comparison does not reveal
|
|
21
|
+
its length.
|
|
22
|
+
|
|
23
|
+
No breaking changes: an app or host that sets only the old names keeps working. Requires
|
|
24
|
+
`@_linked/core` `^2.39.1` (`getShapeGraphs` and `graphRoutingFor` in `utils/graphRouting`).
|
|
25
|
+
|
|
26
|
+
- [#206](https://github.com/linked-fw/server/pull/206) [`fe012b1`](https://github.com/linked-fw/server/commit/fe012b1291d45763d779e8a8b0e9c18900efbca4) Thanks [@flyon](https://github.com/flyon)! - Shape graphs in the app. The boot and `POST /admin/sync-shapes` sync the registered shapes with
|
|
27
|
+
core's `syncShapeGraphs`: each package's shapes in its own named graph
|
|
28
|
+
(`{package IRI}/graph/shapes`, a `ShapesGraph` record in the graph registry), skipped when the
|
|
29
|
+
stored content hash matches. An installed package's graph comes from its built
|
|
30
|
+
`lib/esm/data/shapes.ttl`; the app's own packages (the app and its workspace packages) are
|
|
31
|
+
generated in process, in dev through Vite's loader; the app's single-owner shapes go in
|
|
32
|
+
`{DATA_ROOT}/graph/shapes`. The boot logs the graphs it writes, and the route reports them as
|
|
33
|
+
`shapeGraphs`. A graph refusal `graph-not-routed`, and any graph refusal code the server does not
|
|
34
|
+
know, is answered 400.
|
|
35
|
+
|
|
36
|
+
Breaking changes:
|
|
37
|
+
|
|
38
|
+
- The default-graph shape sync is replaced: shapes are written into shape graphs, and the shapes an
|
|
39
|
+
earlier server wrote into the default graph are removed there once their package's graph holds
|
|
40
|
+
them. A store without graph queries still gets the default-graph sync.
|
|
41
|
+
- An installed package without `lib/esm/data/shapes.ttl` gets no shape graph, and its shapes are
|
|
42
|
+
not generated: packages need a release built with a cli that writes `shapes.ttl`.
|
|
43
|
+
- `ShapeSyncResult.written` (the route's `written`) counts the shape graphs written, not shapes;
|
|
44
|
+
`shapeGraphs` holds the detail.
|
|
45
|
+
- `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true` drops the shape graphs of packages no longer registered
|
|
46
|
+
(never the app's own graph), instead of default-graph node shapes; it is skipped, with a warning,
|
|
47
|
+
while a registered package has no `shapes.ttl`.
|
|
48
|
+
- Requires `@_linked/core` `^2.39.1` (`shapes/syncShapeGraphs`, `shapes/shapesToTurtle`).
|
|
49
|
+
|
|
50
|
+
Deprecated: `materializeShapes` and `SyncShapesFn` in `utils/syncShapes` stay as they were, for
|
|
51
|
+
code built against an earlier server; `syncShapeGraphsInto` replaces them.
|
|
52
|
+
|
|
53
|
+
An own package whose shapes could not be generated (the dev loader failed and the server's core
|
|
54
|
+
holds none of them) is listed in `withoutTurtle`, with a warning, instead of being left out of the
|
|
55
|
+
report.
|
|
56
|
+
|
|
57
|
+
## 3.8.0
|
|
58
|
+
|
|
59
|
+
### Minor Changes
|
|
60
|
+
|
|
61
|
+
- [#204](https://github.com/linked-fw/server/pull/204) [`f1dc1a6`](https://github.com/linked-fw/server/commit/f1dc1a6fec0abae90c711daec51e929a9587b582) Thanks [@flyon](https://github.com/flyon)! - Graph queries through the server (named graphs and the graph registry, `@_linked/core`'s
|
|
62
|
+
`graphQuery`):
|
|
63
|
+
|
|
64
|
+
- `RemoteDataset.graphQuery`: graph queries (`graph_create`, `graph_replace`, `graph_patch`,
|
|
65
|
+
`graph_drop`) go out on the query wire like every other query, with the same token and
|
|
66
|
+
end-user header. A refusal comes back as core's `GraphQueryError` with its code; a server or
|
|
67
|
+
gateway without the method (404, 501) reads as `unsupported-query-kind`, so the ontology sync
|
|
68
|
+
falls back to the default graph there.
|
|
69
|
+
- In the url form, `RemoteDataset` expands `graphs.allOntologies` and `graphs.allShapes` in a
|
|
70
|
+
read's `.from()` set from its own graph-registry cache (per dataset, keyed by the url and the
|
|
71
|
+
dataset the server names in its `ping`, updated by its own graph queries, re-read after 30
|
|
72
|
+
seconds); `invalidateGraphRegistry(dataset?)` drops it. When the registry cannot be read, the
|
|
73
|
+
names are sent unexpanded.
|
|
74
|
+
- The generic plane answers `/call/@_linked/server/graphQuery`: the body is checked with core's
|
|
75
|
+
`parseGraphQuery`, the `graph` rule of the store its record class routes to decides
|
|
76
|
+
(`withAccess({graph})`; no rule refuses with 403), and `LinkedStorage.graphQuery` runs it. A
|
|
77
|
+
store's refusal answers with a status and its code: `{error, code}` (409 `graph-exists`, 409
|
|
78
|
+
`graph-not-registered`, 400 `graph-name-read-only`, 501 `unsupported-query-kind`). Any
|
|
79
|
+
`ServerCallError` carrying a string `code` now sends it in the error body.
|
|
80
|
+
- Control channel: `POST /admin/graph-registry-changed {dataset}` (with `CN_APP_ADMIN_SECRET`)
|
|
81
|
+
drops the process's cached graph registry, so the next expanding read re-reads it.
|
|
82
|
+
- The ontology phase (boot and `POST /admin/sync-shapes`) reports and logs the graphs the sync
|
|
83
|
+
wrote and dropped (`ontologies.graphs`, `ontologies.droppedGraphs`), and says when a store
|
|
84
|
+
without graph queries got the default-graph sync.
|
|
85
|
+
|
|
86
|
+
Breaking changes:
|
|
87
|
+
|
|
88
|
+
- Requires the `@_linked/core` with graph queries (`IDataset.graphQuery`, `NamedGraph`, the graph
|
|
89
|
+
registry cache), which in turn requires `ghcr.io/linked-fw/fuseki:6.2.0-2` or a store that
|
|
90
|
+
builds its hierarchy from the registry; and the `@_linked/server-utils` whose `QueryAccess` has
|
|
91
|
+
the `graph` operation (with an older one every graph query is refused). Both ranges are raised
|
|
92
|
+
at release.
|
|
93
|
+
- Graph queries on the generic plane are refused unless the store declares a `graph` rule with
|
|
94
|
+
`withAccess`.
|
|
95
|
+
- `.from(graphs.allOntologies)` and `.from(graphs.allShapes)` reads through a url-form
|
|
96
|
+
`RemoteDataset` now also select the `OntologyGraph` and `ShapesGraph` records on the data
|
|
97
|
+
server; its read rule decides whether they are answered (when refused, the names go
|
|
98
|
+
unexpanded).
|
|
99
|
+
- A `ServerCallError` that carries a string `code` now sends it in the error body
|
|
100
|
+
(`{error, code}`).
|
|
101
|
+
|
|
3
102
|
## 3.7.0
|
|
4
103
|
|
|
5
104
|
### Minor Changes
|
package/lib/esm/backend.d.ts
CHANGED
|
@@ -14,4 +14,13 @@ export default class LincdServerBackendProvider extends BackendProvider {
|
|
|
14
14
|
updateQuery(json: any): Promise<unknown>;
|
|
15
15
|
createQuery(json: any): Promise<unknown>;
|
|
16
16
|
deleteQuery(json: any): Promise<import("@_linked/core/queries/DeleteQuery.js").DeleteResponse>;
|
|
17
|
+
/**
|
|
18
|
+
* A whole-graph operation (`graph_create`, `graph_replace`, `graph_patch`, `graph_drop`), as
|
|
19
|
+
* `RemoteDataset.graphQuery` sends it. The query is plain data: core's `parseGraphQuery` checks
|
|
20
|
+
* it, the `graph` rule of the store its record class routes to decides (no rule refuses), and
|
|
21
|
+
* `LinkedStorage.graphQuery` runs it outside any shape catalog; no catalog is resolved for it,
|
|
22
|
+
* so the check resolves the same store. A store's refusal answers with its code (see
|
|
23
|
+
* `graphQueryCallError`).
|
|
24
|
+
*/
|
|
25
|
+
graphQuery(json: any): Promise<import("@_linked/core").GraphResult>;
|
|
17
26
|
}
|
package/lib/esm/backend.js
CHANGED
|
@@ -12,7 +12,8 @@ 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, resolveQueryCatalog, toQueryBuilder, } from './utils/queryPlane.js';
|
|
15
|
+
import { authorizeGenericQuery, authorizeGraphQuery, graphQueryCallError, resolveQueryCatalog, toGraphQuery, toQueryBuilder, } from './utils/queryPlane.js';
|
|
16
|
+
import { ServerCallError } from '@_linked/server-utils/utils/ServerCallError';
|
|
16
17
|
import path from 'path';
|
|
17
18
|
import fs from 'fs/promises';
|
|
18
19
|
export default class LincdServerBackendProvider extends BackendProvider {
|
|
@@ -82,6 +83,26 @@ export default class LincdServerBackendProvider extends BackendProvider {
|
|
|
82
83
|
await authorizeGenericQuery('delete', json, query, 'deleteQuery');
|
|
83
84
|
return LinkedStorage.deleteQuery(query);
|
|
84
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* A whole-graph operation (`graph_create`, `graph_replace`, `graph_patch`, `graph_drop`), as
|
|
88
|
+
* `RemoteDataset.graphQuery` sends it. The query is plain data: core's `parseGraphQuery` checks
|
|
89
|
+
* it, the `graph` rule of the store its record class routes to decides (no rule refuses), and
|
|
90
|
+
* `LinkedStorage.graphQuery` runs it outside any shape catalog; no catalog is resolved for it,
|
|
91
|
+
* so the check resolves the same store. A store's refusal answers with its code (see
|
|
92
|
+
* `graphQueryCallError`).
|
|
93
|
+
*/
|
|
94
|
+
async graphQuery(json) {
|
|
95
|
+
const query = toGraphQuery(json);
|
|
96
|
+
await authorizeGraphQuery(json, query, 'graphQuery');
|
|
97
|
+
if (!query)
|
|
98
|
+
throw new ServerCallError(400, 'Invalid graph query');
|
|
99
|
+
try {
|
|
100
|
+
return await LinkedStorage.graphQuery(query);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throw graphQueryCallError(err);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
85
106
|
}
|
|
86
107
|
__decorate([
|
|
87
108
|
callable('public'),
|
|
@@ -119,4 +140,10 @@ __decorate([
|
|
|
119
140
|
__metadata("design:paramtypes", [Object]),
|
|
120
141
|
__metadata("design:returntype", Promise)
|
|
121
142
|
], LincdServerBackendProvider.prototype, "deleteQuery", null);
|
|
143
|
+
__decorate([
|
|
144
|
+
callable('public'),
|
|
145
|
+
__metadata("design:type", Function),
|
|
146
|
+
__metadata("design:paramtypes", [Object]),
|
|
147
|
+
__metadata("design:returntype", Promise)
|
|
148
|
+
], LincdServerBackendProvider.prototype, "graphQuery", null);
|
|
122
149
|
//# sourceMappingURL=backend.js.map
|
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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
|
|
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"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@prefix core: <https://linked.cm/ont/core/> .
|
|
2
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
3
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
4
|
+
@prefix sh: <http://www.w3.org/ns/shacl#> .
|
|
5
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
6
|
+
|
|
7
|
+
<https://linked.cm/shape/server/LincdAPI>
|
|
8
|
+
a sh:NodeShape ;
|
|
9
|
+
sh:targetClass <https://linked.cm/ont/server/LincdAPI> .
|
|
10
|
+
|
|
11
|
+
<https://linked.cm/shape/server/LincdWebApp>
|
|
12
|
+
a sh:NodeShape ;
|
|
13
|
+
sh:property <https://linked.cm/shape/server/LincdWebApp/api> ;
|
|
14
|
+
sh:targetClass <https://linked.cm/ont/server/LincdWebApp> .
|
|
15
|
+
|
|
16
|
+
<https://linked.cm/shape/server/LincdWebApp/api>
|
|
17
|
+
a sh:PropertyShape ;
|
|
18
|
+
rdfs:label "api" ;
|
|
19
|
+
sh:maxCount "1"^^xsd:integer ;
|
|
20
|
+
sh:node <https://linked.cm/shape/server-utils/Lincd_API_Client> ;
|
|
21
|
+
sh:nodeKind sh:IRI ;
|
|
22
|
+
sh:path <https://linked.cm/ont/server/hasAPI> .
|
|
23
|
+
|
|
24
|
+
<https://linked.cm/shape/server/LinkedServer>
|
|
25
|
+
a sh:NodeShape ;
|
|
26
|
+
sh:targetClass <https://linked.cm/ont/server/LincdServer> .
|
|
@@ -84,6 +84,8 @@ export declare class LinkedServer extends Shape {
|
|
|
84
84
|
private linkedPackageDirs;
|
|
85
85
|
/** The app's own workspace packages (`linkedPackage: true`), by real path. */
|
|
86
86
|
private workspacePackageDirs;
|
|
87
|
+
/** The names of the app's own workspace packages. */
|
|
88
|
+
private workspacePackageNames;
|
|
87
89
|
private warnedDuplicateInstalls;
|
|
88
90
|
private resizePathsMap;
|
|
89
91
|
private api;
|
|
@@ -105,32 +107,34 @@ export declare class LinkedServer extends Shape {
|
|
|
105
107
|
initOnly(): Promise<this>;
|
|
106
108
|
private initOnlyBoot;
|
|
107
109
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* the control channel ({@link resyncShapes}). A source reload (HMR)
|
|
114
|
-
* sync shapes by itself.
|
|
110
|
+
* Sync this server's registered shapes into app data, one named graph per package (see
|
|
111
|
+
* utils/syncShapes): each package's shapes in `{package IRI}/graph/shapes`, from its built
|
|
112
|
+
* `shapes.ttl` (an installed package without one gets no shape graph), the app's own packages
|
|
113
|
+
* generated in process in dev (through Vite's loader), and the app's generated single-owner
|
|
114
|
+
* shapes in `{DATA_ROOT}/graph/shapes`. Runs at boot, after the ontology sync, and again when
|
|
115
|
+
* the host asks for it over the control channel ({@link resyncShapes}). A source reload (HMR)
|
|
116
|
+
* does not sync shapes by itself.
|
|
115
117
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
+
* A graph whose stored content hash matches is not written, so a sync of unchanged shapes
|
|
119
|
+
* writes nothing. Shapes an earlier sync wrote into the default graph are removed there once
|
|
120
|
+
* their package's graph holds them; a store without graph queries gets that default-graph sync
|
|
121
|
+
* instead.
|
|
118
122
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* `bootOrphanScope` in utils/syncShapes for why.
|
|
123
|
+
* Shape graphs of packages no longer registered stay unless the operator sets
|
|
124
|
+
* `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true` — see `bootOrphanScope` in utils/syncShapes.
|
|
122
125
|
*
|
|
123
126
|
* Opt-out via `linked.config` `syncShapesOnBoot` (default true); env
|
|
124
|
-
* LINKED_SYNC_SHAPES_ON_BOOT overrides for deployment.
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* pinned shapes separately (linked.backend.storage.js).
|
|
127
|
+
* LINKED_SYNC_SHAPES_ON_BOOT overrides for deployment. A host whose default
|
|
128
|
+
* dataset is a context router (one that can't write context-free) sets
|
|
129
|
+
* `syncShapesOnBoot: false` and syncs its shapes itself.
|
|
128
130
|
*/
|
|
129
131
|
private materializeShapesIntoStore;
|
|
130
132
|
/** One shape sync into the default dataset; throws when it fails. */
|
|
131
133
|
private syncRegisteredShapes;
|
|
134
|
+
/** The root directory of a package with shapes: the app, a discovered linked package, or one the app resolves. */
|
|
135
|
+
private shapePackageDir;
|
|
132
136
|
/**
|
|
133
|
-
* Boot: sync the registered ontologies
|
|
137
|
+
* Boot: sync the registered ontologies into their graphs in app data and run the shape
|
|
134
138
|
* check, before the shape sync (see utils/ontologySync). The sync follows the shape sync's
|
|
135
139
|
* switch (`syncShapesOnBoot`, `LINKED_SYNC_SHAPES_ON_BOOT`), plus the opt-out
|
|
136
140
|
* `LINKED_SYNC_ONTOLOGIES_ON_BOOT=false`; the in-memory class hierarchy is loaded either way.
|
|
@@ -141,7 +145,7 @@ export declare class LinkedServer extends Shape {
|
|
|
141
145
|
* Every discovered linked package and every workspace package of the app registers its
|
|
142
146
|
* ontologies (in dev, an unbuilt one through Vite), then the ontology sync (when
|
|
143
147
|
* `sync` is set) into the default dataset, then the shape check.
|
|
144
|
-
* Orphaned sync nodes are pruned under the shape sync's orphan policy
|
|
148
|
+
* Orphaned ontology graphs (and earlier sync nodes) are pruned under the shape sync's orphan policy
|
|
145
149
|
* (`LINKED_SYNC_SHAPES_PRUNE_ORPHANS`).
|
|
146
150
|
*/
|
|
147
151
|
private runOntologyPhase;
|
|
@@ -318,7 +322,7 @@ export declare class LinkedServer extends Shape {
|
|
|
318
322
|
private assertSessionForLevel;
|
|
319
323
|
handleErrors(fn: any): (req: any, res: any, next: any) => Promise<any>;
|
|
320
324
|
handleErrorsJson(fn: any): (req: any, res: any, next: any) => Promise<any>;
|
|
321
|
-
sendError(res: any, statusCode: number, message: any, logMessage?: string): void;
|
|
325
|
+
sendError(res: any, statusCode: number, message: any, logMessage?: string, code?: string): void;
|
|
322
326
|
/**
|
|
323
327
|
* The stylesheets a dev (Vite) page render needs, inlined into
|
|
324
328
|
* `<style id="ssr-css">` so the first paint is styled before Vite's client
|
|
@@ -137,9 +137,9 @@ import { resolveStaticAccessURL, staticAssetURL, } from '../utils/releaseManifes
|
|
|
137
137
|
import { resolveBootstrapEntry, resolveViteMainEntry, resolveViteServingMode, } from '../utils/bootstrapEntry.js';
|
|
138
138
|
import { resolveRouteAssets } from '../utils/routeAssets.js';
|
|
139
139
|
import { resolveRouteComponent, ssrCssInlineUrls, ssrCssScope, } from '../utils/ssrCss.js';
|
|
140
|
-
import { bootOrphanScope,
|
|
140
|
+
import { appShapesGraph, bootOrphanScope, loadShapeGraphCore, syncShapeGraphsInto, viteShapesTurtleGenerator, } from '../utils/syncShapes.js';
|
|
141
141
|
import { bootSyncsOntologies, loadOntologyCore, runOntologyPhase, ontologyPackageDirs, sourceModuleImporter, shapeSyncOn, syncOntologiesThenShapes, } from '../utils/ontologySync.js';
|
|
142
|
-
import { AdminRequestError, assertReloadablePackages, registerAdminRoutes, } from '../utils/adminChannel.js';
|
|
142
|
+
import { AdminRequestError, assertReloadablePackages, registerAdminRoutes, resolveAdminSecret, } from '../utils/adminChannel.js';
|
|
143
143
|
import { envMs, withTimeout } from '../utils/withTimeout.js';
|
|
144
144
|
import { invalidateSourceUnder } from '../utils/reloadSource.js';
|
|
145
145
|
import { installSpaFallback, repinSpaFallback, } from '../utils/spaFallback.js';
|
|
@@ -286,6 +286,8 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
286
286
|
this.linkedPackageDirs = new Map();
|
|
287
287
|
/** The app's own workspace packages (`linkedPackage: true`), by real path. */
|
|
288
288
|
this.workspacePackageDirs = [];
|
|
289
|
+
/** The names of the app's own workspace packages. */
|
|
290
|
+
this.workspacePackageNames = new Set();
|
|
289
291
|
this.warnedDuplicateInstalls = new Set();
|
|
290
292
|
//from resizedFileName to full resized path (CDN or similar)
|
|
291
293
|
this.resizePathsMap = new Map();
|
|
@@ -366,52 +368,76 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
366
368
|
return this;
|
|
367
369
|
}
|
|
368
370
|
/**
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* the control channel ({@link resyncShapes}). A source reload (HMR)
|
|
375
|
-
* sync shapes by itself.
|
|
371
|
+
* Sync this server's registered shapes into app data, one named graph per package (see
|
|
372
|
+
* utils/syncShapes): each package's shapes in `{package IRI}/graph/shapes`, from its built
|
|
373
|
+
* `shapes.ttl` (an installed package without one gets no shape graph), the app's own packages
|
|
374
|
+
* generated in process in dev (through Vite's loader), and the app's generated single-owner
|
|
375
|
+
* shapes in `{DATA_ROOT}/graph/shapes`. Runs at boot, after the ontology sync, and again when
|
|
376
|
+
* the host asks for it over the control channel ({@link resyncShapes}). A source reload (HMR)
|
|
377
|
+
* does not sync shapes by itself.
|
|
376
378
|
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
+
* A graph whose stored content hash matches is not written, so a sync of unchanged shapes
|
|
380
|
+
* writes nothing. Shapes an earlier sync wrote into the default graph are removed there once
|
|
381
|
+
* their package's graph holds them; a store without graph queries gets that default-graph sync
|
|
382
|
+
* instead.
|
|
379
383
|
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* `bootOrphanScope` in utils/syncShapes for why.
|
|
384
|
+
* Shape graphs of packages no longer registered stay unless the operator sets
|
|
385
|
+
* `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true` — see `bootOrphanScope` in utils/syncShapes.
|
|
383
386
|
*
|
|
384
387
|
* Opt-out via `linked.config` `syncShapesOnBoot` (default true); env
|
|
385
|
-
* LINKED_SYNC_SHAPES_ON_BOOT overrides for deployment.
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
* pinned shapes separately (linked.backend.storage.js).
|
|
388
|
+
* LINKED_SYNC_SHAPES_ON_BOOT overrides for deployment. A host whose default
|
|
389
|
+
* dataset is a context router (one that can't write context-free) sets
|
|
390
|
+
* `syncShapesOnBoot: false` and syncs its shapes itself.
|
|
389
391
|
*/
|
|
390
392
|
async materializeShapesIntoStore() {
|
|
391
393
|
try {
|
|
392
|
-
await this.syncRegisteredShapes();
|
|
394
|
+
await runAsSystem(() => this.syncRegisteredShapes(), 'shape sync at boot');
|
|
393
395
|
}
|
|
394
396
|
catch (err) {
|
|
395
|
-
console.warn('[LinkedServer] shape
|
|
397
|
+
console.warn('[LinkedServer] shape sync failed (non-fatal):', err);
|
|
396
398
|
}
|
|
397
399
|
}
|
|
398
400
|
/** One shape sync into the default dataset; throws when it fails. */
|
|
399
401
|
async syncRegisteredShapes() {
|
|
400
|
-
var _a;
|
|
402
|
+
var _a, _b, _c, _d, _e;
|
|
401
403
|
if (!shapeSyncOn(process.env, (_a = this.config) === null || _a === void 0 ? void 0 : _a.syncShapesOnBoot)) {
|
|
402
404
|
return { written: 0, skipped: 'disabled' };
|
|
403
405
|
}
|
|
404
406
|
const appData = LinkedStorage.getDefaultDataset();
|
|
405
407
|
if (!appData)
|
|
406
408
|
return { written: 0, skipped: 'no-default-dataset' };
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
|
|
409
|
+
const orphanScope = bootOrphanScope();
|
|
410
|
+
const appName = (_b = this.package) === null || _b === void 0 ? void 0 : _b.name;
|
|
411
|
+
const vite = (_c = this.config.server) === null || _c === void 0 ? void 0 : _c.vite;
|
|
412
|
+
const generate = viteShapesTurtleGenerator(vite);
|
|
413
|
+
// Explicit target: every registered shape goes into the app's own data store, regardless of
|
|
414
|
+
// per-shape routing or pins.
|
|
415
|
+
const shapeGraphs = await syncShapeGraphsInto(appData, {
|
|
416
|
+
core: await loadShapeGraphCore(),
|
|
417
|
+
orphanScope,
|
|
418
|
+
isOwn: (name) => name === appName || this.workspacePackageNames.has(name),
|
|
419
|
+
dev: !!generate,
|
|
420
|
+
generate,
|
|
421
|
+
packageDirOf: (name) => this.shapePackageDir(name),
|
|
422
|
+
appGraph: appShapesGraph(),
|
|
423
|
+
appPackage: appName,
|
|
424
|
+
});
|
|
425
|
+
const written = (_d = shapeGraphs.defaultGraph) !== null && _d !== void 0 ? _d : shapeGraphs.written.length + (((_e = shapeGraphs.appGraph) === null || _e === void 0 ? void 0 : _e.status) === 'written' ? 1 : 0);
|
|
426
|
+
return { written, orphanScope, shapeGraphs };
|
|
427
|
+
}
|
|
428
|
+
/** The root directory of a package with shapes: the app, a discovered linked package, or one the app resolves. */
|
|
429
|
+
shapePackageDir(name) {
|
|
430
|
+
var _a, _b;
|
|
431
|
+
if (name === ((_a = this.package) === null || _a === void 0 ? void 0 : _a.name))
|
|
432
|
+
return process.cwd();
|
|
433
|
+
const known = (_b = this.linkedPackageDirs) === null || _b === void 0 ? void 0 : _b.get(name);
|
|
434
|
+
if (known)
|
|
435
|
+
return known;
|
|
436
|
+
const installed = readInstalledPackage(name, process.cwd());
|
|
437
|
+
return installed === null || installed === void 0 ? void 0 : installed.root;
|
|
412
438
|
}
|
|
413
439
|
/**
|
|
414
|
-
* Boot: sync the registered ontologies
|
|
440
|
+
* Boot: sync the registered ontologies into their graphs in app data and run the shape
|
|
415
441
|
* check, before the shape sync (see utils/ontologySync). The sync follows the shape sync's
|
|
416
442
|
* switch (`syncShapesOnBoot`, `LINKED_SYNC_SHAPES_ON_BOOT`), plus the opt-out
|
|
417
443
|
* `LINKED_SYNC_ONTOLOGIES_ON_BOOT=false`; the in-memory class hierarchy is loaded either way.
|
|
@@ -429,7 +455,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
429
455
|
* Every discovered linked package and every workspace package of the app registers its
|
|
430
456
|
* ontologies (in dev, an unbuilt one through Vite), then the ontology sync (when
|
|
431
457
|
* `sync` is set) into the default dataset, then the shape check.
|
|
432
|
-
* Orphaned sync nodes are pruned under the shape sync's orphan policy
|
|
458
|
+
* Orphaned ontology graphs (and earlier sync nodes) are pruned under the shape sync's orphan policy
|
|
433
459
|
* (`LINKED_SYNC_SHAPES_PRUNE_ORPHANS`).
|
|
434
460
|
*/
|
|
435
461
|
async runOntologyPhase(sync) {
|
|
@@ -726,8 +752,8 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
726
752
|
res.sendStatus(200);
|
|
727
753
|
});
|
|
728
754
|
// The host's control channel (/admin/*), open only when the host started
|
|
729
|
-
// this process with
|
|
730
|
-
registerAdminRoutes(this.server,
|
|
755
|
+
// this process with APP_ADMIN_SECRET; see utils/adminChannel.
|
|
756
|
+
registerAdminRoutes(this.server, resolveAdminSecret(), {
|
|
731
757
|
// The host's provisioner starts the app again after it exits.
|
|
732
758
|
exit: () => process.exit(0),
|
|
733
759
|
syncShapes: (request) => this.resyncShapes(request),
|
|
@@ -829,6 +855,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
829
855
|
// Local workspace packages in this app's dependency tree. These are also
|
|
830
856
|
// imported whole at boot (indexLincdPackage), not only their /backend.
|
|
831
857
|
const allLocalPackages = getLincdPackages();
|
|
858
|
+
this.workspacePackageNames = new Set(allLocalPackages.map((pkg) => pkg.packageName));
|
|
832
859
|
this.workspacePackageDirs = allLocalPackages.map((pkg) => {
|
|
833
860
|
try {
|
|
834
861
|
return fsNative.realpathSync(pkg.path);
|
|
@@ -1819,17 +1846,20 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
1819
1846
|
catch (err) {
|
|
1820
1847
|
// A ServerCallError (e.g. 501 for a call no provider handles) carries
|
|
1821
1848
|
// its own status and a message that is safe to send to the client.
|
|
1849
|
+
// A `code` on it (a graph query refusal, see utils/queryPlane) is sent along, so
|
|
1850
|
+
// a remote caller can tell the refusals apart.
|
|
1822
1851
|
if (ServerCallError.is(err)) {
|
|
1823
|
-
|
|
1852
|
+
const code = err.code;
|
|
1853
|
+
return this.sendError(res, err.status, err.message, undefined, typeof code === 'string' ? code : undefined);
|
|
1824
1854
|
}
|
|
1825
1855
|
this.sendError(res, 500, 'internal server error' + (isDevelopment ? ': ' + (err === null || err === void 0 ? void 0 : err.stack) : ''), 'internal server error: ' + (err === null || err === void 0 ? void 0 : err.stack));
|
|
1826
1856
|
}
|
|
1827
1857
|
};
|
|
1828
1858
|
}
|
|
1829
|
-
sendError(res, statusCode = 500, message, logMessage) {
|
|
1859
|
+
sendError(res, statusCode = 500, message, logMessage, code) {
|
|
1830
1860
|
res === null || res === void 0 ? void 0 : res.status(statusCode);
|
|
1831
1861
|
if (message) {
|
|
1832
|
-
res === null || res === void 0 ? void 0 : res.send({ error: message });
|
|
1862
|
+
res === null || res === void 0 ? void 0 : res.send(code ? { error: message, code } : { error: message });
|
|
1833
1863
|
console.warn(paint('red', logMessage || message));
|
|
1834
1864
|
}
|
|
1835
1865
|
}
|