MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 135 lines 3.6 kB view raw
1const knownServerReferences = new WeakMap(); 2const boundCache = new WeakMap(); 3const FunctionBind = Function.prototype.bind; 4const ArraySlice = Array.prototype.slice; 5 6function encodeFormData(reference) { 7 let resolve; 8 let reject; 9 const thenable = new Promise((res, rej) => { 10 resolve = res; 11 reject = rej; 12 }); 13 14 Promise.resolve(reference.bound).then((bound) => { 15 const data = new FormData(); 16 data.append('id', reference.id); 17 data.append('boundCount', String(bound.length)); 18 thenable.status = 'fulfilled'; 19 thenable.value = data; 20 resolve(data); 21 }, (error) => { 22 thenable.status = 'rejected'; 23 thenable.reason = error; 24 reject(error); 25 }); 26 27 return thenable; 28} 29 30function defaultEncodeFormAction(identifierPrefix) { 31 let referenceClosure = knownServerReferences.get(this); 32 let data = null; 33 34 if (referenceClosure.bound !== null) { 35 data = boundCache.get(referenceClosure); 36 if (!data) { 37 data = encodeFormData({ 38 id: referenceClosure.id, 39 bound: referenceClosure.bound, 40 }); 41 boundCache.set(referenceClosure, data); 42 } 43 44 if (data.status === 'rejected') throw data.reason; 45 if (data.status !== 'fulfilled') throw data; 46 47 referenceClosure = data.value; 48 const prefixedData = new FormData(); 49 referenceClosure.forEach((value, key) => { 50 prefixedData.append(`$ACTION_${identifierPrefix}:${key}`, value); 51 }); 52 data = prefixedData; 53 referenceClosure = `$ACTION_REF_${identifierPrefix}`; 54 } else { 55 referenceClosure = `$ACTION_ID_${referenceClosure.id}`; 56 } 57 58 return { 59 name: referenceClosure, 60 method: 'POST', 61 encType: 'multipart/form-data', 62 data, 63 }; 64} 65 66function bind() { 67 const referenceClosure = knownServerReferences.get(this); 68 if (!referenceClosure) return FunctionBind.apply(this, arguments); 69 70 const newFn = referenceClosure.originalBind.apply(this, arguments); 71 const args = ArraySlice.call(arguments, 1); 72 const boundPromise = referenceClosure.bound !== null 73 ? Promise.resolve(referenceClosure.bound).then((boundArgs) => boundArgs.concat(args)) 74 : Promise.resolve(args); 75 76 knownServerReferences.set(newFn, { 77 id: referenceClosure.id, 78 originalBind: newFn.bind, 79 bound: boundPromise, 80 }); 81 82 Object.defineProperties(newFn, { 83 $$FORM_ACTION: { value: this.$$FORM_ACTION }, 84 bind: { value: bind }, 85 }); 86 87 return newFn; 88} 89 90function registerBoundServerReference(reference, id, bound) { 91 knownServerReferences.set(reference, { 92 id, 93 originalBind: reference.bind, 94 bound, 95 }); 96 97 Object.defineProperties(reference, { 98 $$FORM_ACTION: { value: defaultEncodeFormAction }, 99 bind: { value: bind }, 100 }); 101} 102 103async function action(x) { 104 return x; 105} 106 107registerBoundServerReference(action, 'demo', null); 108 109const bound = action.bind(null, 1); 110console.log(`bound.formAction.type:${typeof bound.$$FORM_ACTION}`); 111 112try { 113 const info = bound.$$FORM_ACTION('demo'); 114 console.log(`first.name:${info.name}`); 115} catch (error) { 116 console.log(`first.throw.type:${typeof error}`); 117 console.log(`first.throw.then:${typeof error?.then}`); 118 console.log(`first.throw.status:${error?.status ?? 'missing'}`); 119} 120 121await Promise.resolve(); 122 123try { 124 const info = bound.$$FORM_ACTION('demo'); 125 console.log(`second.name:${info.name}`); 126 console.log(`second.method:${info.method}`); 127 console.log(`second.encType:${info.encType}`); 128 const pairs = []; 129 info.data?.forEach((value, key) => { 130 pairs.push(`${key}=${value}`); 131 }); 132 console.log(`second.data:${pairs.join(',')}`); 133} catch (error) { 134 console.log(`second.throw:${error?.message ?? String(error)}`); 135}