metaowl 0.4.1 → 0.5.0

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 (80) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +12 -0
  3. package/build/runtime/bin/metaowl-build.js +10 -0
  4. package/{bin → build/runtime/bin}/metaowl-create.js +96 -177
  5. package/build/runtime/bin/metaowl-dev.js +10 -0
  6. package/build/runtime/bin/metaowl-generate.js +231 -0
  7. package/build/runtime/bin/metaowl-lint.js +58 -0
  8. package/build/runtime/bin/utils.js +68 -0
  9. package/build/runtime/index.js +141 -0
  10. package/build/runtime/modules/app-mounter.js +65 -0
  11. package/build/runtime/modules/auto-import.js +140 -0
  12. package/build/runtime/modules/cache.js +49 -0
  13. package/build/runtime/modules/composables.js +353 -0
  14. package/build/runtime/modules/error-boundary.js +116 -0
  15. package/build/runtime/modules/fetch.js +31 -0
  16. package/build/runtime/modules/file-router.js +205 -0
  17. package/build/runtime/modules/forms.js +193 -0
  18. package/build/runtime/modules/i18n.js +167 -0
  19. package/build/runtime/modules/layouts.js +163 -0
  20. package/build/runtime/modules/link.js +141 -0
  21. package/build/runtime/modules/meta.js +117 -0
  22. package/build/runtime/modules/odoo-rpc.js +264 -0
  23. package/build/runtime/modules/pwa.js +262 -0
  24. package/build/runtime/modules/router.js +389 -0
  25. package/build/runtime/modules/seo.js +186 -0
  26. package/build/runtime/modules/store.js +196 -0
  27. package/build/runtime/modules/templates-manager.js +52 -0
  28. package/build/runtime/modules/test-utils.js +238 -0
  29. package/build/runtime/vite/plugin.js +183 -0
  30. package/eslint.js +29 -0
  31. package/package.json +28 -10
  32. package/CONTRIBUTING.md +0 -49
  33. package/bin/metaowl-build.js +0 -12
  34. package/bin/metaowl-dev.js +0 -12
  35. package/bin/metaowl-generate.js +0 -339
  36. package/bin/metaowl-lint.js +0 -71
  37. package/bin/utils.js +0 -82
  38. package/eslint.config.js +0 -3
  39. package/index.js +0 -328
  40. package/modules/app-mounter.js +0 -104
  41. package/modules/auto-import.js +0 -225
  42. package/modules/cache.js +0 -59
  43. package/modules/composables.js +0 -600
  44. package/modules/error-boundary.js +0 -228
  45. package/modules/fetch.js +0 -51
  46. package/modules/file-router.js +0 -478
  47. package/modules/forms.js +0 -353
  48. package/modules/i18n.js +0 -333
  49. package/modules/layouts.js +0 -431
  50. package/modules/link.js +0 -255
  51. package/modules/meta.js +0 -119
  52. package/modules/odoo-rpc.js +0 -511
  53. package/modules/pwa.js +0 -515
  54. package/modules/router.js +0 -769
  55. package/modules/seo.js +0 -501
  56. package/modules/store.js +0 -409
  57. package/modules/templates-manager.js +0 -89
  58. package/modules/test-utils.js +0 -532
  59. package/test/auto-import.test.js +0 -110
  60. package/test/cache.test.js +0 -55
  61. package/test/composables.test.js +0 -103
  62. package/test/dynamic-routes.test.js +0 -469
  63. package/test/error-boundary.test.js +0 -126
  64. package/test/fetch.test.js +0 -100
  65. package/test/file-router.test.js +0 -55
  66. package/test/forms.test.js +0 -203
  67. package/test/i18n.test.js +0 -188
  68. package/test/layouts.test.js +0 -395
  69. package/test/link.test.js +0 -189
  70. package/test/meta.test.js +0 -146
  71. package/test/odoo-rpc.test.js +0 -547
  72. package/test/pwa.test.js +0 -154
  73. package/test/router-guards.test.js +0 -229
  74. package/test/router.test.js +0 -77
  75. package/test/seo.test.js +0 -353
  76. package/test/store.test.js +0 -476
  77. package/test/templates-manager.test.js +0 -83
  78. package/test/test-utils.test.js +0 -314
  79. package/vite/plugin.js +0 -290
  80. package/vitest.config.js +0 -8
