@emkodev/emroute 1.8.2-beta.2 → 1.10.0-beta.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.
Files changed (55) hide show
  1. package/core/component/widget.component.ts +1 -1
  2. package/core/pipeline/pipeline.ts +34 -1
  3. package/core/renderer/html.renderer.ts +16 -18
  4. package/core/renderer/md.renderer.ts +2 -4
  5. package/core/renderer/ssr.renderer.ts +5 -13
  6. package/core/server/emroute.server.ts +9 -25
  7. package/dist/core/component/widget.component.d.ts +1 -1
  8. package/dist/core/component/widget.component.js +1 -1
  9. package/dist/core/pipeline/pipeline.d.ts +6 -0
  10. package/dist/core/pipeline/pipeline.js +31 -1
  11. package/dist/core/pipeline/pipeline.js.map +1 -1
  12. package/dist/core/renderer/html.renderer.js +10 -12
  13. package/dist/core/renderer/html.renderer.js.map +1 -1
  14. package/dist/core/renderer/md.renderer.js +2 -4
  15. package/dist/core/renderer/md.renderer.js.map +1 -1
  16. package/dist/core/renderer/ssr.renderer.d.ts +4 -5
  17. package/dist/core/renderer/ssr.renderer.js +3 -15
  18. package/dist/core/renderer/ssr.renderer.js.map +1 -1
  19. package/dist/core/server/emroute.server.d.ts +2 -2
  20. package/dist/core/server/emroute.server.js +7 -23
  21. package/dist/core/server/emroute.server.js.map +1 -1
  22. package/dist/core/widget/widget.registry.d.ts +3 -16
  23. package/dist/core/widget/widget.registry.js +3 -34
  24. package/dist/core/widget/widget.registry.js.map +1 -1
  25. package/dist/emroute.js +435 -96
  26. package/dist/emroute.js.map +14 -12
  27. package/dist/runtime/cache.runtime.d.ts +31 -0
  28. package/dist/runtime/cache.runtime.js +107 -0
  29. package/dist/runtime/cache.runtime.js.map +1 -0
  30. package/dist/runtime/idb.runtime.d.ts +31 -0
  31. package/dist/runtime/idb.runtime.js +178 -0
  32. package/dist/runtime/idb.runtime.js.map +1 -0
  33. package/dist/src/index.d.ts +0 -1
  34. package/dist/src/index.js +0 -1
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/renderer/spa/emroute.app.js +1 -6
  37. package/dist/src/renderer/spa/emroute.app.js.map +1 -1
  38. package/dist/src/renderer/spa/mod.d.ts +4 -3
  39. package/dist/src/renderer/spa/mod.js +5 -3
  40. package/dist/src/renderer/spa/mod.js.map +1 -1
  41. package/dist/src/service-worker/emroute.sw.d.ts +54 -0
  42. package/dist/src/service-worker/emroute.sw.js +181 -0
  43. package/dist/src/service-worker/emroute.sw.js.map +1 -0
  44. package/dist/src/service-worker/mod.d.ts +8 -0
  45. package/dist/src/service-worker/mod.js +9 -0
  46. package/dist/src/service-worker/mod.js.map +1 -0
  47. package/package.json +16 -1
  48. package/runtime/cache.runtime.ts +127 -0
  49. package/runtime/idb.runtime.ts +203 -0
  50. package/src/index.ts +0 -1
  51. package/src/renderer/spa/emroute.app.ts +2 -6
  52. package/src/renderer/spa/mod.ts +6 -4
  53. package/src/service-worker/emroute.sw.ts +264 -0
  54. package/src/service-worker/mod.ts +9 -0
  55. package/core/widget/widget.registry.ts +0 -63
package/dist/emroute.js CHANGED
@@ -472,47 +472,6 @@ class ComponentElement extends HTMLElementBase {
472
472
  }
473
473
  }
474
474
 
