@_linked/server 3.12.1 → 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 +72 -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 +59 -39
- package/lib/esm/shapes/LinkedServer.js +158 -93
- package/lib/esm/shapes/LinkedServer.js.map +1 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.d.ts +52 -20
- package/lib/esm/shapes/quadstores/RemoteDataset.js +134 -32
- package/lib/esm/shapes/quadstores/RemoteDataset.js.map +1 -1
- package/lib/esm/utils/adminChannel.d.ts +23 -51
- package/lib/esm/utils/adminChannel.js +50 -38
- 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 +42 -17
- package/lib/esm/utils/graphTargets.js +62 -69
- package/lib/esm/utils/graphTargets.js.map +1 -1
- package/lib/esm/utils/linkedDependencies.d.ts +53 -7
- package/lib/esm/utils/linkedDependencies.js +130 -12
- package/lib/esm/utils/linkedDependencies.js.map +1 -1
- package/lib/esm/utils/ontologySync.d.ts +55 -31
- package/lib/esm/utils/ontologySync.js +94 -42
- 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 +123 -6
- package/lib/esm/utils/syncShapes.js +166 -35
- package/lib/esm/utils/syncShapes.js.map +1 -1
- package/package.json +4 -4
- package/readme.md +158 -38
|
@@ -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.
|
|
@@ -82,7 +82,7 @@ export declare class LinkedServer extends Shape {
|
|
|
82
82
|
* could not be loaded at all.
|
|
83
83
|
*/
|
|
84
84
|
private linkedPackageDirs;
|
|
85
|
-
/** The app's own workspace packages (`
|
|
85
|
+
/** The app's own workspace packages (`linked.kind` of a package), by real path. */
|
|
86
86
|
private workspacePackageDirs;
|
|
87
87
|
/** The names of the app's own workspace packages. */
|
|
88
88
|
private workspacePackageNames;
|
|
@@ -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,65 +125,82 @@ 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}).
|
|
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
|
-
* changed the app's shape code
|
|
166
|
-
*
|
|
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.
|
|
167
171
|
*
|
|
168
|
-
* `
|
|
169
|
-
*
|
|
170
|
-
* dev (Vite) their modules are invalidated and their
|
|
171
|
-
* ({@link onSourceChange}),
|
|
172
|
-
* the
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
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
|
|
176
182
|
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) fails and releases the queue.
|
|
177
183
|
*/
|
|
178
|
-
|
|
184
|
+
graphContent({ packages, reload }?: {
|
|
179
185
|
packages?: string[];
|
|
180
|
-
|
|
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;
|
|
195
|
+
/**
|
|
196
|
+
* The packages a host may ask to reload: the app itself and its direct dependencies, which
|
|
197
|
+
* include every package in its `linked.unpublishedPackages`, transitively
|
|
198
|
+
* ({@link listedPackageNames}). Read from disk on each call: a list written after the boot
|
|
199
|
+
* counts. A malformed list throws.
|
|
200
|
+
*/
|
|
182
201
|
private reloadablePackages;
|
|
183
202
|
/**
|
|
184
|
-
* 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}.
|
|
185
204
|
* Resolves whether it reloaded anything.
|
|
186
205
|
*/
|
|
187
206
|
private reloadPackageFromDisk;
|
|
@@ -270,7 +289,8 @@ export declare class LinkedServer extends Shape {
|
|
|
270
289
|
protected importModule(specifier: string): Promise<any>;
|
|
271
290
|
/**
|
|
272
291
|
* Filters local workspace packages to only those reachable from the app's
|
|
273
|
-
* dependency tree
|
|
292
|
+
* dependency tree: `dependencies` and `linked.unpublishedPackages`, of the app
|
|
293
|
+
* and of each package reached. Mirrors the logic used by `buildAll` in the cli.
|
|
274
294
|
*/
|
|
275
295
|
private filterPackagesByDependencyTree;
|
|
276
296
|
resizeImage(req: any, res: any): Promise<void>;
|
|
@@ -291,7 +311,7 @@ export declare class LinkedServer extends Shape {
|
|
|
291
311
|
* Reload one package's backend providers from source.
|
|
292
312
|
*
|
|
293
313
|
* Called by the CLI orchestrator's Vite watcher whenever a `.ts`/`.tsx`
|
|
294
|
-
* file inside a workspace package changes, and by {@link
|
|
314
|
+
* file inside a workspace package changes, and by {@link graphContent}.
|
|
295
315
|
* Disposes the package's existing providers (so routes, listeners, timers
|
|
296
316
|
* don't accumulate), drops them from the registry, and re-runs
|
|
297
317
|
* indexPackageBackendProviders to pick up the freshly-imported module.
|
|
@@ -36,11 +36,11 @@ 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';
|
|
43
|
-
import { discoverLinkedDependencies, hasModuleEntry, isBundledBySsr, NODE_IMPORT_CONDITIONS, readDependencyLists, readInstalledPackage, resolveBackendEntry, resolveDevBackendEntry, ssrNoExternalOf, } from '../utils/linkedDependencies.js';
|
|
43
|
+
import { discoverLinkedDependencies, listedPackageNames, unpublishedNames, hasModuleEntry, isBundledBySsr, NODE_IMPORT_CONDITIONS, readDependencyLists, readInstalledPackage, resolveBackendEntry, resolveDevBackendEntry, ssrNoExternalOf, } from '../utils/linkedDependencies.js';
|
|
44
44
|
/**
|
|
45
45
|
* `instanceof ShapeProvider`, tolerating a second copy of server-utils.
|
|
46
46
|
*
|
|
@@ -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';
|
|
@@ -285,7 +286,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
285
286
|
* could not be loaded at all.
|
|
286
287
|
*/
|
|
287
288
|
this.linkedPackageDirs = new Map();
|
|
288
|
-
/** The app's own workspace packages (`
|
|
289
|
+
/** The app's own workspace packages (`linked.kind` of a package), by real path. */
|
|
289
290
|
this.workspacePackageDirs = [];
|
|
290
291
|
/** The names of the app's own workspace packages. */
|
|
291
292
|
this.workspacePackageNames = new Set();
|
|
@@ -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,25 +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}).
|
|
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
|
-
|
|
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
|
+
}
|
|
450
466
|
}
|
|
451
467
|
/**
|
|
452
468
|
* Boot: sync the registered ontologies into their graphs in app data and run the shape
|
|
453
469
|
* check, before the shape sync (see utils/ontologySync). The sync follows the shape sync's
|
|
454
|
-
* switch (
|
|
455
|
-
* `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.
|
|
456
471
|
* Never stops the boot: failures and findings are logged.
|
|
457
472
|
*/
|
|
458
473
|
async syncOntologiesOnBoot() {
|
|
459
474
|
try {
|
|
460
|
-
await runAsSystem(() =>
|
|
475
|
+
await runAsSystem(() => this.runOntologyPhase(this.bootSyncs()), 'ontology sync at boot');
|
|
461
476
|
}
|
|
462
477
|
catch (err) {
|
|
463
478
|
console.warn('[LinkedServer] ontology sync failed (non-fatal):', err);
|
|
@@ -466,7 +481,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
466
481
|
/**
|
|
467
482
|
* Every discovered linked package and every workspace package of the app registers its
|
|
468
483
|
* ontologies (in dev, an unbuilt one through Vite), then the ontology sync (when
|
|
469
|
-
* `sync` is set)
|
|
484
|
+
* `sync` is set), following the routing, then the shape check.
|
|
470
485
|
* Orphaned ontology graphs (and earlier sync nodes) are pruned under the shape sync's orphan policy
|
|
471
486
|
* (`LINKED_SYNC_SHAPES_PRUNE_ORPHANS`).
|
|
472
487
|
*/
|
|
@@ -475,6 +490,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
475
490
|
return runOntologyPhase(LinkedStorage.getDefaultDataset(), {
|
|
476
491
|
core: await loadOntologyCore(),
|
|
477
492
|
sync,
|
|
493
|
+
routed: true,
|
|
478
494
|
orphanScope: bootOrphanScope(),
|
|
479
495
|
dependencies: readDependencyLists((_a = this.linkedPackageDirs) !== null && _a !== void 0 ? _a : new Map(), this.package),
|
|
480
496
|
packageDirs: ontologyPackageDirs(((_b = this.linkedPackageDirs) !== null && _b !== void 0 ? _b : new Map()).values(), (_c = this.workspacePackageDirs) !== null && _c !== void 0 ? _c : []),
|
|
@@ -482,58 +498,101 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
482
498
|
});
|
|
483
499
|
}
|
|
484
500
|
/**
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
* changed the app's shape code
|
|
490
|
-
*
|
|
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.
|
|
491
507
|
*
|
|
492
|
-
* `
|
|
493
|
-
*
|
|
494
|
-
* dev (Vite) their modules are invalidated and their
|
|
495
|
-
* ({@link onSourceChange}),
|
|
496
|
-
* the
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
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
|
|
500
518
|
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) fails and releases the queue.
|
|
501
519
|
*/
|
|
502
|
-
async
|
|
503
|
-
assertReloadablePackages(
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (await this.reloadPackageFromDisk(pkg))
|
|
509
|
-
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`);
|
|
510
526
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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`));
|
|
514
563
|
this.shapeSyncQueue = run.catch(() => undefined);
|
|
515
564
|
return run;
|
|
516
565
|
}
|
|
517
|
-
/**
|
|
566
|
+
/**
|
|
567
|
+
* The packages a host may ask to reload: the app itself and its direct dependencies, which
|
|
568
|
+
* include every package in its `linked.unpublishedPackages`, transitively
|
|
569
|
+
* ({@link listedPackageNames}). Read from disk on each call: a list written after the boot
|
|
570
|
+
* counts. A malformed list throws.
|
|
571
|
+
*/
|
|
518
572
|
reloadablePackages() {
|
|
519
573
|
var _a, _b;
|
|
520
574
|
const names = new Set();
|
|
521
575
|
if ((_a = this.package) === null || _a === void 0 ? void 0 : _a.name)
|
|
522
576
|
names.add(this.package.name);
|
|
577
|
+
let json;
|
|
523
578
|
try {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
for (const name of Object.keys((_b = json[field]) !== null && _b !== void 0 ? _b : {}))
|
|
529
|
-
names.add(name);
|
|
530
|
-
}
|
|
579
|
+
json = JSON.parse(fsNative.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8'));
|
|
580
|
+
}
|
|
581
|
+
catch (_c) {
|
|
582
|
+
return names;
|
|
531
583
|
}
|
|
532
|
-
|
|
584
|
+
if (json.name)
|
|
585
|
+
names.add(json.name);
|
|
586
|
+
for (const field of ['dependencies', 'devDependencies']) {
|
|
587
|
+
for (const name of Object.keys((_b = json[field]) !== null && _b !== void 0 ? _b : {}))
|
|
588
|
+
names.add(name);
|
|
589
|
+
}
|
|
590
|
+
for (const name of listedPackageNames(process.cwd(), json))
|
|
591
|
+
names.add(name);
|
|
533
592
|
return names;
|
|
534
593
|
}
|
|
535
594
|
/**
|
|
536
|
-
* 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}.
|
|
537
596
|
* Resolves whether it reloaded anything.
|
|
538
597
|
*/
|
|
539
598
|
async reloadPackageFromDisk(pkg) {
|
|
@@ -695,7 +754,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
695
754
|
// });
|
|
696
755
|
// before controllers
|
|
697
756
|
await this.initBackendProviders();
|
|
698
|
-
this.
|
|
757
|
+
await this.logSyncTargets();
|
|
699
758
|
await this.syncOntologiesOnBoot();
|
|
700
759
|
await this.materializeShapesIntoStore();
|
|
701
760
|
//START OF EXPRESS ROUTES AND MIDDLEWARE
|
|
@@ -769,7 +828,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
769
828
|
registerAdminRoutes(this.server, resolveAdminSecret(), {
|
|
770
829
|
// The host's provisioner starts the app again after it exits.
|
|
771
830
|
exit: () => process.exit(0),
|
|
772
|
-
|
|
831
|
+
graphContent: (request) => this.graphContent(request),
|
|
773
832
|
});
|
|
774
833
|
// after controller
|
|
775
834
|
await this.callGenericBackendProvidersMethod('setupAfterControllers');
|
|
@@ -823,8 +882,10 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
823
882
|
// LINKED_ONTOLOGY_GRAPH / LINKED_SHAPE_GRAPH: checked (an invalid value stops the boot) and set
|
|
824
883
|
// once as core's global graph targets, before any store is initialised and before the syncs.
|
|
825
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.
|
|
826
886
|
try {
|
|
827
887
|
applyGraphTargetsFromEnv(process.env);
|
|
888
|
+
graphWriterFromEnv(process.env);
|
|
828
889
|
}
|
|
829
890
|
catch (err) {
|
|
830
891
|
throw new FatalStartupError(`[LinkedServer] ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`, { cause: err });
|
|
@@ -1047,18 +1108,21 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
1047
1108
|
}
|
|
1048
1109
|
/**
|
|
1049
1110
|
* Filters local workspace packages to only those reachable from the app's
|
|
1050
|
-
* dependency tree
|
|
1111
|
+
* dependency tree: `dependencies` and `linked.unpublishedPackages`, of the app
|
|
1112
|
+
* and of each package reached. Mirrors the logic used by `buildAll` in the cli.
|
|
1051
1113
|
*/
|
|
1052
1114
|
filterPackagesByDependencyTree(allPackages, appPackageJson) {
|
|
1115
|
+
var _a, _b;
|
|
1053
1116
|
const relevantPackages = new Map();
|
|
1054
1117
|
const packagesToCheck = new Set();
|
|
1055
1118
|
const processedPackages = new Set();
|
|
1056
|
-
// Start with direct dependencies from the app
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1119
|
+
// Start with direct dependencies from the app, its linked.unpublishedPackages among them
|
|
1120
|
+
for (const dep of [
|
|
1121
|
+
...Object.keys((_a = appPackageJson.dependencies) !== null && _a !== void 0 ? _a : {}),
|
|
1122
|
+
...unpublishedNames(appPackageJson),
|
|
1123
|
+
]) {
|
|
1124
|
+
if (allPackages.has(dep)) {
|
|
1125
|
+
packagesToCheck.add(dep);
|
|
1062
1126
|
}
|
|
1063
1127
|
}
|
|
1064
1128
|
// Recursively follow each package's dependencies
|
|
@@ -1073,11 +1137,12 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
1073
1137
|
if (packageDetails) {
|
|
1074
1138
|
relevantPackages.set(packageName, packageDetails);
|
|
1075
1139
|
const pkg = getPackageJSON(packageDetails.path);
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1140
|
+
for (const dep of [
|
|
1141
|
+
...Object.keys((_b = pkg === null || pkg === void 0 ? void 0 : pkg.dependencies) !== null && _b !== void 0 ? _b : {}),
|
|
1142
|
+
...unpublishedNames(pkg),
|
|
1143
|
+
]) {
|
|
1144
|
+
if (allPackages.has(dep) && !processedPackages.has(dep)) {
|
|
1145
|
+
packagesToCheck.add(dep);
|
|
1081
1146
|
}
|
|
1082
1147
|
}
|
|
1083
1148
|
}
|
|
@@ -1538,7 +1603,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
1538
1603
|
* Reload one package's backend providers from source.
|
|
1539
1604
|
*
|
|
1540
1605
|
* Called by the CLI orchestrator's Vite watcher whenever a `.ts`/`.tsx`
|
|
1541
|
-
* file inside a workspace package changes, and by {@link
|
|
1606
|
+
* file inside a workspace package changes, and by {@link graphContent}.
|
|
1542
1607
|
* Disposes the package's existing providers (so routes, listeners, timers
|
|
1543
1608
|
* don't accumulate), drops them from the registry, and re-runs
|
|
1544
1609
|
* indexPackageBackendProviders to pick up the freshly-imported module.
|