@threadedstack/wmd-web 0.0.0-stage → 0.1.12
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/README.md +2601 -2
- package/RELEASE.json +123 -0
- package/export-page/coi-sw.js +20 -0
- package/export-page/index.html +17 -0
- package/export-page/run.css +10 -0
- package/export-page/run.js +236 -0
- package/guest/arm64/Image +4 -0
- package/guest/arm64/Image-4k +4 -0
- package/guest/arm64/initramfs.cpio.gz +0 -0
- package/guest/arm64/vm.wasm +0 -0
- package/package.json +36 -4
- package/page/sw.js +381 -0
- package/relay/control.ts +139 -0
- package/relay/engine.ts +787 -0
- package/relay/hosted.ts +660 -0
- package/relay/keys.ts +53 -0
- package/relay/local-tls.ts +121 -0
- package/relay/lock.ts +81 -0
- package/relay/logfile.ts +138 -0
- package/relay/main.ts +642 -0
- package/relay/policy.ts +241 -0
- package/relay/relay.ts +266 -0
- package/relay/token.ts +116 -0
- package/relay/v1.ts +107 -0
- package/relay/wisp.ts +615 -0
- package/src/base.js +325 -0
- package/src/boot.js +2037 -0
- package/src/calib-worker.js +40 -0
- package/src/fs/9p/codec.js +303 -0
- package/src/fs/9p/server.js +521 -0
- package/src/fs/errno.js +43 -0
- package/src/fs/graph.js +380 -0
- package/src/fs/overlay.js +1018 -0
- package/src/fs/pack-write.js +165 -0
- package/src/fs/pack.js +427 -0
- package/src/fs/seed.js +22 -0
- package/src/fs/svc-snapshot.js +65 -0
- package/src/fs/upper-pack.js +112 -0
- package/src/host/base-cache.js +195 -0
- package/src/host/base-file.js +328 -0
- package/src/host/base-io.js +93 -0
- package/src/host/boot-events.js +209 -0
- package/src/host/chan-service.js +300 -0
- package/src/host/chan-services.js +689 -0
- package/src/host/chan.js +655 -0
- package/src/host/ckpt-file.js +395 -0
- package/src/host/ckpt-io.js +207 -0
- package/src/host/ckpt-point.js +160 -0
- package/src/host/ckpt-save.js +64 -0
- package/src/host/ckpt.js +434 -0
- package/src/host/clock.js +111 -0
- package/src/host/ctl.js +778 -0
- package/src/host/errno.js +18 -0
- package/src/host/fs.js +167 -0
- package/src/host/gcnudge.js +83 -0
- package/src/host/i64shim.js +81 -0
- package/src/host/jit-cache.js +341 -0
- package/src/host/jit-hub-core.js +208 -0
- package/src/host/jit-hub.js +93 -0
- package/src/host/jit-snap.js +226 -0
- package/src/host/jit.js +1903 -0
- package/src/host/layout.js +100 -0
- package/src/host/monitor.js +110 -0
- package/src/host/p9mount.js +27 -0
- package/src/host/ram-mem.js +125 -0
- package/src/host/stdio.js +143 -0
- package/src/host/threads.js +109 -0
- package/src/host/wasi.js +335 -0
- package/src/host/wmd-imports.js +71 -0
- package/src/host/yamux.js +387 -0
- package/src/image-files.js +154 -0
- package/src/net/dhcp-dns.js +126 -0
- package/src/net/net-worker.js +172 -0
- package/src/net/packet.js +227 -0
- package/src/net/peer.js +230 -0
- package/src/net/proto.js +189 -0
- package/src/net/relay-client.js +226 -0
- package/src/net/stack.js +867 -0
- package/src/net/switch.js +160 -0
- package/src/net/wisp-client.js +570 -0
- package/src/net/wisp-transport.js +196 -0
- package/src/net/wisp.js +511 -0
- package/src/registry/client.js +176 -0
- package/src/registry/converter.js +68 -0
- package/src/registry/hub.js +221 -0
- package/src/registry/oci.js +76 -0
- package/src/registry/pack-worker.js +70 -0
- package/src/registry/pull.js +258 -0
- package/src/registry/ref.js +110 -0
- package/src/registry/wasi-mem.js +355 -0
- package/src/worker.js +315 -0
- package/wmdpack.wasm +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// W71: TCP streams over a Wisp relay for code in the page itself (no guest network stack), with
|
|
2
|
+
// the Transport interface wmn's createRelayFetch takes (TLS runs in the page; the relay carries
|
|
3
|
+
// ciphertext only):
|
|
4
|
+
//
|
|
5
|
+
// const t = createWispTransport({ url: 'local' }) // or 'hosted', or a ws(s):// URL
|
|
6
|
+
// const fetch = createRelayFetch({ transport: t }) // wmn: packages/ai/src/relay/fetch.ts
|
|
7
|
+
// const s = await t.connect('registry-1.docker.io', 443, signal)
|
|
8
|
+
// s.write(bytes); const b = await s.read(n); s.end(); s.abort()
|
|
9
|
+
// t.close()
|
|
10
|
+
//
|
|
11
|
+
// WispClient (wisp-client.js) does the protocol, as it does for the guest's stack; this file is the
|
|
12
|
+
// stack it calls back into. read(n) waits for n bytes (fewer at a clean end, undefined when nothing
|
|
13
|
+
// is left; with mode 1, PEEK, the bytes stay queued); a reset or a dropped relay is an error. Bytes
|
|
14
|
+
// taken by the reader go back to the relay as credit (wmd-credit), so a slow reader slows the relay.
|
|
15
|
+
|
|
16
|
+
import { E } from './proto.js'
|
|
17
|
+
import { WispClient } from './wisp-client.js'
|
|
18
|
+
|
|
19
|
+
const PEEK = 1
|
|
20
|
+
|
|
21
|
+
class StreamQueue {
|
|
22
|
+
constructor(onTaken) {
|
|
23
|
+
this.onTaken = onTaken
|
|
24
|
+
this.q = []
|
|
25
|
+
this.n = 0
|
|
26
|
+
this.ended = false
|
|
27
|
+
this.failure = null
|
|
28
|
+
this.want = null
|
|
29
|
+
}
|
|
30
|
+
push(b) {
|
|
31
|
+
if (this.ended) return
|
|
32
|
+
this.q.push(b)
|
|
33
|
+
this.n += b.length
|
|
34
|
+
this.#serve()
|
|
35
|
+
}
|
|
36
|
+
finish() {
|
|
37
|
+
this.ended = true
|
|
38
|
+
this.#serve()
|
|
39
|
+
}
|
|
40
|
+
fail(e) {
|
|
41
|
+
if (this.ended && !this.failure && !this.want) return
|
|
42
|
+
this.failure ??= e
|
|
43
|
+
this.ended = true
|
|
44
|
+
this.#serve()
|
|
45
|
+
}
|
|
46
|
+
read(bytes, mode = 0) {
|
|
47
|
+
if (this.want) return Promise.reject(new Error('a read is already waiting on this stream'))
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
this.want = { bytes, mode, resolve, reject }
|
|
50
|
+
this.#serve()
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
#serve() {
|
|
54
|
+
const w = this.want
|
|
55
|
+
if (!w || (this.n < w.bytes && !this.ended)) return
|
|
56
|
+
this.want = null
|
|
57
|
+
const k = Math.min(w.bytes, this.n)
|
|
58
|
+
if (k === 0) return this.failure ? w.reject(this.failure) : w.resolve(undefined)
|
|
59
|
+
const out = new Uint8Array(k)
|
|
60
|
+
let o = 0
|
|
61
|
+
let i = 0
|
|
62
|
+
while (o < k) {
|
|
63
|
+
const b = this.q[i]
|
|
64
|
+
const take = Math.min(b.length, k - o)
|
|
65
|
+
out.set(b.subarray(0, take), o)
|
|
66
|
+
o += take
|
|
67
|
+
if (w.mode !== PEEK) {
|
|
68
|
+
if (take === b.length) i++
|
|
69
|
+
else this.q[i] = b.subarray(take)
|
|
70
|
+
} else i++
|
|
71
|
+
}
|
|
72
|
+
if (w.mode !== PEEK) {
|
|
73
|
+
this.q.splice(0, i)
|
|
74
|
+
this.n -= k
|
|
75
|
+
this.onTaken(k)
|
|
76
|
+
}
|
|
77
|
+
w.resolve(out)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {object} o
|
|
83
|
+
* @param {string} o.url 'local', 'hosted' or a ws(s):// Wisp URL
|
|
84
|
+
* @param {WispClient} [o.client] a client to use instead (its stack is set to this transport)
|
|
85
|
+
* @param {(line: string) => void} [o.log]
|
|
86
|
+
* @returns {{connect(host: string, port: number, signal?: AbortSignal): Promise<object>, close(): void, client: WispClient}}
|
|
87
|
+
*/
|
|
88
|
+
export function createWispTransport(o = {}) {
|
|
89
|
+
const client = o.client || new WispClient({ url: o.url, log: o.log, instance: o.instance })
|
|
90
|
+
const streams = new Map() // id -> {queue, opened}
|
|
91
|
+
let nextId = 1
|
|
92
|
+
let closed = false
|
|
93
|
+
const stack = {
|
|
94
|
+
onConnected(id, code, _window, why) {
|
|
95
|
+
const s = streams.get(id)
|
|
96
|
+
if (!s?.opened) return
|
|
97
|
+
const { resolve, reject } = s.opened
|
|
98
|
+
s.opened = null
|
|
99
|
+
if (code === E.OK) resolve()
|
|
100
|
+
else {
|
|
101
|
+
streams.delete(id)
|
|
102
|
+
reject(new Error(why || `the relay could not open the stream (code ${code})`))
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
onRelayData(id, b) {
|
|
106
|
+
streams.get(id)?.queue.push(b)
|
|
107
|
+
},
|
|
108
|
+
onRelayEnd(id) {
|
|
109
|
+
const s = streams.get(id)
|
|
110
|
+
if (!s) return
|
|
111
|
+
s.queue.finish()
|
|
112
|
+
s.remoteEnded = true
|
|
113
|
+
if (s.localEnded) {
|
|
114
|
+
streams.delete(id)
|
|
115
|
+
client.closed(id)
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
onRelayAbort(id, _code, why) {
|
|
119
|
+
const s = streams.get(id)
|
|
120
|
+
if (!s) return
|
|
121
|
+
streams.delete(id)
|
|
122
|
+
const e = new Error(why || 'the relay reset the stream')
|
|
123
|
+
if (s.opened) s.opened.reject(e)
|
|
124
|
+
s.queue.fail(e)
|
|
125
|
+
},
|
|
126
|
+
onRelayCredit() {},
|
|
127
|
+
onDnsReply() {},
|
|
128
|
+
onRelayDown(why) {
|
|
129
|
+
const e = new Error(`the relay connection dropped${why ? `: ${why}` : ''}`)
|
|
130
|
+
for (const [id, s] of streams) {
|
|
131
|
+
streams.delete(id)
|
|
132
|
+
if (s.opened) s.opened.reject(e)
|
|
133
|
+
s.queue.fail(e)
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
client.attach(stack)
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
client,
|
|
141
|
+
async connect(host, port, signal) {
|
|
142
|
+
if (closed) throw new Error('the transport is closed')
|
|
143
|
+
signal?.throwIfAborted()
|
|
144
|
+
const id = nextId++
|
|
145
|
+
const s = {
|
|
146
|
+
queue: new StreamQueue(n => client.credit(id, n)),
|
|
147
|
+
opened: null,
|
|
148
|
+
localEnded: false,
|
|
149
|
+
remoteEnded: false,
|
|
150
|
+
}
|
|
151
|
+
streams.set(id, s)
|
|
152
|
+
const opened = new Promise((resolve, reject) => (s.opened = { resolve, reject }))
|
|
153
|
+
const onAbort = () => {
|
|
154
|
+
if (!s.opened) return
|
|
155
|
+
const { reject } = s.opened
|
|
156
|
+
s.opened = null
|
|
157
|
+
streams.delete(id)
|
|
158
|
+
client.abort(id)
|
|
159
|
+
reject(signal.reason ?? new Error('aborted'))
|
|
160
|
+
}
|
|
161
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
162
|
+
client.connect(id, host, port)
|
|
163
|
+
try {
|
|
164
|
+
await opened
|
|
165
|
+
} finally {
|
|
166
|
+
signal?.removeEventListener('abort', onAbort)
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
read: (n, mode) => s.queue.read(n, mode),
|
|
170
|
+
write(b) {
|
|
171
|
+
if (streams.get(id) === s && !s.localEnded) client.data(id, b.slice())
|
|
172
|
+
},
|
|
173
|
+
end() {
|
|
174
|
+
if (s.localEnded || streams.get(id) !== s) return
|
|
175
|
+
s.localEnded = true
|
|
176
|
+
client.end(id)
|
|
177
|
+
if (s.remoteEnded) {
|
|
178
|
+
streams.delete(id)
|
|
179
|
+
client.closed(id)
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
abort() {
|
|
183
|
+
if (streams.get(id) !== s) return
|
|
184
|
+
streams.delete(id)
|
|
185
|
+
client.abort(id)
|
|
186
|
+
s.queue.fail(new Error('the stream was reset'))
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
close() {
|
|
191
|
+
closed = true
|
|
192
|
+
stack.onRelayDown('closed by the page')
|
|
193
|
+
client.ws?.close(1000)
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
}
|