monoidentity 0.0.4 → 0.2.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.
- package/dist/Monoidentity.svelte +73 -2
- package/dist/_createstore.d.ts +10 -0
- package/dist/_createstore.js +9 -0
- package/dist/_localstorage.d.ts +3 -0
- package/dist/_localstorage.js +134 -0
- package/dist/_replay.d.ts +16 -0
- package/dist/_replay.js +97 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +13 -12
- package/dist/trackready.d.ts +1 -1
- package/dist/trackready.js +8 -13
- package/dist/utils-callback.d.ts +3 -9
- package/dist/utils-callback.js +5 -25
- package/dist/utils-timing.d.ts +1 -0
- package/dist/utils-timing.js +21 -0
- package/package.json +4 -2
- package/dist/storage-private-local.d.ts +0 -1
- package/dist/storage-private-local.js +0 -56
- /package/dist/{utils-login.d.ts → utils-base36.d.ts} +0 -0
- /package/dist/{utils-login.js → utils-base36.js} +0 -0
package/dist/Monoidentity.svelte
CHANGED
|
@@ -6,11 +6,82 @@
|
|
|
6
6
|
let { app, scopes, children }: { app: string; scopes: Scope[]; children: Snippet } = $props();
|
|
7
7
|
|
|
8
8
|
let ready = $state(false);
|
|
9
|
-
|
|
9
|
+
let backup: (() => void) | undefined = $state();
|
|
10
|
+
trackReady(
|
|
11
|
+
app,
|
|
12
|
+
scopes,
|
|
13
|
+
(callback) => {
|
|
14
|
+
backup = () => {
|
|
15
|
+
callback();
|
|
16
|
+
backup = undefined;
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
() => (ready = true),
|
|
20
|
+
);
|
|
10
21
|
</script>
|
|
11
22
|
|
|
23
|
+
{#snippet backupUI(yes: () => void, no: () => void)}
|
|
24
|
+
<p>Avoid reconfiguration with a backup folder.</p>
|
|
25
|
+
<div class="buttons">
|
|
26
|
+
<button class="primary" onclick={yes}>Connect</button>
|
|
27
|
+
<button onclick={no}>Skip</button>
|
|
28
|
+
</div>
|
|
29
|
+
{/snippet}
|
|
30
|
+
|
|
12
31
|
{#if ready}
|
|
13
32
|
{@render children()}
|
|
33
|
+
{#if backup}
|
|
34
|
+
<div class="backup toast">
|
|
35
|
+
{@render backupUI(backup, () => (backup = undefined))}
|
|
36
|
+
</div>
|
|
37
|
+
{/if}
|
|
38
|
+
{:else if backup}
|
|
39
|
+
<div class="backup center">
|
|
40
|
+
{@render backupUI(backup, () => (backup = undefined))}
|
|
41
|
+
</div>
|
|
14
42
|
{:else}
|
|
15
|
-
<p
|
|
43
|
+
<p class="center">Setting up</p>
|
|
16
44
|
{/if}
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
.backup {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: 0.5rem;
|
|
51
|
+
|
|
52
|
+
background-color: light-dark(#000, #fff);
|
|
53
|
+
color: light-dark(#fff, #000);
|
|
54
|
+
line-height: 1;
|
|
55
|
+
padding: 0.75rem;
|
|
56
|
+
border-radius: 1.5rem;
|
|
57
|
+
|
|
58
|
+
> .buttons {
|
|
59
|
+
display: flex;
|
|
60
|
+
gap: 0.5rem;
|
|
61
|
+
> button {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex: 1;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
height: 2rem;
|
|
67
|
+
border-radius: 0.75rem;
|
|
68
|
+
&.primary {
|
|
69
|
+
background-color: light-dark(#fff, #000);
|
|
70
|
+
color: light-dark(#000, #fff);
|
|
71
|
+
}
|
|
72
|
+
border: none;
|
|
73
|
+
font: inherit;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
.toast {
|
|
79
|
+
position: fixed;
|
|
80
|
+
right: 1rem;
|
|
81
|
+
top: 1rem;
|
|
82
|
+
z-index: 1000;
|
|
83
|
+
}
|
|
84
|
+
.center {
|
|
85
|
+
margin: auto;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Dict = Record<string, string>;
|
|
2
|
+
type ProxyHandlerWithoutTarget = {
|
|
3
|
+
has?(p: string | symbol): boolean;
|
|
4
|
+
get?(p: string | symbol, receiver: any): any;
|
|
5
|
+
set?(p: string | symbol, newValue: any, receiver: any): boolean;
|
|
6
|
+
deleteProperty?(p: string | symbol): boolean;
|
|
7
|
+
ownKeys?(): ArrayLike<string | symbol>;
|
|
8
|
+
};
|
|
9
|
+
export declare const createStore: (implementation: ProxyHandlerWithoutTarget) => Dict;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const createStore = (implementation) => {
|
|
2
|
+
const target = {};
|
|
3
|
+
const handler = {};
|
|
4
|
+
for (const key of Object.keys(implementation)) {
|
|
5
|
+
const trap = implementation[key];
|
|
6
|
+
handler[key] = (_, ...args) => trap(...args);
|
|
7
|
+
}
|
|
8
|
+
return new Proxy(target, handler);
|
|
9
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { createStore } from "./_createstore.js";
|
|
2
|
+
import { wrapWithReplay } from "./_replay.js";
|
|
3
|
+
import { canBackup } from "./utils-callback.js";
|
|
4
|
+
import { openDB } from "idb";
|
|
5
|
+
const prefix = "monoidentity/";
|
|
6
|
+
const prefixed = (key) => `${prefix}${key}`;
|
|
7
|
+
const unprefixed = (key) => {
|
|
8
|
+
if (!key.startsWith(prefix))
|
|
9
|
+
throw new Error("Key is not prefixed");
|
|
10
|
+
return key.slice(prefix.length);
|
|
11
|
+
};
|
|
12
|
+
const get = (key) => {
|
|
13
|
+
if (typeof key != "string")
|
|
14
|
+
return undefined;
|
|
15
|
+
const value = localStorage.getItem(prefixed(key));
|
|
16
|
+
if (value == null)
|
|
17
|
+
return undefined;
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
export const init = () => createStore({
|
|
21
|
+
has(key) {
|
|
22
|
+
return typeof key == "string" && localStorage.getItem(prefixed(key)) !== null;
|
|
23
|
+
},
|
|
24
|
+
get,
|
|
25
|
+
set(key, value) {
|
|
26
|
+
if (typeof key == "string") {
|
|
27
|
+
localStorage.setItem(prefixed(key), value);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
},
|
|
32
|
+
deleteProperty(key) {
|
|
33
|
+
if (typeof key == "string") {
|
|
34
|
+
localStorage.removeItem(prefixed(key));
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
},
|
|
39
|
+
ownKeys() {
|
|
40
|
+
const keys = [];
|
|
41
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
42
|
+
const key = localStorage.key(i);
|
|
43
|
+
if (key && key.startsWith(prefix)) {
|
|
44
|
+
keys.push(unprefixed(key));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return keys;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
export const wrapWithBackup = (storage, requestBackup) => {
|
|
51
|
+
if (!canBackup)
|
|
52
|
+
return storage;
|
|
53
|
+
if (localStorage["monoidentity-x/backup"] == "off")
|
|
54
|
+
return storage;
|
|
55
|
+
const { proxy, flush, setTransmit, load } = wrapWithReplay(storage);
|
|
56
|
+
const getDir = async () => {
|
|
57
|
+
const db = await openDB("monoidentity-x");
|
|
58
|
+
const handle = (await db.get("handles", "backup"));
|
|
59
|
+
if (!handle)
|
|
60
|
+
throw new Error("No backup handle found");
|
|
61
|
+
return handle;
|
|
62
|
+
};
|
|
63
|
+
const setDir = async (dir) => {
|
|
64
|
+
const db = await openDB("monoidentity-x", 1, {
|
|
65
|
+
upgrade(db) {
|
|
66
|
+
db.createObjectStore("handles");
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
await db.put("handles", dir, "backup");
|
|
70
|
+
};
|
|
71
|
+
const init = async (dir) => {
|
|
72
|
+
let dirCache = {};
|
|
73
|
+
const getDirCached = async (route) => {
|
|
74
|
+
let key = "";
|
|
75
|
+
let parent = dir;
|
|
76
|
+
for (const path of route) {
|
|
77
|
+
key += "/";
|
|
78
|
+
key += path;
|
|
79
|
+
if (!dirCache[key]) {
|
|
80
|
+
dirCache[key] = await parent.getDirectoryHandle(path, { create: true });
|
|
81
|
+
}
|
|
82
|
+
parent = dirCache[key];
|
|
83
|
+
}
|
|
84
|
+
return parent;
|
|
85
|
+
};
|
|
86
|
+
setTransmit(async (path, mod) => {
|
|
87
|
+
const pathParts = path.split("/");
|
|
88
|
+
const parent = await getDirCached(pathParts.slice(0, -1));
|
|
89
|
+
if (mod.type == "set") {
|
|
90
|
+
const file = await parent.getFileHandle(pathParts.at(-1), { create: true });
|
|
91
|
+
const writable = await file.createWritable();
|
|
92
|
+
await writable.write(mod.new);
|
|
93
|
+
await writable.close();
|
|
94
|
+
}
|
|
95
|
+
else if (mod.type == "delete") {
|
|
96
|
+
await parent.removeEntry(pathParts.at(-1));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
await flush();
|
|
100
|
+
};
|
|
101
|
+
if (localStorage["monoidentity-x/backup"] == "on") {
|
|
102
|
+
getDir()
|
|
103
|
+
.then((dir) => init(dir))
|
|
104
|
+
.catch(() => {
|
|
105
|
+
delete localStorage["monoidentity-x/backup"];
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
localStorage["monoidentity-x/backup"] = "off";
|
|
110
|
+
requestBackup(async () => {
|
|
111
|
+
const dir = await showDirectoryPicker({ mode: "readwrite" });
|
|
112
|
+
await setDir(dir);
|
|
113
|
+
await init(dir);
|
|
114
|
+
// Restore from backup
|
|
115
|
+
const backup = {};
|
|
116
|
+
const traverse = async (d, path) => {
|
|
117
|
+
for await (const entry of d.values()) {
|
|
118
|
+
if (entry.kind == "file") {
|
|
119
|
+
const file = await entry.getFile();
|
|
120
|
+
const text = await file.text();
|
|
121
|
+
backup[`${path}${entry.name}`] = text;
|
|
122
|
+
}
|
|
123
|
+
else if (entry.kind == "directory") {
|
|
124
|
+
await traverse(entry, `${path}${entry.name}/`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
await traverse(dir, "");
|
|
129
|
+
await load(backup);
|
|
130
|
+
localStorage["monoidentity-x/backup"] = "on";
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return proxy;
|
|
134
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Dict } from "./_createstore.js";
|
|
2
|
+
type Modification = {
|
|
3
|
+
type: "set";
|
|
4
|
+
old?: string;
|
|
5
|
+
new: string;
|
|
6
|
+
} | {
|
|
7
|
+
type: "delete";
|
|
8
|
+
};
|
|
9
|
+
type Transmit = (path: string, mod: Modification) => Promise<void>;
|
|
10
|
+
export declare const wrapWithReplay: (storage: Dict) => {
|
|
11
|
+
proxy: Dict;
|
|
12
|
+
flush: () => Promise<void>;
|
|
13
|
+
setTransmit(tx: Transmit): void;
|
|
14
|
+
load(external: Dict): Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
package/dist/_replay.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { throttle } from "./utils-timing.js";
|
|
2
|
+
export const wrapWithReplay = (storage) => {
|
|
3
|
+
let modifications = {};
|
|
4
|
+
if (localStorage["monoidentity-x/backup/modifications"]) {
|
|
5
|
+
modifications = JSON.parse(localStorage["monoidentity-x/backup/modifications"]);
|
|
6
|
+
}
|
|
7
|
+
const save = () => {
|
|
8
|
+
if (Object.keys(modifications).length) {
|
|
9
|
+
localStorage["monoidentity-x/backup/modifications"] = JSON.stringify(modifications);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
delete localStorage["monoidentity-x/backup/modifications"];
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
window.addEventListener("beforeunload", save);
|
|
16
|
+
window.addEventListener("pagehide", save);
|
|
17
|
+
let transmit;
|
|
18
|
+
const flush = throttle(async () => {
|
|
19
|
+
try {
|
|
20
|
+
const tx = transmit;
|
|
21
|
+
if (!tx)
|
|
22
|
+
return;
|
|
23
|
+
const paths = Object.keys(modifications);
|
|
24
|
+
const tasks = paths
|
|
25
|
+
.map(async (path) => {
|
|
26
|
+
const mod = modifications[path];
|
|
27
|
+
await tx(path, mod);
|
|
28
|
+
})
|
|
29
|
+
.map((task, i) => task
|
|
30
|
+
.then(() => {
|
|
31
|
+
const path = paths[i];
|
|
32
|
+
delete modifications[path];
|
|
33
|
+
})
|
|
34
|
+
.catch((err) => {
|
|
35
|
+
const path = paths[i];
|
|
36
|
+
console.warn("[monoidentity] transmit failed, will retry later", { path, err });
|
|
37
|
+
}));
|
|
38
|
+
await Promise.all(tasks);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
save();
|
|
42
|
+
}
|
|
43
|
+
}, 1000);
|
|
44
|
+
const proxy = new Proxy(storage, {
|
|
45
|
+
set(target, prop, value) {
|
|
46
|
+
let old = target[prop];
|
|
47
|
+
target[prop] = value;
|
|
48
|
+
const oldMod = modifications[prop];
|
|
49
|
+
if (oldMod?.type == "set")
|
|
50
|
+
old = oldMod.old;
|
|
51
|
+
modifications[prop] = { type: "set", old, new: value };
|
|
52
|
+
flush();
|
|
53
|
+
return true;
|
|
54
|
+
},
|
|
55
|
+
deleteProperty(target, prop) {
|
|
56
|
+
const success = delete target[prop];
|
|
57
|
+
if (success) {
|
|
58
|
+
modifications[prop] = { type: "delete" };
|
|
59
|
+
flush();
|
|
60
|
+
}
|
|
61
|
+
return success;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
proxy,
|
|
66
|
+
flush,
|
|
67
|
+
setTransmit(tx) {
|
|
68
|
+
transmit = tx;
|
|
69
|
+
},
|
|
70
|
+
// load() just resets to the external version
|
|
71
|
+
// if you have modifications, flush() them first
|
|
72
|
+
async load(external) {
|
|
73
|
+
modifications = {};
|
|
74
|
+
for (const [key, value] of Object.entries(external)) {
|
|
75
|
+
storage[key] = value;
|
|
76
|
+
}
|
|
77
|
+
for (const key of Object.keys(storage)) {
|
|
78
|
+
if (!(key in external)) {
|
|
79
|
+
delete storage[key];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// // If for *some* reason, there are pending modifications, rebase and resend them
|
|
83
|
+
// for (const [key, mod] of Object.entries(oldModifications)) {
|
|
84
|
+
// if (mod.type == "set") {
|
|
85
|
+
// if (proxy[key] != mod.old)
|
|
86
|
+
// console.warn(
|
|
87
|
+
// `[monoidentity] modification to "${key}" will be force applied over external change`,
|
|
88
|
+
// );
|
|
89
|
+
// proxy[key] = mod.new;
|
|
90
|
+
// } else if (mod.type == "delete") {
|
|
91
|
+
// delete proxy[key];
|
|
92
|
+
// }
|
|
93
|
+
// }
|
|
94
|
+
// flush();
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/storage.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Login } from "./utils-
|
|
1
|
+
import { type Login } from "./utils-base36.js";
|
|
2
2
|
export declare const setup: (i: Record<string, string>, a: string) => void;
|
|
3
3
|
export declare const getLogin: () => Login;
|
|
4
|
-
export declare const getStorage: (realm: "cache") =>
|
|
4
|
+
export declare const getStorage: (realm: "cache") => import("./_createstore.js").Dict;
|
package/dist/storage.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { stringify, parse } from "devalue";
|
|
2
|
-
import { decode } from "./utils-
|
|
2
|
+
import { decode } from "./utils-base36.js";
|
|
3
|
+
import { createStore } from "./_createstore.js";
|
|
3
4
|
let implementation;
|
|
4
5
|
let app = "";
|
|
5
6
|
export const setup = (i, a) => {
|
|
@@ -7,30 +8,30 @@ export const setup = (i, a) => {
|
|
|
7
8
|
app = a;
|
|
8
9
|
};
|
|
9
10
|
export const getLogin = () => {
|
|
10
|
-
|
|
11
|
-
if (!storage)
|
|
11
|
+
if (!implementation)
|
|
12
12
|
throw new Error("No implementation set");
|
|
13
|
-
const login =
|
|
13
|
+
const login = implementation[".core/login.encjson"];
|
|
14
14
|
if (!login)
|
|
15
15
|
throw new Error("No login found");
|
|
16
16
|
return JSON.parse(decode(login));
|
|
17
17
|
};
|
|
18
18
|
export const getStorage = (realm) => {
|
|
19
19
|
const prefix = (text) => `.${realm}/${app}/${text}`;
|
|
20
|
-
const storage = implementation;
|
|
21
20
|
if (!app)
|
|
22
21
|
throw new Error("No app set");
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const item =
|
|
22
|
+
return createStore({
|
|
23
|
+
get(key) {
|
|
24
|
+
if (!implementation)
|
|
25
|
+
throw new Error("No implementation set");
|
|
26
|
+
const item = implementation[prefix(key)];
|
|
28
27
|
if (!item)
|
|
29
28
|
return undefined;
|
|
30
29
|
return parse(item);
|
|
31
30
|
},
|
|
32
|
-
set(
|
|
33
|
-
|
|
31
|
+
set(key, value) {
|
|
32
|
+
if (!implementation)
|
|
33
|
+
throw new Error("No implementation set");
|
|
34
|
+
implementation[prefix(key)] = stringify(value);
|
|
34
35
|
return true;
|
|
35
36
|
},
|
|
36
37
|
});
|
package/dist/trackready.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type Scope } from "./utils-scope.js";
|
|
2
|
-
export declare const trackReady: (app: string, scopes: Scope[], callback: () => void) => void;
|
|
2
|
+
export declare const trackReady: (app: string, scopes: Scope[], requestBackup: (callback: () => void) => void, callback: () => void) => void;
|
package/dist/trackready.js
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
import { rememberCallback } from "./utils-callback.js";
|
|
2
2
|
import {} from "./utils-scope.js";
|
|
3
|
-
import { init as initLocal } from "./
|
|
3
|
+
import { init as initLocal, wrapWithBackup } from "./_localstorage.js";
|
|
4
4
|
import { setup } from "./storage.js";
|
|
5
|
-
export const trackReady = (app, scopes, callback) => {
|
|
5
|
+
export const trackReady = (app, scopes, requestBackup, callback) => {
|
|
6
6
|
const params = new URLSearchParams(location.hash.slice(1));
|
|
7
7
|
let memory = localStorage.monoidentityMemory
|
|
8
8
|
? JSON.parse(localStorage.monoidentityMemory)
|
|
9
9
|
: undefined;
|
|
10
|
-
let createNew = false;
|
|
11
10
|
let fileTasks = undefined;
|
|
12
11
|
const paramCB = params.get("monoidentitycallback");
|
|
13
12
|
if (paramCB) {
|
|
14
13
|
history.replaceState(null, "", location.pathname);
|
|
15
14
|
const cb = JSON.parse(paramCB);
|
|
16
|
-
console.
|
|
15
|
+
// console.debug("[monoidentity] callback", cb);
|
|
17
16
|
memory = rememberCallback(cb, memory);
|
|
18
17
|
localStorage.monoidentityMemory = JSON.stringify(memory);
|
|
19
|
-
if (cb.connect.method == "file" && cb.connect.createNew) {
|
|
20
|
-
createNew = true;
|
|
21
|
-
}
|
|
22
18
|
fileTasks = cb.fileTasks;
|
|
23
19
|
}
|
|
24
20
|
if (!memory) {
|
|
@@ -34,19 +30,18 @@ export const trackReady = (app, scopes, callback) => {
|
|
|
34
30
|
// TODO
|
|
35
31
|
throw new Error("unimplemented");
|
|
36
32
|
}
|
|
37
|
-
else if (memory.method == "file") {
|
|
38
|
-
// TODO (use createNew here)
|
|
39
|
-
throw new Error("unimplemented");
|
|
40
|
-
}
|
|
41
33
|
else if (memory.method == "localStorage") {
|
|
42
34
|
storage = initLocal();
|
|
35
|
+
storage = wrapWithBackup(storage, requestBackup);
|
|
43
36
|
}
|
|
44
37
|
else {
|
|
45
38
|
throw new Error("unreachable");
|
|
46
39
|
}
|
|
47
40
|
setup(storage, app);
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
if (fileTasks) {
|
|
42
|
+
for (const file in fileTasks) {
|
|
43
|
+
storage[file] = fileTasks[file];
|
|
44
|
+
}
|
|
50
45
|
}
|
|
51
46
|
callback();
|
|
52
47
|
};
|
package/dist/utils-callback.d.ts
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
export declare const supportsFile: boolean;
|
|
2
1
|
type Setup = {
|
|
3
2
|
method: "cloud";
|
|
4
3
|
jwt: string;
|
|
5
|
-
} | {
|
|
6
|
-
method: "file";
|
|
7
|
-
createNew?: never;
|
|
8
4
|
} | {
|
|
9
5
|
method: "localStorage";
|
|
10
6
|
};
|
|
11
|
-
export type Memory = Setup
|
|
12
|
-
knownFiles: string[];
|
|
13
|
-
};
|
|
7
|
+
export type Memory = Setup;
|
|
14
8
|
export type Callback = {
|
|
15
9
|
scopes: string[];
|
|
16
10
|
connect: Setup | {
|
|
17
|
-
method: "
|
|
18
|
-
createNew: boolean;
|
|
11
|
+
method: "localStorage";
|
|
19
12
|
};
|
|
20
13
|
fileTasks: Record<string, string> | undefined;
|
|
21
14
|
};
|
|
15
|
+
export declare const canBackup: boolean;
|
|
22
16
|
export declare const rememberCallback: (data: Callback, pastMemory?: Memory) => Memory;
|
|
23
17
|
export {};
|
package/dist/utils-callback.js
CHANGED
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const canBackup = navigator.userAgent.includes("CrOS") && Boolean(window.showDirectoryPicker);
|
|
2
2
|
export const rememberCallback = (data, pastMemory) => {
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
for (const file of pastMemory.knownFiles) {
|
|
8
|
-
knownFilesSet.add(file);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
if (fileTasks) {
|
|
12
|
-
for (const file of Object.keys(fileTasks)) {
|
|
13
|
-
knownFilesSet.add(file);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
if (scopes.includes("login-recognized")) {
|
|
17
|
-
const path = ".core/login.encjson";
|
|
18
|
-
if (connect.method == "cloud") {
|
|
19
|
-
knownFilesSet.add(path);
|
|
20
|
-
}
|
|
21
|
-
else if (!knownFilesSet.has(path)) {
|
|
22
|
-
console.warn("unexpected login deficit");
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
const knownFiles = Array.from(knownFilesSet);
|
|
26
|
-
return { ...setup, knownFiles };
|
|
3
|
+
const { connect } = data;
|
|
4
|
+
return connect.method == "cloud"
|
|
5
|
+
? { method: "cloud", jwt: connect.jwt }
|
|
6
|
+
: { method: connect.method };
|
|
27
7
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function throttle(fn: () => Promise<unknown>, delay: number): () => Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function throttle(fn, delay) {
|
|
2
|
+
let isRunning = false;
|
|
3
|
+
let hasPendingCall = false;
|
|
4
|
+
return async () => {
|
|
5
|
+
hasPendingCall = true;
|
|
6
|
+
if (isRunning) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
isRunning = true;
|
|
11
|
+
while (hasPendingCall) {
|
|
12
|
+
hasPendingCall = false;
|
|
13
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
14
|
+
await fn();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
isRunning = false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monoidentity",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"repository": "KTibow/monoidentity",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "KTibow"
|
|
@@ -28,13 +28,15 @@
|
|
|
28
28
|
"svelte": "^5.0.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"devalue": "^5.3.2"
|
|
31
|
+
"devalue": "^5.3.2",
|
|
32
|
+
"idb": "^8.0.3"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@sveltejs/adapter-static": "^3.0.9",
|
|
35
36
|
"@sveltejs/kit": "^2.22.0",
|
|
36
37
|
"@sveltejs/package": "^2.0.0",
|
|
37
38
|
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
|
39
|
+
"@types/wicg-file-system-access": "^2023.10.6",
|
|
38
40
|
"publint": "^0.3.2",
|
|
39
41
|
"svelte": "^5.0.0",
|
|
40
42
|
"svelte-check": "^4.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const init: () => Record<string, string>;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const prefix = "monoidentity/";
|
|
2
|
-
const prefixed = (key) => `${prefix}${key}`;
|
|
3
|
-
const unprefixed = (key) => {
|
|
4
|
-
if (!key.startsWith(prefix))
|
|
5
|
-
throw new Error("Key is not prefixed");
|
|
6
|
-
return key.slice(prefix.length);
|
|
7
|
-
};
|
|
8
|
-
const target = {};
|
|
9
|
-
const get = (_, key) => {
|
|
10
|
-
if (typeof key != "string")
|
|
11
|
-
return undefined;
|
|
12
|
-
const value = localStorage.getItem(prefixed(key));
|
|
13
|
-
if (value == null)
|
|
14
|
-
return undefined;
|
|
15
|
-
return value;
|
|
16
|
-
};
|
|
17
|
-
export const init = () => new Proxy(target, {
|
|
18
|
-
get,
|
|
19
|
-
set(_, key, value) {
|
|
20
|
-
if (typeof key == "string") {
|
|
21
|
-
localStorage.setItem(prefixed(key), value);
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
},
|
|
26
|
-
deleteProperty(_, key) {
|
|
27
|
-
if (typeof key == "string") {
|
|
28
|
-
localStorage.removeItem(prefixed(key));
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
return false;
|
|
32
|
-
},
|
|
33
|
-
has(_, key) {
|
|
34
|
-
return typeof key == "string" && localStorage.getItem(prefixed(key)) !== null;
|
|
35
|
-
},
|
|
36
|
-
ownKeys(_) {
|
|
37
|
-
const keys = [];
|
|
38
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
39
|
-
const key = localStorage.key(i);
|
|
40
|
-
if (key && key.startsWith(prefix)) {
|
|
41
|
-
keys.push(unprefixed(key));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return keys;
|
|
45
|
-
},
|
|
46
|
-
getOwnPropertyDescriptor(_, key) {
|
|
47
|
-
if (typeof key == "string" && localStorage.getItem(prefixed(key)) !== null) {
|
|
48
|
-
return {
|
|
49
|
-
enumerable: true,
|
|
50
|
-
configurable: true,
|
|
51
|
-
value: get(target, key),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
return undefined;
|
|
55
|
-
},
|
|
56
|
-
});
|
|
File without changes
|
|
File without changes
|