@_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
|
@@ -15,7 +15,7 @@ import { Shape } from '@_linked/core/shapes/Shape';
|
|
|
15
15
|
* needs; a plain function, an arrow function or a non-function value is not.
|
|
16
16
|
*/
|
|
17
17
|
export declare function isProviderClass(value: unknown): boolean;
|
|
18
|
-
import { type
|
|
18
|
+
import { type GraphContentAnswer } from '../utils/adminChannel.js';
|
|
19
19
|
/**
|
|
20
20
|
* Serve the local upload folder at `/uploads` (where `LocalFileStore` files are
|
|
21
21
|
* reachable), with the headers for untrusted files.
|
|
@@ -108,12 +108,14 @@ export declare class LinkedServer extends Shape {
|
|
|
108
108
|
private initOnlyBoot;
|
|
109
109
|
/**
|
|
110
110
|
* Sync this server's registered shapes into app data, one named graph per package (see
|
|
111
|
-
* utils/syncShapes)
|
|
111
|
+
* utils/syncShapes) in every dataset the storage routing needs it in (core's
|
|
112
|
+
* `syncShapeGraphsRouted`; the default dataset when nothing is pinned, and a graph routing no
|
|
113
|
+
* longer sends to the default dataset is dropped there): each package's shapes in
|
|
114
|
+
* `{package IRI}/graph/shapes`, from its built
|
|
112
115
|
* `shapes.ttl` (an installed package without one gets no shape graph), the app's own packages
|
|
113
116
|
* 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
|
|
115
|
-
*
|
|
116
|
-
* does not sync shapes by itself.
|
|
117
|
+
* shapes in `{DATA_ROOT}/graph/shapes`. Runs at boot, after the ontology sync. A source reload
|
|
118
|
+
* (HMR) does not sync shapes.
|
|
117
119
|
*
|
|
118
120
|
* A graph whose stored content hash matches is not written, so a sync of unchanged shapes
|
|
119
121
|
* writes nothing. Shapes an earlier sync wrote into the default graph are removed there once
|
|
@@ -123,62 +125,73 @@ export declare class LinkedServer extends Shape {
|
|
|
123
125
|
* Shape graphs of packages no longer registered stay unless the operator sets
|
|
124
126
|
* `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true` — see `bootOrphanScope` in utils/syncShapes.
|
|
125
127
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* `syncShapesOnBoot: false` and syncs its shapes itself.
|
|
128
|
+
* Runs when {@link bootSyncs}: the app writes its own graphs and `linked.config` does not set
|
|
129
|
+
* `syncShapesOnBoot: false`. A host that writes the app's graphs (`LINKED_GRAPH_WRITER=host`)
|
|
130
|
+
* reads their content through `POST /admin/graph-content` ({@link graphContent}).
|
|
130
131
|
*/
|
|
131
132
|
private materializeShapesIntoStore;
|
|
132
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Whether the boot syncs the shapes and the ontologies: the app writes its own graphs
|
|
135
|
+
* (`LINKED_GRAPH_WRITER` is `app`) and `linked.config` does not set `syncShapesOnBoot: false`.
|
|
136
|
+
*/
|
|
137
|
+
private bootSyncs;
|
|
138
|
+
/** One shape sync, following the routing; throws when it fails in any dataset. */
|
|
133
139
|
private syncRegisteredShapes;
|
|
134
140
|
/** The root directory of a package with shapes: the app, a discovered linked package, or one the app resolves. */
|
|
135
141
|
private shapePackageDir;
|
|
136
142
|
/**
|
|
137
|
-
* Boot: one
|
|
138
|
-
*
|
|
139
|
-
* {@link syncPinWarning}). Only for the syncs this boot runs.
|
|
143
|
+
* Boot: one line naming the datasets the boot syncs write into, when the routing sends any graph
|
|
144
|
+
* beyond the default dataset (see {@link syncTargetsLine}). Only for the syncs this boot runs.
|
|
140
145
|
*/
|
|
141
|
-
private
|
|
146
|
+
private logSyncTargets;
|
|
142
147
|
/**
|
|
143
148
|
* Boot: sync the registered ontologies into their graphs in app data and run the shape
|
|
144
149
|
* check, before the shape sync (see utils/ontologySync). The sync follows the shape sync's
|
|
145
|
-
* switch (
|
|
146
|
-
* `LINKED_SYNC_ONTOLOGIES_ON_BOOT=false`; the in-memory class hierarchy is loaded either way.
|
|
150
|
+
* switch ({@link bootSyncs}); the in-memory class hierarchy is loaded either way.
|
|
147
151
|
* Never stops the boot: failures and findings are logged.
|
|
148
152
|
*/
|
|
149
153
|
private syncOntologiesOnBoot;
|
|
150
154
|
/**
|
|
151
155
|
* Every discovered linked package and every workspace package of the app registers its
|
|
152
156
|
* ontologies (in dev, an unbuilt one through Vite), then the ontology sync (when
|
|
153
|
-
* `sync` is set)
|
|
157
|
+
* `sync` is set), following the routing, then the shape check.
|
|
154
158
|
* Orphaned ontology graphs (and earlier sync nodes) are pruned under the shape sync's orphan policy
|
|
155
159
|
* (`LINKED_SYNC_SHAPES_PRUNE_ORPHANS`).
|
|
156
160
|
*/
|
|
157
161
|
private runOntologyPhase;
|
|
158
|
-
/** Tail of the
|
|
162
|
+
/** Tail of the graph content's queue: one request at a time. */
|
|
159
163
|
private shapeSyncQueue;
|
|
160
164
|
/**
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* code — writes its own shapes and their hashes.
|
|
165
|
+
* What the app's `'package'`-mode syncs would write, writing nothing (`POST /admin/graph-content`):
|
|
166
|
+
* per package its shape graph's Turtle and its ontology graph's N-Triples with the hash each
|
|
167
|
+
* record gets and its sources, and the app's own shape graph (`null` when it has no single-owner
|
|
168
|
+
* shapes; left out without `DATA_ROOT`). A host that writes the app's graphs itself
|
|
169
|
+
* (`LINKED_GRAPH_WRITER=host`) reads them here, after it changed the app's shape code: the app
|
|
170
|
+
* is the only process that loads that code.
|
|
168
171
|
*
|
|
169
|
-
* `
|
|
170
|
-
*
|
|
171
|
-
* dev (Vite) their modules are invalidated and their
|
|
172
|
-
* ({@link onSourceChange}),
|
|
173
|
-
* the
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
172
|
+
* `reload` names the packages whose source changed: the app itself or its direct dependencies
|
|
173
|
+
* (its `linked.unpublishedPackages` among them), never `@_linked/*`
|
|
174
|
+
* ({@link assertReloadablePackages}). In dev (Vite) their modules are invalidated and their
|
|
175
|
+
* backends reloaded first ({@link onSourceChange}), then the ontologies are registered again and
|
|
176
|
+
* the shape check runs, so a shape added since the boot is in the answer; without Vite (a built
|
|
177
|
+
* app) nothing is reloaded. `reloaded` names the packages reloaded. `packages` limits the
|
|
178
|
+
* answer. Each package's Turtle comes from where the shape sync takes it (an installed package
|
|
179
|
+
* without `shapes.ttl` has none: `withoutTurtle`).
|
|
180
|
+
* Refused with {@link AdminConflictError} unless both graph targets are `'package'`. Runs one at
|
|
181
|
+
* a time, as the system, never as the caller of a request; a run that takes longer than
|
|
177
182
|
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) fails and releases the queue.
|
|
178
183
|
*/
|
|
179
|
-
|
|
184
|
+
graphContent({ packages, reload }?: {
|
|
180
185
|
packages?: string[];
|
|
181
|
-
|
|
186
|
+
reload?: string[];
|
|
187
|
+
}): Promise<GraphContentAnswer>;
|
|
188
|
+
/** Reload each of `packages` from disk (dev only); the ones actually reloaded. */
|
|
189
|
+
private reloadPackages;
|
|
190
|
+
/**
|
|
191
|
+
* Run `fn` in the graph content's queue (one at a time), as the system, failing it after
|
|
192
|
+
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) and releasing the queue.
|
|
193
|
+
*/
|
|
194
|
+
private inShapeSyncQueue;
|
|
182
195
|
/**
|
|
183
196
|
* The packages a host may ask to reload: the app itself and its direct dependencies, which
|
|
184
197
|
* include every package in its `linked.unpublishedPackages`, transitively
|
|
@@ -187,7 +200,7 @@ export declare class LinkedServer extends Shape {
|
|
|
187
200
|
*/
|
|
188
201
|
private reloadablePackages;
|
|
189
202
|
/**
|
|
190
|
-
* Re-read one package's source from disk (dev only); see {@link
|
|
203
|
+
* Re-read one package's source from disk (dev only); see {@link graphContent}.
|
|
191
204
|
* Resolves whether it reloaded anything.
|
|
192
205
|
*/
|
|
193
206
|
private reloadPackageFromDisk;
|
|
@@ -298,7 +311,7 @@ export declare class LinkedServer extends Shape {
|
|
|
298
311
|
* Reload one package's backend providers from source.
|
|
299
312
|
*
|
|
300
313
|
* Called by the CLI orchestrator's Vite watcher whenever a `.ts`/`.tsx`
|
|
301
|
-
* file inside a workspace package changes, and by {@link
|
|
314
|
+
* file inside a workspace package changes, and by {@link graphContent}.
|
|
302
315
|
* Disposes the package's existing providers (so routes, listeners, timers
|
|
303
316
|
* don't accumulate), drops them from the registry, and re-runs
|
|
304
317
|
* indexPackageBackendProviders to pick up the freshly-imported module.
|
|
@@ -36,7 +36,7 @@ import { paint } from '../utils/paint.js';
|
|
|
36
36
|
import { nativeImport } from '../utils/nativeImport.js';
|
|
37
37
|
import { createJsonBodyParser } from '../utils/jsonBodyParser.js';
|
|
38
38
|
import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
|
|
39
|
-
import {
|
|
39
|
+
import { getShapeClass, getSuperShapesClasses, } from '@_linked/core/utils/ShapeClass';
|
|
40
40
|
import path from 'path';
|
|
41
41
|
import { pathToFileURL } from 'node:url';
|
|
42
42
|
import { FatalStartupError, isFatalError } from '../utils/fatalError.js';
|
|
@@ -137,10 +137,11 @@ 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 { appShapesGraph, bootOrphanScope, loadShapeGraphCore,
|
|
141
|
-
import {
|
|
142
|
-
import {
|
|
143
|
-
import {
|
|
140
|
+
import { appShapesGraph, bootOrphanScope, loadShapeGraphCore, graphContentOf, syncRoutedShapeGraphs, viteShapesTurtleGenerator, } from '../utils/syncShapes.js';
|
|
141
|
+
import { loadOntologyCore, runOntologyPhase, ontologyPackageDirs, sourceModuleImporter, shapeSyncOn, } from '../utils/ontologySync.js';
|
|
142
|
+
import { getGraphTargets } from '@_linked/core/utils/graphTargets';
|
|
143
|
+
import { applyGraphTargetsFromEnv, appWritesGraphs, GRAPH_WRITER_ENV, graphWriterFromEnv, syncTargetsLine } from '../utils/graphTargets.js';
|
|
144
|
+
import { AdminRequestError, assertReloadablePackages, AdminConflictError, registerAdminRoutes, resolveAdminSecret, } from '../utils/adminChannel.js';
|
|
144
145
|
import { envMs, withTimeout } from '../utils/withTimeout.js';
|
|
145
146
|
import { invalidateSourceUnder } from '../utils/reloadSource.js';
|
|
146
147
|
import { installSpaFallback, repinSpaFallback, } from '../utils/spaFallback.js';
|
|
@@ -298,7 +299,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
298
299
|
* error is logged and contained.
|
|
299
300
|
*/
|
|
300
301
|
this.booting = false;
|
|
301
|
-
/** Tail of the
|
|
302
|
+
/** Tail of the graph content's queue: one request at a time. */
|
|
302
303
|
this.shapeSyncQueue = Promise.resolve();
|
|
303
304
|
/** Tail of the {@link onSourceChange} queue: reloads never interleave. */
|
|
304
305
|
this.sourceReloadQueue = Promise.resolve();
|
|
@@ -364,19 +365,21 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
364
365
|
this.initPackage();
|
|
365
366
|
this.server = express();
|
|
366
367
|
await this.initBackendProviders();
|
|
367
|
-
this.
|
|
368
|
+
await this.logSyncTargets();
|
|
368
369
|
await this.syncOntologiesOnBoot();
|
|
369
370
|
await this.materializeShapesIntoStore();
|
|
370
371
|
return this;
|
|
371
372
|
}
|
|
372
373
|
/**
|
|
373
374
|
* Sync this server's registered shapes into app data, one named graph per package (see
|
|
374
|
-
* utils/syncShapes)
|
|
375
|
+
* utils/syncShapes) in every dataset the storage routing needs it in (core's
|
|
376
|
+
* `syncShapeGraphsRouted`; the default dataset when nothing is pinned, and a graph routing no
|
|
377
|
+
* longer sends to the default dataset is dropped there): each package's shapes in
|
|
378
|
+
* `{package IRI}/graph/shapes`, from its built
|
|
375
379
|
* `shapes.ttl` (an installed package without one gets no shape graph), the app's own packages
|
|
376
380
|
* generated in process in dev (through Vite's loader), and the app's generated single-owner
|
|
377
|
-
* shapes in `{DATA_ROOT}/graph/shapes`. Runs at boot, after the ontology sync
|
|
378
|
-
*
|
|
379
|
-
* does not sync shapes by itself.
|
|
381
|
+
* shapes in `{DATA_ROOT}/graph/shapes`. Runs at boot, after the ontology sync. A source reload
|
|
382
|
+
* (HMR) does not sync shapes.
|
|
380
383
|
*
|
|
381
384
|
* A graph whose stored content hash matches is not written, so a sync of unchanged shapes
|
|
382
385
|
* writes nothing. Shapes an earlier sync wrote into the default graph are removed there once
|
|
@@ -386,10 +389,9 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
386
389
|
* Shape graphs of packages no longer registered stay unless the operator sets
|
|
387
390
|
* `LINKED_SYNC_SHAPES_PRUNE_ORPHANS=true` — see `bootOrphanScope` in utils/syncShapes.
|
|
388
391
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
* `syncShapesOnBoot: false` and syncs its shapes itself.
|
|
392
|
+
* Runs when {@link bootSyncs}: the app writes its own graphs and `linked.config` does not set
|
|
393
|
+
* `syncShapesOnBoot: false`. A host that writes the app's graphs (`LINKED_GRAPH_WRITER=host`)
|
|
394
|
+
* reads their content through `POST /admin/graph-content` ({@link graphContent}).
|
|
393
395
|
*/
|
|
394
396
|
async materializeShapesIntoStore() {
|
|
395
397
|
try {
|
|
@@ -399,22 +401,28 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
399
401
|
console.warn('[LinkedServer] shape sync failed (non-fatal):', err);
|
|
400
402
|
}
|
|
401
403
|
}
|
|
402
|
-
/**
|
|
404
|
+
/**
|
|
405
|
+
* Whether the boot syncs the shapes and the ontologies: the app writes its own graphs
|
|
406
|
+
* (`LINKED_GRAPH_WRITER` is `app`) and `linked.config` does not set `syncShapesOnBoot: false`.
|
|
407
|
+
*/
|
|
408
|
+
bootSyncs() {
|
|
409
|
+
var _a;
|
|
410
|
+
return appWritesGraphs(process.env) && shapeSyncOn((_a = this.config) === null || _a === void 0 ? void 0 : _a.syncShapesOnBoot);
|
|
411
|
+
}
|
|
412
|
+
/** One shape sync, following the routing; throws when it fails in any dataset. */
|
|
403
413
|
async syncRegisteredShapes() {
|
|
404
|
-
var _a, _b
|
|
405
|
-
if (!
|
|
406
|
-
return
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (!appData)
|
|
410
|
-
return { written: 0, skipped: 'no-default-dataset' };
|
|
414
|
+
var _a, _b;
|
|
415
|
+
if (!this.bootSyncs())
|
|
416
|
+
return;
|
|
417
|
+
if (!LinkedStorage.getDefaultDataset())
|
|
418
|
+
return;
|
|
411
419
|
const orphanScope = bootOrphanScope();
|
|
412
|
-
const appName = (
|
|
413
|
-
const vite = (
|
|
420
|
+
const appName = (_a = this.package) === null || _a === void 0 ? void 0 : _a.name;
|
|
421
|
+
const vite = (_b = this.config.server) === null || _b === void 0 ? void 0 : _b.vite;
|
|
414
422
|
const generate = viteShapesTurtleGenerator(vite);
|
|
415
|
-
//
|
|
416
|
-
//
|
|
417
|
-
|
|
423
|
+
// Follows the routing: each package's shape graph goes to every dataset its shapes (and the
|
|
424
|
+
// shape shapes) route to; the default dataset is one of them unless routing sends all elsewhere.
|
|
425
|
+
await syncRoutedShapeGraphs({
|
|
418
426
|
core: await loadShapeGraphCore(),
|
|
419
427
|
orphanScope,
|
|
420
428
|
isOwn: (name) => name === appName || this.workspacePackageNames.has(name),
|
|
@@ -424,8 +432,6 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
424
432
|
appGraph: appShapesGraph(),
|
|
425
433
|
appPackage: appName,
|
|
426
434
|
});
|
|
427
|
-
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);
|
|
428
|
-
return { written, orphanScope, shapeGraphs };
|
|
429
435
|
}
|
|
430
436
|
/** The root directory of a package with shapes: the app, a discovered linked package, or one the app resolves. */
|
|
431
437
|
shapePackageDir(name) {
|
|
@@ -439,30 +445,34 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
439
445
|
return installed === null || installed === void 0 ? void 0 : installed.root;
|
|
440
446
|
}
|
|
441
447
|
/**
|
|
442
|
-
* Boot: one
|
|
443
|
-
*
|
|
444
|
-
* {@link syncPinWarning}). Only for the syncs this boot runs.
|
|
448
|
+
* Boot: one line naming the datasets the boot syncs write into, when the routing sends any graph
|
|
449
|
+
* beyond the default dataset (see {@link syncTargetsLine}). Only for the syncs this boot runs.
|
|
445
450
|
*/
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
451
|
+
async logSyncTargets() {
|
|
452
|
+
if (!appWritesGraphs(process.env)) {
|
|
453
|
+
console.log(`[LinkedServer] ${GRAPH_WRITER_ENV}=host: the host writes this app's shape and ontology graphs; ` +
|
|
454
|
+
'the boot writes none (the host reads them through POST /admin/graph-content).');
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
const on = this.bootSyncs();
|
|
459
|
+
const line = syncTargetsLine({ shapeSync: on, ontologySync: on }, await import('@_linked/core/shapes/syncTargets'));
|
|
460
|
+
if (line)
|
|
461
|
+
console.log(line);
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
console.warn('[LinkedServer] could not name the datasets the boot syncs write into:', err);
|
|
465
|
+
}
|
|
455
466
|
}
|
|
456
467
|
/**
|
|
457
468
|
* Boot: sync the registered ontologies into their graphs in app data and run the shape
|
|
458
469
|
* check, before the shape sync (see utils/ontologySync). The sync follows the shape sync's
|
|
459
|
-
* switch (
|
|
460
|
-
* `LINKED_SYNC_ONTOLOGIES_ON_BOOT=false`; the in-memory class hierarchy is loaded either way.
|
|
470
|
+
* switch ({@link bootSyncs}); the in-memory class hierarchy is loaded either way.
|
|
461
471
|
* Never stops the boot: failures and findings are logged.
|
|
462
472
|
*/
|
|
463
473
|
async syncOntologiesOnBoot() {
|
|
464
474
|
try {
|
|
465
|
-
await runAsSystem(() =>
|
|
475
|
+
await runAsSystem(() => this.runOntologyPhase(this.bootSyncs()), 'ontology sync at boot');
|
|
466
476
|
}
|
|
467
477
|
catch (err) {
|
|
468
478
|
console.warn('[LinkedServer] ontology sync failed (non-fatal):', err);
|
|
@@ -471,7 +481,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
471
481
|
/**
|
|
472
482
|
* Every discovered linked package and every workspace package of the app registers its
|
|
473
483
|
* ontologies (in dev, an unbuilt one through Vite), then the ontology sync (when
|
|
474
|
-
* `sync` is set)
|
|
484
|
+
* `sync` is set), following the routing, then the shape check.
|
|
475
485
|
* Orphaned ontology graphs (and earlier sync nodes) are pruned under the shape sync's orphan policy
|
|
476
486
|
* (`LINKED_SYNC_SHAPES_PRUNE_ORPHANS`).
|
|
477
487
|
*/
|
|
@@ -480,6 +490,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
480
490
|
return runOntologyPhase(LinkedStorage.getDefaultDataset(), {
|
|
481
491
|
core: await loadOntologyCore(),
|
|
482
492
|
sync,
|
|
493
|
+
routed: true,
|
|
483
494
|
orphanScope: bootOrphanScope(),
|
|
484
495
|
dependencies: readDependencyLists((_a = this.linkedPackageDirs) !== null && _a !== void 0 ? _a : new Map(), this.package),
|
|
485
496
|
packageDirs: ontologyPackageDirs(((_b = this.linkedPackageDirs) !== null && _b !== void 0 ? _b : new Map()).values(), (_c = this.workspacePackageDirs) !== null && _c !== void 0 ? _c : []),
|
|
@@ -487,36 +498,68 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
487
498
|
});
|
|
488
499
|
}
|
|
489
500
|
/**
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
496
|
-
* code — writes its own shapes and their hashes.
|
|
501
|
+
* What the app's `'package'`-mode syncs would write, writing nothing (`POST /admin/graph-content`):
|
|
502
|
+
* per package its shape graph's Turtle and its ontology graph's N-Triples with the hash each
|
|
503
|
+
* record gets and its sources, and the app's own shape graph (`null` when it has no single-owner
|
|
504
|
+
* shapes; left out without `DATA_ROOT`). A host that writes the app's graphs itself
|
|
505
|
+
* (`LINKED_GRAPH_WRITER=host`) reads them here, after it changed the app's shape code: the app
|
|
506
|
+
* is the only process that loads that code.
|
|
497
507
|
*
|
|
498
|
-
* `
|
|
499
|
-
*
|
|
500
|
-
* dev (Vite) their modules are invalidated and their
|
|
501
|
-
* ({@link onSourceChange}),
|
|
502
|
-
* the
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
508
|
+
* `reload` names the packages whose source changed: the app itself or its direct dependencies
|
|
509
|
+
* (its `linked.unpublishedPackages` among them), never `@_linked/*`
|
|
510
|
+
* ({@link assertReloadablePackages}). In dev (Vite) their modules are invalidated and their
|
|
511
|
+
* backends reloaded first ({@link onSourceChange}), then the ontologies are registered again and
|
|
512
|
+
* the shape check runs, so a shape added since the boot is in the answer; without Vite (a built
|
|
513
|
+
* app) nothing is reloaded. `reloaded` names the packages reloaded. `packages` limits the
|
|
514
|
+
* answer. Each package's Turtle comes from where the shape sync takes it (an installed package
|
|
515
|
+
* without `shapes.ttl` has none: `withoutTurtle`).
|
|
516
|
+
* Refused with {@link AdminConflictError} unless both graph targets are `'package'`. Runs one at
|
|
517
|
+
* a time, as the system, never as the caller of a request; a run that takes longer than
|
|
506
518
|
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) fails and releases the queue.
|
|
507
519
|
*/
|
|
508
|
-
async
|
|
509
|
-
assertReloadablePackages(
|
|
510
|
-
const
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if (await this.reloadPackageFromDisk(pkg))
|
|
515
|
-
reloaded.push(pkg);
|
|
520
|
+
async graphContent({ packages, reload = [] } = {}) {
|
|
521
|
+
assertReloadablePackages(reload, this.reloadablePackages());
|
|
522
|
+
const graphTargets = getGraphTargets();
|
|
523
|
+
for (const [setting, env] of [['ontologyGraph', 'LINKED_ONTOLOGY_GRAPH'], ['shapeGraph', 'LINKED_SHAPE_GRAPH']]) {
|
|
524
|
+
if (graphTargets[setting] !== 'package') {
|
|
525
|
+
throw new AdminConflictError(`${setting} is ${JSON.stringify(graphTargets[setting])} (${env}); the graph content is what the 'package' mode writes, one graph per package`);
|
|
516
526
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
527
|
+
}
|
|
528
|
+
return this.inShapeSyncQueue('the graph content', 'graph content requested by the host', async () => {
|
|
529
|
+
var _a, _b;
|
|
530
|
+
const reloaded = await this.reloadPackages(reload);
|
|
531
|
+
if (reloaded.length)
|
|
532
|
+
await this.runOntologyPhase(false);
|
|
533
|
+
const appName = (_a = this.package) === null || _a === void 0 ? void 0 : _a.name;
|
|
534
|
+
const generate = viteShapesTurtleGenerator((_b = this.config.server) === null || _b === void 0 ? void 0 : _b.vite);
|
|
535
|
+
const content = await graphContentOf({
|
|
536
|
+
core: await loadShapeGraphCore(),
|
|
537
|
+
isOwn: (name) => name === appName || this.workspacePackageNames.has(name),
|
|
538
|
+
dev: !!generate,
|
|
539
|
+
generate,
|
|
540
|
+
packageDirOf: (name) => this.shapePackageDir(name),
|
|
541
|
+
appGraph: appShapesGraph(),
|
|
542
|
+
...(packages ? { packages } : {}),
|
|
543
|
+
});
|
|
544
|
+
return { graphTargets, ...content, reloaded };
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
/** Reload each of `packages` from disk (dev only); the ones actually reloaded. */
|
|
548
|
+
async reloadPackages(packages) {
|
|
549
|
+
const reloaded = [];
|
|
550
|
+
for (const pkg of packages) {
|
|
551
|
+
if (await this.reloadPackageFromDisk(pkg))
|
|
552
|
+
reloaded.push(pkg);
|
|
553
|
+
}
|
|
554
|
+
return reloaded;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Run `fn` in the graph content's queue (one at a time), as the system, failing it after
|
|
558
|
+
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) and releasing the queue.
|
|
559
|
+
*/
|
|
560
|
+
inShapeSyncQueue(what, why, fn) {
|
|
561
|
+
const timeoutMs = envMs('LINKED_SHAPE_SYNC_TIMEOUT_MS', 120000);
|
|
562
|
+
const run = this.shapeSyncQueue.then(() => withTimeout(runAsSystem(fn, why), timeoutMs, `${what} did not finish within ${timeoutMs} ms`));
|
|
520
563
|
this.shapeSyncQueue = run.catch(() => undefined);
|
|
521
564
|
return run;
|
|
522
565
|
}
|
|
@@ -549,7 +592,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
549
592
|
return names;
|
|
550
593
|
}
|
|
551
594
|
/**
|
|
552
|
-
* Re-read one package's source from disk (dev only); see {@link
|
|
595
|
+
* Re-read one package's source from disk (dev only); see {@link graphContent}.
|
|
553
596
|
* Resolves whether it reloaded anything.
|
|
554
597
|
*/
|
|
555
598
|
async reloadPackageFromDisk(pkg) {
|
|
@@ -711,7 +754,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
711
754
|
// });
|
|
712
755
|
// before controllers
|
|
713
756
|
await this.initBackendProviders();
|
|
714
|
-
this.
|
|
757
|
+
await this.logSyncTargets();
|
|
715
758
|
await this.syncOntologiesOnBoot();
|
|
716
759
|
await this.materializeShapesIntoStore();
|
|
717
760
|
//START OF EXPRESS ROUTES AND MIDDLEWARE
|
|
@@ -785,7 +828,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
785
828
|
registerAdminRoutes(this.server, resolveAdminSecret(), {
|
|
786
829
|
// The host's provisioner starts the app again after it exits.
|
|
787
830
|
exit: () => process.exit(0),
|
|
788
|
-
|
|
831
|
+
graphContent: (request) => this.graphContent(request),
|
|
789
832
|
});
|
|
790
833
|
// after controller
|
|
791
834
|
await this.callGenericBackendProvidersMethod('setupAfterControllers');
|
|
@@ -839,8 +882,10 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
839
882
|
// LINKED_ONTOLOGY_GRAPH / LINKED_SHAPE_GRAPH: checked (an invalid value stops the boot) and set
|
|
840
883
|
// once as core's global graph targets, before any store is initialised and before the syncs.
|
|
841
884
|
// A rejected boot does not end the process by itself, so the error is fatal.
|
|
885
|
+
// LINKED_GRAPH_WRITER: checked here too, so an invalid value stops the boot before anything runs.
|
|
842
886
|
try {
|
|
843
887
|
applyGraphTargetsFromEnv(process.env);
|
|
888
|
+
graphWriterFromEnv(process.env);
|
|
844
889
|
}
|
|
845
890
|
catch (err) {
|
|
846
891
|
throw new FatalStartupError(`[LinkedServer] ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`, { cause: err });
|
|
@@ -1558,7 +1603,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
1558
1603
|
* Reload one package's backend providers from source.
|
|
1559
1604
|
*
|
|
1560
1605
|
* Called by the CLI orchestrator's Vite watcher whenever a `.ts`/`.tsx`
|
|
1561
|
-
* file inside a workspace package changes, and by {@link
|
|
1606
|
+
* file inside a workspace package changes, and by {@link graphContent}.
|
|
1562
1607
|
* Disposes the package's existing providers (so routes, listeners, timers
|
|
1563
1608
|
* don't accumulate), drops them from the registry, and re-runs
|
|
1564
1609
|
* indexPackageBackendProviders to pick up the freshly-imported module.
|