@wevu/web-apis
Version:
Web API polyfills and global installers for mini-program runtimes
208 lines (207 loc) • 7.89 kB
JavaScript
import { h as resolveUrlConstructor$1, m as resolveTextEncoderConstructor, n as cloneArrayBuffer, r as cloneArrayBufferView, t as RequestGlobalsEventTarget } from "./shared-BB491DgN.mjs";
import { URLPolyfill } from "./url.mjs";
import { resolveWebSocketMiniProgramOptions } from "./networkDefaults.mjs";
import { BlobPolyfill } from "./web.mjs";
import { wpi } from "@wevu/api";
//#region src/websocket.ts
const WHITESPACE_RE = /\s/u;
function isValidProtocol(protocol) {
return [...protocol].every((char) => {
const code = char.charCodeAt(0);
return code >= 33 && code <= 126 && char !== "," && !WHITESPACE_RE.test(char);
});
}
function createDomLikeError(name, message) {
if (typeof DOMException === "function") return new DOMException(message, name);
const error = new Error(message);
error.name = name;
return error;
}
function encodeReasonLength(reason) {
const TextEncoderConstructor = resolveTextEncoderConstructor();
if (TextEncoderConstructor) return new TextEncoderConstructor().encode(reason).byteLength;
return unescape(encodeURIComponent(reason)).length;
}
function normalizeCloseCode(code) {
if (code == null) return;
if (code === 1e3 || code >= 3e3 && code <= 4999) return code;
throw createDomLikeError("InvalidAccessError", `Failed to execute close: invalid code "${code}"`);
}
function normalizeCloseReason(reason) {
if (reason == null) return;
const normalized = String(reason);
if (encodeReasonLength(normalized) > 123) throw createDomLikeError("SyntaxError", "Failed to execute close: reason is longer than 123 bytes");
return normalized;
}
function resolveUrlConstructor() {
return resolveUrlConstructor$1() ?? URLPolyfill;
}
function normalizeProtocols(protocols) {
if (protocols == null) return;
const normalized = (Array.isArray(protocols) ? [...protocols] : [protocols]).map((protocol) => String(protocol));
const unique = /* @__PURE__ */ new Set();
for (const protocol of normalized) {
if (!isValidProtocol(protocol)) throw new SyntaxError(`Failed to construct 'WebSocket': invalid subprotocol "${protocol}"`);
if (unique.has(protocol)) throw new SyntaxError(`Failed to construct 'WebSocket': duplicated subprotocol "${protocol}"`);
unique.add(protocol);
}
return normalized;
}
function isWebSocketPolyfillInit(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function resolveWebSocketProtocols(protocols) {
if (isWebSocketPolyfillInit(protocols)) return normalizeProtocols(protocols.protocols);
return normalizeProtocols(protocols);
}
function resolveWebSocketMiniProgramConfig(protocols) {
if (!isWebSocketPolyfillInit(protocols)) return resolveWebSocketMiniProgramOptions();
return resolveWebSocketMiniProgramOptions(protocols.miniProgram, protocols.miniprogram);
}
function normalizeUrl(url) {
const parsed = new (resolveUrlConstructor())(String(url));
if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") throw new SyntaxError(`Failed to construct 'WebSocket': invalid URL "${url}"`);
if (parsed.hash) throw new SyntaxError(`Failed to construct 'WebSocket': URL contains fragment "${url}"`);
return parsed.toString();
}
function isSocketTask(value) {
return typeof value === "object" && value !== null && typeof value.send === "function" && typeof value.close === "function";
}
function toBinaryPayload(data) {
if (typeof data === "string") return data;
if (data instanceof ArrayBuffer) return cloneArrayBuffer(data);
if (ArrayBuffer.isView(data)) return cloneArrayBufferView(data);
if (typeof Blob !== "undefined" && data instanceof Blob) return data.arrayBuffer();
throw new TypeError("Failed to execute send: data must be a string, ArrayBuffer, ArrayBufferView or Blob");
}
function createErrorEvent(error) {
return {
type: "error",
error,
message: typeof error === "object" && error !== null && "errMsg" in error ? String(error.errMsg ?? "") : error instanceof Error ? error.message : void 0
};
}
function createCloseEvent(result) {
return {
type: "close",
code: result?.code ?? 1e3,
reason: result?.reason ?? "",
wasClean: (result?.code ?? 1e3) === 1e3
};
}
function createMessageEvent(url, data) {
return {
type: "message",
data,
origin: new (resolveUrlConstructor())(url).origin
};
}
function getRawConnectSocket() {
const adapter = wpi.getAdapter?.() ?? wpi.raw;
const target = wpi.resolveTarget?.("connectSocket");
if (!adapter || !target?.supported) return;
const method = adapter[target.target];
return typeof method === "function" ? method.bind(adapter) : void 0;
}
var WebSocketPolyfill = class WebSocketPolyfill extends RequestGlobalsEventTarget {
static CONNECTING = 0;
static OPEN = 1;
static CLOSING = 2;
static CLOSED = 3;
CONNECTING = WebSocketPolyfill.CONNECTING;
OPEN = WebSocketPolyfill.OPEN;
CLOSING = WebSocketPolyfill.CLOSING;
CLOSED = WebSocketPolyfill.CLOSED;
extensions = "";
protocol = "";
url;
binaryType = "blob";
bufferedAmount = 0;
readyState = WebSocketPolyfill.CONNECTING;
onclose = null;
onerror = null;
onmessage = null;
onopen = null;
socketTask;
constructor(url, protocols) {
super();
this.url = normalizeUrl(url);
const connectSocket = getRawConnectSocket();
if (!connectSocket) throw createDomLikeError("NotSupportedError", "WebSocket is not supported in the current mini-program runtime");
const normalizedProtocols = resolveWebSocketProtocols(protocols);
const task = connectSocket({
...resolveWebSocketMiniProgramConfig(protocols),
url: this.url,
protocols: normalizedProtocols,
fail: (error) => {
this.emitError(error);
this.closeFromRuntime();
}
});
if (!isSocketTask(task)) throw createDomLikeError("NetworkError", "Failed to create mini-program SocketTask");
this.socketTask = task;
task.onOpen(() => {
if (this.readyState !== WebSocketPolyfill.CONNECTING) return;
this.readyState = WebSocketPolyfill.OPEN;
this.dispatchEvent({ type: "open" });
});
task.onMessage((result) => {
if (this.readyState === WebSocketPolyfill.CLOSED) return;
const data = typeof result.data === "string" ? result.data : this.binaryType === "arraybuffer" ? cloneArrayBuffer(result.data) : new BlobPolyfill([cloneArrayBuffer(result.data)]);
this.dispatchEvent(createMessageEvent(this.url, data));
});
task.onError((error) => {
this.emitError(error);
});
task.onClose((result) => {
this.closeFromRuntime(result);
});
}
close(code, reason) {
if (this.readyState === WebSocketPolyfill.CLOSING || this.readyState === WebSocketPolyfill.CLOSED) return;
const normalizedCode = normalizeCloseCode(code);
const normalizedReason = normalizeCloseReason(reason);
this.readyState = WebSocketPolyfill.CLOSING;
this.socketTask?.close({
code: normalizedCode,
reason: normalizedReason,
fail: (error) => {
this.emitError(error);
}
});
}
send(data) {
if (this.readyState === WebSocketPolyfill.CONNECTING) throw createDomLikeError("InvalidStateError", "Failed to execute send: WebSocket is still in CONNECTING state");
if (this.readyState !== WebSocketPolyfill.OPEN || !this.socketTask) throw createDomLikeError("InvalidStateError", "Failed to execute send: WebSocket is not open");
const payload = toBinaryPayload(data);
if (payload instanceof Promise) {
payload.then((resolved) => {
this.socketTask?.send({
data: resolved,
fail: (error) => {
this.emitError(error);
}
});
}).catch((error) => {
this.emitError(error);
});
return;
}
this.socketTask.send({
data: payload,
fail: (error) => {
this.emitError(error);
}
});
}
closeFromRuntime(result) {
if (this.readyState === WebSocketPolyfill.CLOSED) return;
this.readyState = WebSocketPolyfill.CLOSED;
this.dispatchEvent(createCloseEvent(result));
}
emitError(error) {
this.dispatchEvent(createErrorEvent(error));
}
};
//#endregion
export { WebSocketPolyfill };