@jsenv/core 41.4.1 → 41.4.2
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/dist/build/build.js +173 -4
- package/dist/build/jsenv_core_packages.js +91 -1
- package/dist/html/client_monitor_page.html +4 -1
- package/dist/html/clients_page.html +13 -5
- package/dist/js/client_reporter.js +185 -109
- package/dist/js/page_switcher.js +731 -0
- package/dist/start_dev_server/jsenv_core_packages.js +695 -4
- package/dist/start_dev_server/start_dev_server.js +357 -78
- package/package.json +2 -2
- package/src/dev/dev_server_plugins/dev_server_plugin_serve_source_files.js +55 -5
- package/src/dev/start_dev_server.js +13 -3
- package/src/kitchen/kitchen.js +12 -3
- package/src/kitchen/url_graph/url_graph.js +11 -0
- package/src/plugins/client_monitoring/client/client_monitor_page.html +4 -1
- package/src/plugins/client_monitoring/client/client_reporter.js +185 -109
- package/src/plugins/client_monitoring/client/clients_page.html +13 -5
- package/src/plugins/client_monitoring/jsenv_plugin_client_monitoring.js +76 -68
- package/src/plugins/page_switcher/client/page_switcher.js +731 -0
- package/src/plugins/page_switcher/jsenv_plugin_page_switcher.js +42 -0
- package/src/plugins/protocol_file/html_pages.js +122 -0
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +24 -0
- package/src/plugins/resolution_node_esm/node_esm_resolver.js +5 -0
package/dist/build/build.js
CHANGED
|
@@ -4,7 +4,7 @@ import { jsenvPluginMinification } from "@jsenv/plugin-minification";
|
|
|
4
4
|
import { jsenvPluginTranspilation, jsenvPluginJsModuleFallback } from "@jsenv/plugin-transpilation";
|
|
5
5
|
import { memoryUsage } from "node:process";
|
|
6
6
|
import { readFileSync, existsSync, readdirSync, lstatSync, realpathSync } from "node:fs";
|
|
7
|
-
import { lookupPackageDirectory, registerDirectoryLifecycle, urlToRelativeUrl, createDetailedMessage, stringifyUrlSite, generateContentFrame, validateResponseIntegrity, urlIsOrIsInsideOf, ensureWindowsDriveLetter, setUrlFilename, moveUrl, getCallerPosition, urlToBasename, urlToExtension, asSpecifierWithoutSearch, asUrlWithoutSearch, injectQueryParamsIntoSpecifier, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, urlToFileSystemPath, writeFileSync, createLogger, URL_META, applyNodeEsmResolution, normalizeUrl, ANSI, RUNTIME_COMPAT, CONTENT_TYPE, readPackageAtOrNull, urlToFilename, DATA_URL, errorToHTML, normalizeImportMap, composeTwoImportMaps, resolveImport, JS_QUOTES, readCustomConditionsFromProcessArgs, readEntryStatSync, ensurePathnameTrailingSlash, compareFileUrls, applyFileSystemMagicResolution, getExtensionsToTry, setUrlExtension, isSpecifierForNodeBuiltin, injectQueryParams, renderDetails, humanizeDuration, humanizeFileSize, renderTable, renderBigSection, distributePercentages, humanizeMemory, comparePathnames, UNICODE, escapeRegexpSpecialChars, injectQueryParamIntoSpecifierWithoutEncoding, renderUrlOrRelativeUrlFilename, assertAndNormalizeDirectoryUrl, Abort, raceProcessTeardownEvents, startMonitoringCpuUsage, startMonitoringMemoryUsage, inferRuntimeCompatFromClosestPackage, browserDefaultRuntimeCompat, nodeDefaultRuntimeCompat, clearDirectorySync, createTaskLog, createLookupPackageDirectory, ensureEmptyDirectory, updateJsonFileSync, createDynamicLog } from "./jsenv_core_packages.js";
|
|
7
|
+
import { lookupPackageDirectory, registerDirectoryLifecycle, urlToRelativeUrl, createDetailedMessage, stringifyUrlSite, generateContentFrame, validateResponseIntegrity, urlIsOrIsInsideOf, ensureWindowsDriveLetter, setUrlFilename, moveUrl, getCallerPosition, urlToBasename, urlToExtension, asSpecifierWithoutSearch, asUrlWithoutSearch, injectQueryParamsIntoSpecifier, bufferToEtag, isFileSystemPath, urlToPathname, setUrlBasename, urlToFileSystemPath, writeFileSync, createLogger, URL_META, applyNodeEsmResolution, normalizeUrl, ANSI, RUNTIME_COMPAT, CONTENT_TYPE, readPackageAtOrNull, urlToFilename, DATA_URL, errorToHTML, normalizeImportMap, composeTwoImportMaps, resolveImport, JS_QUOTES, readCustomConditionsFromProcessArgs, collectFiles, readEntryStatSync, ensurePathnameTrailingSlash, compareFileUrls, applyFileSystemMagicResolution, getExtensionsToTry, setUrlExtension, isSpecifierForNodeBuiltin, injectQueryParams, renderDetails, humanizeDuration, humanizeFileSize, renderTable, renderBigSection, distributePercentages, humanizeMemory, comparePathnames, UNICODE, escapeRegexpSpecialChars, injectQueryParamIntoSpecifierWithoutEncoding, renderUrlOrRelativeUrlFilename, assertAndNormalizeDirectoryUrl, Abort, raceProcessTeardownEvents, startMonitoringCpuUsage, startMonitoringMemoryUsage, inferRuntimeCompatFromClosestPackage, browserDefaultRuntimeCompat, nodeDefaultRuntimeCompat, clearDirectorySync, createTaskLog, createLookupPackageDirectory, ensureEmptyDirectory, updateJsonFileSync, createDynamicLog } from "./jsenv_core_packages.js";
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
import { generateSourcemapFileUrl, createMagicSource, composeTwoSourcemaps, generateSourcemapDataUrl, SOURCEMAP } from "@jsenv/sourcemap";
|
|
10
10
|
import { createPluginsController } from "@jsenv/server/src/plugins_controller.js";
|
|
@@ -2286,6 +2286,17 @@ const createUrlInfo = (url, context) => {
|
|
|
2286
2286
|
if (referenceFromOther.gotInlined()) {
|
|
2287
2287
|
const urlInfoReferencingThisOne = referenceFromOther.ownerUrlInfo;
|
|
2288
2288
|
considerModified(urlInfoReferencingThisOne);
|
|
2289
|
+
continue;
|
|
2290
|
+
}
|
|
2291
|
+
// A reference with a versioning effect writes this url's VERSION into
|
|
2292
|
+
// its owner's cooked content (the ?v= param, read from package.json):
|
|
2293
|
+
// this url modified means that content now embeds a stale version, so
|
|
2294
|
+
// the owner is as modified as an owner of inlined content. Without
|
|
2295
|
+
// this, the owner's cooked content survives the modification and a
|
|
2296
|
+
// validity check that "heals" this url (see isValid re-reading files
|
|
2297
|
+
// from disk) leaves the graph claiming the owner is fresh.
|
|
2298
|
+
if (referenceFromOther.hasVersioningEffect) {
|
|
2299
|
+
considerModified(referenceFromOther.ownerUrlInfo);
|
|
2289
2300
|
}
|
|
2290
2301
|
}
|
|
2291
2302
|
for (const searchParamVariant of urlInfo.searchParamVariantSet) {
|
|
@@ -3717,14 +3728,23 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
3717
3728
|
if (!urlInfo.url.startsWith("ignore:")) {
|
|
3718
3729
|
try {
|
|
3719
3730
|
await urlInfo.dependencies.startCollecting(async () => {
|
|
3731
|
+
// Each phase timed into urlInfo.timing: the dev server turns it into
|
|
3732
|
+
// a server-timing response header, so devtools show where the time
|
|
3733
|
+
// to cook a file goes (fetch vs transform vs finalize).
|
|
3734
|
+
const timePhase = async (name, phase) => {
|
|
3735
|
+
const start = performance.now();
|
|
3736
|
+
await phase();
|
|
3737
|
+
urlInfo.timing[name] = performance.now() - start;
|
|
3738
|
+
};
|
|
3739
|
+
|
|
3720
3740
|
// "fetchUrlContent" hook
|
|
3721
|
-
await urlInfo.fetchContent();
|
|
3741
|
+
await timePhase("fetch", () => urlInfo.fetchContent());
|
|
3722
3742
|
|
|
3723
3743
|
// "transform" hook
|
|
3724
|
-
await urlInfo.transformContent();
|
|
3744
|
+
await timePhase("transform", () => urlInfo.transformContent());
|
|
3725
3745
|
|
|
3726
3746
|
// "finalize" hook
|
|
3727
|
-
await urlInfo.finalizeContent();
|
|
3747
|
+
await timePhase("finalize", () => urlInfo.finalizeContent());
|
|
3728
3748
|
});
|
|
3729
3749
|
} catch (e) {
|
|
3730
3750
|
urlInfo.error = e;
|
|
@@ -6302,6 +6322,11 @@ const addRelationshipWithPackageJson = ({
|
|
|
6302
6322
|
String(packageJsonContentAsBuffer),
|
|
6303
6323
|
);
|
|
6304
6324
|
}
|
|
6325
|
+
// Checked on disk at every validation rather than trusted to the watcher:
|
|
6326
|
+
// what this file decides (the package version, hence the ?v= importers
|
|
6327
|
+
// embed) ends up in the browser's immutable cache, so a request racing the
|
|
6328
|
+
// watcher must never be answered from a stale package.json.
|
|
6329
|
+
packageJsonReference.urlInfo.revalidateOnFileSystem = true;
|
|
6305
6330
|
};
|
|
6306
6331
|
|
|
6307
6332
|
const createResolverWithFallbackOnError = (mainResolver, fallbackResolver) => {
|
|
@@ -6527,6 +6552,127 @@ const jsenvPluginVersionSearchParam = () => {
|
|
|
6527
6552
|
};
|
|
6528
6553
|
};
|
|
6529
6554
|
|
|
6555
|
+
/*
|
|
6556
|
+
* The .html files under the served directory, as urls one can navigate to.
|
|
6557
|
+
*
|
|
6558
|
+
* Lives here, next to the plugin that owns the filesystem, because more than
|
|
6559
|
+
* one feature wants the same list: the client dashboard sends a browser to one
|
|
6560
|
+
* of them, the page switcher (cmd+K) opens one in the current tab. Whoever asks
|
|
6561
|
+
* gets the same answer.
|
|
6562
|
+
*
|
|
6563
|
+
* Each page comes with what kind of page it is, read from where it sits and
|
|
6564
|
+
* what it is called — the two conventions this repo already follows:
|
|
6565
|
+
* - "experiment": something tried out, under a lab/ directory or named
|
|
6566
|
+
* *_experiment.html;
|
|
6567
|
+
* - "demo": something shown, under a demos/ directory or named *_demo.html;
|
|
6568
|
+
* - "page": everything else.
|
|
6569
|
+
*
|
|
6570
|
+
* Scanned on demand rather than watched: the list is asked for when a human
|
|
6571
|
+
* opens a picker, which is rare and never on a hot path, and a short cache is
|
|
6572
|
+
* enough to keep a burst of asks from walking the tree twice.
|
|
6573
|
+
*/
|
|
6574
|
+
|
|
6575
|
+
|
|
6576
|
+
const SCAN_TTL_MS = 5000;
|
|
6577
|
+
|
|
6578
|
+
// What to walk and what to skip, in the shape @jsenv/url-meta reads: the last
|
|
6579
|
+
// matching pattern wins, so the exclusions come after the "every .html" rule.
|
|
6580
|
+
// Dependencies, build output and jsenv's own caches hold no page worth going to.
|
|
6581
|
+
const HTML_PAGE_ASSOCIATIONS = {
|
|
6582
|
+
page: {
|
|
6583
|
+
"**/*.html": true,
|
|
6584
|
+
"**/.*/": false,
|
|
6585
|
+
"**/node_modules/": false,
|
|
6586
|
+
"**/dist/": false,
|
|
6587
|
+
"**/build/": false,
|
|
6588
|
+
"**/coverage/": false,
|
|
6589
|
+
"**/git_ignored/": false,
|
|
6590
|
+
"**/old/": false,
|
|
6591
|
+
},
|
|
6592
|
+
experiment: {
|
|
6593
|
+
"**/lab/**/*.html": true,
|
|
6594
|
+
"**/*_experiment.html": true,
|
|
6595
|
+
},
|
|
6596
|
+
demo: {
|
|
6597
|
+
"**/demos/**/*.html": true,
|
|
6598
|
+
"**/*_demo.html": true,
|
|
6599
|
+
},
|
|
6600
|
+
};
|
|
6601
|
+
|
|
6602
|
+
// An experiment inside a demos/ directory is an experiment: the more specific
|
|
6603
|
+
// of the two wins, and "shown" is the weaker claim.
|
|
6604
|
+
const readKind = (meta) => {
|
|
6605
|
+
if (meta.experiment) {
|
|
6606
|
+
return "experiment";
|
|
6607
|
+
}
|
|
6608
|
+
if (meta.demo) {
|
|
6609
|
+
return "demo";
|
|
6610
|
+
}
|
|
6611
|
+
return "page";
|
|
6612
|
+
};
|
|
6613
|
+
|
|
6614
|
+
// Which package a page belongs to: the nearest directory above it holding a
|
|
6615
|
+
// package.json, said as a url so whoever draws a tree can mark that very node.
|
|
6616
|
+
// Not the root itself — everything is under it, and "the whole repo" is not a
|
|
6617
|
+
// package one distinguishes from another. Memoized per directory: a scan asks
|
|
6618
|
+
// the same question once per file and there are hundreds of them.
|
|
6619
|
+
const createPackageDirectoryFinder = (rootDirectoryUrl) => {
|
|
6620
|
+
const cache = new Map();
|
|
6621
|
+
const find = (directoryUrl) => {
|
|
6622
|
+
if (cache.has(directoryUrl)) {
|
|
6623
|
+
return cache.get(directoryUrl);
|
|
6624
|
+
}
|
|
6625
|
+
let result = null;
|
|
6626
|
+
if (directoryUrl.length > String(rootDirectoryUrl).length) {
|
|
6627
|
+
result = existsSync(new URL("./package.json", directoryUrl))
|
|
6628
|
+
? directoryUrl
|
|
6629
|
+
: find(new URL("../", directoryUrl).href);
|
|
6630
|
+
}
|
|
6631
|
+
cache.set(directoryUrl, result);
|
|
6632
|
+
return result;
|
|
6633
|
+
};
|
|
6634
|
+
return find;
|
|
6635
|
+
};
|
|
6636
|
+
|
|
6637
|
+
const createHtmlPageLister = ({ rootDirectoryUrl }) => {
|
|
6638
|
+
let cache = null;
|
|
6639
|
+
let cachedAt = 0;
|
|
6640
|
+
|
|
6641
|
+
return async () => {
|
|
6642
|
+
if (!rootDirectoryUrl) {
|
|
6643
|
+
return [];
|
|
6644
|
+
}
|
|
6645
|
+
const now = Date.now();
|
|
6646
|
+
if (cache && now - cachedAt < SCAN_TTL_MS) {
|
|
6647
|
+
return cache;
|
|
6648
|
+
}
|
|
6649
|
+
const fileResultArray = await collectFiles({
|
|
6650
|
+
directoryUrl: rootDirectoryUrl,
|
|
6651
|
+
associations: HTML_PAGE_ASSOCIATIONS,
|
|
6652
|
+
predicate: (meta) => Boolean(meta.page),
|
|
6653
|
+
});
|
|
6654
|
+
const findPackageDirectory = createPackageDirectoryFinder(rootDirectoryUrl);
|
|
6655
|
+
const pages = fileResultArray.map(({ relativeUrl, meta }) => {
|
|
6656
|
+
const fileUrl = new URL(relativeUrl, rootDirectoryUrl).href;
|
|
6657
|
+
const packageDirectoryUrl = findPackageDirectory(
|
|
6658
|
+
new URL("./", fileUrl).href,
|
|
6659
|
+
);
|
|
6660
|
+
return {
|
|
6661
|
+
url: `/${relativeUrl}`,
|
|
6662
|
+
kind: readKind(meta),
|
|
6663
|
+
// Relative to the root and without its trailing slash, which is how a
|
|
6664
|
+
// tree names its own nodes.
|
|
6665
|
+
packageUrl: packageDirectoryUrl
|
|
6666
|
+
? `/${packageDirectoryUrl.slice(String(rootDirectoryUrl).length).replace(/\/$/, "")}`
|
|
6667
|
+
: null,
|
|
6668
|
+
};
|
|
6669
|
+
});
|
|
6670
|
+
cache = pages;
|
|
6671
|
+
cachedAt = now;
|
|
6672
|
+
return pages;
|
|
6673
|
+
};
|
|
6674
|
+
};
|
|
6675
|
+
|
|
6530
6676
|
/*
|
|
6531
6677
|
* NICE TO HAVE:
|
|
6532
6678
|
*
|
|
@@ -7171,6 +7317,8 @@ const jsenvPluginProtocolFile = ({
|
|
|
7171
7317
|
packageDirectory,
|
|
7172
7318
|
sourceFilesConfig,
|
|
7173
7319
|
}) => {
|
|
7320
|
+
const listHtmlPages = createHtmlPageLister({ rootDirectoryUrl });
|
|
7321
|
+
|
|
7174
7322
|
return [
|
|
7175
7323
|
jsenvPluginFsRedirection({
|
|
7176
7324
|
spa,
|
|
@@ -7226,6 +7374,27 @@ const jsenvPluginProtocolFile = ({
|
|
|
7226
7374
|
);
|
|
7227
7375
|
},
|
|
7228
7376
|
},
|
|
7377
|
+
{
|
|
7378
|
+
name: "jsenv:html_pages",
|
|
7379
|
+
appliesDuring: "dev",
|
|
7380
|
+
serverRoutes: [
|
|
7381
|
+
{
|
|
7382
|
+
endpoint: "GET /.internal/pages.json",
|
|
7383
|
+
description:
|
|
7384
|
+
"The .html files served under the source directory, as urls to navigate to.",
|
|
7385
|
+
availableMediaTypes: ["application/json"],
|
|
7386
|
+
declarationSource: import.meta.url,
|
|
7387
|
+
fetch: async () => ({
|
|
7388
|
+
status: 200,
|
|
7389
|
+
headers: {
|
|
7390
|
+
"content-type": "application/json",
|
|
7391
|
+
"cache-control": "no-store",
|
|
7392
|
+
},
|
|
7393
|
+
body: JSON.stringify(await listHtmlPages()),
|
|
7394
|
+
}),
|
|
7395
|
+
},
|
|
7396
|
+
],
|
|
7397
|
+
},
|
|
7229
7398
|
...(directoryListing
|
|
7230
7399
|
? [
|
|
7231
7400
|
jsenvPluginDirectoryListing({
|
|
@@ -3625,6 +3625,96 @@ const readStat = (
|
|
|
3625
3625
|
});
|
|
3626
3626
|
};
|
|
3627
3627
|
|
|
3628
|
+
const collectFiles = async ({
|
|
3629
|
+
signal = new AbortController().signal,
|
|
3630
|
+
directoryUrl,
|
|
3631
|
+
associations,
|
|
3632
|
+
predicate,
|
|
3633
|
+
}) => {
|
|
3634
|
+
const rootDirectoryUrl = assertAndNormalizeDirectoryUrl(directoryUrl);
|
|
3635
|
+
if (typeof predicate !== "function") {
|
|
3636
|
+
throw new TypeError(`predicate must be a function, got ${predicate}`);
|
|
3637
|
+
}
|
|
3638
|
+
associations = URL_META.resolveAssociations(associations, rootDirectoryUrl);
|
|
3639
|
+
|
|
3640
|
+
const collectOperation = Abort.startOperation();
|
|
3641
|
+
collectOperation.addAbortSignal(signal);
|
|
3642
|
+
|
|
3643
|
+
const matchingFileResultArray = [];
|
|
3644
|
+
const visitDirectory = async (directoryUrl) => {
|
|
3645
|
+
collectOperation.throwIfAborted();
|
|
3646
|
+
const directoryItems = await readDirectory(directoryUrl);
|
|
3647
|
+
|
|
3648
|
+
await Promise.all(
|
|
3649
|
+
directoryItems.map(async (directoryItem) => {
|
|
3650
|
+
const directoryChildNodeUrl = `${directoryUrl}${directoryItem}`;
|
|
3651
|
+
collectOperation.throwIfAborted();
|
|
3652
|
+
const directoryChildNodeStats = await readEntryStat(
|
|
3653
|
+
directoryChildNodeUrl,
|
|
3654
|
+
{
|
|
3655
|
+
// we ignore symlink because recursively traversed
|
|
3656
|
+
// so symlinked file will be discovered.
|
|
3657
|
+
// Moreover if they lead outside of directoryPath it can become a problem
|
|
3658
|
+
// like infinite recursion of whatever.
|
|
3659
|
+
// that we could handle using an object of pathname already seen but it will be useless
|
|
3660
|
+
// because directoryPath is recursively traversed
|
|
3661
|
+
followLink: false,
|
|
3662
|
+
},
|
|
3663
|
+
);
|
|
3664
|
+
|
|
3665
|
+
if (directoryChildNodeStats.isDirectory()) {
|
|
3666
|
+
const subDirectoryUrl = `${directoryChildNodeUrl}/`;
|
|
3667
|
+
if (
|
|
3668
|
+
!URL_META.urlChildMayMatch({
|
|
3669
|
+
url: subDirectoryUrl,
|
|
3670
|
+
associations,
|
|
3671
|
+
predicate,
|
|
3672
|
+
})
|
|
3673
|
+
) {
|
|
3674
|
+
return;
|
|
3675
|
+
}
|
|
3676
|
+
await visitDirectory(subDirectoryUrl);
|
|
3677
|
+
return;
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3680
|
+
if (directoryChildNodeStats.isFile()) {
|
|
3681
|
+
const meta = URL_META.applyAssociations({
|
|
3682
|
+
url: directoryChildNodeUrl,
|
|
3683
|
+
associations,
|
|
3684
|
+
});
|
|
3685
|
+
if (!predicate(meta)) return;
|
|
3686
|
+
const relativeUrl = urlToRelativeUrl(
|
|
3687
|
+
directoryChildNodeUrl,
|
|
3688
|
+
rootDirectoryUrl,
|
|
3689
|
+
);
|
|
3690
|
+
matchingFileResultArray.push({
|
|
3691
|
+
url: new URL(relativeUrl, rootDirectoryUrl).href,
|
|
3692
|
+
relativeUrl: decodeURIComponent(relativeUrl),
|
|
3693
|
+
meta,
|
|
3694
|
+
fileStats: directoryChildNodeStats,
|
|
3695
|
+
});
|
|
3696
|
+
return;
|
|
3697
|
+
}
|
|
3698
|
+
}),
|
|
3699
|
+
);
|
|
3700
|
+
};
|
|
3701
|
+
|
|
3702
|
+
try {
|
|
3703
|
+
await visitDirectory(rootDirectoryUrl);
|
|
3704
|
+
|
|
3705
|
+
// When we operate on thoose files later it feels more natural
|
|
3706
|
+
// to perform operation in the same order they appear in the filesystem.
|
|
3707
|
+
// It also allow to get a predictable return value.
|
|
3708
|
+
// For that reason we sort matchingFileResultArray
|
|
3709
|
+
matchingFileResultArray.sort((leftFile, rightFile) => {
|
|
3710
|
+
return comparePathnames(leftFile.relativeUrl, rightFile.relativeUrl);
|
|
3711
|
+
});
|
|
3712
|
+
return matchingFileResultArray;
|
|
3713
|
+
} finally {
|
|
3714
|
+
await collectOperation.end();
|
|
3715
|
+
}
|
|
3716
|
+
};
|
|
3717
|
+
|
|
3628
3718
|
const writeEntryPermissionsSync = (source, permissions) => {
|
|
3629
3719
|
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
3630
3720
|
|
|
@@ -11141,4 +11231,4 @@ const escapeRegexpSpecialChars = (string) => {
|
|
|
11141
11231
|
});
|
|
11142
11232
|
};
|
|
11143
11233
|
|
|
11144
|
-
export { ANSI, Abort, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, UNICODE, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, browserDefaultRuntimeCompat, bufferToEtag, clearDirectorySync, compareFileUrls, comparePathnames, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createDynamicLog, createLogger, createLookupPackageDirectory, createTaskLog, distributePercentages, ensureEmptyDirectory, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, escapeRegexpSpecialChars, generateContentFrame, getCallerPosition, getExtensionsToTry, humanizeDuration, humanizeFileSize, humanizeMemory, inferRuntimeCompatFromClosestPackage, injectQueryParamIntoSpecifierWithoutEncoding, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, nodeDefaultRuntimeCompat, normalizeImportMap, normalizeUrl, raceProcessTeardownEvents, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, renderBigSection, renderDetails, renderTable, renderUrlOrRelativeUrlFilename, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, startMonitoringCpuUsage, startMonitoringMemoryUsage, stringifyUrlSite, updateJsonFileSync, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };
|
|
11234
|
+
export { ANSI, Abort, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, UNICODE, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, browserDefaultRuntimeCompat, bufferToEtag, clearDirectorySync, collectFiles, compareFileUrls, comparePathnames, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createDynamicLog, createLogger, createLookupPackageDirectory, createTaskLog, distributePercentages, ensureEmptyDirectory, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, escapeRegexpSpecialChars, generateContentFrame, getCallerPosition, getExtensionsToTry, humanizeDuration, humanizeFileSize, humanizeMemory, inferRuntimeCompatFromClosestPackage, injectQueryParamIntoSpecifierWithoutEncoding, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, nodeDefaultRuntimeCompat, normalizeImportMap, normalizeUrl, raceProcessTeardownEvents, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, renderBigSection, renderDetails, renderTable, renderUrlOrRelativeUrlFilename, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, startMonitoringCpuUsage, startMonitoringMemoryUsage, stringifyUrlSite, updateJsonFileSync, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };
|
|
@@ -303,8 +303,11 @@ button:hover {
|
|
|
303
303
|
const v = version && version !== "unknown" ? ` ${version}` : "";
|
|
304
304
|
return `${label}${v}`;
|
|
305
305
|
};
|
|
306
|
+
// The ip is part of who a client is: it tells the main client
|
|
307
|
+
// (localhost — this machine) from a device on the network.
|
|
308
|
+
const ipLabel = client.local ? "localhost" : client.ip;
|
|
306
309
|
return (
|
|
307
|
-
[part(client.runtime, RUNTIME_LABELS), part(client.os)]
|
|
310
|
+
[part(client.runtime, RUNTIME_LABELS), part(client.os), ipLabel]
|
|
308
311
|
.filter(Boolean)
|
|
309
312
|
.join(" · ") || clientId.slice(0, 8)
|
|
310
313
|
);
|
|
@@ -275,12 +275,12 @@ ul.list li:last-child {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
const headHtml = `<tr>
|
|
278
|
-
<th>First seen</th><th>OS</th><th>Browser</th><th>Active tab</th>
|
|
278
|
+
<th>First seen</th><th>OS</th><th>Browser</th><th>IP</th><th>Active tab</th>
|
|
279
279
|
<th>Last activity</th><th>Logs</th><th></th>
|
|
280
280
|
</tr>`;
|
|
281
281
|
document.getElementById("head").innerHTML = headHtml;
|
|
282
282
|
document.getElementById("head2").innerHTML = headHtml;
|
|
283
|
-
const COLSPAN =
|
|
283
|
+
const COLSPAN = 8;
|
|
284
284
|
|
|
285
285
|
const ago = (ts) => {
|
|
286
286
|
const s = Math.round((Date.now() - ts) / 1000);
|
|
@@ -420,11 +420,21 @@ ul.list li:last-child {
|
|
|
420
420
|
<a class="monitor" href="/.internal/client?id=${encodeURIComponent(d.id)}">Monitor →</a>
|
|
421
421
|
</div>`;
|
|
422
422
|
};
|
|
423
|
+
// The ip says how the client reaches the server, which is what tells the
|
|
424
|
+
// main client (this machine, via localhost) from a device on the network.
|
|
425
|
+
const ipCell = (d) => {
|
|
426
|
+
if (!d.ip) {
|
|
427
|
+
return '<span class="muted">—</span>';
|
|
428
|
+
}
|
|
429
|
+
const label = d.local ? "localhost" : d.ip;
|
|
430
|
+
return `<span title="${escapeHtml(d.ip)}">${escapeHtml(label)}</span>`;
|
|
431
|
+
};
|
|
423
432
|
const rowHtml = (d) => {
|
|
424
433
|
return `<tr>
|
|
425
434
|
<td>${firstSeenCell(d)}</td>
|
|
426
435
|
<td>${named(d.os)}</td>
|
|
427
436
|
<td>${named(d.runtime)}</td>
|
|
437
|
+
<td>${ipCell(d)}</td>
|
|
428
438
|
<td>${tabCell(d)}</td>
|
|
429
439
|
<td>${activityCell(d)}</td>
|
|
430
440
|
<td>${d.logCount}</td>
|
|
@@ -537,9 +547,7 @@ ul.list li:last-child {
|
|
|
537
547
|
return navigablePages;
|
|
538
548
|
}
|
|
539
549
|
try {
|
|
540
|
-
navigablePages = await (
|
|
541
|
-
await fetch("/.internal/clients/pages.json")
|
|
542
|
-
).json();
|
|
550
|
+
navigablePages = await (await fetch("/.internal/pages.json")).json();
|
|
543
551
|
} catch {
|
|
544
552
|
navigablePages = [];
|
|
545
553
|
}
|