@_linked/server 3.8.0 → 3.10.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 +71 -0
- package/lib/esm/data/shapes.ttl +26 -0
- package/lib/esm/shapes/LinkedServer.d.ts +20 -16
- package/lib/esm/shapes/LinkedServer.js +55 -28
- package/lib/esm/shapes/LinkedServer.js.map +1 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.d.ts +1 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.js +1 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.js.map +1 -1
- package/lib/esm/utils/adminChannel.d.ts +24 -2
- package/lib/esm/utils/adminChannel.js +40 -13
- 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 +2 -2
- package/lib/esm/utils/ontologySync.js +2 -2
- package/lib/esm/utils/ontologySync.js.map +1 -1
- package/lib/esm/utils/queryPlane.js +9 -3
- 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 +38 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# @\_linked/server
|
|
2
2
|
|
|
3
|
+
## 3.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#208](https://github.com/linked-fw/server/pull/208) [`94a7776`](https://github.com/linked-fw/server/commit/94a77765eb3009fd531e0db2bfdfd1b59a9a2d79) Thanks [@flyon](https://github.com/flyon)! - The host control channel no longer reads the deprecated `CN_APP_ADMIN_SECRET` and
|
|
8
|
+
`x-cn-admin-secret` names that 3.9.0 still accepted. Its secret comes from `APP_ADMIN_SECRET` and
|
|
9
|
+
the `x-app-admin-secret` header only.
|
|
10
|
+
|
|
11
|
+
Breaking changes:
|
|
12
|
+
|
|
13
|
+
- `CN_APP_ADMIN_SECRET` is no longer read: a process started with only that variable has no
|
|
14
|
+
control channel (every `/admin/*` route answers 404). Set `APP_ADMIN_SECRET`.
|
|
15
|
+
- The `x-cn-admin-secret` header is no longer read: a request that carries the secret only there
|
|
16
|
+
answers 401. Send `x-app-admin-secret`.
|
|
17
|
+
- `LEGACY_ADMIN_SECRET_ENV`, `LEGACY_ADMIN_SECRET_HEADER` and the `warn` handler of
|
|
18
|
+
`registerAdminRoutes` are removed, and `resolveAdminSecret` takes only the environment.
|
|
19
|
+
|
|
20
|
+
## 3.9.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- [#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
|
|
25
|
+
`x-app-admin-secret` header. New route `GET /admin/routes` answers the graph routes the process
|
|
26
|
+
runs with: `{datasets: [{store, id?, default, routes, defaultGraphShapes}]}`, one entry per dataset
|
|
27
|
+
the router knows, `routes` as `{<shape IRI>: <graph IRI>}` as core's routing holds them once the
|
|
28
|
+
app's storage file has run, and the shapes each dataset writes to its default graph. Like every
|
|
29
|
+
`/admin/*` route it answers 404 when the process has no admin secret.
|
|
30
|
+
|
|
31
|
+
Renamed, with a fallback (each old name logs one deprecation warning per process):
|
|
32
|
+
|
|
33
|
+
- `CN_APP_ADMIN_SECRET` → `APP_ADMIN_SECRET`; the old variable is read when the new one is unset.
|
|
34
|
+
- The `x-cn-admin-secret` header → `x-app-admin-secret`; the old header is read when the new one
|
|
35
|
+
is absent.
|
|
36
|
+
|
|
37
|
+
The secret is compared through sha256 digests of both sides, so the comparison does not reveal
|
|
38
|
+
its length.
|
|
39
|
+
|
|
40
|
+
No breaking changes: an app or host that sets only the old names keeps working. Requires
|
|
41
|
+
`@_linked/core` `^2.39.1` (`getShapeGraphs` and `graphRoutingFor` in `utils/graphRouting`).
|
|
42
|
+
|
|
43
|
+
- [#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
|
|
44
|
+
core's `syncShapeGraphs`: each package's shapes in its own named graph
|
|
45
|
+
(`{package IRI}/graph/shapes`, a `ShapesGraph` record in the graph registry), skipped when the
|
|
46
|
+
stored content hash matches. An installed package's graph comes from its built
|
|
47
|
+
`lib/esm/data/shapes.ttl`; the app's own packages (the app and its workspace packages) are
|
|
48
|
+
generated in process, in dev through Vite's loader; the app's single-owner shapes go in
|
|
49
|
+
`{DATA_ROOT}/graph/shapes`. The boot logs the graphs it writes, and the route reports them as
|
|
50
|
+
`shapeGraphs`. A graph refusal `graph-not-routed`, and any graph refusal code the server does not
|
|
51
|
+
know, is answered 400.
|
|
52
|
+
|
|
53
|
+
Breaking changes:
|
|
54
|
+
|
|
55
|
+
- The default-graph shape sync is replaced: shapes are written into shape graphs, and the shapes an
|
|
56
|
+
earlier server wrote into the default graph are removed there once their package's graph holds
|
|
57
|
+
them. A store without graph queries still gets the default-graph sync.
|
|
58
|
+
- An installed package without `lib/esm/data/shapes.ttl` gets no shape graph, and its shapes are
|
|
59
|
+
not generated: packages need a release built with a cli that writes `shapes.ttl`.
|
|
60
|
+
- `ShapeSyncResult.written` (the route's `written`) counts the shape graphs written, not shapes;
|
|
61
|
+
`shapeGraphs` holds the detail.
|
|
62
|
+
- `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true` drops the shape graphs of packages no longer registered
|
|
63
|
+
(never the app's own graph), instead of default-graph node shapes; it is skipped, with a warning,
|
|
64
|
+
while a registered package has no `shapes.ttl`.
|
|
65
|
+
- Requires `@_linked/core` `^2.39.1` (`shapes/syncShapeGraphs`, `shapes/shapesToTurtle`).
|
|
66
|
+
|
|
67
|
+
Deprecated: `materializeShapes` and `SyncShapesFn` in `utils/syncShapes` stay as they were, for
|
|
68
|
+
code built against an earlier server; `syncShapeGraphsInto` replaces them.
|
|
69
|
+
|
|
70
|
+
An own package whose shapes could not be generated (the dev loader failed and the server's core
|
|
71
|
+
holds none of them) is listed in `withoutTurtle`, with a warning, instead of being left out of the
|
|
72
|
+
report.
|
|
73
|
+
|
|
3
74
|
## 3.8.0
|
|
4
75
|
|
|
5
76
|
### Minor Changes
|
|
@@ -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,30 +107,32 @@ 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
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
|
|
@@ -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,49 +368,73 @@ 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
440
|
* Boot: sync the registered ontologies into their graphs in app data and run the shape
|
|
@@ -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);
|