Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/probe_opfsstamp.mjs

2.8 KiB, 1 run

created by r2519314175:59, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// probe_opfsstamp.mjs — does an OPFS file keep the same `lastModified` when nobody
2// writes it?
3//
4// The live view decides a source has changed by asking every watched file for
5// `lastModified` and `size` and comparing the answer with last time's. That is the
6// only mechanism there is — the File System Access API has no change events — so if
7// a browser hands back a fresh time on every `getFile()`, the watch loop rebuilds
8// once a second with nothing edited, which is exactly what the author reported and
9// what headless Chromium here does NOT do. So ask each engine, with no app in the
10// way: one file, written once, asked five times over five seconds.
11//
12// It asserts nothing and is not a verifier.
13//
14// eval "$(bash dev/world.sh 5 --env)"
15// node dev/probe_opfsstamp.mjs # every engine that is installed
16// node dev/probe_opfsstamp.mjs chromium webkit
17import os from 'node:os';
18import path from 'node:path';
19import { pathToFileURL } from 'node:url';
20
21const PW = process.env.DAIMOND_PW
22 || path.join(os.homedir(), '.red-pw/node_modules/playwright-core/index.mjs');
23const pw = await import(pathToFileURL(PW).href);
24const APP = process.env.DAIMOND_APP || 'http://localhost:8777';
25
26const want = process.argv.slice(2).filter(a => !a.startsWith('-'));
27const engines = (want.length ? want : ['chromium', 'firefox', 'webkit']);
28
29const SCRIPT = async () => {
30 const out = { steps: [] };
31 try {
32 const root = await navigator.storage.getDirectory();
33 const h = await root.getFileHandle('stampprobe.txt', { create: true });
34 const w = await h.createWritable();
35 await w.write('one');
36 await w.close();
37 for (let i = 0; i < 5; i++) {
38 const f = await h.getFile();
39 out.steps.push({ at: i, lastModified: f.lastModified, size: f.size });
40 await new Promise(r => setTimeout(r, 1000));
41 }
42 } catch (e) {
43 out.error = String(e && e.message ? e.message : e);
44 }
45 return out;
46};
47
48for (const name of engines) {
49 const type = pw[name];
50 if (!type) { console.log(`${name}: not in playwright-core`); continue; }
51 let b;
52 try {
53 b = await type.launch({ headless: true });
54 } catch (e) {
55 console.log(`${name}: will not launch — ${String(e.message).split('\n')[0]}`);
56 continue;
57 }
58 try {
59 const p = await b.newPage();
60 await p.goto(APP + '/index.html', { waitUntil: 'domcontentloaded' });
61 const r = await p.evaluate(SCRIPT);
62 if (r.error) { console.log(`${name}: ${r.error}`); }
63 else {
64 const st = r.steps.map(s => s.lastModified);
65 const same = st.every(v => v === st[0]);
66 console.log(`${name}: lastModified ${same ? 'STABLE' : 'MOVES'} — ${st.join(', ')}`);
67 if (!same) console.log(' → a watch loop on this engine rebuilds on every poll');
68 }
69 const ua = await p.evaluate(() => navigator.userAgent);
70 console.log(` ${ua}`);
71 } catch (e) {
72 console.log(`${name}: ${String(e.message).split('\n')[0]}`);
73 }
74 await b.close();
75}