@threadedstack/wmd-web 0.0.0-stage → 0.1.13

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.
Files changed (92) hide show
  1. package/README.md +2601 -2
  2. package/RELEASE.json +123 -0
  3. package/export-page/coi-sw.js +20 -0
  4. package/export-page/index.html +17 -0
  5. package/export-page/run.css +10 -0
  6. package/export-page/run.js +236 -0
  7. package/guest/arm64/Image +4 -0
  8. package/guest/arm64/Image-4k +4 -0
  9. package/guest/arm64/initramfs.cpio.gz +0 -0
  10. package/guest/arm64/vm.wasm +0 -0
  11. package/package.json +36 -4
  12. package/page/sw.js +381 -0
  13. package/relay/control.ts +139 -0
  14. package/relay/engine.ts +787 -0
  15. package/relay/hosted.ts +660 -0
  16. package/relay/keys.ts +53 -0
  17. package/relay/local-tls.ts +121 -0
  18. package/relay/lock.ts +81 -0
  19. package/relay/logfile.ts +138 -0
  20. package/relay/main.ts +642 -0
  21. package/relay/policy.ts +241 -0
  22. package/relay/relay.ts +266 -0
  23. package/relay/token.ts +116 -0
  24. package/relay/v1.ts +107 -0
  25. package/relay/wisp.ts +615 -0
  26. package/src/base.js +325 -0
  27. package/src/boot.js +2037 -0
  28. package/src/calib-worker.js +40 -0
  29. package/src/fs/9p/codec.js +303 -0
  30. package/src/fs/9p/server.js +521 -0
  31. package/src/fs/errno.js +43 -0
  32. package/src/fs/graph.js +380 -0
  33. package/src/fs/overlay.js +1018 -0
  34. package/src/fs/pack-write.js +165 -0
  35. package/src/fs/pack.js +427 -0
  36. package/src/fs/seed.js +22 -0
  37. package/src/fs/svc-snapshot.js +65 -0
  38. package/src/fs/upper-pack.js +112 -0
  39. package/src/host/base-cache.js +195 -0
  40. package/src/host/base-file.js +328 -0
  41. package/src/host/base-io.js +93 -0
  42. package/src/host/boot-events.js +209 -0
  43. package/src/host/chan-service.js +300 -0
  44. package/src/host/chan-services.js +689 -0
  45. package/src/host/chan.js +655 -0
  46. package/src/host/ckpt-file.js +395 -0
  47. package/src/host/ckpt-io.js +207 -0
  48. package/src/host/ckpt-point.js +160 -0
  49. package/src/host/ckpt-save.js +64 -0
  50. package/src/host/ckpt.js +434 -0
  51. package/src/host/clock.js +111 -0
  52. package/src/host/ctl.js +778 -0
  53. package/src/host/errno.js +18 -0
  54. package/src/host/fs.js +167 -0
  55. package/src/host/gcnudge.js +83 -0
  56. package/src/host/i64shim.js +81 -0
  57. package/src/host/jit-cache.js +341 -0
  58. package/src/host/jit-hub-core.js +208 -0
  59. package/src/host/jit-hub.js +93 -0
  60. package/src/host/jit-snap.js +226 -0
  61. package/src/host/jit.js +1903 -0
  62. package/src/host/layout.js +100 -0
  63. package/src/host/monitor.js +110 -0
  64. package/src/host/p9mount.js +27 -0
  65. package/src/host/ram-mem.js +125 -0
  66. package/src/host/stdio.js +143 -0
  67. package/src/host/threads.js +109 -0
  68. package/src/host/wasi.js +335 -0
  69. package/src/host/wmd-imports.js +71 -0
  70. package/src/host/yamux.js +387 -0
  71. package/src/image-files.js +154 -0
  72. package/src/net/dhcp-dns.js +126 -0
  73. package/src/net/net-worker.js +172 -0
  74. package/src/net/packet.js +227 -0
  75. package/src/net/peer.js +230 -0
  76. package/src/net/proto.js +189 -0
  77. package/src/net/relay-client.js +226 -0
  78. package/src/net/stack.js +867 -0
  79. package/src/net/switch.js +160 -0
  80. package/src/net/wisp-client.js +570 -0
  81. package/src/net/wisp-transport.js +196 -0
  82. package/src/net/wisp.js +511 -0
  83. package/src/registry/client.js +176 -0
  84. package/src/registry/converter.js +68 -0
  85. package/src/registry/hub.js +221 -0
  86. package/src/registry/oci.js +76 -0
  87. package/src/registry/pack-worker.js +70 -0
  88. package/src/registry/pull.js +258 -0
  89. package/src/registry/ref.js +110 -0
  90. package/src/registry/wasi-mem.js +355 -0
  91. package/src/worker.js +315 -0
  92. package/wmdpack.wasm +0 -0