475
- // dist/core/widget/widget.registry.js
476
- class WidgetRegistry {
477
- entries = new Map;
478
- add(widget, modulePath) {
479
- this.entries.set(widget.name, { widget, modulePath });
480
- }
481
- addLazy(name, modulePath) {
482
- if (!this.entries.has(name)) {
483
- this.entries.set(name, { modulePath });
484
- }
485
- }
486
- get(name) {
487
- return this.entries.get(name)?.widget;
488
- }
489
- getModulePath(name) {
490
- return this.entries.get(name)?.modulePath;
491
- }
492
- [Symbol.iterator]() {
493
- const entries = this.entries.values();
494
- return function* () {
495
- for (const entry of entries) {
496
- if (entry.widget)
497
- yield entry.widget;
498
- }
499
- }();
500
- }
501
- }
502
- function extractWidgetExport(mod) {
503
- for (const value of Object.values(mod)) {
504
- if (!value)
505
- continue;
506
- if (typeof value === "object" && "getData" in value) {
507
- return value;
508
- }
509
- if (typeof value === "function" && value.prototype?.getData) {
510
- return new value;
511
- }
512
- }
513
- return null;
514
- }
515
-
516
475
  // dist/core/router/route.trie.js
517
476
  class RouteTrie {
518
477
  tree;
@@ -735,6 +694,33 @@ class Pipeline {
735
694
  }
736
695
  return hierarchy;
737
696
  }
