@zcomponent/core 1.27.2 → 1.28.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.
package/lib/cache.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export interface CacheOptions {
2
+ key: string;
3
+ expireMS?: number;
4
+ revalidateMS?: number;
5
+ expireError?: number;
6
+ cacheError?: boolean;
7
+ lifetime?: 'pageload' | 'session' | 'persistent';
8
+ }
9
+ export interface CachePerformer<T> extends CacheOptions {
10
+ perform: () => Promise<T>;
11
+ }
12
+ export declare function cache<T>(opts: CachePerformer<T>): Promise<T>;
package/lib/cache.js ADDED
@@ -0,0 +1,139 @@
1
+ const dbPromise = new Promise((resolve, reject) => {
2
+ const db = indexedDB.open('zee-cache-db');
3
+ db.onupgradeneeded = event => {
4
+ if (!event || !event.target)
5
+ return;
6
+ const db = event.target.result;
7
+ db.createObjectStore('zee-cache', { keyPath: 'key' });
8
+ };
9
+ db.onsuccess = event => {
10
+ if (!event || !event.target)
11
+ return;
12
+ const idb = event.target.result;
13
+ resolve(idb);
14
+ clearExpired();
15
+ };
16
+ db.onerror = event => {
17
+ reject(event);
18
+ };
19
+ });
20
+ const pageLoadByKey = new Map();
21
+ const inflight = new Map();
22
+ function isError(result) {
23
+ return typeof result['err'] === 'string';
24
+ }
25
+ export async function cache(opts) {
26
+ const lifetime = opts.lifetime ?? 'pageload';
27
+ const now = Date.now();
28
+ let entry;
29
+ if (lifetime === 'pageload') {
30
+ entry = pageLoadByKey.get(opts.key);
31
+ if (entry && entry.expireTime && entry.expireTime < now) {
32
+ pageLoadByKey.delete(opts.key);
33
+ entry = undefined;
34
+ }
35
+ }
36
+ else {
37
+ if (lifetime === 'persistent' || window.sessionStorage.getItem(opts.key) === '1') {
38
+ entry = await getItemFromDB(opts.key);
39
+ if (entry && entry.expireTime && entry.expireTime < now) {
40
+ // Will be deleted next open
41
+ entry = undefined;
42
+ }
43
+ }
44
+ }
45
+ const revalidate = entry && opts.revalidateMS && entry.startTime && now - entry.startTime > opts.revalidateMS;
46
+ const revalidateEntry = revalidate ? entry : undefined;
47
+ if (!entry) {
48
+ entry = await inflight.get(opts.key);
49
+ }
50
+ if (!entry || revalidate) {
51
+ const promise = (async () => {
52
+ let ret;
53
+ try {
54
+ const res = await opts.perform();
55
+ ret = {
56
+ key: opts.key,
57
+ expireTime: opts.expireMS ? Date.now() + opts.expireMS : undefined,
58
+ startTime: now,
59
+ result: { val: res },
60
+ };
61
+ }
62
+ catch (err) {
63
+ if (!opts.cacheError)
64
+ throw err;
65
+ ret = {
66
+ key: opts.key,
67
+ expireTime: opts.expireError ? Date.now() + opts.expireError : undefined,
68
+ startTime: now,
69
+ result: { err: err instanceof Error ? err.message : typeof err === 'string' ? err : 'Unknown error' },
70
+ };
71
+ }
72
+ if (lifetime === 'pageload') {
73
+ pageLoadByKey.set(opts.key, ret);
74
+ }
75
+ else {
76
+ await addItemToDB(ret);
77
+ if (lifetime === 'session')
78
+ window.sessionStorage.setItem(opts.key, '1');
79
+ }
80
+ return ret;
81
+ })();
82
+ inflight.set(opts.key, promise);
83
+ promise
84
+ .then(() => {
85
+ inflight.delete(opts.key);
86
+ })
87
+ .catch(() => {
88
+ inflight.delete(opts.key);
89
+ });
90
+ if (!revalidate)
91
+ entry = await promise;
92
+ else
93
+ entry = revalidateEntry;
94
+ }
95
+ if (!entry)
96
+ throw new Error('No value');
97
+ if (isError(entry.result))
98
+ throw new Error(entry.result.err);
99
+ return entry.result.val;
100
+ }
101
+ function handleRequest(r) {
102
+ return new Promise((resolve, reject) => {
103
+ r.onsuccess = () => {
104
+ resolve(r.result);
105
+ };
106
+ r.onerror = event => {
107
+ reject(event);
108
+ };
109
+ });
110
+ }
111
+ async function clearExpired() {
112
+ const db = await dbPromise;
113
+ const os = db.transaction('zee-cache', 'readonly').objectStore('zee-cache');
114
+ const objects = (await handleRequest(os.getAll()));
115
+ const toDelete = new Set();
116
+ const now = Date.now();
117
+ for (const obj of objects) {
118
+ if (obj.expireTime && obj.expireTime < now) {
119
+ toDelete.add(obj.key);
120
+ }
121
+ }
122
+ if (toDelete.size > 0) {
123
+ const os = db.transaction('zee-cache', 'readwrite').objectStore('zee-cache');
124
+ for (const key of toDelete) {
125
+ os.delete(key);
126
+ }
127
+ os.transaction.commit();
128
+ }
129
+ }
130
+ async function addItemToDB(e) {
131
+ const db = await dbPromise;
132
+ const os = db.transaction('zee-cache', 'readwrite').objectStore('zee-cache');
133
+ await handleRequest(os.put(e));
134
+ }
135
+ async function getItemFromDB(key) {
136
+ const db = await dbPromise;
137
+ const os = db.transaction('zee-cache', 'readonly').objectStore('zee-cache');
138
+ return await handleRequest(os.get(key));
139
+ }
@@ -41,6 +41,10 @@ export interface TextAlertOptions {
41
41
  * Text color of the text alert.
42
42
  */
43
43
  textColor?: string;
44
+ /**
45
+ * Delay in milliseconds before showing the text alert.
46
+ */
47
+ delay?: number;
44
48
  }
45
49
  /**
46
50
  * Triggers the display of a text alert.
@@ -39,7 +39,7 @@ export class TextAlertContext extends Context {
39
39
  const timeout = options?.timeout ?? 3000;
40
40
  setTimeout(() => {
41
41
  entry.classList.add('zcomponent-textalert-shown');
42
- }, 30);
42
+ }, options?.delay ?? 30);
43
43
  if (timeout >= 0) {
44
44
  setTimeout(() => {
45
45
  entry.classList.remove('zcomponent-textalert-shown');
package/lib/index.d.ts CHANGED
@@ -16,3 +16,4 @@ export * from './observable';
16
16
  export * from './contexts/environmentcontext';
17
17
  export * from './contexts/audiocontextcontext';
18
18
  export * from './animation';
19
+ export { cache, CacheOptions, CachePerformer } from './cache';
package/lib/index.js CHANGED
@@ -16,6 +16,7 @@ export * from './observable';
16
16
  export * from './contexts/environmentcontext';
17
17
  export * from './contexts/audiocontextcontext';
18
18
  export * from './animation';
19
+ export { cache } from './cache';
19
20
  const link = document.createElement('link');
20
21
  link.setAttribute('rel', 'stylesheet');
21
22
  link.setAttribute('href', new URL('../css/zcomponent.css', import.meta.url).toString());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zcomponent/core",
3
- "version": "1.27.2",
3
+ "version": "1.28.0-beta.1",
4
4
  "description": "The core component model and built-in functionality for Mattercraft.",
5
5
  "author": "Zappar Limited",
6
6
  "license": "Proprietary",