@_linked/server 3.6.0 → 3.6.1
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 +6 -0
- package/lib/esm/shapes/LinkedServer.d.ts +17 -7
- package/lib/esm/shapes/LinkedServer.js +51 -16
- package/lib/esm/shapes/LinkedServer.js.map +1 -1
- package/lib/esm/utils/adminChannel.d.ts +15 -0
- package/lib/esm/utils/adminChannel.js +30 -2
- package/lib/esm/utils/adminChannel.js.map +1 -1
- package/lib/esm/utils/withTimeout.d.ts +7 -0
- package/lib/esm/utils/withTimeout.js +17 -0
- package/lib/esm/utils/withTimeout.js.map +1 -0
- package/package.json +1 -1
- package/readme.md +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @\_linked/server
|
|
2
2
|
|
|
3
|
+
## 3.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#200](https://github.com/linked-fw/server/pull/200) [`935d0ca`](https://github.com/linked-fw/server/commit/935d0caf014cae8c71ae3b40963305ac8d43792c) Thanks [@flyon](https://github.com/flyon)! - `POST /admin/sync-shapes` now answers which packages it reloaded (`reloaded`) and the node shapes registered afterwards (`shapes`), refuses (400) package names that are not valid npm names, `@_linked/*` packages, or neither the app nor one of its direct dependencies, and times out a hung sync (`LINKED_SHAPE_SYNC_TIMEOUT_MS`, default 120 s) or source reload (`LINKED_SOURCE_RELOAD_TIMEOUT_MS`, default 60 s) so the queue is released. The README documents the control channel and the side effects of a reload and a restart.
|
|
8
|
+
|
|
3
9
|
## 3.6.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -135,16 +135,25 @@ export declare class LinkedServer extends Shape {
|
|
|
135
135
|
* changed the app's shape code, so the app — the only process that loads that
|
|
136
136
|
* code — writes its own shapes and their hashes.
|
|
137
137
|
*
|
|
138
|
-
* `packages` names the packages whose source changed
|
|
139
|
-
*
|
|
140
|
-
* (
|
|
141
|
-
*
|
|
142
|
-
*
|
|
138
|
+
* `packages` names the packages whose source changed: the app itself or its
|
|
139
|
+
* direct dependencies, never `@_linked/*` ({@link assertReloadablePackages}). In
|
|
140
|
+
* dev (Vite) their modules are invalidated and their backends reloaded first
|
|
141
|
+
* ({@link onSourceChange}), so a shape added since the boot is registered before
|
|
142
|
+
* the sync; without Vite (a built app) nothing is reloaded. The answer names the
|
|
143
|
+
* packages reloaded and the node shapes registered after it, so the host can check
|
|
144
|
+
* that the shape it wrote is there. Runs as the system, never as the caller of a
|
|
145
|
+
* request, one at a time; a run that takes longer than
|
|
146
|
+
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) fails and releases the queue.
|
|
143
147
|
*/
|
|
144
148
|
resyncShapes({ packages }?: {
|
|
145
149
|
packages?: string[];
|
|
146
150
|
}): Promise<ShapeSyncResult>;
|
|
147
|
-
/**
|
|
151
|
+
/** The packages a host may ask to reload: the app itself and its direct dependencies. */
|
|
152
|
+
private reloadablePackages;
|
|
153
|
+
/**
|
|
154
|
+
* Re-read one package's source from disk (dev only); see {@link resyncShapes}.
|
|
155
|
+
* Resolves whether it reloaded anything.
|
|
156
|
+
*/
|
|
148
157
|
private reloadPackageFromDisk;
|
|
149
158
|
/**
|
|
150
159
|
* Boot the server and listen. Rejects — and makes the process exit non-zero —
|
|
@@ -257,7 +266,8 @@ export declare class LinkedServer extends Shape {
|
|
|
257
266
|
* don't accumulate), drops them from the registry, and re-runs
|
|
258
267
|
* indexPackageBackendProviders to pick up the freshly-imported module.
|
|
259
268
|
* Reloads run one at a time: each re-registers routes on the shared express
|
|
260
|
-
* stack.
|
|
269
|
+
* stack. One that takes longer than `LINKED_SOURCE_RELOAD_TIMEOUT_MS` (default
|
|
270
|
+
* 60 s) rejects and releases the queue.
|
|
261
271
|
*
|
|
262
272
|
* Dispose calls have a 5 s soft timeout. A hanging dispose logs a
|
|
263
273
|
* warning and is abandoned so HMR doesn't stall the whole dev loop.
|
|
@@ -37,7 +37,7 @@ import { nativeImport } from '../utils/nativeImport.js';
|
|
|
37
37
|
import { createJsonBodyParser } from '../utils/jsonBodyParser.js';
|
|
38
38
|
import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
|
|
39
39
|
import { autoLoadOntologyData } from '@_linked/core/utils/Package';
|
|
40
|
-
import { getShapeClass, getSuperShapesClasses, } from '@_linked/core/utils/ShapeClass';
|
|
40
|
+
import { getAllShapeClasses, getShapeClass, getSuperShapesClasses, } from '@_linked/core/utils/ShapeClass';
|
|
41
41
|
import path from 'path';
|
|
42
42
|
import { pathToFileURL } from 'node:url';
|
|
43
43
|
import { isFatalError } from '../utils/fatalError.js';
|
|
@@ -139,7 +139,8 @@ import { resolveBootstrapEntry, resolveViteMainEntry, resolveViteServingMode, }
|
|
|
139
139
|
import { resolveRouteAssets } from '../utils/routeAssets.js';
|
|
140
140
|
import { resolveRouteComponent, ssrCssInlineUrls, ssrCssScope, } from '../utils/ssrCss.js';
|
|
141
141
|
import { materializeShapes } from '../utils/syncShapes.js';
|
|
142
|
-
import { AdminRequestError, registerAdminRoutes, } from '../utils/adminChannel.js';
|
|
142
|
+
import { AdminRequestError, assertReloadablePackages, registerAdminRoutes, } from '../utils/adminChannel.js';
|
|
143
|
+
import { envMs, withTimeout } from '../utils/withTimeout.js';
|
|
143
144
|
import { invalidateSourceUnder } from '../utils/reloadSource.js';
|
|
144
145
|
import { installSpaFallback, repinSpaFallback, } from '../utils/spaFallback.js';
|
|
145
146
|
import { LincdAPI } from './LincdAPI.js';
|
|
@@ -415,22 +416,53 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
415
416
|
* changed the app's shape code, so the app — the only process that loads that
|
|
416
417
|
* code — writes its own shapes and their hashes.
|
|
417
418
|
*
|
|
418
|
-
* `packages` names the packages whose source changed
|
|
419
|
-
*
|
|
420
|
-
* (
|
|
421
|
-
*
|
|
422
|
-
*
|
|
419
|
+
* `packages` names the packages whose source changed: the app itself or its
|
|
420
|
+
* direct dependencies, never `@_linked/*` ({@link assertReloadablePackages}). In
|
|
421
|
+
* dev (Vite) their modules are invalidated and their backends reloaded first
|
|
422
|
+
* ({@link onSourceChange}), so a shape added since the boot is registered before
|
|
423
|
+
* the sync; without Vite (a built app) nothing is reloaded. The answer names the
|
|
424
|
+
* packages reloaded and the node shapes registered after it, so the host can check
|
|
425
|
+
* that the shape it wrote is there. Runs as the system, never as the caller of a
|
|
426
|
+
* request, one at a time; a run that takes longer than
|
|
427
|
+
* `LINKED_SHAPE_SYNC_TIMEOUT_MS` (default 120 s) fails and releases the queue.
|
|
423
428
|
*/
|
|
424
429
|
async resyncShapes({ packages = [] } = {}) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
+
assertReloadablePackages(packages, this.reloadablePackages());
|
|
431
|
+
const timeoutMs = envMs('LINKED_SHAPE_SYNC_TIMEOUT_MS', 120000);
|
|
432
|
+
const run = this.shapeSyncQueue.then(() => withTimeout(runAsSystem(async () => {
|
|
433
|
+
const reloaded = [];
|
|
434
|
+
for (const pkg of packages) {
|
|
435
|
+
if (await this.reloadPackageFromDisk(pkg))
|
|
436
|
+
reloaded.push(pkg);
|
|
437
|
+
}
|
|
438
|
+
const result = await this.syncRegisteredShapes();
|
|
439
|
+
return { ...result, reloaded, shapes: [...getAllShapeClasses().keys()].sort() };
|
|
440
|
+
}, 'shape sync requested by the host'), timeoutMs, `the shape sync did not finish within ${timeoutMs} ms`));
|
|
430
441
|
this.shapeSyncQueue = run.catch(() => undefined);
|
|
431
442
|
return run;
|
|
432
443
|
}
|
|
433
|
-
/**
|
|
444
|
+
/** The packages a host may ask to reload: the app itself and its direct dependencies. */
|
|
445
|
+
reloadablePackages() {
|
|
446
|
+
var _a, _b;
|
|
447
|
+
const names = new Set();
|
|
448
|
+
if ((_a = this.package) === null || _a === void 0 ? void 0 : _a.name)
|
|
449
|
+
names.add(this.package.name);
|
|
450
|
+
try {
|
|
451
|
+
const json = JSON.parse(fsNative.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8'));
|
|
452
|
+
if (json.name)
|
|
453
|
+
names.add(json.name);
|
|
454
|
+
for (const field of ['dependencies', 'devDependencies']) {
|
|
455
|
+
for (const name of Object.keys((_b = json[field]) !== null && _b !== void 0 ? _b : {}))
|
|
456
|
+
names.add(name);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
catch (_c) { }
|
|
460
|
+
return names;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Re-read one package's source from disk (dev only); see {@link resyncShapes}.
|
|
464
|
+
* Resolves whether it reloaded anything.
|
|
465
|
+
*/
|
|
434
466
|
async reloadPackageFromDisk(pkg) {
|
|
435
467
|
var _a, _b, _c;
|
|
436
468
|
const root = pkg === ((_a = this.package) === null || _a === void 0 ? void 0 : _a.name) ? process.cwd() : this.packageDirFor(pkg);
|
|
@@ -438,7 +470,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
438
470
|
throw new AdminRequestError(`unknown package: ${pkg}`);
|
|
439
471
|
const graph = (_c = (_b = this.config.server) === null || _b === void 0 ? void 0 : _b.vite) === null || _c === void 0 ? void 0 : _c.moduleGraph;
|
|
440
472
|
if (!graph)
|
|
441
|
-
return; // a built app: there is no source to reload
|
|
473
|
+
return false; // a built app: there is no source to reload
|
|
442
474
|
const roots = [root];
|
|
443
475
|
try {
|
|
444
476
|
roots.push(fsNative.realpathSync(root));
|
|
@@ -446,6 +478,7 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
446
478
|
catch (_d) { }
|
|
447
479
|
invalidateSourceUnder(graph, roots);
|
|
448
480
|
await this.onSourceChange(pkg);
|
|
481
|
+
return true;
|
|
449
482
|
}
|
|
450
483
|
// async serveData(req,res) {
|
|
451
484
|
// let nodeURI = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
|
|
@@ -1416,13 +1449,15 @@ let LinkedServer = class LinkedServer extends Shape {
|
|
|
1416
1449
|
* don't accumulate), drops them from the registry, and re-runs
|
|
1417
1450
|
* indexPackageBackendProviders to pick up the freshly-imported module.
|
|
1418
1451
|
* Reloads run one at a time: each re-registers routes on the shared express
|
|
1419
|
-
* stack.
|
|
1452
|
+
* stack. One that takes longer than `LINKED_SOURCE_RELOAD_TIMEOUT_MS` (default
|
|
1453
|
+
* 60 s) rejects and releases the queue.
|
|
1420
1454
|
*
|
|
1421
1455
|
* Dispose calls have a 5 s soft timeout. A hanging dispose logs a
|
|
1422
1456
|
* warning and is abandoned so HMR doesn't stall the whole dev loop.
|
|
1423
1457
|
*/
|
|
1424
1458
|
async onSourceChange(pkg) {
|
|
1425
|
-
const
|
|
1459
|
+
const timeoutMs = envMs('LINKED_SOURCE_RELOAD_TIMEOUT_MS', 60000);
|
|
1460
|
+
const run = this.sourceReloadQueue.then(() => withTimeout(this.reloadPackageSource(pkg), timeoutMs, `the reload of ${pkg} did not finish within ${timeoutMs} ms`));
|
|
1426
1461
|
this.sourceReloadQueue = run.catch(() => undefined);
|
|
1427
1462
|
return run;
|
|
1428
1463
|
}
|