8000 Add a storage equivalent for JS · pyscript/pyscript@16a4880 · GitHub
[go: up one dir, main page]

Skip to content

Commit 16a4880

Browse files
committed
Add a storage equivalent for JS
1 parent e36a57e commit 16a4880

File tree

8 files changed

+204
-217
lines changed

8 files changed

+204
-217
lines changed

pyscript.core/package-lock.json

Lines changed: 80 additions & 213 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyscript.core/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@
4444
"license": "APACHE-2.0",
4545
"dependencies": {
4646
"@ungap/with-resolvers": "^0.1.0",
47+
"@webreflection/idb-map": "^0.3.1",
4748
"basic-devtools": "^0.1.6",
48-
"polyscript": "^0.15.3",
49+
"polyscript": "^0.15.5",
50+
"sabayon": "^0.5.2",
4951
"sticky-module": "^0.1.1",
5052
"to-json-callback": "^0.1.1",
5153
"type-checked-collections": "^0.1.7"
@@ -64,7 +66,7 @@
6466
"@xterm/addon-fit": "^0.10.0",
6567
"@xterm/addon-web-links": "^0.11.0",
6668
"bun": "^1.1.27",
67-
"chokidar": "^3.6.0",
69+
"chokidar": "^4.0.0",
6870
"codemirror": "^6.0.1",
6971
"eslint": "^9.10.0",
7072
"flatted": "^3.3.1",

pyscript.core/rollup/core.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,17 @@ export default [
4040
warn(warning);
4141
},
4242
},
43+
{
44+
input: "./src/storage.js",
45+
plugins: plugins.concat(
46+
process.env.NO_MIN
47+
? [nodeResolve(), commonjs()]
48+
: [nodeResolve(), commonjs(), terser()],
49+
),
50+
output: {
51+
esModule: true,
52+
dir: "./dist",
53+
sourcemap: true,
54+
},
55+
},
4356
];

pyscript.core/src/storage.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { ArrayBuffer, TypedArray } from "sabayon/shared";
2+
import IDBMapSync from "@webreflection/idb-map/sync";
3+
import { parse, stringify } from "flatted";
4+
5+
const to_idb = value => {
6+
if (value == null) return stringify(["null", 0]);
7+
/* eslint-disable no-fallthrough */
8+
switch (typeof value) {
9+
case "object": {
10+
if (value instanceof TypedArray)
11+
return stringify(["memoryview", [...value]]);
12+
if (value instanceof ArrayBuffer)
13+
return stringify(["bytearray", [...new Uint8Array(value)]]);
14+
}
15+
case "string":
16+
case "number":
17+
case "boolean":
18+
return stringify(["generic", value]);
19+
default: throw new TypeError(`Unexpected value: ${String(value)}`);
20+
}
21+
};
22+
23+
const from_idb = value => {
24+
const [kind, result] = parse(value);
25+
if (kind === "null") return null;
26+
if (kind === "generic") return result;
27+
if (kind === "bytearray") return new Uint8Array(value).buffer;
28+
if (kind === "memoryview") return new Uint8Array(value);
29+
return value;
30+
};
31+
32+
// this export simulate pyscript.storage exposed in the Python world
33+
export const storage = async name => {
34+
if (!name) throw new SyntaxError("The storage name must be defined");
35+
36+
const store = new IDBMapSync(`@pyscript/${name}`);
37+
const map = new Map();
38+
await store.sync();
39+
for (const [k, v] of store.entries())
40+
map.set(k, from_idb(v));
41+
42+
const clear = () => {
43+
map.clear();
44+
store.clear();
45+
};
46+
47+
const sync = async () => {
48+
await store.sync();
49+
};
50+
51+
return new Proxy(map, {
52+
ownKeys: (map) => [...map.keys()],
53+
has: (map, name) => map.has(name),
54+
get: (map, name) => {
55+
if (name === "clear") return clear;
56+
if (name === "sync") return sync;
57+
return map.get(name);
58+
},
59+
set: (map, name, value) => {
60+
map.set(name, value);
61+
store.set(name, to_idb(value));
62+
return true;
63+
},
64+
deleteProperty: (map, name) => {
65+
if (map.has(name)) {
66+
map.delete(name);
67+
store.delete(name);
68+
}
69+
return true;
70+
},
71+
});
72+
};

