@reidelsaltres/pureper 0.1.49
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/LICENSE +21 -0
- package/README.md +6 -0
- package/out/foundation/Fetcher.d.ts +6 -0
- package/out/foundation/Fetcher.d.ts.map +1 -0
- package/out/foundation/Fetcher.js +18 -0
- package/out/foundation/Fetcher.js.map +1 -0
- package/out/foundation/Theme.d.ts +9 -0
- package/out/foundation/Theme.d.ts.map +1 -0
- package/out/foundation/Theme.js +28 -0
- package/out/foundation/Theme.js.map +1 -0
- package/out/foundation/Triplet.d.ts +44 -0
- package/out/foundation/Triplet.d.ts.map +1 -0
- package/out/foundation/Triplet.js +174 -0
- package/out/foundation/Triplet.js.map +1 -0
- package/out/foundation/api/ElementHolder.d.ts +4 -0
- package/out/foundation/api/ElementHolder.d.ts.map +1 -0
- package/out/foundation/api/ElementHolder.js +2 -0
- package/out/foundation/api/ElementHolder.js.map +1 -0
- package/out/foundation/api/EmptyConstructor.d.ts +7 -0
- package/out/foundation/api/EmptyConstructor.d.ts.map +1 -0
- package/out/foundation/api/EmptyConstructor.js +2 -0
- package/out/foundation/api/EmptyConstructor.js.map +1 -0
- package/out/foundation/api/Lazy.d.ts +7 -0
- package/out/foundation/api/Lazy.d.ts.map +1 -0
- package/out/foundation/api/Lazy.js +12 -0
- package/out/foundation/api/Lazy.js.map +1 -0
- package/out/foundation/component_api/Component.d.ts +58 -0
- package/out/foundation/component_api/Component.d.ts.map +1 -0
- package/out/foundation/component_api/Component.js +61 -0
- package/out/foundation/component_api/Component.js.map +1 -0
- package/out/foundation/component_api/Page.d.ts +8 -0
- package/out/foundation/component_api/Page.d.ts.map +1 -0
- package/out/foundation/component_api/Page.js +8 -0
- package/out/foundation/component_api/Page.js.map +1 -0
- package/out/foundation/component_api/UniHtml.d.ts +43 -0
- package/out/foundation/component_api/UniHtml.d.ts.map +1 -0
- package/out/foundation/component_api/UniHtml.js +67 -0
- package/out/foundation/component_api/UniHtml.js.map +1 -0
- package/out/foundation/component_api/mixin/Proto.d.ts +33 -0
- package/out/foundation/component_api/mixin/Proto.d.ts.map +1 -0
- package/out/foundation/component_api/mixin/Proto.js +43 -0
- package/out/foundation/component_api/mixin/Proto.js.map +1 -0
- package/out/foundation/worker/Router.d.ts +31 -0
- package/out/foundation/worker/Router.d.ts.map +1 -0
- package/out/foundation/worker/Router.js +95 -0
- package/out/foundation/worker/Router.js.map +1 -0
- package/out/foundation/worker/ServiceWorker.d.ts +25 -0
- package/out/foundation/worker/ServiceWorker.d.ts.map +1 -0
- package/out/foundation/worker/ServiceWorker.js +141 -0
- package/out/foundation/worker/ServiceWorker.js.map +1 -0
- package/out/foundation/worker/api/Clients.d.ts +4 -0
- package/out/foundation/worker/api/Clients.d.ts.map +1 -0
- package/out/foundation/worker/api/Clients.js +2 -0
- package/out/foundation/worker/api/Clients.js.map +1 -0
- package/out/foundation/worker/api/ExtendableEvent.d.ts +4 -0
- package/out/foundation/worker/api/ExtendableEvent.d.ts.map +1 -0
- package/out/foundation/worker/api/ExtendableEvent.js +2 -0
- package/out/foundation/worker/api/ExtendableEvent.js.map +1 -0
- package/out/foundation/worker/api/ExtendableMessageEvent.d.ts +6 -0
- package/out/foundation/worker/api/ExtendableMessageEvent.d.ts.map +1 -0
- package/out/foundation/worker/api/ExtendableMessageEvent.js +2 -0
- package/out/foundation/worker/api/ExtendableMessageEvent.js.map +1 -0
- package/out/foundation/worker/api/FetchEvent.d.ts +6 -0
- package/out/foundation/worker/api/FetchEvent.d.ts.map +1 -0
- package/out/foundation/worker/api/FetchEvent.js +2 -0
- package/out/foundation/worker/api/FetchEvent.js.map +1 -0
- package/out/foundation/worker/api/ServiceWorkerGlobalScope.d.ts +14 -0
- package/out/foundation/worker/api/ServiceWorkerGlobalScope.d.ts.map +1 -0
- package/out/foundation/worker/api/ServiceWorkerGlobalScope.js +2 -0
- package/out/foundation/worker/api/ServiceWorkerGlobalScope.js.map +1 -0
- package/out/index.d.ts +13 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +11 -0
- package/out/index.js.map +1 -0
- package/package.json +39 -0
- package/src/foundation/Fetcher.ts +20 -0
- package/src/foundation/Theme.ts +37 -0
- package/src/foundation/Triplet.ts +208 -0
- package/src/foundation/api/ElementHolder.ts +3 -0
- package/src/foundation/api/EmptyConstructor.ts +6 -0
- package/src/foundation/api/Lazy.ts +12 -0
- package/src/foundation/component_api/Component.ts +92 -0
- package/src/foundation/component_api/Page.ts +9 -0
- package/src/foundation/component_api/UniHtml.ts +82 -0
- package/src/foundation/component_api/mixin/Proto.ts +67 -0
- package/src/foundation/worker/Router.ts +124 -0
- package/src/foundation/worker/ServiceWorker.ts +160 -0
- package/src/foundation/worker/api/Clients.ts +3 -0
- package/src/foundation/worker/api/ExtendableEvent.ts +3 -0
- package/src/foundation/worker/api/ExtendableMessageEvent.ts +6 -0
- package/src/foundation/worker/api/FetchEvent.ts +6 -0
- package/src/foundation/worker/api/ServiceWorkerGlobalScope.ts +14 -0
- package/src/index.ts +22 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Add RouterConfig type to window for TypeScript
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
RouterConfig?: { ASSET_PATH: string };
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
import { AnyConstructor } from "../component_api/mixin/Proto";
|
|
8
|
+
import Page from "../component_api/Page";
|
|
9
|
+
import UniHtml from "../component_api/UniHtml.js";
|
|
10
|
+
import { ServiceWorkerGlobalScope } from "./api/ServiceWorkerGlobalScope";
|
|
11
|
+
|
|
12
|
+
const globals = self as any as ServiceWorkerGlobalScope;
|
|
13
|
+
|
|
14
|
+
export interface Route<T extends UniHtml = UniHtml> {
|
|
15
|
+
route: string;
|
|
16
|
+
path: string;
|
|
17
|
+
|
|
18
|
+
pageFactory: (search?: URLSearchParams) => T;
|
|
19
|
+
}
|
|
20
|
+
export enum AccessType {
|
|
21
|
+
OFFLINE,
|
|
22
|
+
ONLINE,
|
|
23
|
+
BOTH
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const ROUTES: Route[] = [];
|
|
27
|
+
export const TO_CACHE: string[] = [];
|
|
28
|
+
|
|
29
|
+
export abstract class Router {
|
|
30
|
+
public static savePersistedRoute(url: URL) {
|
|
31
|
+
try {
|
|
32
|
+
sessionStorage.setItem("spa:persisted-route", url.toJSON());
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn("[Router Init]: Unable to access sessionStorage.", error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
public static getPersistedRoute(): URL | null {
|
|
38
|
+
try {
|
|
39
|
+
const item = sessionStorage.getItem("spa:persisted-route");
|
|
40
|
+
return item ? URL.parse(item) : null;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.warn("[Router Init]: Unable to access sessionStorage.", error);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
public static clearPersistedRoute() {
|
|
47
|
+
try {
|
|
48
|
+
sessionStorage.removeItem("spa:persisted-route");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.warn("[Router Init]: Unable to clear persisted route.", error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
public static legacyRouteTo(route: string) {
|
|
56
|
+
let url = new URL(route, window.location.origin);
|
|
57
|
+
if (window.location.pathname !== route) {
|
|
58
|
+
window.location.replace(url.href);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
public static tryRouteTo(url: URL) {
|
|
62
|
+
try {
|
|
63
|
+
const found: Route = this.tryFindRoute(url);
|
|
64
|
+
const page: UniHtml = this.createPage(found, url.searchParams);
|
|
65
|
+
|
|
66
|
+
page.load(document.getElementById('page')!);
|
|
67
|
+
|
|
68
|
+
if (typeof window !== "undefined" && window.location) {
|
|
69
|
+
window.history.pushState(page, '', url.href);
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error("[Router]: Unable to route to ", url.href, error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
public static tryFindRoute(url: URL): Route {
|
|
76
|
+
const found = ROUTES.find(r => r.route === url.pathname);
|
|
77
|
+
if (!found) {
|
|
78
|
+
throw new Error(`[Router]: Route not found: ${url.pathname}`);
|
|
79
|
+
}
|
|
80
|
+
return found;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public static async registerRoute<T extends UniHtml>(path: string, route: string, pageFactory: (search?: URLSearchParams) => T,
|
|
84
|
+
inheritedRoute?: Route): Promise<Route> {
|
|
85
|
+
|
|
86
|
+
let prepRoute = route
|
|
87
|
+
let fullRoute = inheritedRoute ? inheritedRoute.route + prepRoute : prepRoute;
|
|
88
|
+
|
|
89
|
+
let routeObj: Route = { route: fullRoute, path, pageFactory };
|
|
90
|
+
|
|
91
|
+
ROUTES.push(routeObj);
|
|
92
|
+
console.log(`[Router]: Registered route: ${fullRoute} -> ${path}`);
|
|
93
|
+
|
|
94
|
+
return Promise.resolve(routeObj);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
private static createPage(route: Route, search?: URLSearchParams): UniHtml {
|
|
99
|
+
return route.pageFactory(search);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
document.addEventListener('click', e => {
|
|
104
|
+
const target = e.target as Element | null;
|
|
105
|
+
if (target) {
|
|
106
|
+
const link = target.closest('a[data-link]') ?? target.closest('re-button[data-link]');
|
|
107
|
+
if (link) {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
const url : URL = new URL(link.getAttribute('href')!, window.location.origin);
|
|
110
|
+
Router.tryRouteTo(url);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
116
|
+
const checkRoutes = () => {
|
|
117
|
+
const routes = ROUTES;
|
|
118
|
+
if (routes) {
|
|
119
|
+
console.log('[Init] [Router]: available routes =', routes.map(r => r.route).join(', '));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
checkRoutes();
|
|
124
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Worker for Pureper SPA
|
|
3
|
+
* Handles client-side routing by intercepting navigation requests
|
|
4
|
+
* and serving index.html for all SPA routes
|
|
5
|
+
*/
|
|
6
|
+
import type { ExtendableEvent } from './api/ExtendableEvent';
|
|
7
|
+
import type { FetchEvent } from './api/FetchEvent';
|
|
8
|
+
import type { ExtendableMessageEvent } from './api/ExtendableMessageEvent';
|
|
9
|
+
import type { Clients } from './api/Clients';
|
|
10
|
+
import type { ServiceWorkerGlobalScope } from './api/ServiceWorkerGlobalScope';
|
|
11
|
+
|
|
12
|
+
// Type assertion for Service Worker context
|
|
13
|
+
declare let self: ServiceWorkerGlobalScope
|
|
14
|
+
const swSelf = self;
|
|
15
|
+
|
|
16
|
+
// Автоматически генерируем CACHE_NAME из base.json
|
|
17
|
+
//import base from '../../../data/base.json';
|
|
18
|
+
const CACHE_NAME = `pureper-v1`;
|
|
19
|
+
|
|
20
|
+
const BASE_PATH = '/Pureper'; // GitHub Pages base path
|
|
21
|
+
const IS_GITHUB_PAGES = swSelf.location.hostname.includes('github.io');
|
|
22
|
+
|
|
23
|
+
const STATIC_ASSETS: string[] = [
|
|
24
|
+
'/index.html'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
if ('serviceWorker' in navigator) {
|
|
28
|
+
window.addEventListener('load', () => {
|
|
29
|
+
navigator.serviceWorker.register('../out/src/foundation/worker/ServiceWorker.js', { type: 'module' })
|
|
30
|
+
.then((registration) => {
|
|
31
|
+
console.log('ServiceWorker registration successful:', registration.scope);
|
|
32
|
+
})
|
|
33
|
+
.catch((error) => {
|
|
34
|
+
console.error('ServiceWorker registration failed:', error);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
window.addEventListener('fetch', (event: FetchEvent) => {
|
|
38
|
+
event.respondWith(
|
|
39
|
+
caches.match(event.request).then((cachedResponse) => {
|
|
40
|
+
console.log(`[ServiceWorker]: Fetching ${event.request.url}`);
|
|
41
|
+
return cachedResponse || fetch(event.request);
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Install event - cache static assets
|
|
49
|
+
*/
|
|
50
|
+
window.addEventListener('install', (event: ExtendableEvent) => {
|
|
51
|
+
console.log('ServiceWorker: Installing...');
|
|
52
|
+
const assetsToCache = [
|
|
53
|
+
...STATIC_ASSETS
|
|
54
|
+
];
|
|
55
|
+
// Remove duplicates
|
|
56
|
+
const uniqueAssets = Array.from(new Set(assetsToCache));
|
|
57
|
+
event.waitUntil(
|
|
58
|
+
caches.open(CACHE_NAME)
|
|
59
|
+
.then((cache: Cache) => {
|
|
60
|
+
console.log('ServiceWorker: Caching static assets and SPA routes');
|
|
61
|
+
return cache.addAll(uniqueAssets);
|
|
62
|
+
})
|
|
63
|
+
.then(() => {
|
|
64
|
+
console.log('ServiceWorker: Installation complete');
|
|
65
|
+
return swSelf.skipWaiting();
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export default class ServiceWorker {
|
|
71
|
+
/**
|
|
72
|
+
* Sends a message to the service worker to cache a specific URL.
|
|
73
|
+
* @param url The URL of the resource to cache.
|
|
74
|
+
*/
|
|
75
|
+
static async addToCache(url: string): Promise<void> {
|
|
76
|
+
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
|
|
77
|
+
navigator.serviceWorker.controller.postMessage({
|
|
78
|
+
type: 'CACHE_URL',
|
|
79
|
+
url: url
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
// Fallback: try to cache directly using the Cache API
|
|
83
|
+
try {
|
|
84
|
+
const cache = await caches.open('pureper-v1');
|
|
85
|
+
const response = await fetch(url);
|
|
86
|
+
if (response.ok) {
|
|
87
|
+
await cache.put(url, response);
|
|
88
|
+
console.log('[ServiceWorker]: Resource cached directly:', url);
|
|
89
|
+
}
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.warn('[ServiceWorker]: Failed to cache resource:', url, e);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Asks the service worker to skip waiting and activate the new version.
|
|
98
|
+
*/
|
|
99
|
+
static skipWaiting(): void {
|
|
100
|
+
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
|
|
101
|
+
navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING' });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Gets the version of the currently active service worker.
|
|
107
|
+
* @returns A promise that resolves with the version string.
|
|
108
|
+
*/
|
|
109
|
+
static getVersion(): Promise<string> {
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
|
|
112
|
+
const messageChannel = new MessageChannel();
|
|
113
|
+
messageChannel.port1.onmessage = (event) => {
|
|
114
|
+
if (event.data.error) {
|
|
115
|
+
reject(event.data.error);
|
|
116
|
+
} else {
|
|
117
|
+
resolve(event.data.version);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
navigator.serviceWorker.controller.postMessage({ type: 'GET_VERSION' }, [messageChannel.port2]);
|
|
121
|
+
} else {
|
|
122
|
+
reject('Service worker not available.');
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Checks if the given URL is present in the Service Worker's cache.
|
|
129
|
+
*/
|
|
130
|
+
static isCached(url: string): Promise<boolean> {
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
|
|
133
|
+
const mc = new MessageChannel();
|
|
134
|
+
mc.port1.onmessage = (ev) => {
|
|
135
|
+
if (ev.data && typeof ev.data.cached === 'boolean') {
|
|
136
|
+
resolve(ev.data.cached);
|
|
137
|
+
} else {
|
|
138
|
+
resolve(false);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
navigator.serviceWorker.controller.postMessage({ type: 'HAS_URL', url }, [mc.port2]);
|
|
142
|
+
} else {
|
|
143
|
+
// Fallback: try CacheStorage directly (same-origin only)
|
|
144
|
+
caches.match(url).then(match => resolve(!!match)).catch(() => resolve(false));
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Checks if the browser is online.
|
|
151
|
+
*/
|
|
152
|
+
static async isOnline(): Promise<boolean> {
|
|
153
|
+
try {
|
|
154
|
+
const response = await fetch('./index.html', { cache: 'no-store' });
|
|
155
|
+
return response && response.ok;
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Clients } from './Clients';
|
|
2
|
+
import { ExtendableEvent } from './ExtendableEvent';
|
|
3
|
+
import { FetchEvent } from './FetchEvent';
|
|
4
|
+
import { ExtendableMessageEvent } from './ExtendableMessageEvent';
|
|
5
|
+
|
|
6
|
+
export interface ServiceWorkerGlobalScope {
|
|
7
|
+
skipWaiting(): Promise<void>;
|
|
8
|
+
clients: Clients;
|
|
9
|
+
location: Location;
|
|
10
|
+
addEventListener(type: 'install', listener: (event: ExtendableEvent) => void): void;
|
|
11
|
+
addEventListener(type: 'activate', listener: (event: ExtendableEvent) => void): void;
|
|
12
|
+
addEventListener(type: 'fetch', listener: (event: FetchEvent) => void): void;
|
|
13
|
+
addEventListener(type: 'message', listener: (event: ExtendableMessageEvent) => void): void;
|
|
14
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Public package entry — re-export foundation APIs
|
|
2
|
+
export { default as ElementHolder } from './foundation/api/ElementHolder';
|
|
3
|
+
export { default as EmptyConstructor } from './foundation/api/EmptyConstructor';
|
|
4
|
+
export { default as Lazy } from './foundation/api/Lazy';
|
|
5
|
+
|
|
6
|
+
export * from './foundation/component_api/mixin/Proto';
|
|
7
|
+
|
|
8
|
+
export { default as UniHtml } from './foundation/component_api/UniHtml';
|
|
9
|
+
export { default as Page } from './foundation/component_api/Page';
|
|
10
|
+
export { default as Component } from './foundation/component_api/Component';
|
|
11
|
+
|
|
12
|
+
export { default as Triplet } from './foundation/Triplet';
|
|
13
|
+
|
|
14
|
+
export { default as Fetcher } from './foundation/Fetcher';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export { Router } from './foundation/worker/Router';
|
|
18
|
+
export { default as ServiceWorker } from './foundation/worker/ServiceWorker';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export * from './foundation/Theme';
|