697
+ async findWidgetModulePath(name) {
698
+ const response = await this.runtime.query(WIDGETS_MANIFEST_PATH);
699
+ if (response.status === 404)
700
+ return;
701
+ const entries = await response.json();
702
+ return entries.find((e) => e.name === name)?.modulePath;
703
+ }
704
+ async loadWidget(name) {
705
+ const path = await this.findWidgetModulePath(name);
706
+ if (!path)
707
+ return;
708
+ const mod = await this.loadModule(path);
709
+ return this.extractWidgetComponent(mod);
710
+ }
711
+ extractWidgetComponent(mod) {
712
+ for (const value of Object.values(mod)) {
713
+ if (!value)
714
+ continue;
715
+ if (typeof value === "object" && "getData" in value) {
716
+ return value;
717
+ }
718
+ if (typeof value === "function" && value.prototype?.getData) {
719
+ return new value;
720
+ }
721
+ }
722
+ return;
723
+ }
738
724
  async loadModule(modulePath) {
739
725
  const loader = this.moduleLoaders[modulePath];
740
726
  if (loader) {
@@ -1040,25 +1026,14 @@ var page_component_default = new PageComponent;
1040
1026
  // dist/core/renderer/ssr.renderer.js
1041
1027
  class SsrRenderer {
1042
1028
  pipeline;
1043
- widgets;
1044
1029
  logger;
1045
- constructor(pipeline, options = {}) {
1030
+ constructor(pipeline, _options = {}) {
1046
1031
  this.pipeline = pipeline;
1047
1032
  this.logger = pipeline.logger;
1048
- this.widgets = options.widgets ?? null;
1049
1033
  }
1050
1034
  async resolveWidget(name) {
1051
- if (!this.widgets)
1052
- return;
1053
- const widget = this.widgets.get(name);
1054
- if (widget)
1055
- return widget;
1056
- const path = this.widgets.getModulePath(name);
1057
- if (!path)
1058
- return;
1059
1035
  try {
1060
- const mod = await this.pipeline.loadModule(path);
1061
- return extractWidgetExport(mod) ?? undefined;
1036
+ return await this.pipeline.loadWidget(name);
1062
1037
  } catch (e) {
1063
1038
  this.logger.error(`[${this.label}] Failed to load widget "${name}"`, e instanceof Error ? e : undefined);
1064
1039
  return;
@@ -1227,18 +1202,16 @@ class SsrHtmlRenderer extends SsrRenderer {
1227
1202
  let content = rawContent;
1228
1203
  content = await this.expandMarkdown(content);
1229
1204
  content = this.attributeSlots(content, route.pattern);
1230
- if (this.widgets) {
1231
- content = await resolveWidgetTags(content, (name) => this.resolveWidget(name), routeInfo, async (name) => {
1232
- const modulePath = this.widgets.getModulePath(name);
1233
- if (modulePath) {
1234
- const mod = await this.pipeline.loadModule(modulePath);
1235
- const inlined = this.pipeline.getModuleFiles(mod);
1236
- if (inlined)
1237
- return inlined;
1238
- }
1239
- return {};
1240
- }, this.pipeline.contextProvider, this.logger);
1241
- }
1205
+ content = await resolveWidgetTags(content, (name) => this.resolveWidget(name), routeInfo, async (name) => {
1206
+ const modulePath = await this.pipeline.findWidgetModulePath(name);
1207
+ if (modulePath) {
1208
+ const mod = await this.pipeline.loadModule(modulePath);
1209
+ const inlined = this.pipeline.getModuleFiles(mod);
1210
+ if (inlined)
1211
+ return inlined;
1212
+ }
1213
+ return {};
1214
+ }, this.pipeline.contextProvider, this.logger);
1242
1215
  return { content, ...title !== undefined ? { title } : {} };
1243
1216
  }
1244
1217
  renderContent(component, args) {
@@ -1350,9 +1323,7 @@ class SsrMdRenderer extends SsrRenderer {
1350
1323
  const { content: rawContent, title } = await this.loadRouteContent(routeInfo, route, isLeaf, signal);
1351
1324
  let content = rawContent;
1352
1325
  content = content.replaceAll(BARE_SLOT_BLOCK, routerSlotBlock(route.pattern));
1353
- if (this.widgets) {
1354
- content = await this.resolveWidgets(content, routeInfo);
1355
- }
1326
+ content = await this.resolveWidgets(content, routeInfo);
1356
1327
  return { content, ...title !== undefined ? { title } : {} };
1357
1328
  }
1358
1329
  renderContent(component, args) {
@@ -1382,7 +1353,7 @@ Path: \`${url.pathname}\``;
1382
1353
  }
1383
1354
  try {
1384
1355
  let files;
1385
- const modulePath = this.widgets.getModulePath(block.widgetName);
1356
+ const modulePath = await this.pipeline.findWidgetModulePath(block.widgetName);
1386
1357
  if (modulePath) {
1387
1358
  const mod = await this.pipeline.loadModule(modulePath);
1388
1359
  files = this.pipeline.getModuleFiles(mod);
@@ -1467,31 +1438,13 @@ class Emroute {
1467
1438
  ...config.extendContext ? { contextProvider: config.extendContext } : {},
1468
1439
  ...config.moduleLoaders ? { moduleLoaders: config.moduleLoaders } : {}
1469
1440
  });
1470
- let widgets;
1471
- const widgetsResponse = await runtime.query(WIDGETS_MANIFEST_PATH);
1472
- if (widgetsResponse.status !== 404) {
1473
- const entries = await widgetsResponse.json();
1474
- widgets = new WidgetRegistry;
1475
- for (const entry of entries) {
1476
- widgets.addLazy(entry.name, entry.modulePath);
1477
- }
1478
- }
1479
- if (config.widgets) {
1480
- if (!widgets)
1481
- widgets = new WidgetRegistry;
1482
- for (const w of config.widgets)
1483
- widgets.add(w);
1484
- }
1485
1441
  let ssrHtmlRenderer = null;
1486
1442
  let ssrMdRenderer = null;
1487
1443
  if (spa !== "only") {
1488
1444
  ssrHtmlRenderer = new SsrHtmlRenderer(pipeline, {
1489
- ...config.markdownRenderer ? { markdownRenderer: config.markdownRenderer } : {},
1490
- ...widgets ? { widgets } : {}
1491
- });
1492
- ssrMdRenderer = new SsrMdRenderer(pipeline, {
1493
- ...widgets ? { widgets } : {}
1445
+ ...config.markdownRenderer ? { markdownRenderer: config.markdownRenderer } : {}
1494
1446
  });
1447
+ ssrMdRenderer = new SsrMdRenderer(pipeline);
1495
1448
  }
1496
1449
  const title = config.title ?? "emroute";
1497
1450
  const shellBase = spa === "root" || spa === "only" ? appBase : htmlBase;
@@ -1574,6 +1527,11 @@ class Emroute {
1574
1527
  static async buildHtmlShell(runtime, title, basePath, spa) {
1575
1528
  const baseTag = basePath ? `
1576
1529
  <base href="${escapeHtml(basePath)}/">` : "";
1530
+ let manifestTag = "";
1531
+ if ((await runtime.query("/manifest.json")).status !== 404) {
1532
+ manifestTag = `
1533
+ <link rel="manifest" href="/manifest.json">`;
1534
+ }
1577
1535
  let cssTag = "";
1578
1536
  if ((await runtime.query("/main.css")).status !== 404) {
1579
1537
  cssTag = `
@@ -1602,7 +1560,7 @@ ${importMap}
1602
1560
  <meta charset="utf-8">
1603
1561
  <meta name="viewport" content="width=device-width, initial-scale=1">
1604
1562
  <title>${escapeHtml(title)}</title>
1605
- <style>@view-transition { navigation: auto; } router-slot { display: contents; }</style>${cssTag}
1563
+ <style>@view-transition { navigation: auto; } router-slot { display: contents; }</style>${manifestTag}${cssTag}
1606
1564
  </head>
1607
1565
  <body>
1608
1566
  <router-slot></router-slot>${importMapHtml}${scriptHtml}
@@ -2426,9 +2384,7 @@ async function bootEmrouteApp(options) {
2426
2384
  const elementsResponse = await runtime.handle(ELEMENTS_MANIFEST_PATH);
2427
2385
  const elementEntries = elementsResponse.ok ? await elementsResponse.json() : [];
2428
2386
  const moduleLoaders = buildLazyLoaders(routeTree, widgetEntries, elementEntries, runtime);
2429
- const widgets = new WidgetRegistry;
2430
2387
  for (const entry of widgetEntries) {
2431
- widgets.addLazy(entry.name, entry.modulePath);
2432
2388
  ComponentElement.registerLazy(entry.name, moduleLoaders[entry.modulePath]);
2433
2389
  }
2434
2390
  for (const entry of elementEntries) {
@@ -2450,7 +2406,6 @@ async function bootEmrouteApp(options) {
2450
2406
  const mdRenderer = MarkdownElement.getConfiguredRenderer();
2451
2407
  const server = await Emroute.create({
2452
2408
  routeTree,
2453
- widgets,
2454
2409
  moduleLoaders,
2455
2410
  ...mdRenderer ? { markdownRenderer: mdRenderer } : {},
2456
2411
  ...options?.extendContext ? { extendContext: options.extendContext } : {}
@@ -3031,6 +2986,388 @@ function createOverlayService() {
3031
2986
  dismissAll
3032
2987
  };
3033
2988
  }
2989
+ // dist/runtime/cache.runtime.js
2990
+ var __rewriteRelativeImportExtension2 = function(path, preserveJsx) {
2991
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
2992
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
2993
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
2994
+ });
2995
+ }
2996
+ return path;
2997
+ };
2998
+
2999
+ class CacheRuntime extends Runtime {
3000
+ cache = null;
3001
+ cacheName;
3002
+ constructor(cacheName, config = {}) {
3003
+ super(config);
3004
+ this.cacheName = cacheName;
3005
+ }
3006
+ async getCache() {
3007
+ this.cache ??= await caches.open(this.cacheName);
3008
+ return this.cache;
3009
+ }
3010
+ handle(resource, init) {
3011
+ const path = this.parsePath(resource);
3012
+ const method = init?.method ?? "GET";
3013
+ switch (method) {
3014
+ case "PUT":
3015
+ return this.write(path, init?.body ?? null);
3016
+ case "DELETE":
3017
+ return this.delete(path);
3018
+ default:
3019
+ return this.read(path);
3020
+ }
3021
+ }
3022
+ query(resource, options) {
3023
+ if (options?.as === "text") {
3024
+ return this.read(this.parsePath(resource)).then(async (r) => {
3025
+ if (r.status === 404)
3026
+ throw new Error(`Not found: ${this.parsePath(resource)}`);
3027
+ return r.text();
3028
+ });
3029
+ }
3030
+ return this.handle(resource, options);
3031
+ }
3032
+ async loadModule(path) {
3033
+ const response = await this.read(path);
3034
+ if (response.status === 404) {
3035
+ throw new Error(`Module not found in cache: ${path}`);
3036
+ }
3037
+ const js = await response.text();
3038
+ const blob = new Blob([js], { type: "application/javascript" });
3039
+ const objectUrl = URL.createObjectURL(blob);
3040
+ try {
3041
+ return await import(__rewriteRelativeImportExtension2(objectUrl));
3042
+ } finally {
3043
+ URL.revokeObjectURL(objectUrl);
3044
+ }
3045
+ }
3046
+ async read(path) {
3047
+ const cache = await this.getCache();
3048
+ const key = new Request(this.toFakeUrl(path));
3049
+ const cached = await cache.match(key);
3050
+ if (!cached)
3051
+ return new Response("Not Found", { status: 404 });
3052
+ return cached;
3053
+ }
3054
+ async write(path, body) {
3055
+ const cache = await this.getCache();
3056
+ const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
3057
+ const contentType = CONTENT_TYPES.get(ext) ?? "application/octet-stream";
3058
+ const response = new Response(body, {
3059
+ status: 200,
3060
+ headers: { "Content-Type": contentType }
3061
+ });
3062
+ await cache.put(new Request(this.toFakeUrl(path)), response);
3063
+ return new Response(null, { status: 204 });
3064
+ }
3065
+ async delete(path) {
3066
+ const cache = await this.getCache();
3067
+ await cache.delete(new Request(this.toFakeUrl(path)));
3068
+ return new Response(null, { status: 204 });
3069
+ }
3070
+ parsePath(resource) {
3071
+ if (typeof resource === "string")
3072
+ return resource;
3073
+ if (resource instanceof URL)
3074
+ return resource.pathname;
3075
+ return new URL(resource.url).pathname;
3076
+ }
3077
+ toFakeUrl(path) {
3078
+ return `https://emroute-cache${path}`;
3079
+ }
3080
+ }
3081
+
3082
+ // dist/runtime/idb.runtime.js
3083
+ var __rewriteRelativeImportExtension3 = function(path, preserveJsx) {
3084
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3085
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
3086
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
3087
+ });
3088
+ }
3089
+ return path;
3090
+ };
3091
+ var STORE_NAME = "files";
3092
+
3093
+ class IdbRuntime extends Runtime {
3094
+ db = null;
3095
+ dbName;
3096
+ constructor(dbName, config = {}) {
3097
+ super(config);
3098
+ this.dbName = dbName;
3099
+ }
3100
+ open() {
3101
+ if (this.db)
3102
+ return Promise.resolve(this.db);
3103
+ return new Promise((resolve, reject) => {
3104
+ const request = indexedDB.open(this.dbName, 1);
3105
+ request.onupgradeneeded = () => {
3106
+ request.result.createObjectStore(STORE_NAME);
3107
+ };
3108
+ request.onsuccess = () => {
3109
+ this.db = request.result;
3110
+ resolve(this.db);
3111
+ };
3112
+ request.onerror = () => reject(request.error);
3113
+ });
3114
+ }
3115
+ handle(resource, init) {
3116
+ const [pathname, method, body] = this.parse(resource, init);
3117
+ switch (method) {
3118
+ case "PUT":
3119
+ return this.write(pathname, body);
3120
+ case "DELETE":
3121
+ return this.delete(pathname);
3122
+ default:
3123
+ return this.read(pathname);
3124
+ }
3125
+ }
3126
+ query(resource, options) {
3127
+ if (options?.as === "text") {
3128
+ const pathname = this.parsePath(resource);
3129
+ return this.get(pathname).then((data) => {
3130
+ if (!data)
3131
+ throw new Error(`Not found: ${pathname}`);
3132
+ return new TextDecoder().decode(data);
3133
+ });
3134
+ }
3135
+ return this.handle(resource, options);
3136
+ }
3137
+ async loadModule(path) {
3138
+ const data = await this.get(path);
3139
+ if (!data)
3140
+ throw new Error(`Module not found in IDB: ${path}`);
3141
+ const buf = data.buffer;
3142
+ const blob = new Blob([buf], { type: "application/javascript" });
3143
+ const objectUrl = URL.createObjectURL(blob);
3144
+ try {
3145
+ return await import(__rewriteRelativeImportExtension3(objectUrl));
3146
+ } finally {
3147
+ URL.revokeObjectURL(objectUrl);
3148
+ }
3149
+ }
3150
+ async read(path) {
3151
+ if (path.endsWith("/")) {
3152
+ const children = await this.listChildren(path);
3153
+ if (children.length === 0)
3154
+ return new Response("Not Found", { status: 404 });
3155
+ return Response.json(children);
3156
+ }
3157
+ const data = await this.get(path);
3158
+ if (!data) {
3159
+ const children = await this.listChildren(path + "/");
3160
+ if (children.length > 0)
3161
+ return Response.json(children);
3162
+ return new Response("Not Found", { status: 404 });
3163
+ }
3164
+ const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
3165
+ return new Response(data.buffer, {
3166
+ status: 200,
3167
+ headers: { "Content-Type": CONTENT_TYPES.get(ext) ?? "application/octet-stream" }
3168
+ });
3169
+ }
3170
+ async write(path, body) {
3171
+ const data = body ? new Uint8Array(await new Response(body).arrayBuffer()) : new Uint8Array;
3172
+ await this.put(path, data);
3173
+ return new Response(null, { status: 204 });
3174
+ }
3175
+ async delete(path) {
3176
+ const db = await this.open();
3177
+ return new Promise((resolve, reject) => {
3178
+ const tx = db.transaction(STORE_NAME, "readwrite");
3179
+ tx.objectStore(STORE_NAME).delete(path);
3180
+ tx.oncomplete = () => resolve(new Response(null, { status: 204 }));
3181
+ tx.onerror = () => reject(tx.error);
3182
+ });
3183
+ }
3184
+ async get(path) {
3185
+ const db = await this.open();
3186
+ return new Promise((resolve, reject) => {
3187
+ const tx = db.transaction(STORE_NAME, "readonly");
3188
+ const req = tx.objectStore(STORE_NAME).get(path);
3189
+ req.onsuccess = () => resolve(req.result);
3190
+ req.onerror = () => reject(req.error);
3191
+ });
3192
+ }
3193
+ async put(path, data) {
3194
+ const db = await this.open();
3195
+ return new Promise((resolve, reject) => {
3196
+ const tx = db.transaction(STORE_NAME, "readwrite");
3197
+ tx.objectStore(STORE_NAME).put(data, path);
3198
+ tx.oncomplete = () => resolve();
3199
+ tx.onerror = () => reject(tx.error);
3200
+ });
3201
+ }
3202
+ async listChildren(prefix) {
3203
+ const db = await this.open();
3204
+ return new Promise((resolve, reject) => {
3205
+ const tx = db.transaction(STORE_NAME, "readonly");
3206
+ const store = tx.objectStore(STORE_NAME);
3207
+ const range = IDBKeyRange.bound(prefix, prefix + "￿", false, false);
3208
+ const req = store.getAllKeys(range);
3209
+ req.onsuccess = () => {
3210
+ const entries = new Set;
3211
+ for (const key of req.result) {
3212
+ const rest = key.slice(prefix.length);
3213
+ const slashIdx = rest.indexOf("/");
3214
+ if (slashIdx === -1) {
3215
+ entries.add(rest);
3216
+ } else {
3217
+ entries.add(rest.slice(0, slashIdx + 1));
3218
+ }
3219
+ }
3220
+ resolve([...entries]);
3221
+ };
3222
+ req.onerror = () => reject(req.error);
3223
+ });
3224
+ }
3225
+ parsePath(resource) {
3226
+ if (typeof resource === "string")
3227
+ return resource;
3228
+ if (resource instanceof URL)
3229
+ return resource.pathname;
3230
+ return new URL(resource.url).pathname;
3231
+ }
3232
+ parse(resource, init) {
3233
+ const pathname = this.parsePath(resource);
3234
+ if (typeof resource === "string" || resource instanceof URL) {
3235
+ return [pathname, init?.method ?? "GET", init?.body ?? null];
3236
+ }
3237
+ return [
3238
+ pathname,
3239
+ init?.method ?? resource.method,
3240
+ init?.body ?? resource.body
3241
+ ];
3242
+ }
3243
+ }
3244
+
3245
+ // dist/src/service-worker/emroute.sw.js
3246
+ class SwRuntime extends Runtime {
3247
+ cache;
3248
+ idb;
3249
+ constructor(cache, idb) {
3250
+ super();
3251
+ this.cache = cache;
3252
+ this.idb = idb;
3253
+ }
3254
+ handle(resource, init) {
3255
+ const method = init?.method ?? "GET";
3256
+ if (method === "PUT" || method === "DELETE") {
3257
+ return this.idb.handle(resource, init);
3258
+ }
3259
+ return this.cache.handle(resource, init).then(async (r) => {
3260
+ if (r.status !== 404)
3261
+ return r;
3262
+ return this.idb.handle(resource, init);
3263
+ });
3264
+ }
3265
+ query(resource, options) {
3266
+ if (options?.as === "text") {
3267
+ return this.handle(resource, options).then(async (r) => {
3268
+ if (r.status === 404) {
3269
+ const path = typeof resource === "string" ? resource : resource instanceof URL ? resource.pathname : new URL(resource.url).pathname;
3270
+ throw new Error(`Not found: ${path}`);
3271
+ }
3272
+ return r.text();
3273
+ });
3274
+ }
3275
+ return this.handle(resource, options);
3276
+ }
3277
+ async loadModule(path) {
3278
+ try {
3279
+ return await this.cache.loadModule(path);
3280
+ } catch {
3281
+ return await this.idb.loadModule(path);
3282
+ }
3283
+ }
3284
+ }
3285
+ function createEmrouteSW(options) {
3286
+ const { cacheName, precache, content = [], dbName = "emroute-content", origin = self.location.origin } = options;
3287
+ const cacheRuntime = new CacheRuntime(cacheName);
3288
+ const idbRuntime = new IdbRuntime(dbName);
3289
+ const swRuntime = new SwRuntime(cacheRuntime, idbRuntime);
3290
+ let emroute = null;
3291
+ async function getEmroute() {
3292
+ if (emroute)
3293
+ return emroute;
3294
+ emroute = await Emroute.create({
3295
+ spa: options.spa ?? "only",
3296
+ ...options.basePath ? { basePath: options.basePath } : {},
3297
+ ...options.title ? { title: options.title } : {},
3298
+ ...options.markdownRenderer ? { markdownRenderer: options.markdownRenderer } : {},
3299
+ ...options.extendContext ? { extendContext: options.extendContext } : {}
3300
+ }, swRuntime);
3301
+ return emroute;
3302
+ }
3303
+ self.addEventListener("install", (event) => {
3304
+ event.waitUntil((async () => {
3305
+ if (precache.length > 0) {
3306
+ const cache = await caches.open(cacheName);
3307
+ await Promise.all(precache.map(async (path) => {
3308
+ try {
3309
+ const response = await fetch(`${origin}${path}`);
3310
+ if (response.ok) {
3311
+ await cache.put(new Request(`https://emroute-cache${path}`), response);
3312
+ }
3313
+ } catch {
3314
+ console.error(`[emroute-sw] Failed to precache asset: ${path}`);
3315
+ }
3316
+ }));
3317
+ }
3318
+ if (content.length > 0) {
3319
+ await Promise.all(content.map(async (path) => {
3320
+ try {
3321
+ const response = await fetch(`${origin}${path}`);
3322
+ if (response.ok) {
3323
+ const data = new Uint8Array(await response.arrayBuffer());
3324
+ await idbRuntime.handle(path, {
3325
+ method: "PUT",
3326
+ body: data
3327
+ });
3328
+ }
3329
+ } catch {
3330
+ console.error(`[emroute-sw] Failed to precache content: ${path}`);
3331
+ }
3332
+ }));
3333
+ }
3334
+ await self.skipWaiting();
3335
+ })());
3336
+ });
3337
+ self.addEventListener("activate", (event) => {
3338
+ event.waitUntil((async () => {
3339
+ const keys = await caches.keys();
3340
+ await Promise.all(keys.filter((key) => key !== cacheName && key.startsWith("emroute")).map((key) => caches.delete(key)));
3341
+ await self.clients.claim();
3342
+ })());
3343
+ });
3344
+ self.addEventListener("fetch", (event) => {
3345
+ const url = new URL(event.request.url);
3346
+ if (url.origin !== self.location.origin)
3347
+ return;
3348
+ event.respondWith(handleFetch(event.request, url));
3349
+ });
3350
+ async function handleFetch(request, url) {
3351
+ if (request.mode === "navigate") {
3352
+ try {
3353
+ const server = await getEmroute();
3354
+ const response = await server.handleRequest(request);
3355
+ if (response)
3356
+ return response;
3357
+ } catch (e) {
3358
+ console.error("[emroute-sw] Navigation error:", e);
3359
+ }
3360
+ }
3361
+ const cached = await swRuntime.handle(url.pathname);
3362
+ if (cached.status !== 404)
3363
+ return cached;
3364
+ try {
3365
+ return await fetch(request);
3366
+ } catch {
3367
+ return new Response("Offline", { status: 503 });
3368
+ }
3369
+ }
3370
+ }
3034
3371
  // dist/src/widget/page-title.widget.js
3035
3372
  class PageTitleWidget extends WidgetComponent {
3036
3373
  name = "page-title";
@@ -3109,23 +3446,25 @@ export {
3109
3446
  scopeWidgetCss,
3110
3447
  escapeHtml,
3111
3448
  createOverlayService,
3449
+ createEmrouteSW,
3112
3450
  createEmrouteApp,
3113
3451
  bootEmrouteApp,
3114
- WidgetRegistry,
3115
3452
  WidgetComponent,
3116
3453
  RouterSlot,
3117
3454
  RouteTrie,
3118
3455
  PageTitleWidget,
3119
3456
  PageComponent,
3120
3457
  MarkdownElement,
3458
+ IdbRuntime,
3121
3459
  FetchRuntime,
3122
3460
  EmrouteApp,
3123
3461
  Emroute,
3124
3462
  DEFAULT_BASE_PATH,
3125
3463
  ComponentElement,
3126
3464
  Component,
3465
+ CacheRuntime,
3127
3466
  BreadcrumbWidget
3128
3467
  };
3129
3468
 
3130
- //# debugId=BD93EBE5532178BB64756E2164756E21
3469
+ //# debugId=2FED2FA156FADB3E64756E2164756E21
3131
3470
  //# sourceMappingURL=emroute.js.map