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