UNPKG

zustand-duck

Version:

Simple reducer writing and cross-process state sharing.

178 lines 7.06 kB
import { duck } from '../duck'; export const MASTER_PORT_ID = 'master'; const delay = () => new Promise(resolve => setTimeout(resolve, 0)); export const sharedDuck = (options) => { const { name, port, channel = '', channels } = options; const unsubscribeList = []; if (channels) { let unsubscribeChannel = channels.subscribe((_channel, event) => { if (_channel === channel && event === 'remove') { unsubscribeChannel(); unsubscribeList.forEach(fn => fn()); } }); } if (port.id === MASTER_PORT_ID) { const mirrorPortIdSet = new Set(); const actionRewrite = ({ action, payload }, origin) => { mirrorPortIdSet.forEach(portId => { port.send(portId, { event: 'forward/replica', portId: MASTER_PORT_ID, name, channel, data: { action, payload }, }); }); return origin(...payload); }; options = { initialize: async (api, resolve) => { if (channel === (channels?.default ?? '')) { // only default channel need delay 0s // beause other channel always create later await delay(); } const unsubscribe = port.onMessage(message => { if (message.name !== name) { return; } // only default channel handle register event if (channel === channels?.default && message.event === 'register/replica') { channels?.notify(message.channel, 'add'); port.send(message.portId, { event: 'register/success', name, channel: message.channel, portId: MASTER_PORT_ID, data: {}, }); return; } if (message.channel !== channel) { return; } switch (message.event) { case 'state/request': { mirrorPortIdSet.add(message.portId); const state = api.getState(); port.send(message.portId, { event: 'state/response', name, channel, portId: MASTER_PORT_ID, data: state, }); break; } case 'forward/master': { mirrorPortIdSet.forEach(portId => { port.send(portId, { event: 'forward/replica', portId: MASTER_PORT_ID, name, channel, data: message.data, }); api.originActions[message.data.action](...message.data.payload); }); break; } } }); unsubscribeList.push(unsubscribe); resolve(api); }, actionRewrite, ...options, }; } else { let localId = 0; const actionResolveMap = new Map(); const actionRewrite = ({ action, payload }, origin) => { const actionId = `${port.id}_${localId++}`; port.send(MASTER_PORT_ID, { event: 'forward/master', portId: port.id, name, channel, data: { id: actionId, action, payload, }, }); const promise = new Promise(resolve => actionResolveMap.set(actionId, resolve)); return promise; }; options = { initialize: async (api, resolve) => { await delay(); const unsubscribe = port.onMessage(message => { if (message.name !== name) { return; } if (message.channel !== channel) { return; } switch (message.event) { case 'state/response': { api.setState(message.data); resolve(api); break; } case 'forward/replica': { api.originActions[message.data.action](...message.data.payload); const resolve = message.data.id && actionResolveMap.get(message.data.id); if (resolve) { resolve(message.data.payload); actionResolveMap.delete(message.data.id); } break; } } }); unsubscribeList.push(unsubscribe); if (channels) { await new Promise(resolve => { // register replica const unsubscribe = port.onMessage((message) => { if (message.name !== name) { return; } if (message.channel !== channel) { return; } if (message.event === 'register/success') { resolve(void 0); unsubscribe(); } }); unsubscribeList.push(unsubscribe); port.send(MASTER_PORT_ID, { event: 'register/replica', name, portId: port.id, channel, data: {}, }); }); } port.send(MASTER_PORT_ID, { event: 'state/request', name, portId: port.id, channel, data: {}, }); }, actionRewrite, ...options, }; } return duck({ ...options, }); }; //# sourceMappingURL=shared-duck.js.map