arcane-os 0.9.0 → 0.10.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 +24 -0
- package/README.md +14 -8
- package/browser-runtime/pwa.mjs +215 -0
- package/docs/reference/README.md +3 -2
- package/docs/reference/asset-versioning.md +13 -0
- package/docs/reference/inventory/package-api.json +29 -1
- package/docs/reference/pwa.md +183 -0
- package/docs/reference/runtime-modules.md +7 -1
- package/docs/reference/sdk-api.md +45 -1
- package/package.json +3 -2
- package/runtime/arcane/modules/Errors.js +1 -1
- package/runtime/arcane/modules/HTMLImport.js +13 -3
- package/schemas/arcane-app.schema.json +28 -0
- package/schemas/arcane-package.schema.json +30 -0
- package/src/app-descriptor.mjs +10 -2
- package/src/dev-server.mjs +252 -13
- package/src/import-map.mjs +186 -11
- package/src/packager/core.mjs +140 -17
- package/src/pwa-worker.mjs +227 -0
- package/src/pwa.mjs +311 -0
- package/src/targets/index.mjs +1 -0
- package/src/toolchain.mjs +1 -0
package/src/dev-server.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Is from 'strong-type';
|
|
2
2
|
import {constants as FS_CONSTANTS} from 'node:fs';
|
|
3
|
-
import {lstat,open,readFile,realpath} from 'node:fs/promises';
|
|
3
|
+
import {lstat,open,readFile,readdir,realpath} from 'node:fs/promises';
|
|
4
4
|
import http from 'node:http';
|
|
5
5
|
import https from 'node:https';
|
|
6
6
|
import os from 'node:os';
|
|
@@ -8,9 +8,10 @@ import path from 'node:path';
|
|
|
8
8
|
import {resolveWorkspace} from './workspace.mjs';
|
|
9
9
|
import {createEventQueue} from './event-queue.mjs';
|
|
10
10
|
import {
|
|
11
|
-
inspectImportMapHtml,MANAGED_IMPORT_MAP_ATTRIBUTE,
|
|
12
|
-
readWorkspaceAssetVersion,rewriteAssetReferences
|
|
11
|
+
applyPwaEntryReferences,inspectImportMapHtml,MANAGED_IMPORT_MAP_ATTRIBUTE,
|
|
12
|
+
readWorkspaceAssetVersion,rewriteAssetReferences,versionAssetUrl
|
|
13
13
|
} from './import-map.mjs';
|
|
14
|
+
import {createPwaArtifacts,selectPwaFiles} from './pwa.mjs';
|
|
14
15
|
|
|
15
16
|
const is = new Is(false);
|
|
16
17
|
|
|
@@ -29,6 +30,7 @@ const MIME_TYPES=new Map([
|
|
|
29
30
|
['.svg','image/svg+xml; charset=utf-8'],
|
|
30
31
|
['.txt','text/plain; charset=utf-8'],
|
|
31
32
|
['.wasm','application/wasm'],
|
|
33
|
+
['.webmanifest','application/manifest+json; charset=utf-8'],
|
|
32
34
|
['.webp','image/webp'],
|
|
33
35
|
['.woff','font/woff'],
|
|
34
36
|
['.woff2','font/woff2']
|
|
@@ -318,17 +320,25 @@ async function openSafeFile(root,segments){
|
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
322
|
|
|
321
|
-
async function sendFile(response,opened,{head=false,assetVersion,revalidate=false,onReference}={}){
|
|
323
|
+
async function sendFile(response,opened,{head=false,assetVersion,revalidate=false,onReference,pwaEntry=false}={}){
|
|
322
324
|
const extension=path.extname(opened.candidate).toLowerCase();
|
|
323
|
-
|
|
325
|
+
let content=assetVersion!==undefined&&/\.(?:m?js|html?|css|json)$/iu.test(extension)
|
|
324
326
|
?Buffer.from(rewriteAssetReferences(opened.content.toString('utf8'),{
|
|
325
327
|
filePath:opened.candidate,
|
|
326
328
|
version:assetVersion,
|
|
327
329
|
onReference
|
|
328
330
|
}),'utf8')
|
|
329
331
|
:opened.content;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
+
if (pwaEntry) {
|
|
333
|
+
content = Buffer.from(
|
|
334
|
+
applyPwaEntryReferences(
|
|
335
|
+
content.toString('utf8'),
|
|
336
|
+
{manifestUrl: '/arcane.webmanifest', bootstrapUrl: '/arcane-pwa.mjs'}
|
|
337
|
+
),
|
|
338
|
+
'utf8'
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
// Stable PWA URLs and entry documents revalidate while retaining HTTP caching.
|
|
332
342
|
response.writeHead(200,{
|
|
333
343
|
'content-type':MIME_TYPES.get(extension)||'application/octet-stream',
|
|
334
344
|
'content-length':content.byteLength,
|
|
@@ -402,6 +412,7 @@ async function sourceRoutes(workspaceRoot,appId,{
|
|
|
402
412
|
const appMapping={
|
|
403
413
|
prefix:['apps',resolved.appId],
|
|
404
414
|
root:resolved.appRoot,
|
|
415
|
+
include:resolved.app.manifest.include,
|
|
405
416
|
allow:relative=>sourcePathAllowed(relative,resolved.app.manifest)
|
|
406
417
|
};
|
|
407
418
|
if(sdkRuntimeSourceRoot!==undefined){
|
|
@@ -415,6 +426,7 @@ async function sourceRoutes(workspaceRoot,appId,{
|
|
|
415
426
|
workspaceRoot:resolved.workspaceRoot,
|
|
416
427
|
workspaceMode:resolved.config.workspaceMode,
|
|
417
428
|
appId:resolved.appId,
|
|
429
|
+
app:resolved.app.manifest,
|
|
418
430
|
startPath:`/apps/${resolved.appId}/${resolved.app.manifest.entry}`,
|
|
419
431
|
runtime:sdkSource.runtime,
|
|
420
432
|
mappings:[appMapping,...sdkSource.mappings]
|
|
@@ -425,12 +437,14 @@ async function sourceRoutes(workspaceRoot,appId,{
|
|
|
425
437
|
workspaceRoot:resolved.workspaceRoot,
|
|
426
438
|
workspaceMode:'integrated',
|
|
427
439
|
appId:resolved.appId,
|
|
440
|
+
app:resolved.app.manifest,
|
|
428
441
|
startPath:`/apps/${resolved.appId}/${resolved.app.manifest.entry}`,
|
|
429
442
|
mappings:[
|
|
430
443
|
appMapping,
|
|
431
444
|
...resolved.config.sharedPayloads['browser-runtime'].map(route=>({
|
|
432
445
|
prefix:route.destination.split('/'),
|
|
433
446
|
root:path.join(resolved.workspaceRoot,...route.source.split('/')),
|
|
447
|
+
include:route.include,
|
|
434
448
|
allow:relative=>sharedPathAllowed(relative,route)
|
|
435
449
|
}))
|
|
436
450
|
]
|
|
@@ -441,12 +455,14 @@ async function sourceRoutes(workspaceRoot,appId,{
|
|
|
441
455
|
workspaceRoot:resolved.workspaceRoot,
|
|
442
456
|
workspaceMode:'external',
|
|
443
457
|
appId:resolved.appId,
|
|
458
|
+
app:resolved.app.manifest,
|
|
444
459
|
startPath:`/apps/${resolved.appId}/${resolved.app.manifest.entry}`,
|
|
445
460
|
mappings:[
|
|
446
461
|
appMapping,
|
|
447
462
|
{
|
|
448
463
|
prefix:['arcane'],
|
|
449
464
|
root:runtimeRoot,
|
|
465
|
+
include:[...SDK_INSTALLED_ARCANE_ROOTS],
|
|
450
466
|
allow:sdkInstalledArcanePathAllowed
|
|
451
467
|
}
|
|
452
468
|
]
|
|
@@ -457,10 +473,25 @@ async function packagedRoutes(releaseRoot){
|
|
|
457
473
|
if(!is.string(releaseRoot)||!releaseRoot.trim())fail('releaseRoot is required in packaged mode.','ARCANE_USAGE');
|
|
458
474
|
const requested=path.resolve(releaseRoot);
|
|
459
475
|
const canonical=await canonicalRealDirectory(requested,'Packaged release root');
|
|
476
|
+
let pwa = false;
|
|
477
|
+
let startPath = '/index.html';
|
|
478
|
+
try {
|
|
479
|
+
const manifest = JSON.parse(await readFile(path.join(canonical, 'arcane-offline.json'), 'utf8'));
|
|
480
|
+
pwa = manifest.schemaVersion === 1;
|
|
481
|
+
if (pwa) {
|
|
482
|
+
const release = JSON.parse(
|
|
483
|
+
await readFile(path.join(canonical, 'ARCANE_APP_RELEASE.json'), 'utf8')
|
|
484
|
+
);
|
|
485
|
+
startPath = `/${release.app.entry}`;
|
|
486
|
+
}
|
|
487
|
+
} catch (error) {
|
|
488
|
+
if (error.code !== 'ENOENT') throw error;
|
|
489
|
+
}
|
|
460
490
|
return {
|
|
461
491
|
workspaceRoot:null,
|
|
462
492
|
appId:null,
|
|
463
|
-
|
|
493
|
+
pwa,
|
|
494
|
+
startPath,
|
|
464
495
|
mappings:[{
|
|
465
496
|
prefix:[],
|
|
466
497
|
root:canonical
|
|
@@ -468,6 +499,135 @@ async function packagedRoutes(releaseRoot){
|
|
|
468
499
|
};
|
|
469
500
|
}
|
|
470
501
|
|
|
502
|
+
async function sourcePwaAssets(routeSet, mappings, signal, resourceUrls, resourcePaths) {
|
|
503
|
+
const records = new Map();
|
|
504
|
+
const resources = new Map();
|
|
505
|
+
for (const mapping of mappings) {
|
|
506
|
+
const visited = new Set();
|
|
507
|
+
async function visitSourceResource(relative) {
|
|
508
|
+
throwIfAborted(signal);
|
|
509
|
+
const key = relative.join('/');
|
|
510
|
+
if (visited.has(key)) return;
|
|
511
|
+
visited.add(key);
|
|
512
|
+
if (relative.length > 0 && mapping.allow && !mapping.allow(relative)) return;
|
|
513
|
+
const location = path.join(mapping.root, ...relative);
|
|
514
|
+
let info;
|
|
515
|
+
try {
|
|
516
|
+
info = await lstat(location);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
if (error.code === 'ENOENT') return;
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
if (info.isDirectory()) {
|
|
522
|
+
const entries = await readdir(location, {withFileTypes: true});
|
|
523
|
+
for (const entry of entries) {
|
|
524
|
+
await visitSourceResource([...relative, entry.name]);
|
|
525
|
+
}
|
|
526
|
+
} else if (info.isFile()) {
|
|
527
|
+
const segments = [...mapping.prefix, ...relative];
|
|
528
|
+
const url = `/${segments.map(encodeURIComponent).join('/')}`;
|
|
529
|
+
const appResource = mapping.prefix[0] === 'apps'
|
|
530
|
+
&& mapping.prefix[1] === routeSet.appId;
|
|
531
|
+
const logical = (appResource ? relative : segments).join('/');
|
|
532
|
+
records.set(logical, url);
|
|
533
|
+
resources.set(url, {mapping, relative});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
for (const selected of mapping.include ?? ['']) {
|
|
537
|
+
await visitSourceResource(selected ? selected.split('/') : []);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const origin = 'http://arcane.invalid';
|
|
541
|
+
const entryUrl = new URL(routeSet.startPath, origin);
|
|
542
|
+
const pending = [{url: entryUrl, documentUrl: entryUrl}];
|
|
543
|
+
const visited = new Set();
|
|
544
|
+
const referencesByPath = new Map();
|
|
545
|
+
for (const selected of routeSet.app.include) {
|
|
546
|
+
if (!/\.html?$/iu.test(selected)) continue;
|
|
547
|
+
const url = new URL(
|
|
548
|
+
`/apps/${routeSet.appId}/${selected.split('/').map(encodeURIComponent).join('/')}`,
|
|
549
|
+
origin
|
|
550
|
+
);
|
|
551
|
+
pending.push({url, documentUrl: url});
|
|
552
|
+
}
|
|
553
|
+
let runtimeRootsAdded = false;
|
|
554
|
+
for (const current of pending) {
|
|
555
|
+
throwIfAborted(signal);
|
|
556
|
+
const pathname = current.url.pathname;
|
|
557
|
+
const record = resources.get(pathname);
|
|
558
|
+
const context = `${pathname}\n${current.documentUrl.origin}${current.documentUrl.pathname}${current.documentUrl.search}`;
|
|
559
|
+
if (!record || visited.has(context)) continue;
|
|
560
|
+
const managedMap = path.posix.basename(pathname) === 'arcane.importmap.json';
|
|
561
|
+
if (!/\.(?:m?js|html?|css)$/iu.test(pathname) && !managedMap) continue;
|
|
562
|
+
visited.add(context);
|
|
563
|
+
resourcePaths.add(decodeURIComponent(pathname));
|
|
564
|
+
let references = referencesByPath.get(pathname);
|
|
565
|
+
if (!references) {
|
|
566
|
+
const opened = await openSafeFile(record.mapping.root, record.relative);
|
|
567
|
+
if (!opened) continue;
|
|
568
|
+
references = [];
|
|
569
|
+
rewriteAssetReferences(
|
|
570
|
+
opened.content.toString('utf8'),
|
|
571
|
+
{
|
|
572
|
+
filePath: opened.candidate,
|
|
573
|
+
version: null,
|
|
574
|
+
onReference: function rememberSourceReference(reference) {
|
|
575
|
+
references.push(reference);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
referencesByPath.set(pathname, references);
|
|
580
|
+
}
|
|
581
|
+
const authoredBase = references.find(function documentBaseReference(reference) {
|
|
582
|
+
return reference.baseHref;
|
|
583
|
+
})?.baseHref;
|
|
584
|
+
const documentUrl = authoredBase ? new URL(authoredBase, current.url) : current.documentUrl;
|
|
585
|
+
if (!runtimeRootsAdded && pathname === entryUrl.pathname) {
|
|
586
|
+
runtimeRootsAdded = true;
|
|
587
|
+
for (const url of resources.keys()) {
|
|
588
|
+
if (/^\/arcane\/.*\.(?:m?js|html?|css)$/iu.test(url)
|
|
589
|
+
|| path.posix.basename(url) === 'arcane.importmap.json') {
|
|
590
|
+
pending.push({url: new URL(url, origin), documentUrl});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
for (const {url, kind, baseHref, baseKind} of references) {
|
|
595
|
+
if (kind === 'import' && !/^(?:\.{1,2}\/|\/)/u.test(url)) continue;
|
|
596
|
+
let target;
|
|
597
|
+
let base;
|
|
598
|
+
try {
|
|
599
|
+
base = baseHref ? new URL(baseHref, current.url)
|
|
600
|
+
: managedMap || baseKind === 'document' ? documentUrl : current.url;
|
|
601
|
+
target = new URL(versionAssetUrl(url, null), base);
|
|
602
|
+
} catch {
|
|
603
|
+
// Non-URL source values retain their authored behavior.
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (target.origin !== origin || !resources.has(target.pathname)) continue;
|
|
607
|
+
resourceUrls.add(`${target.pathname}${target.search}`);
|
|
608
|
+
const traversable = kind !== 'fetch'
|
|
609
|
+
&& (kind !== 'asset' || /\.css(?:[?#]|$)/iu.test(url));
|
|
610
|
+
if (traversable) {
|
|
611
|
+
pending.push({
|
|
612
|
+
url: target,
|
|
613
|
+
documentUrl: kind === 'document' ? target : documentUrl
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const assets = selectPwaFiles([...records.keys()].sort(), routeSet.app.pwa).map(
|
|
619
|
+
function selectedSourceUrl(relative) {
|
|
620
|
+
return records.get(relative);
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
const selectedUrls = new Set(assets);
|
|
624
|
+
for (const resourceUrl of resourceUrls) {
|
|
625
|
+
const url = new URL(resourceUrl, 'http://arcane.invalid');
|
|
626
|
+
if (selectedUrls.has(url.pathname)) selectedUrls.add(resourceUrl);
|
|
627
|
+
}
|
|
628
|
+
return [...selectedUrls].sort();
|
|
629
|
+
}
|
|
630
|
+
|
|
471
631
|
function listen(server,{host,port,signal}){
|
|
472
632
|
return new Promise((resolve,reject)=>{
|
|
473
633
|
const cleanup=()=>{
|
|
@@ -598,6 +758,7 @@ async function startOwnedDevServer({
|
|
|
598
758
|
})
|
|
599
759
|
:await packagedRoutes(releaseRoot);
|
|
600
760
|
const mappings=deterministicMappings(routeSet.mappings);
|
|
761
|
+
const pwaEnabled = mode === 'source' ? routeSet.app?.pwa?.enabled === true : routeSet.pwa;
|
|
601
762
|
async function selectedAssetVersion(){
|
|
602
763
|
if(mode!=='source')return undefined;
|
|
603
764
|
return sdkRuntimeSourceRoot===undefined
|
|
@@ -612,6 +773,57 @@ async function startOwnedDevServer({
|
|
|
612
773
|
if(info.isSymbolicLink()||!info.isDirectory())fail(`Server route root must be a real directory: ${mapping.root}.`);
|
|
613
774
|
mapping.root=await realpath(mapping.root);
|
|
614
775
|
}
|
|
776
|
+
let pwaArtifacts;
|
|
777
|
+
let pwaInventoryTask;
|
|
778
|
+
const pwaResourceUrls = new Set();
|
|
779
|
+
function developmentPwaArtifacts(assets = []) {
|
|
780
|
+
return createPwaArtifacts(
|
|
781
|
+
{
|
|
782
|
+
app: {
|
|
783
|
+
id: routeSet.appId,
|
|
784
|
+
displayName: routeSet.app.displayName,
|
|
785
|
+
version: routeSet.app.version,
|
|
786
|
+
entry: routeSet.startPath
|
|
787
|
+
},
|
|
788
|
+
sdkVersion: assetVersion,
|
|
789
|
+
pwa: routeSet.app.pwa,
|
|
790
|
+
files: [],
|
|
791
|
+
assets,
|
|
792
|
+
navigationAliases: {'/': routeSet.startPath},
|
|
793
|
+
basePath: '/',
|
|
794
|
+
appBase: `/apps/${routeSet.appId}/`,
|
|
795
|
+
runtimeBase: '/arcane/sdk/',
|
|
796
|
+
mode: 'development'
|
|
797
|
+
}
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
async function sourcePwaArtifact(targetPath) {
|
|
801
|
+
if (targetPath === '/arcane-sw.js'
|
|
802
|
+
|| (targetPath === '/arcane-offline.json' && !pwaArtifacts)) {
|
|
803
|
+
if (!pwaInventoryTask) {
|
|
804
|
+
pwaInventoryTask = Promise.all(
|
|
805
|
+
[sourcePwaAssets(routeSet, mappings, signal, pwaResourceUrls, resourcePaths), selectedAssetVersion()]
|
|
806
|
+
).then(
|
|
807
|
+
function prepareSourceOfflineInventory([assets, version]) {
|
|
808
|
+
assetVersion = version;
|
|
809
|
+
pwaArtifacts = developmentPwaArtifacts(assets);
|
|
810
|
+
return pwaArtifacts;
|
|
811
|
+
}
|
|
812
|
+
).finally(
|
|
813
|
+
function releaseSourceInventoryTask() {
|
|
814
|
+
pwaInventoryTask = null;
|
|
815
|
+
}
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
await pwaInventoryTask;
|
|
819
|
+
}
|
|
820
|
+
const artifacts = pwaArtifacts ?? developmentPwaArtifacts();
|
|
821
|
+
return artifacts.files.find(
|
|
822
|
+
function requestedPwaFile(file) {
|
|
823
|
+
return `/${file.path}` === targetPath;
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
}
|
|
615
827
|
// Remember actual resource edges as their owners are served, not every
|
|
616
828
|
// HTML/JS/CSS file in an application's document or attachment corpus.
|
|
617
829
|
const resourcePaths=new Set([routeSet.startPath]);
|
|
@@ -627,6 +839,16 @@ async function startOwnedDevServer({
|
|
|
627
839
|
const target=parseRequestTarget(request.url);
|
|
628
840
|
if(!target){deny(response,400,'Invalid request path.');return;}
|
|
629
841
|
const {segments}=target;
|
|
842
|
+
if (mode === 'source' && pwaEnabled
|
|
843
|
+
&& ['/arcane.webmanifest', '/arcane-offline.json', '/arcane-sw.js', '/arcane-pwa.mjs'].includes(target.path)) {
|
|
844
|
+
const generated = await sourcePwaArtifact(target.path);
|
|
845
|
+
await sendFile(
|
|
846
|
+
response,
|
|
847
|
+
{candidate: generated.path, content: Buffer.from(generated.content, 'utf8')},
|
|
848
|
+
{head: request.method === 'HEAD', revalidate: true}
|
|
849
|
+
);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
630
852
|
if(segments.length===0){
|
|
631
853
|
response.writeHead(302,{location:routeSet.startPath});
|
|
632
854
|
response.end();
|
|
@@ -659,7 +881,10 @@ async function startOwnedDevServer({
|
|
|
659
881
|
// An unrelated document is not a source validation surface.
|
|
660
882
|
}
|
|
661
883
|
}
|
|
662
|
-
const
|
|
884
|
+
const selectedPwaDocument = mode === 'source' && pwaEnabled && html
|
|
885
|
+
&& mapping.prefix[0] === 'apps' && mapping.prefix[1] === routeSet.appId
|
|
886
|
+
&& routeSet.app.include.includes(relative.join('/'));
|
|
887
|
+
const entryDocument=target.path===routeSet.startPath||managedDocument||selectedPwaDocument;
|
|
663
888
|
const managedMap=path.basename(opened.candidate)==='arcane.importmap.json';
|
|
664
889
|
// A live server can span an SDK upgrade. Refresh the small
|
|
665
890
|
// version record on navigation, not on each resource request.
|
|
@@ -667,9 +892,22 @@ async function startOwnedDevServer({
|
|
|
667
892
|
const runtimeResource=segments[0]==='arcane';
|
|
668
893
|
const browserResource=['script','style','worker','sharedworker','serviceworker']
|
|
669
894
|
.includes(request.headers['sec-fetch-dest']);
|
|
670
|
-
const rewrite=runtimeResource||entryDocument||browserResource
|
|
895
|
+
const rewrite=runtimeResource||entryDocument||browserResource||managedMap
|
|
671
896
|
||resourcePaths.has(target.path);
|
|
672
|
-
const onReference=({url,kind,baseHref,baseKind})
|
|
897
|
+
const onReference = function observeServedResource({url,kind,baseHref,baseKind}) {
|
|
898
|
+
if (pwaEnabled && kind !== 'fetch'
|
|
899
|
+
&& !(kind === 'import' && !/^(?:\.{1,2}\/|\/)/u.test(url))) {
|
|
900
|
+
try {
|
|
901
|
+
const documentUrl = new URL(target.path, 'http://arcane.invalid');
|
|
902
|
+
const base = baseHref ? new URL(baseHref, documentUrl) : documentUrl;
|
|
903
|
+
const resource = new URL(versionAssetUrl(url, null), base);
|
|
904
|
+
if (resource.origin === documentUrl.origin) {
|
|
905
|
+
pwaResourceUrls.add(`${resource.pathname}${resource.search}`);
|
|
906
|
+
}
|
|
907
|
+
} catch {
|
|
908
|
+
// Non-URL resource values remain under their existing owner.
|
|
909
|
+
}
|
|
910
|
+
}
|
|
673
911
|
if(baseKind==='document'||kind==='fetch'||(kind==='asset'&&!/\.css(?:[?#]|$)/iu.test(url))
|
|
674
912
|
||(kind==='import'&&!/^(?:\.{1,2}\/|\/)/u.test(url)))return;
|
|
675
913
|
try{
|
|
@@ -684,8 +922,9 @@ async function startOwnedDevServer({
|
|
|
684
922
|
};
|
|
685
923
|
await sendFile(response,opened,{
|
|
686
924
|
head:request.method==='HEAD',
|
|
687
|
-
assetVersion:rewrite?assetVersion:undefined,
|
|
688
|
-
revalidate:entryDocument||managedMap,
|
|
925
|
+
assetVersion:rewrite&&mode==='source'?(pwaEnabled?null:assetVersion):undefined,
|
|
926
|
+
revalidate:pwaEnabled||entryDocument||managedMap,
|
|
927
|
+
pwaEntry:mode==='source'&&pwaEnabled&&entryDocument,
|
|
689
928
|
onReference
|
|
690
929
|
});
|
|
691
930
|
});
|