@@ -0,0 +1,40 @@
1
+ // Throttle detector (boot.js, host/monitor.js createThrottleMeter). Every intervalMs
2
+ // this Worker times a fixed amount of integer work and a 10 ms Atomics.wait, and
3
+ // posts {t:'calib', workMs, waitLateMs}. When the browser deprioritises the page's
4
+ // process (Chromium lowers the priority of a minimised or occluded window's renderer),
5
+ // every Worker of the page, the guest's vCPUs included, runs slower; the page compares
6
+ // workMs with the fastest run it has seen. The work is ~1% of one core.
7
+
8
+ const WORK = 5_000_000
9
+ const cell = new Int32Array(new SharedArrayBuffer(4))
10
+ let sink = 0
11
+
12
+ function work() {
13
+ let x = 0x12345678 ^ sink
14
+ for (let i = 0; i < WORK; i++) {
15
+ x ^= x << 13
16
+ x ^= x >>> 17
17
+ x ^= x << 5
18
+ }
19
+ sink = x
20
+ }
21
+
22
+ function measure() {
23
+ const t0 = performance.now()
24
+ work()
25
+ const t1 = performance.now()
26
+ Atomics.wait(cell, 0, 0, 10)
27
+ const t2 = performance.now()
28
+ self.postMessage({ t: 'calib', workMs: t1 - t0, waitLateMs: Math.max(0, t2 - t1 - 10) })
29
+ }
30
+
31
+ self.onmessage = e => {
32
+ for (let i = 0; i < 3; i++) work() // warm up the JIT so the first samples are not the slowest
33
+ if (e.data.t === 'once') {
34
+ for (let i = 0; i < e.data.n; i++) measure()
35
+ return
36
+ }
37
+ if (e.data.t !== 'start') return
38
+ measure()
39
+ setInterval(measure, e.data.intervalMs)
40
+ }
@@ -0,0 +1,303 @@
1
+ // 9P2000.L message codec: every T and R message the Linux v9fs client and a
2
+ // 9P2000.L server exchange, both directions (a server decodes T and encodes R;
3
+ // tests and clients do the opposite).
4
+ //
5
+ // Frame: size[4] (little endian, including itself) type[1] tag[2] fields.
6
+ // Strings are u16 length + bytes, held as byte strings (one char per byte,
7
+ // see ../pack.js). u64 fields decode to a Number when they fit in 53 bits,
8
+ // else a BigInt, and encode from either.
9
+
10
+ export const NOTAG = 0xffff
11
+ export const NOFID = 0xffffffff
12
+ export const HEADER = 7
13
+ /** Rread/Rwrite/Treaddir overhead: header + count[4] (P9_IOHDRSZ is 24 for Twrite). */
14
+ export const IOHDRSZ = 24
15
+
16
+ // Field kinds.
17
+ const U8 = 'u8', U16 = 'u16', U32 = 'u32', U64 = 'u64', S = 's', QID = 'qid'
18
+ const DATA = 'data', WNAMES = 'wnames', QIDS = 'qids'
19
+
20
+ /** type number -> [name, [[field, kind], ...]] */
21
+ export const MESSAGES = {
22
+ 6: ['Tlerror', []],
23
+ 7: ['Rlerror', [['ecode', U32]]],
24
+ 8: ['Tstatfs', [['fid', U32]]],
25
+ 9: ['Rstatfs', [['fstype', U32], ['bsize', U32], ['blocks', U64], ['bfree', U64], ['bavail', U64],
26
+ ['files', U64], ['ffree', U64], ['fsid', U64], ['namelen', U32]]],
27
+ 12: ['Tlopen', [['fid', U32], ['flags', U32]]],
28
+ 13: ['Rlopen', [['qid', QID], ['iounit', U32]]],
29
+ 14: ['Tlcreate', [['fid', U32], ['name', S], ['flags', U32], ['mode', U32], ['gid', U32]]],
30
+ 15: ['Rlcreate', [['qid', QID], ['iounit', U32]]],
31
+ 16: ['Tsymlink', [['fid', U32], ['name', S], ['symtgt', S], ['gid', U32]]],
32
+ 17: ['Rsymlink', [['qid', QID]]],
33
+ 18: ['Tmknod', [['dfid', U32], ['name', S], ['mode', U32], ['major', U32], ['minor', U32], ['gid', U32]]],
34
+ 19: ['Rmknod', [['qid', QID]]],
35
+ 20: ['Trename', [['fid', U32], ['dfid', U32], ['name', S]]],
36
+ 21: ['Rrename', []],
37
+ 22: ['Treadlink', [['fid', U32]]],
38
+ 23: ['Rreadlink', [['target', S]]],
39
+ 24: ['Tgetattr', [['fid', U32], ['mask', U64]]],
40
+ 25: ['Rgetattr', [['valid', U64], ['qid', QID], ['mode', U32], ['uid', U32], ['gid', U32],
41
+ ['nlink', U64], ['rdev', U64], ['size', U64], ['blksize', U64], ['blocks', U64],
42
+ ['atime_sec', U64], ['atime_nsec', U64], ['mtime_sec', U64], ['mtime_nsec', U64],
43
+ ['ctime_sec', U64], ['ctime_nsec', U64], ['btime_sec', U64], ['btime_nsec', U64],
44
+ ['gen', U64], ['data_version', U64]]],
45
+ 26: ['Tsetattr', [['fid', U32], ['valid', U32], ['mode', U32], ['uid', U32], ['gid', U32], ['size', U64],
46
+ ['atime_sec', U64], ['atime_nsec', U64], ['mtime_sec', U64], ['mtime_nsec', U64]]],
47
+ 27: ['Rsetattr', []],
48
+ 30: ['Txattrwalk', [['fid', U32], ['newfid', U32], ['name', S]]],
49
+ 31: ['Rxattrwalk', [['size', U64]]],
50
+ 32: ['Txattrcreate', [['fid', U32], ['name', S], ['size', U64], ['flags', U32]]],
51
+ 33: ['Rxattrcreate', []],
52
+ 40: ['Treaddir', [['fid', U32], ['offset', U64], ['count', U32]]],
53
+ 41: ['Rreaddir', [['data', DATA]]],
54
+ 50: ['Tfsync', [['fid', U32], ['datasync', U32]]],
55
+ 51: ['Rfsync', []],
56
+ 52: ['Tlock', [['fid', U32], ['ltype', U8], ['flags', U32], ['start', U64], ['length', U64],
57
+ ['proc_id', U32], ['client_id', S]]],
58
+ 53: ['Rlock', [['status', U8]]],
59
+ 54: ['Tgetlock', [['fid', U32], ['ltype', U8], ['start', U64], ['length', U64], ['proc_id', U32], ['client_id', S]]],
60
+ 55: ['Rgetlock', [['ltype', U8], ['start', U64], ['length', U64], ['proc_id', U32], ['client_id', S]]],
61
+ 70: ['Tlink', [['dfid', U32], ['fid', U32], ['name', S]]],
62
+ 71: ['Rlink', []],
63
+ 72: ['Tmkdir', [['dfid', U32], ['name', S], ['mode', U32], ['gid', U32]]],
64
+ 73: ['Rmkdir', [['qid', QID]]],
65
+ 74: ['Trenameat', [['olddirfid', U32], ['oldname', S], ['newdirfid', U32], ['newname', S]]],
66
+ 75: ['Rrenameat', []],
67
+ 76: ['Tunlinkat', [['dirfid', U32], ['name', S], ['flags', U32]]],
68
+ 77: ['Runlinkat', []],
69
+ 100: ['Tversion', [['msize', U32], ['version', S]]],
70
+ 101: ['Rversion', [['msize', U32], ['version', S]]],
71
+ 102: ['Tauth', [['afid', U32], ['uname', S], ['aname', S], ['n_uname', U32]]],
72
+ 103: ['Rauth', [['aqid', QID]]],
73
+ 104: ['Tattach', [['fid', U32], ['afid', U32], ['uname', S], ['aname', S], ['n_uname', U32]]],
74
+ 105: ['Rattach', [['qid', QID]]],
75
+ 108: ['Tflush', [['oldtag', U16]]],
76
+ 109: ['Rflush', []],
77
+ 110: ['Twalk', [['fid', U32], ['newfid', U32], ['wnames', WNAMES]]],
78
+ 111: ['Rwalk', [['qids', QIDS]]],
79
+ 116: ['Tread', [['fid', U32], ['offset', U64], ['count', U32]]],
80
+ 117: ['Rread', [['data', DATA]]],
81
+ 118: ['Twrite', [['fid', U32], ['offset', U64], ['data', DATA]]],
82
+ 119: ['Rwrite', [['count', U32]]],
83
+ 120: ['Tclunk', [['fid', U32]]],
84
+ 121: ['Rclunk', []],
85
+ 122: ['Tremove', [['fid', U32]]],
86
+ 123: ['Rremove', []],
87
+ }
88
+
89
+ /** name -> type number */
90
+ export const T = {}
91
+ for (const [num, [name]] of Object.entries(MESSAGES)) T[name] = Number(num)
92
+
93
+ export class ProtoError extends Error {}
94
+
95
+ const TWO32 = 2 ** 32
96
+ const MAX_SAFE = Number.MAX_SAFE_INTEGER
97
+
98
+ function strBytesLen(s) {
99
+ return s.length
100
+ }
101
+
102
+ function sizeOf(kind, v) {
103
+ switch (kind) {
104
+ case U8: return 1
105
+ case U16: return 2
106
+ case U32: return 4
107
+ case U64: return 8
108
+ case S: return 2 + strBytesLen(v)
109
+ case QID: return 13
110
+ case DATA: return 4 + v.length
111
+ case WNAMES: { let n = 2; for (const s of v) n += 2 + s.length; return n }
112
+ case QIDS: return 2 + 13 * v.length
113
+ }
114
+ throw new ProtoError(`kind ${kind}`)
115
+ }
116
+
117
+ /** Writes into a DataView at a moving offset. */
118
+ export class Writer {
119
+ constructor(u8) {
120
+ this.u8 = u8
121
+ this.dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength)
122
+ this.p = 0
123
+ }
124
+ u8_(v) { this.dv.setUint8(this.p, v); this.p += 1 }
125
+ u16(v) { this.dv.setUint16(this.p, v, true); this.p += 2 }
126
+ u32(v) { this.dv.setUint32(this.p, v >>> 0, true); this.p += 4 }
127
+ u64(v) {
128
+ if (typeof v === 'number' && v >= 0 && v <= MAX_SAFE && Number.isInteger(v)) {
129
+ this.dv.setUint32(this.p, v % TWO32, true)
130
+ this.dv.setUint32(this.p + 4, Math.floor(v / TWO32), true)
131
+ } else {
132
+ this.dv.setBigUint64(this.p, BigInt.asUintN(64, BigInt(v)), true)
133
+ }
134
+ this.p += 8
135
+ }
136
+ str(s) {
137
+ if (s.length > 0xffff) throw new ProtoError('string too long')
138
+ this.u16(s.length)
139
+ const u8 = this.u8
140
+ for (let i = 0; i < s.length; i++) {
141
+ const c = s.charCodeAt(i)
142
+ if (c > 0xff) throw new ProtoError('not a byte string')
143
+ u8[this.p + i] = c
144
+ }
145
+ this.p += s.length
146
+ }
147
+ qid(q) {
148
+ this.u8_(q.type)
149
+ this.u32(q.version || 0)
150
+ this.u64(q.path)
151
+ }
152
+ bytes(b) {
153
+ this.u8.set(b, this.p)
154
+ this.p += b.length
155
+ }
156
+ }
157
+
158
+ /** Reads from a Uint8Array at a moving offset; ProtoError when short. */
159
+ export class Reader {
160
+ constructor(u8, p = 0, end = u8.length) {
161
+ this.u8 = u8
162
+ this.dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength)
163
+ this.p = p
164
+ this.end = end
165
+ }
166
+ need(n) {
167
+ if (this.p + n > this.end) throw new ProtoError('message too short')
168
+ }
169
+ u8_() { this.need(1); return this.dv.getUint8(this.p++) }
170
+ u16() { this.need(2); const v = this.dv.getUint16(this.p, true); this.p += 2; return v }
171
+ u32() { this.need(4); const v = this.dv.getUint32(this.p, true); this.p += 4; return v }
172
+ u64() {
173
+ this.need(8)
174
+ const lo = this.dv.getUint32(this.p, true)
175
+ const hi = this.dv.getUint32(this.p + 4, true)
176
+ this.p += 8
177
+ if (hi < 0x200000) return hi * TWO32 + lo
178
+ return (BigInt(hi) << 32n) | BigInt(lo)
179
+ }
180
+ str() {
181
+ const n = this.u16()
182
+ this.need(n)
183
+ const s = String.fromCharCode.apply(null, this.u8.subarray(this.p, this.p + n))
184
+ this.p += n
185
+ return s
186
+ }
187
+ qid() {
188
+ const type = this.u8_()
189
+ const version = this.u32()
190
+ const path = this.u64()
191
+ return { type, version, path }
192
+ }
193
+ bytes(n) {
194
+ this.need(n)
195
+ const b = this.u8.subarray(this.p, this.p + n)
196
+ this.p += n
197
+ return b
198
+ }
199
+ }
200
+
201
+ function writeField(w, kind, v) {
202
+ switch (kind) {
203
+ case U8: return w.u8_(v)
204
+ case U16: return w.u16(v)
205
+ case U32: return w.u32(v)
206
+ case U64: return w.u64(v)
207
+ case S: return w.str(v)
208
+ case QID: return w.qid(v)
209
+ case DATA: w.u32(v.length); return w.bytes(v)
210
+ case WNAMES: w.u16(v.length); for (const s of v) w.str(s); return
211
+ case QIDS: w.u16(v.length); for (const q of v) w.qid(q); return
212
+ }
213
+ }
214
+
215
+ function readField(r, kind) {
216
+ switch (kind) {
217
+ case U8: return r.u8_()
218
+ case U16: return r.u16()
219
+ case U32: return r.u32()
220
+ case U64: return r.u64()
221
+ case S: return r.str()
222
+ case QID: return r.qid()
223
+ case DATA: return r.bytes(r.u32())
224
+ case WNAMES: { const n = r.u16(); const out = new Array(n); for (let i = 0; i < n; i++) out[i] = r.str(); return out }
225
+ case QIDS: { const n = r.u16(); const out = new Array(n); for (let i = 0; i < n; i++) out[i] = r.qid(); return out }
226
+ }
227
+ }
228
+
229
+ /** Encodes message type (number or name) with tag and fields into a frame. */
230
+ export function encode(type, tag, fields = {}) {
231
+ if (typeof type === 'string') type = T[type]
232
+ const spec = MESSAGES[type]
233
+ if (!spec) throw new ProtoError(`unknown message type ${type}`)
234
+ let size = HEADER
235
+ for (const [name, kind] of spec[1]) size += sizeOf(kind, fields[name])
236
+ if (size > 0xffffffff) throw new ProtoError('message too large')
237
+ const out = new Uint8Array(size)
238
+ const w = new Writer(out)
239
+ w.u32(size)
240
+ w.u8_(type)
241
+ w.u16(tag)
242
+ for (const [name, kind] of spec[1]) writeField(w, kind, fields[name])
243
+ return out
244
+ }
245
+
246
+ /** The frame's header: {size, type, tag}. */
247
+ export function header(buf) {
248
+ if (buf.length < HEADER) throw new ProtoError('message too short')
249
+ const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
250
+ return { size: dv.getUint32(0, true), type: buf[4], tag: dv.getUint16(5, true) }
251
+ }
252
+
253
+ /** Decodes a frame into {type, op (the message's name), tag, ...fields}. Byte fields are views into buf. */
254
+ export function decode(buf) {
255
+ const { size, type, tag } = header(buf)
256
+ if (size < HEADER || size > buf.length) throw new ProtoError('bad frame size')
257
+ const spec = MESSAGES[type]
258
+ if (!spec) {
259
+ const e = new ProtoError(`unknown message type ${type}`)
260
+ e.tag = tag
261
+ e.type = type
262
+ throw e
263
+ }
264
+ const r = new Reader(buf, HEADER, size)
265
+ const m = { type, op: spec[0], tag }
266
+ for (const [name, kind] of spec[1]) m[name] = readField(r, kind)
267
+ if (r.p !== size) throw new ProtoError(`${spec[0]}: ${size - r.p} trailing bytes`)
268
+ return m
269
+ }
270
+
271
+ /** One Rreaddir entry's encoded size. */
272
+ export function direntSize(name) {
273
+ return 13 + 8 + 1 + 2 + name.length
274
+ }
275
+
276
+ /** Encodes dirents [{qid, offset, type, name}] (Rreaddir's data). */
277
+ export function encodeDirents(list) {
278
+ let n = 0
279
+ for (const d of list) n += direntSize(d.name)
280
+ const out = new Uint8Array(n)
281
+ const w = new Writer(out)
282
+ for (const d of list) {
283
+ w.qid(d.qid)
284
+ w.u64(d.offset)
285
+ w.u8_(d.type)
286
+ w.str(d.name)
287
+ }
288
+ return out
289
+ }
290
+
291
+ /** Decodes Rreaddir's data into [{qid, offset, type, name}]. */
292
+ export function decodeDirents(data) {
293
+ const r = new Reader(data)
294
+ const out = []
295
+ while (r.p < r.end) {
296
+ const qid = r.qid()
297
+ const offset = r.u64()
298
+ const type = r.u8_()
299
+ const name = r.str()
300
+ out.push({ qid, offset, type, name })
301
+ }
302
+ return out
303
+ }