@bytecodealliance/preview3-shim 0.1.1 → 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.
Files changed (112) hide show
  1. package/dist/nodejs/cli.d.ts +51 -0
  2. package/dist/nodejs/cli.js +106 -0
  3. package/dist/nodejs/clocks.d.ts +55 -0
  4. package/dist/nodejs/clocks.js +83 -0
  5. package/dist/nodejs/filesystem/descriptor.d.ts +443 -0
  6. package/dist/nodejs/filesystem/descriptor.js +1010 -0
  7. package/dist/nodejs/filesystem/error.d.ts +61 -0
  8. package/dist/nodejs/filesystem/error.js +86 -0
  9. package/dist/nodejs/filesystem/utils.d.ts +8 -0
  10. package/dist/nodejs/filesystem/utils.js +25 -0
  11. package/dist/nodejs/filesystem.js +2 -0
  12. package/dist/nodejs/finalization.d.ts +2 -0
  13. package/{lib → dist}/nodejs/finalization.js +15 -21
  14. package/dist/nodejs/future.d.ts +98 -0
  15. package/dist/nodejs/future.js +176 -0
  16. package/dist/nodejs/http/client.d.ts +22 -0
  17. package/dist/nodejs/http/client.js +138 -0
  18. package/dist/nodejs/http/error.d.ts +18 -0
  19. package/dist/nodejs/http/error.js +62 -0
  20. package/dist/nodejs/http/fields.d.ts +144 -0
  21. package/dist/nodejs/http/fields.js +327 -0
  22. package/dist/nodejs/http/request.d.ts +256 -0
  23. package/dist/nodejs/http/request.js +606 -0
  24. package/dist/nodejs/http/response.d.ts +82 -0
  25. package/dist/nodejs/http/response.js +202 -0
  26. package/dist/nodejs/http/server.d.ts +13 -0
  27. package/dist/nodejs/http/server.js +127 -0
  28. package/dist/nodejs/http.d.ts +19 -0
  29. package/{lib → dist}/nodejs/http.js +13 -18
  30. package/dist/nodejs/index.js +8 -0
  31. package/dist/nodejs/random.d.ts +5 -0
  32. package/{lib → dist}/nodejs/random.js +2 -8
  33. package/dist/nodejs/sockets/address.d.ts +78 -0
  34. package/dist/nodejs/sockets/address.js +120 -0
  35. package/dist/nodejs/sockets/error.d.ts +83 -0
  36. package/dist/nodejs/sockets/error.js +120 -0
  37. package/dist/nodejs/sockets/ip-name-lookup.d.ts +18 -0
  38. package/dist/nodejs/sockets/ip-name-lookup.js +84 -0
  39. package/dist/nodejs/sockets/tcp.d.ts +333 -0
  40. package/dist/nodejs/sockets/tcp.js +723 -0
  41. package/dist/nodejs/sockets/udp.d.ts +224 -0
  42. package/dist/nodejs/sockets/udp.js +475 -0
  43. package/dist/nodejs/sockets.d.ts +11 -0
  44. package/{lib → dist}/nodejs/sockets.js +2 -5
  45. package/dist/nodejs/stream.d.ts +161 -0
  46. package/dist/nodejs/stream.js +388 -0
  47. package/dist/nodejs/workers/cli-worker.d.ts +1 -0
  48. package/dist/nodejs/workers/cli-worker.js +41 -0
  49. package/dist/nodejs/workers/filesystem-worker.d.ts +1 -0
  50. package/dist/nodejs/workers/filesystem-worker.js +102 -0
  51. package/dist/nodejs/workers/http-worker.d.ts +1 -0
  52. package/dist/nodejs/workers/http-worker.js +355 -0
  53. package/dist/nodejs/workers/resource-worker.d.ts +13 -0
  54. package/dist/nodejs/workers/resource-worker.js +152 -0
  55. package/dist/nodejs/workers/tcp-worker.d.ts +2 -0
  56. package/dist/nodejs/workers/tcp-worker.js +318 -0
  57. package/dist/nodejs/workers/udp-worker.d.ts +1 -0
  58. package/dist/nodejs/workers/udp-worker.js +156 -0
  59. package/package.json +20 -12
  60. package/types/interfaces/wasi-cli-environment.d.ts +3 -3
  61. package/types/interfaces/wasi-cli-exit.d.ts +12 -1
  62. package/types/interfaces/wasi-cli-run.d.ts +1 -1
  63. package/types/interfaces/wasi-cli-stderr.d.ts +3 -3
  64. package/types/interfaces/wasi-cli-stdin.d.ts +5 -5
  65. package/types/interfaces/wasi-cli-stdout.d.ts +3 -3
  66. package/types/interfaces/wasi-cli-terminal-input.d.ts +1 -1
  67. package/types/interfaces/wasi-cli-terminal-output.d.ts +1 -1
  68. package/types/interfaces/wasi-cli-terminal-stderr.d.ts +1 -1
  69. package/types/interfaces/wasi-cli-terminal-stdin.d.ts +1 -1
  70. package/types/interfaces/wasi-cli-terminal-stdout.d.ts +1 -1
  71. package/types/interfaces/wasi-cli-types.d.ts +5 -5
  72. package/types/interfaces/wasi-clocks-monotonic-clock.d.ts +3 -3
  73. package/types/interfaces/wasi-clocks-system-clock.d.ts +6 -6
  74. package/types/interfaces/wasi-clocks-types.d.ts +1 -1
  75. package/types/interfaces/wasi-filesystem-preopens.d.ts +1 -1
  76. package/types/interfaces/wasi-filesystem-types.d.ts +74 -74
  77. package/types/interfaces/wasi-http-client.d.ts +1 -1
  78. package/types/interfaces/wasi-http-handler.d.ts +1 -1
  79. package/types/interfaces/wasi-http-types.d.ts +34 -34
  80. package/types/interfaces/wasi-random-insecure-seed.d.ts +5 -5
  81. package/types/interfaces/wasi-random-insecure.d.ts +5 -5
  82. package/types/interfaces/wasi-random-random.d.ts +5 -5
  83. package/types/interfaces/wasi-sockets-ip-name-lookup.d.ts +11 -11
  84. package/types/interfaces/wasi-sockets-types.d.ts +121 -121
  85. package/types/wasi-cli-command.d.ts +23 -23
  86. package/types/wasi-http-service.d.ts +14 -14
  87. package/lib/nodejs/cli.js +0 -127
  88. package/lib/nodejs/clocks.js +0 -100
  89. package/lib/nodejs/filesystem/descriptor.js +0 -1093
  90. package/lib/nodejs/filesystem/error.js +0 -92
  91. package/lib/nodejs/filesystem/utils.js +0 -19
  92. package/lib/nodejs/future.js +0 -190
  93. package/lib/nodejs/http/client.js +0 -161
  94. package/lib/nodejs/http/error.js +0 -71
  95. package/lib/nodejs/http/fields.js +0 -373
  96. package/lib/nodejs/http/request.js +0 -691
  97. package/lib/nodejs/http/response.js +0 -227
  98. package/lib/nodejs/http/server.js +0 -157
  99. package/lib/nodejs/sockets/address.js +0 -146
  100. package/lib/nodejs/sockets/error.js +0 -123
  101. package/lib/nodejs/sockets/ip-name-lookup.js +0 -93
  102. package/lib/nodejs/sockets/tcp.js +0 -801
  103. package/lib/nodejs/sockets/udp.js +0 -516
  104. package/lib/nodejs/stream.js +0 -437
  105. package/lib/nodejs/workers/cli-worker.js +0 -49
  106. package/lib/nodejs/workers/filesystem-worker.js +0 -119
  107. package/lib/nodejs/workers/http-worker.js +0 -417
  108. package/lib/nodejs/workers/resource-worker.js +0 -195
  109. package/lib/nodejs/workers/tcp-worker.js +0 -370
  110. package/lib/nodejs/workers/udp-worker.js +0 -194
  111. /package/{lib/nodejs/filesystem.js → dist/nodejs/filesystem.d.ts} +0 -0
  112. /package/{lib/nodejs/index.js → dist/nodejs/index.d.ts} +0 -0
