comlink-4.3.1.js (11387B)
1/** 2 * Copyright 2019 Google Inc. All Rights Reserved. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, 9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 * See the License for the specific language governing permissions and 11 * limitations under the License. 12 */ 13const proxyMarker = Symbol("Comlink.proxy"); 14const createEndpoint = Symbol("Comlink.endpoint"); 15const releaseProxy = Symbol("Comlink.releaseProxy"); 16const throwMarker = Symbol("Comlink.thrown"); 17const isObject = (val) => (typeof val === "object" && val !== null) || typeof val === "function"; 18/** 19 * Internal transfer handle to handle objects marked to proxy. 20 */ 21const proxyTransferHandler = { 22 canHandle: (val) => { 23 console.log("checking ", typeof(val), val, isObject(val) && val[proxyMarker], Object.keys(val), Object.keys(val.__proto__)); 24 return isObject(val) && val[proxyMarker]; 25 }, 26 serialize(obj) { 27 const { port1, port2 } = new MessageChannel(); 28 expose(obj, port1); 29 return [port2, [port2]]; 30 }, 31 deserialize(port) { 32 port.start(); 33 return wrap(port); 34 }, 35}; 36/** 37 * Internal transfer handler to handle thrown exceptions. 38 */ 39const throwTransferHandler = { 40 canHandle: (value) => isObject(value) && throwMarker in value, 41 serialize({ value }) { 42 let serialized; 43 if (value instanceof Error) { 44 serialized = { 45 isError: true, 46 value: { 47 message: value.message, 48 name: value.name, 49 stack: value.stack, 50 }, 51 }; 52 } 53 else { 54 serialized = { isError: false, value }; 55 } 56 return [serialized, []]; 57 }, 58 deserialize(serialized) { 59 if (serialized.isError) { 60 throw Object.assign(new Error(serialized.value.message), serialized.value); 61 } 62 throw serialized.value; 63 }, 64}; 65/** 66 * Allows customizing the serialization of certain values. 67 */ 68const transferHandlers = new Map([ 69 ["proxy", proxyTransferHandler], 70 ["throw", throwTransferHandler], 71]); 72function expose(obj, ep = self) { 73 ep.addEventListener("message", function callback(ev) { 74 if (!ev || !ev.data) { 75 return; 76 } 77 console.log(ev); 78 const { id, type, path } = Object.assign({ path: [] }, ev.data); 79 const argumentList = (ev.data.argumentList || []).map(fromWireValue); 80 let returnValue; 81 try { 82 const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj); 83 const rawValue = path.reduce((obj, prop) => obj[prop], obj); 84 switch (type) { 85 case "GET" /* GET */: 86 { 87 returnValue = rawValue; 88 } 89 break; 90 case "SET" /* SET */: 91 { 92 parent[path.slice(-1)[0]] = fromWireValue(ev.data.value); 93 returnValue = true; 94 } 95 break; 96 case "APPLY" /* APPLY */: 97 { 98 returnValue = rawValue.apply(parent, argumentList); 99 console.log("aplly return"); 100 console.log(returnValue); 101 console.log(Object.keys(returnValue.__proto__)); 102 } 103 break; 104 case "CONSTRUCT" /* CONSTRUCT */: 105 { 106 const value = new rawValue(...argumentList); 107 returnValue = proxy(value); 108 } 109 break; 110 case "ENDPOINT" /* ENDPOINT */: 111 { 112 const { port1, port2 } = new MessageChannel(); 113 expose(obj, port2); 114 returnValue = transfer(port1, [port1]); 115 } 116 break; 117 case "RELEASE" /* RELEASE */: 118 { 119 returnValue = undefined; 120 } 121 break; 122 default: 123 return; 124 } 125 } 126 catch (value) { 127 returnValue = { value, [throwMarker]: 0 }; 128 } 129 Promise.resolve(returnValue) 130 .catch((value) => { 131 return { value, [throwMarker]: 0 }; 132 }) 133 .then((returnValue) => { 134 const [wireValue, transferables] = toWireValue(returnValue); 135 ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables); 136 if (type === "RELEASE" /* RELEASE */) { 137 // detach and deactive after sending release response above. 138 ep.removeEventListener("message", callback); 139 closeEndPoint(ep); 140 } 141 }); 142 }); 143 if (ep.start) { 144 ep.start(); 145 } 146} 147function isMessagePort(endpoint) { 148 return endpoint.constructor.name === "MessagePort"; 149} 150function closeEndPoint(endpoint) { 151 if (isMessagePort(endpoint)) 152 endpoint.close(); 153} 154function wrap(ep, target) { 155 return createProxy(ep, [], target); 156} 157function throwIfProxyReleased(isReleased) { 158 if (isReleased) { 159 throw new Error("Proxy has been released and is not useable"); 160 } 161} 162function createProxy(ep, path = [], target = function () { }) { 163 let isProxyReleased = false; 164 const proxy = new Proxy(target, { 165 get(_target, prop) { 166 throwIfProxyReleased(isProxyReleased); 167 if (prop === releaseProxy) { 168 return () => { 169 return requestResponseMessage(ep, { 170 type: "RELEASE" /* RELEASE */, 171 path: path.map((p) => p.toString()), 172 }).then(() => { 173 closeEndPoint(ep); 174 isProxyReleased = true; 175 }); 176 }; 177 } 178 if (prop === "then") { 179 if (path.length === 0) { 180 return { then: () => proxy }; 181 } 182 const r = requestResponseMessage(ep, { 183 type: "GET" /* GET */, 184 path: path.map((p) => p.toString()), 185 }).then(fromWireValue); 186 return r.then.bind(r); 187 } 188 return createProxy(ep, [...path, prop]); 189 }, 190 set(_target, prop, rawValue) { 191 throwIfProxyReleased(isProxyReleased); 192 // FIXME: ES6 Proxy Handler `set` methods are supposed to return a 193 // boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯ 194 const [value, transferables] = toWireValue(rawValue); 195 return requestResponseMessage(ep, { 196 type: "SET" /* SET */, 197 path: [...path, prop].map((p) => p.toString()), 198 value, 199 }, transferables).then(fromWireValue); 200 }, 201 apply(_target, _thisArg, rawArgumentList) { 202 throwIfProxyReleased(isProxyReleased); 203 const last = path[path.length - 1]; 204 if (last === createEndpoint) { 205 return requestResponseMessage(ep, { 206 type: "ENDPOINT" /* ENDPOINT */, 207 }).then(fromWireValue); 208 } 209 // We just pretend that `bind()` didn’t happen. 210 if (last === "bind") { 211 return createProxy(ep, path.slice(0, -1)); 212 } 213 const [argumentList, transferables] = processArguments(rawArgumentList); 214 return requestResponseMessage(ep, { 215 type: "APPLY" /* APPLY */, 216 path: path.map((p) => p.toString()), 217 argumentList, 218 }, transferables).then(fromWireValue); 219 }, 220 construct(_target, rawArgumentList) { 221 throwIfProxyReleased(isProxyReleased); 222 const [argumentList, transferables] = processArguments(rawArgumentList); 223 return requestResponseMessage(ep, { 224 type: "CONSTRUCT" /* CONSTRUCT */, 225 path: path.map((p) => p.toString()), 226 argumentList, 227 }, transferables).then(fromWireValue); 228 }, 229 }); 230 return proxy; 231} 232function myFlat(arr) { 233 return Array.prototype.concat.apply([], arr); 234} 235function processArguments(argumentList) { 236 const processed = argumentList.map(toWireValue); 237 return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))]; 238} 239const transferCache = new WeakMap(); 240function transfer(obj, transfers) { 241 transferCache.set(obj, transfers); 242 return obj; 243} 244function proxy(obj) { 245 return Object.assign(obj, { [proxyMarker]: true }); 246} 247function windowEndpoint(w, context = self, targetOrigin = "*") { 248 return { 249 postMessage: (msg, transferables) => w.postMessage(msg, targetOrigin, transferables), 250 addEventListener: context.addEventListener.bind(context), 251 removeEventListener: context.removeEventListener.bind(context), 252 }; 253} 254function toWireValue(value) { 255 for (const [name, handler] of transferHandlers) { 256 if (handler.canHandle(value)) { 257 const [serializedValue, transferables] = handler.serialize(value); 258 return [ 259 { 260 type: "HANDLER" /* HANDLER */, 261 name, 262 value: serializedValue, 263 }, 264 transferables, 265 ]; 266 } 267 } 268 return [ 269 { 270 type: "RAW" /* RAW */, 271 value, 272 }, 273 transferCache.get(value) || [], 274 ]; 275} 276function fromWireValue(value) { 277 switch (value.type) { 278 case "HANDLER" /* HANDLER */: 279 return transferHandlers.get(value.name).deserialize(value.value); 280 case "RAW" /* RAW */: 281 return value.value; 282 } 283} 284function requestResponseMessage(ep, msg, transfers) { 285 return new Promise((resolve) => { 286 const id = generateUUID(); 287 ep.addEventListener("message", function l(ev) { 288 if (!ev.data || !ev.data.id || ev.data.id !== id) { 289 return; 290 } 291 ep.removeEventListener("message", l); 292 resolve(ev.data); 293 }); 294 if (ep.start) { 295 ep.start(); 296 } 297 ep.postMessage(Object.assign({ id }, msg), transfers); 298 }); 299} 300function generateUUID() { 301 return new Array(4) 302 .fill(0) 303 .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)) 304 .join("-"); 305} 306 307export { createEndpoint, expose, proxy, proxyMarker, releaseProxy, transfer, transferHandlers, windowEndpoint, wrap }; 308//# sourceMappingURL=comlink-4.3.1.js.map