pyscript.core/tests/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
a:hover { opacity: 1; }
1515
</style>
1616
</head>
17-
<body><ul><li><strong><a href="./config/index.html">config</a></strong><ul><li><a href="./config/ambiguous-config.html">ambiguous-config<small>.html</small></a></li><li><a href="./config/same-config.html">same-config<small>.html</small></a></li><li><a href="./config/too-many-config.html">too-many-config<small>.html</small></a></li><li><a href="./config/too-many-py-config.html">too-many-py-config<small>.html</small></a></li></ul></li><li><strong><a href="./issue-7015/index.html">issue-7015</a></strong></li><li><strong><span>js-integration</span></strong><ul><li><a href="./js-integration/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./js-integration/config-url.html">config-url<small>.html</small></a></li><li><strong><a href="./js-integration/fetch/index.html">fetch</a></strong></li><li><a href="./js-integration/ffi.html">ffi<small>.html</small></a></li><li><a href="./js-integration/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./js-integration/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./js-integration/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./js-integration/loader/index.html">loader</a></strong></li><li><a href="./js-integration/mpy.html">mpy<small>.html</small></a></li><li><a href="./js-integration/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./js-integration/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./js-integration/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./js-integration/py-terminals.html">py-terminals<small>.html</small></a></li><li><a href="./js-integration/storage.html">storage<small>.html</small></a></li><li><strong><a href="./js-integration/workers/index.html">workers</a></strong><ul><li><a href="./js-integration/workers/named.html">named<small>.html</small></a></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./piratical/index.html">piratical</a></strong></li><li><strong><a href="./py-editor/index.html">py-editor</a></strong><ul><li><a href="./py-editor/issue-2056.html">issue-2056<small>.html</small></a></li><li><a href="./py-editor/service-worker.html">service-worker<small>.html</small></a></li></ul></li><li><strong><a href="./py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><strong><a href="./pyscript_dom/index.html">pyscript_dom</a></strong></li><li><strong><a href="./service-worker/index.html">service-worker</a></strong></li><li><strong><a href="./ui/index.html">ui</a></strong><ul><li><a href="./ui/gallery.html">gallery<small>.html</small></a></li></ul></li></ul></body>
17+
<body><ul><li><strong><a href="./config/index.html">config</a></strong><ul><li><a href="./config/ambiguous-config.html">ambiguous-config<small>.html</small></a></li><li><a href="./config/same-config.html">same-config<small>.html</small></a></li><li><a href="./config/too-many-config.html">too-many-config<small>.html</small></a></li><li><a href="./config/too-many-py-config.html">too-many-py-config<small>.html</small></a></li></ul></li><li><strong><a href="./issue-7015/index.html">issue-7015</a></strong></li><li><strong><span>js-integration</span></strong><ul><li><a href="./js-integration/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./js-integration/config-url.html">config-url<small>.html</small></a></li><li><strong><a href="./js-integration/fetch/index.html">fetch</a></strong></li><li><a href="./js-integration/ffi.html">ffi<small>.html</small></a></li><li><a href="./js-integration/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./js-integration/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./js-integration/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./js-integration/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./js-integration/loader/index.html">loader</a></strong></li><li><a href="./js-integration/mpy.html">mpy<small>.html</small></a></li><li><a href="./js-integration/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./js-integration/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./js-integration/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./js-integration/py-terminals.html">py-terminals<small>.html</small></a></li><li><a href="./js-integration/storage.html">storage<small>.html</small></a></li><li><strong><a href="./js-integration/workers/index.html">workers</a></strong><ul><li><a href="./js-integration/workers/named.html">named<small>.html</small></a></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./piratical/index.html">piratical</a></strong></li><li><strong><a href="./py-editor/index.html">py-editor</a></strong><ul><li><a href="./py-editor/issue-2056.html">issue-2056<small>.html</small></a></li><li><a href="./py-editor/service-worker.html">service-worker<small>.html</small></a></li></ul></li><li><strong><a href="./py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><strong><a href="./pyscript_dom/index.html">pyscript_dom</a></strong></li><li><strong><a href="./service-worker/index.html">service-worker</a></strong></li><li><strong><a href="./ui/index.html">ui</a></strong><ul><li><a href="./ui/gallery.html">gallery<small>.html</small></a></li></ul></li></ul></body>
1818
</html>

pyscript.core/tests/integration.spec.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ test('MicroPython + Storage', async ({ page }) => {
8888
await page.waitForSelector('html.ok');
8989
});
9090

91+
test('MicroPython + JS Storage', async ({ page }) => {
92+
await page.goto('http://localhost:8080/tests/js-integration/js-storage.html');
93+
await page.waitForSelector('html.ok');
94+
});
95+
9196
test('MicroPython + workers', async ({ page }) => {
9297
await page.goto('http://localhost:8080/tests/js-integration/workers/index.html');
9398
await page.waitForSelector('html.mpy.py');
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<link rel="stylesheet" href="../../dist/core.css">
7+
<script type="module">
8+
import { storage } from '../../dist/storage.js';
9+
10+
const store = await storage('js-storage');
11+
store.test = [1, 2, 3].map(String);
12+
await store.sync();
13+
14+
// be sure the store is not empty before bootstrap
15+
import('../../dist/core.js');
16+
</script>
17+
</head>
18+
<body>
19+
<script type="mpy">
20+
from pyscript import storage, document
21+
22+
store = await storage("js-storage")
23+
24+
if ",".join(store["test"]) == "1,2,3":
25+
document.documentElement.classList.add("ok")
26+
</script>
27+
</body>
28+
</html>

pyscript.core/types/core.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ declare const exportedHooks: {
5454
};
5555
};
5656
declare const exportedConfig: {};
57-
declare const exportedWhenDefined: (type: string) => Promise<object>;
57+
declare const exportedWhenDefined: any;
5858
export { stdlib, optional, inputFailure, TYPES, relative_url, exportedPyWorker as PyWorker, exportedMPWorker as MPWorker, exportedHooks as hooks, exportedConfig as config, exportedWhenDefined as whenDefined };

0 commit comments

Comments
 (0)
0