@@ -1,227 +0,0 @@
1
- import { FutureReader, future } from "../future.js";
2
- import { StreamReader, readableByteStreamFromReader } from "../stream.js";
3
- import { HttpError } from "./error.js";
4
- import { Fields, _fieldsLock } from "./fields.js";
5
-
6
- const symbolDispose = Symbol.dispose || Symbol.for("dispose");
7
-
8
- let RESPONSE_CREATE_TOKEN = null;
9
- function token() {
10
- return (RESPONSE_CREATE_TOKEN ??= Symbol("ResponseCreateToken"));
11
- }
12
-
13
- export class Response {
14
- #statusCode = 200;
15
- #headers = null;
16
- #contents = null;
17
- #trailersFuture = null;
18
- #responseFuture = null;
19
- #contentsDispose = null;
20
- #bodyOpen = false;
21
- #bodyEnded = false;
22
-
23
- /**
24
- * @private
25
- * Use Response.new(...) to create an instance
26
- */
27
- constructor(t) {
28
- if (t !== token()) {
29
- throw new Error("Use Response.new(...) to create a Response");
30
- }
31
- }
32
-
33
- /**
34
- * Construct a new Response with default status-code 200.
35
- *
36
- * WIT:
37
- * ```
38
- * new: static func(
39
- * headers: headers,
40
- * contents: option<stream<u8>>,
41
- * trailers: future<result<option<trailers>, error-code>>
42
- * ) -> tuple<response, future<result<_, error-code>>>;
43
- * ```
44
- *
45
- * @param {Fields} headers immutable headers resource
46
- * @param {?StreamReader|object} contents optional body stream
47
- * @param {FutureReader|Promise} trailers future for trailers
48
- * @returns {{ res: Response, future: FutureReader }}
49
- * @throws {HttpError} with payload.tag 'invalid-argument' for invalid arguments
50
- */
51
- static new(headers, contents, trailers) {
52
- if (!(headers instanceof Fields)) {
53
- throw new HttpError("invalid-argument", "headers must be Fields");
54
- }
55
-
56
- if (!(trailers instanceof FutureReader) && (!trailers || typeof trailers.then !== "function")) {
57
- throw new HttpError("invalid-argument", "trailers must be FutureReader or Promise");
58
- }
59
-
60
- let dispose = null;
61
- if (contents != null && !(contents instanceof StreamReader)) {
62
- try {
63
- dispose = contents[symbolDispose]?.bind(contents);
64
- const inner = readableByteStreamFromReader(contents, { name: "contents" });
65
- contents = new StreamReader(inner);
66
- } catch (err) {
67
- throw new HttpError("invalid-argument", err.message);
68
- }
69
- }
70
-
71
- // Generated P3 futures are lazy thenables so we want to observe them now so early error paths don't hang.
72
- if (!(trailers instanceof FutureReader)) {
73
- const promise = Promise.resolve(trailers);
74
- trailers = new FutureReader(promise);
75
- }
76
-
77
- const response = new Response(token());
78
- response.#headers = _fieldsLock(headers);
79
- response.#contents = contents;
80
- response.#contentsDispose = dispose;
81
- response.#trailersFuture = trailers;
82
-
83
- const { tx, rx } = future();
84
- response.#responseFuture = tx;
85
-
86
- return [response, rx];
87
- }
88
-
89
- [symbolDispose]() {
90
- if (this.#contents && !this.#bodyOpen && !this.#bodyEnded) {
91
- this.#contentsDispose?.();
92
- this.#closeBody();
93
- }
94
- this.#contents = null;
95
- this.#contentsDispose = null;
96
- this.#bodyOpen = false;
97
- this.#bodyEnded = true;
98
- }
99
-
100
- /**
101
- * Get the HTTP Status Code for the Response.
102
- *
103
- * WIT:
104
- * ```
105
- * status-code: func() -> status-code;
106
- * ```
107
- *
108
- * @returns {number}
109
- */
110
- getStatusCode() {
111
- return this.#statusCode;
112
- }
113
-
114
- /**
115
- * Set the HTTP Status Code for the Response.
116
- *
117
- * WIT:
118
- * ```
119
- * set-status-code: func(status-code: status-code) -> result;
120
- * ```
121
- *
122
- * @param {number} code
123
- * @throws {HttpError} with payload.tag 'invalid-argument'
124
- * if code is not an integer in [100,599]
125
- */
126
- setStatusCode(code) {
127
- if (typeof code !== "number" || !Number.isInteger(code) || code < 100 || code > 599) {
128
- throw new HttpError("invalid-argument", `Invalid status code: ${code}`);
129
- }
130
- this.#statusCode = code;
131
- }
132
-
133
- /**
134
- * Get the headers associated with the Response.
135
- *
136
- * WIT:
137
- * ```
138
- * headers: func() -> headers;
139
- * ```
140
- *
141
- * @returns {Fields}
142
- */
143
- getHeaders() {
144
- return this.#headers;
145
- }
146
-
147
- /**
148
- * Get body stream and trailers future, consuming the resource.
149
- *
150
- * WIT:
151
- * ```
152
- * consume-body: static func(this: response, res: future<result<_, error-code>>)
153
- * -> tuple<stream<u8>, future<result<option<trailers>, error-code>>>;
154
- * ```
155
- *
156
- * @param {Response} response - The response to consume.
157
- * @param {FutureReader} res - A future for communicating errors.
158
- * @returns {[StreamReader, FutureReader]} A tuple of [body stream, trailers future].
159
- * @throws {HttpError} with payload.tag 'invalid-state' if body has already been opened or consumed.
160
- */
161
- static consumeBody(response, res) {
162
- if (res && typeof res.then === "function") {
163
- // TODO: Use res to abort/close the body transport on errors.
164
- void Promise.resolve(res).catch(() => {});
165
- }
166
-
167
- if (response.#bodyOpen) {
168
- throw new HttpError("invalid-state", "body already opened and not yet closed");
169
- }
170
-
171
- if (response.#bodyEnded) {
172
- throw new HttpError("invalid-state", "body has already been consumed");
173
- }
174
-
175
- if (!response.#contents) {
176
- response.#bodyEnded = true;
177
- return [response.#contents, response.#trailersFuture];
178
- }
179
-
180
- const reader = response.#contents;
181
- response.#bodyOpen = true;
182
-
183
- const readFn = reader.read.bind(reader);
184
- reader.read = async (...args) => {
185
- const chunk = await readFn(...args);
186
- if (chunk === null) {
187
- response.#bodyEnded = true;
188
- response.#bodyOpen = false;
189
- }
190
-
191
- return chunk;
192
- };
193
-
194
- const closedFn = reader.close.bind(reader);
195
- reader.close = () => {
196
- response.#closeBody(closedFn);
197
- };
198
-
199
- const cancelFn = reader.cancel.bind(reader);
200
- reader.cancel = async (...args) => {
201
- await response.#closeBody(() => cancelFn(...args));
202
- };
203
-
204
- return [response.#contents, response.#trailersFuture];
205
- }
206
-
207
- // Internal: call to complete the response transmission
208
- _resolve(result) {
209
- if (this.#responseFuture) {
210
- this.#responseFuture.write(result);
211
- this.#responseFuture = null;
212
- }
213
- }
214
-
215
- #closeBody(close = this.#contents?.close.bind(this.#contents)) {
216
- const closeTrailers = !this.#bodyEnded;
217
- try {
218
- return close?.();
219
- } finally {
220
- if (closeTrailers) {
221
- this.#trailersFuture.close();
222
- }
223
- this.#bodyEnded = true;
224
- this.#bodyOpen = false;
225
- }
226
- }
227
- }
@@ -1,157 +0,0 @@
1
- import { EventEmitter } from "node:events";
2
-
3
- import { ResourceWorker } from "../workers/resource-worker.js";
4
- import { StreamReader, readableByteStreamFromReader } from "../stream.js";
5
- import { FutureReader, future } from "../future.js";
6
- import { Request } from "./request.js";
7
- import { Response } from "./response.js";
8
-
9
- import { HttpError } from "./error.js";
10
- import {
11
- _fieldsFromEntriesChecked,
12
- _readTrailersForTransport,
13
- _trailerResultFromEntries,
14
- } from "./fields.js";
15
-
16
- let WORKER = null;
17
- function worker() {
18
- return (WORKER ??= new ResourceWorker(new URL("../workers/http-worker.js", import.meta.url)));
19
- }
20
-
21
- /**
22
- * HttpServer uses a background worker thread to handle raw HTTP connections,
23
- * while the main thread invokes the user-provided handler and sends back the
24
- * response.
25
- */
26
- export class HttpServer extends EventEmitter {
27
- #serverId = null;
28
- #handler = null;
29
- #shutdown = null;
30
-
31
- constructor(handler) {
32
- super();
33
-
34
- if (typeof handler?.handle !== "function") {
35
- throw new HttpError("invalid-argument", "Not a valid HTTP server component to execute.");
36
- }
37
- this.#handler = handler;
38
- }
39
-
40
- async listen(port, host) {
41
- this.#serverId = await worker().run({
42
- op: "server-start",
43
- port,
44
- host,
45
- });
46
-
47
- this.#shutdown = this.#serveLoop();
48
- }
49
-
50
- async stop() {
51
- if (!this.#serverId) {
52
- return;
53
- }
54
-
55
- await worker().run({
56
- op: "server-stop",
57
- serverId: this.#serverId,
58
- });
59
-
60
- await this.#shutdown;
61
- }
62
-
63
- async close() {
64
- if (!this.#serverId) {
65
- return;
66
- }
67
-
68
- await this.stop();
69
- await worker().run({
70
- op: "server-close",
71
- serverId: this.#serverId,
72
- });
73
-
74
- this.#serverId = null;
75
- }
76
-
77
- async #serveLoop() {
78
- let next;
79
-
80
- const nextRequest = async () =>
81
- await worker().run({
82
- op: "server-next",
83
- serverId: this.#serverId,
84
- });
85
-
86
- while ((next = await nextRequest()) !== null) {
87
- try {
88
- const { requestId } = next;
89
-
90
- const req = requestFromParts(next);
91
- const outcome = await this.#handler.handle(req);
92
-
93
- if (outcome.tag === "ok") {
94
- const res = outcome.val;
95
- const { rx: resRx } = future();
96
- const [body, trailers] = Response.consumeBody(res, resRx);
97
- const { port1: tx, port2: rx } = new MessageChannel();
98
-
99
- const stream = readableByteStreamFromReader(body, { name: "response body" });
100
-
101
- _readTrailersForTransport(trailers)
102
- .then((val) => tx.postMessage({ val }))
103
- .catch((err) => tx.postMessage({ err: HttpError.from(err).payload }))
104
- .finally(() => tx.close());
105
-
106
- await worker().run(
107
- {
108
- op: "server-response",
109
- serverId: this.#serverId,
110
- requestId,
111
- statusCode: res.getStatusCode(),
112
- headers: res.getHeaders().copyAll(),
113
- trailers: rx,
114
- stream,
115
- },
116
- [stream, rx],
117
- );
118
- } else {
119
- await worker().run({
120
- op: "server-response",
121
- serverId: this.#serverId,
122
- requestId,
123
- statusCode: 500,
124
- headers: [],
125
- });
126
- }
127
- } catch (err) {
128
- this.emit("error", err);
129
- }
130
- }
131
- }
132
- }
133
-
134
- const requestFromParts = (parts) => {
135
- const { headers, body, trailers, method, url } = parts;
136
-
137
- // Create a promise that resolves when the worker sends a message with trailers
138
- const promise = new Promise((resolve, reject) => {
139
- trailers.once("message", ({ val, err }) => {
140
- trailers.close();
141
- if (err) {
142
- reject(err);
143
- } else {
144
- resolve(val);
145
- }
146
- });
147
- });
148
-
149
- const future = new FutureReader(promise.then(_trailerResultFromEntries));
150
- const contents = new StreamReader(body);
151
- const fields = _fieldsFromEntriesChecked(headers);
152
-
153
- const [req] = Request.new(fields, contents, future, null);
154
- req.setMethod(method);
155
- req.setPathWithQuery(url);
156
- return req;
157
- };
@@ -1,146 +0,0 @@
1
- // Adapted from preview2-shim/lib/io/worker-sockets.js
2
- import { networkInterfaces } from "node:os";
3
-
4
- // TODO(tandr): switch to generated types
5
- export const IP_ADDRESS_FAMILY = {
6
- IPV4: "ipv4",
7
- IPV6: "ipv6",
8
- };
9
-
10
- export const tupleToIpv6 = (segments) => {
11
- if (segments.length !== 8) {
12
- return null;
13
- }
14
- return segments.map((segment) => segment.toString(16)).join(":");
15
- };
16
-
17
- export const tupleToIpv4 = (segments) => {
18
- if (segments.length !== 4) {
19
- return null;
20
- }
21
- return segments.map((segment) => segment.toString(10)).join(".");
22
- };
23
-
24
- export const ipv6ToTuple = (ipv6) => {
25
- const [lhs, rhs = ""] = ipv6.includes("::") ? ipv6.split("::") : [ipv6];
26
- const lhsParts = lhs === "" ? [] : lhs.split(":");
27
- const rhsParts = rhs === "" ? [] : rhs.split(":");
28
- return [...lhsParts, ...Array(8 - lhsParts.length - rhsParts.length).fill(0), ...rhsParts].map(
29
- (segment) => {
30
- const val = parseInt(segment, 16);
31
- if (val < 0 || val > 0xffff || Number.isNaN(val)) {
32
- throw new Error(`Invalid IPv6 segment: ${segment}`);
33
- }
34
- return val;
35
- },
36
- );
37
- };
38
-
39
- export const ipv4ToTuple = (ipv4) => {
40
- return ipv4.split(".").map((segment) => {
41
- const val = parseInt(segment, 10);
42
- if (val < 0 || val > 255 || Number.isNaN(val)) {
43
- throw new Error(`Invalid IPv4 segment: ${segment}`);
44
- }
45
- return val;
46
- });
47
- };
48
-
49
- export const isMulticastIpAddress = ({ tag, val: { address } }) =>
50
- (tag === "ipv4" && address[0] >= 0xe0 && address[0] <= 0xef) ||
51
- (tag === "ipv6" && (address[0] & 0xff00) === 0xff00);
52
-
53
- export const isIpv4MappedAddress = ({ tag, val: { address } }) =>
54
- tag === "ipv6" &&
55
- address[0] === 0 &&
56
- address[1] === 0 &&
57
- address[2] === 0 &&
58
- address[3] === 0 &&
59
- address[4] === 0 &&
60
- address[5] === 0xffff;
61
-
62
- export const isWildcardIpAddress = ({ val: { address } }) => address.every((b) => b === 0);
63
-
64
- export const isBroadcastIpAddress = ({ tag, val: { address } }) =>
65
- tag === "ipv4" && address.every((b) => b === 0xff);
66
-
67
- export const isUnicastIpAddress = (a) =>
68
- !isMulticastIpAddress(a) && !isBroadcastIpAddress(a) && !isWildcardIpAddress(a);
69
-
70
- export const isLoopbackIpAddress = ({ tag, val: { address } }) =>
71
- (tag === "ipv4" && address[0] === 127) ||
72
- (tag === "ipv6" && address.slice(0, 7).every((segment) => segment === 0) && address[7] === 1);
73
-
74
- export const ipAddressConflict = (a, b) =>
75
- a.tag === b.tag &&
76
- Number(a.val.port) === Number(b.val.port) &&
77
- Number(a.val.port) !== 0 &&
78
- (isWildcardIpAddress(a) ||
79
- isWildcardIpAddress(b) ||
80
- a.val.address.every((segment, idx) => segment === b.val.address[idx]));
81
-
82
- /**
83
- * Serialize a socket‐address to text.
84
- * @param {{tag:string,val:{address:number[]}}} ipAddr
85
- * @returns {string|null}
86
- */
87
- export const serializeIpAddress = ({ tag, val: { address } }) => {
88
- switch (tag) {
89
- case "ipv4":
90
- return tupleToIpv4(address);
91
- case "ipv6":
92
- return tupleToIpv6(address);
93
- default:
94
- throw new Error(`Unknown IP tag: ${tag}`);
95
- }
96
- };
97
-
98
- /**
99
- * Create a socket‐address object.
100
- * @param {'ipv4'|'ipv6'} family
101
- * @param {string} host IP text
102
- * @param {number|bigint} port
103
- * @returns {{tag:string,val:Object}}
104
- */
105
- export const makeIpAddress = (family, host, port) => {
106
- let address;
107
- switch (family) {
108
- case "ipv4":
109
- address = ipv4ToTuple(host);
110
- break;
111
- case "ipv6":
112
- address = ipv6ToTuple(host);
113
- break;
114
- default:
115
- throw new Error(`Unknown IP family: ${family}`);
116
- }
117
-
118
- const base = { address, port };
119
- return family === "ipv4"
120
- ? { tag: "ipv4", val: base }
121
- : { tag: "ipv6", val: { ...base, flowInfo: 0, scopeId: 0 } };
122
- };
123
-
124
- /**
125
- * Determine whether an IP address can be used as a local bind address.
126
- * @param {{tag:'ipv4'|'ipv6',val:{address:number[]}}} ipAddress
127
- * @returns {boolean}
128
- */
129
- export const isBindableIpAddress = (ipAddress) => {
130
- if (isWildcardIpAddress(ipAddress)) {
131
- return true;
132
- }
133
-
134
- const expectedFamily = ipAddress.tag === "ipv4" ? "IPv4" : "IPv6";
135
- const expectedAddress = ipAddress.val.address;
136
- return Object.values(networkInterfaces())
137
- .flat()
138
- .some((iface) => {
139
- if (iface?.family !== expectedFamily) {
140
- return false;
141
- }
142
- const ifaceAddress =
143
- ipAddress.tag === "ipv4" ? ipv4ToTuple(iface.address) : ipv6ToTuple(iface.address);
144
- return expectedAddress.every((segment, idx) => segment === ifaceAddress[idx]);
145
- });
146
- };
@@ -1,123 +0,0 @@
1
- export const ERROR_MAP = {
2
- EACCES: "access-denied",
3
- EPERM: "access-denied",
4
- EOPNOTSUPP: "not-supported",
5
- ENOPROTOOPT: "not-supported",
6
- EPFNOSUPPORT: "not-supported",
7
- EPROTONOSUPPORT: "not-supported",
8
- ESOCKTNOSUPPORT: "not-supported",
9
- EINVAL: "invalid-argument",
10
- EAFNOSUPPORT: "invalid-argument",
11
- ENOMEM: "out-of-memory",
12
- ENOBUFS: "out-of-memory",
13
- ETIMEDOUT: "timeout",
14
- EADDRINUSE: "address-in-use",
15
- EADDRNOTAVAIL: "address-not-bindable",
16
- EHOSTUNREACH: "remote-unreachable",
17
- EHOSTDOWN: "remote-unreachable",
18
- ENETUNREACH: "remote-unreachable",
19
- ENETDOWN: "remote-unreachable",
20
- ENONET: "remote-unreachable",
21
- ECONNREFUSED: "connection-refused",
22
- EPIPE: "connection-broken",
23
- ECONNRESET: "connection-reset",
24
- ECONNABORTED: "connection-aborted",
25
- EMSGSIZE: "datagram-too-large",
26
- ENOTCONN: "invalid-state",
27
- EISCONN: "invalid-state",
28
- EALREADY: "invalid-state",
29
- EDESTADDRREQ: "invalid-argument",
30
- };
31
-
32
- export const CODE_MAP = {
33
- 4053: "invalid-state",
34
- 4083: "invalid-state",
35
- ENOTCONN: "invalid-state",
36
- EBADF: "invalid-state",
37
- EACCES: "access-denied",
38
- EPERM: "access-denied",
39
- ENOTSUP: "not-supported",
40
- EOPNOTSUPP: "not-supported",
41
- ENOPROTOOPT: "not-supported",
42
- EPFNOSUPPORT: "not-supported",
43
- EPROTONOSUPPORT: "not-supported",
44
- ESOCKTNOSUPPORT: "not-supported",
45
- EINVAL: "invalid-argument",
46
- EAFNOSUPPORT: "invalid-argument",
47
- ENOMEM: "out-of-memory",
48
- ENOBUFS: "out-of-memory",
49
- 4090: "address-not-bindable",
50
- EADDRNOTAVAIL: "address-not-bindable",
51
- 4091: "address-in-use",
52
- EADDRINUSE: "address-in-use",
53
- EHOSTUNREACH: "remote-unreachable",
54
- EHOSTDOWN: "remote-unreachable",
55
- ENETUNREACH: "remote-unreachable",
56
- ENETDOWN: "remote-unreachable",
57
- ENONET: "remote-unreachable",
58
- ECONNREFUSED: "connection-refused",
59
- EPIPE: "connection-broken",
60
- ECONNRESET: "connection-reset",
61
- ECONNABORTED: "connection-aborted",
62
- EMSGSIZE: "datagram-too-large",
63
- };
64
-
65
- /**
66
- * Custom error for Socket operations with JCO compatible payload.
67
- *
68
- * https://bytecodealliance.github.io/jco/wit-type-representations.html#result-considerations-idiomatic-js-errors-for-host-implementations
69
- */
70
- export class SocketError extends Error {
71
- /**
72
- * @param {string} tag – machine‐readable error tag
73
- * @param {string} [message] – human‐readable message
74
- * @param {any} [val] – optional extra data
75
- */
76
- constructor(tag, message, val, opts = {}) {
77
- super(message ?? `Error: ${tag}`, { cause: opts.cause });
78
- this.name = "SocketError";
79
- this.payload = val !== undefined ? { tag, val } : { tag };
80
- }
81
-
82
- /**
83
- * Create or rewrap an error into SocketError
84
- * @param {any} err – number, string, Error, or SocketError
85
- */
86
- static from(err) {
87
- if (err instanceof SocketError) {
88
- return err;
89
- }
90
-
91
- let tag,
92
- message = undefined,
93
- val = undefined;
94
-
95
- if (typeof err === "number") {
96
- tag = CODE_MAP[err] ?? "other";
97
- message = `Error code ${err}`;
98
- if (tag === "other") {
99
- val = message;
100
- }
101
- } else if (typeof err === "string") {
102
- tag = ERROR_MAP[err] ?? "other";
103
- if (tag === "other") {
104
- val = err;
105
- }
106
- } else if (err && typeof err.code === "string") {
107
- tag = ERROR_MAP[err.code] ?? "other";
108
- message = err.message;
109
- if (tag === "other") {
110
- val = err.code;
111
- }
112
- } else if (err && typeof err.message === "string" && ERROR_MAP[err.message]) {
113
- tag = ERROR_MAP[err.message];
114
- message = err.message;
115
- } else {
116
- tag = "other";
117
- message = err?.message;
118
- val = message;
119
- }
120
-
121
- return new SocketError(tag, message, val);
122
- }
123
- }