@@ -0,0 +1,262 @@
1
+ /**
2
+ * @module PWA
3
+ *
4
+ * Progressive Web App utilities for MetaOwl applications.
5
+ */
6
+ export function generateManifest(options) {
7
+ const { name, shortName, description, startUrl = './', display = 'standalone', themeColor = '#000000', backgroundColor = '#ffffff', scope = './', icons = [] } = options;
8
+ const manifest = {
9
+ name,
10
+ short_name: shortName,
11
+ start_url: startUrl,
12
+ display,
13
+ theme_color: themeColor,
14
+ background_color: backgroundColor,
15
+ scope,
16
+ orientation: 'any'
17
+ };
18
+ if (description) {
19
+ manifest.description = description;
20
+ }
21
+ if (icons.length > 0) {
22
+ manifest.icons = icons;
23
+ }
24
+ return manifest;
25
+ }
26
+ export async function registerServiceWorker(path, options = {}) {
27
+ const { onUpdate, onReady } = options;
28
+ if (!('serviceWorker' in navigator)) {
29
+ console.warn('[PWA] Service workers not supported');
30
+ return null;
31
+ }
32
+ try {
33
+ const registration = await navigator.serviceWorker.register(path);
34
+ registration.addEventListener('updatefound', () => {
35
+ const newWorker = registration.installing;
36
+ if (!newWorker)
37
+ return;
38
+ newWorker.addEventListener('statechange', () => {
39
+ if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
40
+ if (onUpdate) {
41
+ onUpdate(registration);
42
+ }
43
+ }
44
+ else if (newWorker.state === 'activated') {
45
+ if (onReady) {
46
+ onReady(registration);
47
+ }
48
+ }
49
+ });
50
+ });
51
+ if (registration.active && onReady) {
52
+ onReady(registration);
53
+ }
54
+ return registration;
55
+ }
56
+ catch (error) {
57
+ console.error('[PWA] Service worker registration failed:', error);
58
+ return null;
59
+ }
60
+ }
61
+ export async function unregisterServiceWorker() {
62
+ if (!('serviceWorker' in navigator)) {
63
+ return false;
64
+ }
65
+ try {
66
+ const registration = await navigator.serviceWorker.ready;
67
+ await registration.unregister();
68
+ return true;
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ export function isStandalone() {
75
+ if (window.matchMedia) {
76
+ const standaloneNavigator = window.navigator;
77
+ return window.matchMedia('(display-mode: standalone)').matches || standaloneNavigator.standalone === true;
78
+ }
79
+ return false;
80
+ }
81
+ export function isOnline() {
82
+ return navigator.onLine;
83
+ }
84
+ export function subscribeToConnectivity(callbacks) {
85
+ const { onOnline, onOffline } = callbacks;
86
+ const handleOnline = () => {
87
+ if (onOnline)
88
+ onOnline();
89
+ };
90
+ const handleOffline = () => {
91
+ if (onOffline)
92
+ onOffline();
93
+ };
94
+ window.addEventListener('online', handleOnline);
95
+ window.addEventListener('offline', handleOffline);
96
+ return () => {
97
+ window.removeEventListener('online', handleOnline);
98
+ window.removeEventListener('offline', handleOffline);
99
+ };
100
+ }
101
+ export async function requestPersistentStorage() {
102
+ if (navigator.storage?.persist) {
103
+ return await navigator.storage.persist();
104
+ }
105
+ return false;
106
+ }
107
+ export async function getStorageInfo() {
108
+ if (navigator.storage?.estimate) {
109
+ return await navigator.storage.estimate();
110
+ }
111
+ return null;
112
+ }
113
+ export async function sync(tag) {
114
+ if (!('serviceWorker' in navigator)) {
115
+ return false;
116
+ }
117
+ try {
118
+ const registration = await navigator.serviceWorker.ready;
119
+ const syncRegistration = registration;
120
+ if (syncRegistration.sync) {
121
+ await syncRegistration.sync.register(tag);
122
+ return true;
123
+ }
124
+ }
125
+ catch {
126
+ // Background sync not supported or failed
127
+ }
128
+ return false;
129
+ }
130
+ export async function subscribeToPush(options) {
131
+ const { serverUrl, publicKey } = options;
132
+ if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
133
+ console.warn('[PWA] Push notifications not supported');
134
+ return null;
135
+ }
136
+ try {
137
+ const permission = await Notification.requestPermission();
138
+ if (permission !== 'granted') {
139
+ return null;
140
+ }
141
+ const registration = await navigator.serviceWorker.ready;
142
+ const subscription = await registration.pushManager.subscribe({
143
+ userVisibleOnly: true,
144
+ applicationServerKey: urlBase64ToUint8Array(publicKey)
145
+ });
146
+ if (serverUrl) {
147
+ await fetch(serverUrl, {
148
+ method: 'POST',
149
+ headers: { 'Content-Type': 'application/json' },
150
+ body: JSON.stringify(subscription)
151
+ });
152
+ }
153
+ return subscription;
154
+ }
155
+ catch (error) {
156
+ console.error('[PWA] Push subscription failed:', error);
157
+ return null;
158
+ }
159
+ }
160
+ export async function unsubscribeFromPush() {
161
+ if (!('serviceWorker' in navigator)) {
162
+ return false;
163
+ }
164
+ try {
165
+ const registration = await navigator.serviceWorker.ready;
166
+ const subscription = await registration.pushManager.getSubscription();
167
+ if (subscription) {
168
+ await subscription.unsubscribe();
169
+ return true;
170
+ }
171
+ }
172
+ catch {
173
+ // Ignore errors
174
+ }
175
+ return false;
176
+ }
177
+ export async function showNotification(title, options = {}) {
178
+ if (!('serviceWorker' in navigator)) {
179
+ return;
180
+ }
181
+ try {
182
+ const registration = await navigator.serviceWorker.ready;
183
+ await registration.showNotification(title, options);
184
+ }
185
+ catch (error) {
186
+ console.error('[PWA] Show notification failed:', error);
187
+ }
188
+ }
189
+ function urlBase64ToUint8Array(base64String) {
190
+ const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
191
+ const base64 = (base64String + padding)
192
+ .replace(/-/g, '+')
193
+ .replace(/_/g, '/');
194
+ const rawData = window.atob(base64);
195
+ const outputArray = new Uint8Array(rawData.length);
196
+ for (let index = 0; index < rawData.length; ++index) {
197
+ outputArray[index] = rawData.charCodeAt(index);
198
+ }
199
+ return outputArray;
200
+ }
201
+ export const cache = {
202
+ async add(cacheName, urls) {
203
+ if (!('caches' in window))
204
+ return;
205
+ const cacheStorage = await caches.open(cacheName);
206
+ await cacheStorage.addAll(urls);
207
+ },
208
+ async remove(cacheName, urls) {
209
+ if (!('caches' in window))
210
+ return;
211
+ const cacheStorage = await caches.open(cacheName);
212
+ for (const url of urls) {
213
+ await cacheStorage.delete(url);
214
+ }
215
+ },
216
+ async clear() {
217
+ if (!('caches' in window))
218
+ return;
219
+ const cacheKeys = await caches.keys();
220
+ await Promise.all(cacheKeys.map((key) => caches.delete(key)));
221
+ },
222
+ async info() {
223
+ if (!('caches' in window))
224
+ return [];
225
+ const cacheKeys = await caches.keys();
226
+ const info = [];
227
+ for (const key of cacheKeys) {
228
+ const cacheStorage = await caches.open(key);
229
+ const requests = await cacheStorage.keys();
230
+ info.push({ name: key, size: requests.length });
231
+ }
232
+ return info;
233
+ }
234
+ };
235
+ export function checkCapabilities() {
236
+ return {
237
+ serviceWorker: 'serviceWorker' in navigator,
238
+ push: 'PushManager' in window,
239
+ notifications: 'Notification' in window,
240
+ backgroundSync: false,
241
+ persistentStorage: Boolean(navigator.storage?.persist),
242
+ addToHomeScreen: !isStandalone(),
243
+ offline: 'serviceWorker' in navigator
244
+ };
245
+ }
246
+ export const PWA = {
247
+ generateManifest,
248
+ registerServiceWorker,
249
+ unregisterServiceWorker,
250
+ isStandalone,
251
+ isOnline,
252
+ subscribeToConnectivity,
253
+ requestPersistentStorage,
254
+ getStorageInfo,
255
+ sync,
256
+ subscribeToPush,
257
+ unsubscribeFromPush,
258
+ showNotification,
259
+ cache,
260
+ checkCapabilities
261
+ };
262
+ export default PWA;
@@ -0,0 +1,389 @@
1
+ /**
2
+ * @module Router
3
+ *
4
+ * Enhanced router with navigation guards support.
5
+ */
6
+ let currentRoute = null;
7
+ let previousRoute = null;
8
+ const beforeEachGuards = [];
9
+ const afterEachHooks = [];
10
+ let navigating = false;
11
+ let cancelCurrentNavigation = null;
12
+ let spaNavigationCallback = null;
13
+ let spaEnabled = false;
14
+ class Router {
15
+ routes;
16
+ routeMap;
17
+ constructor(routes) {
18
+ this.routes = routes;
19
+ this.routeMap = new Map();
20
+ for (const route of routes) {
21
+ for (const path of route.path) {
22
+ this.routeMap.set(path, route);
23
+ }
24
+ }
25
+ }
26
+ resolve(path) {
27
+ const currentPath = path || document.location.pathname;
28
+ if (this.routeMap.has(currentPath)) {
29
+ return this.routeMap.get(currentPath) || null;
30
+ }
31
+ for (const route of this.routes) {
32
+ if (this.matchRoute(route, currentPath)) {
33
+ return route;
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+ matchRoute(route, path) {
39
+ for (const routePath of route.path) {
40
+ if (this.pathMatches(routePath, path)) {
41
+ return true;
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+ pathMatches(routePath, currentPath) {
47
+ if (!routePath.includes(':') && !routePath.includes('*')) {
48
+ const normalizedRoute = routePath.replace(/\/$/, '') || '/';
49
+ const normalizedCurrent = currentPath.replace(/\/$/, '') || '/';
50
+ return normalizedRoute === normalizedCurrent;
51
+ }
52
+ let pattern = routePath
53
+ .replace(/\//g, '\\/')
54
+ .replace(/:([^/(]+)\(\.\*\)/g, '(.*)')
55
+ .replace(/\/:([^/(]+)\?/g, '(?:/([^/]+))?')
56
+ .replace(/:([^/(?\s]+)/g, '([^/]+)')
57
+ .replace(/\*/g, '(.*)');
58
+ pattern = '^' + pattern + '$';
59
+ return new RegExp(pattern).test(currentPath);
60
+ }
61
+ extractParams(route, path) {
62
+ const params = {};
63
+ for (const routePath of route.path) {
64
+ const match = this.matchAndExtract(routePath, path);
65
+ if (match) {
66
+ Object.assign(params, match);
67
+ }
68
+ }
69
+ return params;
70
+ }
71
+ matchAndExtract(routePath, currentPath) {
72
+ if (!routePath.includes(':')) {
73
+ return null;
74
+ }
75
+ const paramNames = [];
76
+ const pattern = routePath
77
+ .replace(/:([^/(]+)\(\.\*\)/g, (_match, name) => {
78
+ paramNames.push(name);
79
+ return '(.*)';
80
+ })
81
+ .replace(/\/:([^/(]+)\?/g, (_match, name) => {
82
+ paramNames.push(name);
83
+ return '(?:/([^/]+))?';
84
+ })
85
+ .replace(/:([^/(?\s]+)/g, (_match, name) => {
86
+ paramNames.push(name);
87
+ return '([^/]+)';
88
+ });
89
+ const matches = currentPath.match(new RegExp('^' + pattern + '$'));
90
+ if (!matches)
91
+ return null;
92
+ const params = {};
93
+ for (let index = 0; index < paramNames.length; index++) {
94
+ const value = matches[index + 1];
95
+ if (value !== undefined) {
96
+ params[paramNames[index]] = value;
97
+ }
98
+ }
99
+ return params;
100
+ }
101
+ }
102
+ const injectedRouteSets = new WeakSet();
103
+ export async function processRoutes(routes, customPath) {
104
+ const targetPath = customPath || document.location.pathname;
105
+ if (!injectedRouteSets.has(routes)) {
106
+ injectedRouteSets.add(routes);
107
+ for (const route of routes) {
108
+ const originalPaths = [...route.path];
109
+ for (const path of originalPaths) {
110
+ if (typeof path === 'string') {
111
+ injectSystemRoutes(route, path);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ const routerInstance = new Router(routes);
117
+ const toRoute = routerInstance.resolve(targetPath);
118
+ if (!toRoute) {
119
+ throw new Error(`No route found for "${targetPath}".`);
120
+ }
121
+ const to = buildRouteObject(toRoute, routerInstance);
122
+ const from = currentRoute;
123
+ try {
124
+ await runGuards(to, from);
125
+ previousRoute = currentRoute;
126
+ currentRoute = to;
127
+ for (const hook of afterEachHooks) {
128
+ hook(to, from);
129
+ }
130
+ return [toRoute];
131
+ }
132
+ catch (error) {
133
+ if (isNavigationRedirect(error) && error.path) {
134
+ window.location.href = error.path;
135
+ return null;
136
+ }
137
+ throw error;
138
+ }
139
+ }
140
+ function buildRouteObject(routeDef, routerInstance) {
141
+ const currentPath = document.location.pathname;
142
+ const params = routerInstance.extractParams(routeDef, currentPath);
143
+ return {
144
+ name: routeDef.name,
145
+ path: routeDef.path,
146
+ fullPath: currentPath,
147
+ component: routeDef.component,
148
+ meta: routeDef.meta || {},
149
+ beforeEnter: routeDef.beforeEnter,
150
+ params,
151
+ query: parseQuery(document.location.search)
152
+ };
153
+ }
154
+ function parseQuery(search) {
155
+ const query = {};
156
+ if (!search || search === '?')
157
+ return query;
158
+ const params = new URLSearchParams(search.substring(1));
159
+ for (const [key, value] of params) {
160
+ const existing = query[key];
161
+ if (existing) {
162
+ if (Array.isArray(existing)) {
163
+ existing.push(value);
164
+ }
165
+ else {
166
+ query[key] = [existing, value];
167
+ }
168
+ }
169
+ else {
170
+ query[key] = value;
171
+ }
172
+ }
173
+ return query;
174
+ }
175
+ async function runGuards(to, from) {
176
+ navigating = true;
177
+ let cancelled = false;
178
+ cancelCurrentNavigation = () => {
179
+ cancelled = true;
180
+ };
181
+ try {
182
+ for (const guard of beforeEachGuards) {
183
+ if (cancelled)
184
+ break;
185
+ const result = await runGuard(guard, to, from);
186
+ if (result === false) {
187
+ throw new NavigationCancelled();
188
+ }
189
+ if (typeof result === 'string') {
190
+ throw new NavigationRedirect(result);
191
+ }
192
+ if (result && typeof result === 'object' && 'path' in result && typeof result.path === 'string') {
193
+ throw new NavigationRedirect(result.path);
194
+ }
195
+ }
196
+ if (to.beforeEnter && !cancelled) {
197
+ const result = await runGuard(to.beforeEnter, to, from);
198
+ if (result === false) {
199
+ throw new NavigationCancelled();
200
+ }
201
+ if (typeof result === 'string') {
202
+ throw new NavigationRedirect(result);
203
+ }
204
+ }
205
+ }
206
+ finally {
207
+ navigating = false;
208
+ cancelCurrentNavigation = null;
209
+ }
210
+ }
211
+ async function runGuard(guard, to, from) {
212
+ return await new Promise((resolve, reject) => {
213
+ const next = (result) => {
214
+ if (result instanceof Error) {
215
+ reject(result);
216
+ }
217
+ else {
218
+ resolve(result);
219
+ }
220
+ };
221
+ try {
222
+ const guardResult = guard(to, from, next);
223
+ if (guardResult && typeof guardResult.then === 'function') {
224
+ ;
225
+ guardResult.then(resolve).catch(reject);
226
+ }
227
+ else if (guardResult !== undefined) {
228
+ resolve(guardResult);
229
+ }
230
+ }
231
+ catch (error) {
232
+ reject(error);
233
+ }
234
+ });
235
+ }
236
+ export function resetRouter() {
237
+ beforeEachGuards.length = 0;
238
+ afterEachHooks.length = 0;
239
+ navigating = false;
240
+ cancelCurrentNavigation = null;
241
+ currentRoute = null;
242
+ previousRoute = null;
243
+ spaNavigationCallback = null;
244
+ spaEnabled = false;
245
+ }
246
+ class NavigationCancelled extends Error {
247
+ constructor() {
248
+ super('Navigation cancelled');
249
+ this.name = 'NavigationCancelled';
250
+ }
251
+ }
252
+ class NavigationRedirect extends Error {
253
+ path;
254
+ constructor(path) {
255
+ super('Navigation redirect');
256
+ this.name = 'NavigationRedirect';
257
+ this.path = path;
258
+ }
259
+ }
260
+ function isNavigationRedirect(error) {
261
+ return error instanceof Error && error.name === 'NavigationRedirect' && 'path' in error;
262
+ }
263
+ export function beforeEach(guard) {
264
+ beforeEachGuards.push(guard);
265
+ return () => {
266
+ const index = beforeEachGuards.indexOf(guard);
267
+ if (index > -1)
268
+ beforeEachGuards.splice(index, 1);
269
+ };
270
+ }
271
+ export function afterEach(hook) {
272
+ afterEachHooks.push(hook);
273
+ return () => {
274
+ const index = afterEachHooks.indexOf(hook);
275
+ if (index > -1)
276
+ afterEachHooks.splice(index, 1);
277
+ };
278
+ }
279
+ export function getCurrentRoute() {
280
+ return currentRoute;
281
+ }
282
+ export function getPreviousRoute() {
283
+ return previousRoute;
284
+ }
285
+ export function isNavigating() {
286
+ return navigating;
287
+ }
288
+ export function cancelNavigation() {
289
+ if (cancelCurrentNavigation) {
290
+ cancelCurrentNavigation();
291
+ }
292
+ }
293
+ export function _setSpaNavigationCallback(callback) {
294
+ spaNavigationCallback = callback;
295
+ }
296
+ export function setSpaMode(enabled) {
297
+ spaEnabled = enabled;
298
+ }
299
+ export function isSpaMode() {
300
+ return spaEnabled;
301
+ }
302
+ export async function navigateTo(path, options = {}) {
303
+ const { replace = false } = options;
304
+ if (!spaEnabled || !spaNavigationCallback) {
305
+ if (replace) {
306
+ window.location.replace(path);
307
+ }
308
+ else {
309
+ window.location.href = path;
310
+ }
311
+ return false;
312
+ }
313
+ try {
314
+ if (replace) {
315
+ window.history.replaceState({ path }, '', path);
316
+ }
317
+ else {
318
+ window.history.pushState({ path }, '', path);
319
+ }
320
+ await spaNavigationCallback(path);
321
+ return true;
322
+ }
323
+ catch (error) {
324
+ console.error('[metaowl] SPA navigation failed:', error);
325
+ window.location.href = path;
326
+ return false;
327
+ }
328
+ }
329
+ export function navigate(path, options = {}) {
330
+ const { replace = false, reload = true } = options;
331
+ if (reload || !spaEnabled) {
332
+ if (replace) {
333
+ window.location.replace(path);
334
+ }
335
+ else {
336
+ window.location.href = path;
337
+ }
338
+ }
339
+ else {
340
+ void navigateTo(path, { replace });
341
+ }
342
+ }
343
+ export function push(path) {
344
+ void navigateTo(path, { replace: false });
345
+ }
346
+ export function replace(path) {
347
+ void navigateTo(path, { replace: true });
348
+ }
349
+ export function back() {
350
+ window.history.back();
351
+ }
352
+ export function forward() {
353
+ window.history.forward();
354
+ }
355
+ export function go(n) {
356
+ window.history.go(n);
357
+ }
358
+ export const router = {
359
+ beforeEach,
360
+ afterEach,
361
+ get currentRoute() { return getCurrentRoute(); },
362
+ get previousRoute() { return getPreviousRoute(); },
363
+ get isNavigating() { return isNavigating(); },
364
+ cancel: cancelNavigation,
365
+ push,
366
+ replace,
367
+ back,
368
+ forward,
369
+ go,
370
+ navigate,
371
+ navigateTo,
372
+ setSpaMode,
373
+ isSpaMode
374
+ };
375
+ function injectSystemRoutes(route, path) {
376
+ if (path === '/') {
377
+ if (!route.path.includes('/index.html'))
378
+ route.path.push('/index.html');
379
+ }
380
+ else {
381
+ if (!route.path.includes(`${path}.html`))
382
+ route.path.push(`${path}.html`);
383
+ if (!route.path.includes(`${path}/`))
384
+ route.path.push(`${path}/`);
385
+ if (!route.path.includes(`${path}/index.html`))
386
+ route.path.push(`${path}/index.html`);
387
+ }
388
+ return route;
389
+ }