Files
24kycj 5fb269cc0a fix(web): 本地优先保存与 design 页体验修复
- 保存先写本地缓存,登录后可选云端同步;退出登录不再因文件操作跳转登录

- 修复 Layui 遮罩残留、design2 保存后线条、toast 被 finally 提前关闭

- 保存过程恢复 loading spinner;成功提示 2.5 秒

- 云端 payload 瘦身与体积超限提示;后端 schema 迁移与 FileService 容错

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 19:24:34 +08:00

649 lines
24 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function () {
'use strict';
var POLL_TIMEOUT_MS = 5 * 60 * 1000;
var PAY_STEP_LABELS = ['选择支付', '扫码付款', '激活生效'];
var _displayChannels = ['alipay'];
var _settingsPromise = null;
var CHANNEL_UI = {
alipay: { name: '支付宝', hint: '网页支付', icon: '支', iconCls: 'alipay' },
wechat: { name: '微信支付', hint: '扫码付款', icon: '微', iconCls: 'wechat' },
};
function esc(s) {
if (s == null) return '';
return String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function apiBase() {
var cfg = (typeof window !== 'undefined' && window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base);
if (cfg) return String(cfg).replace(/\/+$/, '');
if (typeof window !== 'undefined' && window.location) {
return window.location.origin + '/api/v1';
}
return '/api/v1';
}
function apiFetch(url, opts) {
if (typeof window.soonAuthedFetch === 'function') return window.soonAuthedFetch(url, opts || {});
opts = opts || {};
var token = localStorage.getItem('soon_access') || '';
opts.headers = Object.assign({}, opts.headers || {});
if (token) opts.headers.Authorization = 'Bearer ' + token;
return fetch(url, opts);
}
function parseApiJson(r) {
return r.text().then(function (text) {
try {
return JSON.parse(text);
} catch (e) {
return { ok: false, message: '服务暂时不可用,请稍后重试' };
}
});
}
function apiGet(p) {
return apiFetch(apiBase() + p).then(function (r) {
if (!r.ok && r.status === 401) return { ok: false, message: '未登录或会话已过期' };
return parseApiJson(r);
});
}
function apiPost(p, body) {
return apiFetch(apiBase() + p, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body || {}),
}).then(function (r) {
if (!r.ok && r.status === 401) return { ok: false, message: '未登录或会话已过期' };
return parseApiJson(r);
});
}
function isSafeAlipayGateway(url) {
if (!url || typeof url !== 'string') return false;
try {
var u = new URL(url);
return u.protocol === 'https:'
&& (u.hostname === 'openapi.alipay.com' || u.hostname === 'openapi.alipaydev.com');
} catch (e) {
return false;
}
}
var ALIPAY_WINDOW_NAME = 'soon_alipay_pay';
function openAlipayPayWindow() {
try {
var w = window.open('about:blank', ALIPAY_WINDOW_NAME);
if (w) {
w.document.title = '支付宝收银台';
w.document.body.innerHTML =
'<p style="font-family:sans-serif;text-align:center;padding:48px 24px;color:#333">正在跳转支付宝…</p>';
}
return w;
} catch (e) {
return null;
}
}
function submitAlipayForm(payForm, payWindow) {
if (!payForm || !payForm.gateway || !payForm.params) return false;
if (!isSafeAlipayGateway(payForm.gateway)) return false;
var win = payWindow;
if (!win || win.closed) {
win = openAlipayPayWindow();
}
if (!win) return false;
var form = document.createElement('form');
form.method = 'POST';
form.action = payForm.gateway;
form.target = ALIPAY_WINDOW_NAME;
form.acceptCharset = 'UTF-8';
form.style.display = 'none';
Object.keys(payForm.params).forEach(function (key) {
var val = payForm.params[key];
if (val == null || val === '') return;
var input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = String(val);
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
setTimeout(function () {
try {
document.body.removeChild(form);
} catch (e) { /* ignore */ }
}, 0);
return true;
}
function parseDisplayChannels(raw) {
if (Array.isArray(raw)) {
var list = raw.filter(function (ch) { return ch === 'alipay' || ch === 'wechat'; });
return list.length ? list : ['alipay'];
}
var parts = String(raw || 'alipay').split(/[\s,]+/).filter(Boolean);
var out = [];
parts.forEach(function (ch) {
if ((ch === 'alipay' || ch === 'wechat') && out.indexOf(ch) < 0) out.push(ch);
});
return out.length ? out : ['alipay'];
}
function loadDisplayChannels() {
if (_settingsPromise) return _settingsPromise;
_settingsPromise = apiGet('/settings').then(function (j) {
if (j.ok && j.data) {
if (j.data.payment_display_channels) {
_displayChannels = parseDisplayChannels(j.data.payment_display_channels);
} else if (j.data['payment.display_channels'] != null) {
_displayChannels = parseDisplayChannels(j.data['payment.display_channels']);
}
}
return _displayChannels;
}).catch(function () {
_displayChannels = ['alipay'];
return _displayChannels;
});
return _settingsPromise;
}
function displayChannels() {
return _displayChannels.slice();
}
function defaultChannel(channels) {
var list = channels || _displayChannels;
return list[0] || 'alipay';
}
function resolveChannel(requested, channels) {
var list = channels || _displayChannels;
if (requested && list.indexOf(requested) >= 0) return requested;
return defaultChannel(list);
}
function channelCardHtml(ch, active, selectable) {
var ui = CHANNEL_UI[ch];
if (!ui) return '';
var cls = 'soon-pay-channel' + (active ? ' is-active' : '') + (selectable ? '' : ' is-fixed');
var inner = '<span class="soon-pay-channel__radio" aria-hidden="true"></span>' +
'<span class="soon-pay-channel__icon soon-pay-channel__icon--' + ui.iconCls + '">' + ui.icon + '</span>' +
'<span class="soon-pay-channel__text"><span class="soon-pay-channel__name">' + ui.name + '</span>' +
'<span class="soon-pay-channel__hint">' + ui.hint + '</span></span>';
if (selectable) {
return '<button type="button" class="' + cls + '" data-ch="' + ch + '">' + inner + '</button>';
}
return '<div class="' + cls + '" data-ch="' + ch + '">' + inner + '</div>';
}
function payChannelsHtml(activeChannel, channels, opts) {
opts = opts || {};
channels = channels || _displayChannels;
var showChannel = activeChannel || defaultChannel(channels);
var selectable = opts.selectable !== false && channels.length > 1;
var list = selectable ? channels : [showChannel];
var cards = list.map(function (ch) {
return channelCardHtml(ch, ch === showChannel, selectable);
}).join('');
if (!cards) return '';
return '<section class="soon-pay-section soon-pay-section--channels' +
(selectable ? '' : ' soon-pay-section--channels-fixed') + '" id="payChannels">' +
'<h4 class="soon-pay-section__title">支付方式</h4>' +
'<div class="soon-pay-channels' + (list.length === 1 ? ' soon-pay-channels--single' : '') + '">' +
cards + '</div></section>';
}
function payStepLabel(step, channel) {
if (step === 2 && channel === 'alipay') return '网页支付';
if (step === 2 && channel === 'wechat') return '扫码付款';
return PAY_STEP_LABELS[step - 1];
}
function payStepperHtml(activeStep, channel) {
var parts = [];
for (var i = 1; i <= 3; i++) {
if (i > 1) {
var bridgeCls = 'soon-pay-stepper__bridge';
if (i - 1 < activeStep) bridgeCls += ' is-done';
parts.push('<li class="' + bridgeCls + '" aria-hidden="true"></li>');
}
var cls = 'soon-pay-stepper__item';
if (i < activeStep) cls += ' is-done';
else if (i === activeStep) cls += ' is-active';
parts.push('<li class="' + cls + '">' +
'<span class="soon-pay-stepper__dot">' + (i < activeStep ? '✓' : String(i)) + '</span>' +
'<span class="soon-pay-stepper__label">' + payStepLabel(i, channel) + '</span></li>');
}
return '<ol class="soon-pay-stepper" id="payStepper">' + parts.join('') + '</ol>';
}
function updatePayStep(root, step, channel) {
if (!root) return;
var html = payStepperHtml(step, channel || defaultChannel());
var old = root.querySelector('#payStepper');
if (old) old.outerHTML = html;
}
function payEmptyStateHtml(channel) {
var ui = CHANNEL_UI[channel] || CHANNEL_UI[defaultChannel()];
return '<div class="soon-pay-checkout-empty">' +
'<div class="soon-pay-checkout-empty__icon" aria-hidden="true">' +
'<svg viewBox="0 0 48 48" fill="none"><rect x="8" y="12" width="32" height="24" rx="4" stroke="currentColor" stroke-width="1.5"/>' +
'<path d="M8 20h32" stroke="currentColor" stroke-width="1.5"/>' +
'<circle cx="32" cy="30" r="3" fill="currentColor" opacity="0.5"/></svg></div>' +
'<p class="soon-pay-checkout-empty__title">等待生成付款信息</p>' +
'<p class="soon-pay-checkout-empty__desc">点击下方按钮,将使用' + esc(ui.name) + '' + esc(ui.hint) + '</p></div>';
}
function renderPayPayload(data, qrEl, orderNoEl, launchOpts) {
launchOpts = launchOpts || {};
if (orderNoEl && data.order_no) {
orderNoEl.textContent = '订单号 ' + data.order_no;
orderNoEl.style.display = '';
}
if (data.pay_form && data.pay_form.gateway) {
if (!isSafeAlipayGateway(data.pay_form.gateway)) return '支付网关无效';
var payForm = data.pay_form;
var launched = launchOpts.launched === true;
qrEl.innerHTML =
'<div class="soon-pay-checkout-ready soon-pay-checkout-ready--alipay">' +
'<div class="soon-pay-checkout-ready__brand soon-pay-checkout-ready__brand--alipay">支</div>' +
'<p class="soon-pay-checkout-ready__title">支付宝电脑网站支付</p>' +
'<p class="soon-pay-checkout-ready__desc">' +
(launched
? '支付窗口已打开,请在新窗口完成付款;本页将自动检测支付结果'
: '未能自动打开支付窗口,请允许弹窗后点击下方按钮') +
'</p>' +
'<button type="button" class="soon-btn soon-pay-checkout-ready__btn" id="goAlipay">' +
(launched ? '重新打开支付页面' : '打开支付宝付款') +
'</button></div>';
var alipayBtn = qrEl.querySelector('#goAlipay');
if (alipayBtn) {
alipayBtn.onclick = function () {
if (!submitAlipayForm(payForm)) {
if (typeof window.soonToast === 'function') {
window.soonToast('无法打开支付窗口,请允许浏览器弹窗', 'warn');
}
}
};
}
return '';
}
if (data.qrcode) {
qrEl.innerHTML =
'<div class="soon-pay-checkout-ready">' +
'<div class="soon-pay-checkout-ready__brand soon-pay-checkout-ready__brand--wechat">微</div>' +
'<p class="soon-pay-checkout-ready__title">微信扫码支付</p>' +
'<div class="soon-pay-checkout-ready__qr" id="payQrCanvas"></div>' +
'<p class="soon-pay-checkout-ready__desc">请使用微信扫一扫,有效期约 2 小时</p></div>';
if (typeof QRCode !== 'undefined') {
new QRCode(qrEl.querySelector('#payQrCanvas'), { text: data.qrcode, width: 188, height: 188 });
}
return '';
}
return '未获取到支付信息';
}
function paySheetHtml(opts) {
opts = opts || {};
var resumeOrderNo = opts.orderNo || null;
var channels = opts.displayChannels || _displayChannels;
var channel = (resumeOrderNo && opts.channel)
? opts.channel
: resolveChannel(opts.channel, channels);
var embedded = opts.variant === 'embedded';
var backBtn = !embedded && opts.showBack
? '<button type="button" class="soon-btn soon-btn--ghost soon-btn--sm soon-pay-sheet__back" id="payBack">返回选择方案</button>'
: '';
var headBlock = embedded ? '' :
'<header class="soon-pay-sheet__head">' +
'<div class="soon-pay-sheet__head-main">' +
'<span class="soon-pay-sheet__eyebrow">' + (resumeOrderNo ? '继续支付' : '确认激活') + '</span>' +
'<h3 class="soon-pay-sheet__title">' + esc(opts.planName || '') + '</h3>' +
'<p class="soon-pay-sheet__order" id="payOrderNo"' +
(resumeOrderNo ? '' : ' style="display:none"') + '>' +
(resumeOrderNo ? '订单号 ' + esc(resumeOrderNo) : '') + '</p></div>' +
'<div class="soon-pay-sheet__head-price">' +
'<span class="soon-pay-sheet__currency">¥</span>' +
'<span class="soon-pay-sheet__amount">' + esc(opts.priceDisplay || '') + '</span></div></header>';
var orderInBody = embedded
? '<p class="soon-pay-sheet__order soon-pay-sheet__order--inline" id="payOrderNo"' +
(resumeOrderNo ? '' : ' style="display:none"') + '>' +
(resumeOrderNo ? '订单号 ' + esc(resumeOrderNo) : '') + '</p>'
: '';
return '<div class="soon-pay-sheet' + (embedded ? ' soon-pay-sheet--embedded' : '') + '">' +
(backBtn ? '<div class="soon-pay-sheet__toolbar">' + backBtn + '</div>' : '') +
headBlock +
'<div class="soon-pay-sheet__body">' +
orderInBody +
payStepperHtml(1, channel) +
payChannelsHtml(channel, channels, { selectable: !resumeOrderNo && channels.length > 1 }) +
'<section class="soon-pay-section soon-pay-section--checkout">' +
'<div class="soon-pay-qr-panel" id="payQr">' + payEmptyStateHtml(channel) + '</div></section>' +
'<div class="soon-pay-sheet__foot">' +
'<p class="soon-pay-status" id="payStatus"></p>' +
'<div class="soon-pay-poll" id="payPollHint" aria-live="polite">' +
'<span class="soon-pay-poll__spin" aria-hidden="true"></span>' +
'<span class="soon-pay-poll__text">正在检测支付结果</span></div>' +
'<button type="button" class="soon-btn soon-pay-sheet__cta" id="payConfirm">' +
(resumeOrderNo ? '继续支付' : '确认并支付') + '</button>' +
'<button type="button" class="soon-btn soon-btn--ghost soon-btn--sm soon-pay-sheet__cta soon-pay-sheet__cta--secondary" id="payManualRefresh" style="display:none">已完成支付,刷新状态</button>' +
'</div></div></div>';
}
function bindPaySheet(sheetRoot, opts) {
opts = opts || {};
var planId = opts.planId;
var resumeOrderNo = opts.orderNo || null;
var channels = opts.displayChannels || _displayChannels;
var channel = (resumeOrderNo && opts.channel)
? opts.channel
: resolveChannel(opts.channel, channels);
var onPaid = typeof opts.onPaid === 'function' ? opts.onPaid : function () {};
var pollTimer = null;
var pollStartedAt = 0;
var orderNo = resumeOrderNo;
var paymentReady = false;
if (!sheetRoot) return { destroy: function () {} };
var manualBtn = sheetRoot.querySelector('#payManualRefresh');
var statusEl = sheetRoot.querySelector('#payStatus');
var pollHint = sheetRoot.querySelector('#payPollHint');
var confirmBtn = sheetRoot.querySelector('#payConfirm');
var qrEl = sheetRoot.querySelector('#payQr');
var orderNoEl = sheetRoot.querySelector('#payOrderNo');
var backBtn = sheetRoot.querySelector('#payBack');
var checking = false;
var pollTickTimer = null;
var MANUAL_REFRESH_LABEL = '已完成支付,刷新状态';
function setStatus(text, type) {
if (!statusEl) return;
statusEl.textContent = text || '';
statusEl.className = 'soon-pay-status' + (type ? ' soon-pay-status--' + type : '');
statusEl.style.display = text ? 'block' : 'none';
}
function setPolling(active) {
sheetRoot.classList.toggle('soon-pay-sheet--polling', !!active);
if (!active && pollHint) pollHint.classList.remove('is-tick');
}
function flashPollTick() {
if (!pollHint) return;
pollHint.classList.add('is-tick');
if (pollTickTimer) clearTimeout(pollTickTimer);
pollTickTimer = setTimeout(function () {
pollHint.classList.remove('is-tick');
}, 450);
}
function setStep(step) {
updatePayStep(sheetRoot, step, channel);
sheetRoot.classList.toggle('soon-pay-sheet--checkout', step >= 2);
var chSec = sheetRoot.querySelector('#payChannels');
if (chSec) chSec.style.display = step >= 2 ? 'none' : '';
}
function stopPoll() {
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
}
setPolling(false);
}
function startPoll() {
stopPoll();
setPolling(true);
pollStartedAt = Date.now();
checkOrderStatus(false);
pollTimer = setInterval(function () {
if (Date.now() - pollStartedAt > POLL_TIMEOUT_MS) {
stopPoll();
setStatus('自动检测超时,如已支付请点击下方按钮', 'err');
return;
}
checkOrderStatus(false);
}, 3000);
}
function handlePaid() {
stopPoll();
setStep(3);
setStatus('支付成功,正在更新会员状态…', 'ok');
setTimeout(onPaid, 600);
}
function checkOrderStatus(manual) {
if (!orderNo || checking) return;
checking = true;
if (statusEl) statusEl.classList.add('is-checking');
if (manual && manualBtn) {
manualBtn.disabled = true;
manualBtn.classList.add('is-loading');
manualBtn.textContent = '查询中…';
} else {
flashPollTick();
}
apiGet('/pay/orders/' + encodeURIComponent(orderNo)).then(function (st) {
if (st.ok && st.data && st.data.status === 'paid') {
handlePaid();
return;
}
if (manual) {
setStatus('暂未收到支付结果,请完成付款后再试', 'warn');
}
}).finally(function () {
checking = false;
if (statusEl) statusEl.classList.remove('is-checking');
if (manual && manualBtn) {
manualBtn.disabled = false;
manualBtn.classList.remove('is-loading');
manualBtn.textContent = MANUAL_REFRESH_LABEL;
}
});
}
if (manualBtn) manualBtn.onclick = function () { checkOrderStatus(true); };
function resetPaymentView() {
paymentReady = false;
stopPoll();
setStep(1);
if (manualBtn) manualBtn.style.display = 'none';
if (confirmBtn) confirmBtn.style.display = '';
qrEl.innerHTML = payEmptyStateHtml(channel);
if (orderNoEl && !resumeOrderNo) {
orderNoEl.textContent = '';
orderNoEl.style.display = 'none';
}
setStatus('');
}
function applyCheckout(data, payWindow) {
orderNo = data.order_no;
var launched = false;
if (channel === 'alipay' && data.pay_form && data.pay_form.gateway) {
launched = submitAlipayForm(data.pay_form, payWindow);
}
var err = renderPayPayload(data, qrEl, orderNoEl, { launched: launched });
if (err) {
setStatus(err, 'err');
return;
}
paymentReady = true;
setStep(2);
if (confirmBtn) confirmBtn.style.display = 'none';
if (manualBtn) manualBtn.style.display = '';
if (channel === 'alipay') {
setStatus(
launched ? '支付窗口已打开,请在新窗口完成付款' : '未能自动打开支付窗口,请允许弹窗或点击下方按钮',
launched ? 'ok' : 'err'
);
} else {
setStatus('请扫码完成支付,本页将自动检测支付结果', 'ok');
}
startPoll();
}
function submitCheckout() {
if (confirmBtn) confirmBtn.disabled = true;
setStatus('正在生成付款信息…');
qrEl.innerHTML = '<div class="soon-pay-checkout-loading"><div class="soon-skeleton soon-skeleton--h200"></div>' +
'<p>正在连接支付渠道…</p></div>';
var payWindow = channel === 'alipay' ? openAlipayPayWindow() : null;
var req;
if (resumeOrderNo || orderNo) {
var existingNo = orderNo || resumeOrderNo;
req = apiPost('/pay/orders/' + encodeURIComponent(existingNo) + '/checkout', { channel: channel });
} else {
req = apiPost('/pay/orders', { plan_id: planId, channel: channel });
}
req.then(function (j) {
if (confirmBtn) confirmBtn.disabled = false;
if (!j.ok) {
if (payWindow && !payWindow.closed) payWindow.close();
setStatus(j.message || '下单失败', 'err');
qrEl.innerHTML = payEmptyStateHtml(channel);
return;
}
applyCheckout(j.data, payWindow);
}).catch(function () {
if (confirmBtn) confirmBtn.disabled = false;
if (payWindow && !payWindow.closed) payWindow.close();
setStatus('网络错误,请重试', 'err');
qrEl.innerHTML = payEmptyStateHtml(channel);
});
}
sheetRoot.querySelectorAll('.soon-pay-channel:not(.is-fixed)').forEach(function (el) {
el.onclick = function () {
if (el.classList.contains('is-active')) return;
sheetRoot.querySelectorAll('.soon-pay-channel').forEach(function (x) { x.classList.remove('is-active'); });
el.classList.add('is-active');
channel = el.dataset.ch;
if (paymentReady) resetPaymentView();
};
});
if (confirmBtn) confirmBtn.onclick = submitCheckout;
if (backBtn && typeof opts.onBack === 'function') backBtn.onclick = opts.onBack;
function destroy() {
stopPoll();
if (pollTickTimer) {
clearTimeout(pollTickTimer);
pollTickTimer = null;
}
paymentReady = false;
orderNo = resumeOrderNo;
}
return {
destroy: destroy,
reset: resetPaymentView,
};
}
function openPayModal(opts) {
opts = opts || {};
if (typeof layer === 'undefined' || !layer.open) return null;
var channels = _displayChannels;
var channel = (opts.orderNo && opts.channel)
? opts.channel
: resolveChannel(opts.channel, channels);
var html = paySheetHtml({
planName: opts.planName,
priceDisplay: opts.priceDisplay,
orderNo: opts.orderNo,
channel: channel,
displayChannels: channels,
});
var payCtrl = null;
var width = Math.min(480, window.innerWidth - 32);
var idx = layer.open({
type: 1,
skin: 'soon-layer',
title: false,
closeBtn: 1,
fixed: true,
offset: 'auto',
shade: [0.72, '#000'],
shadeClose: !!opts.shadeClose,
maxWidth: width,
area: [width + 'px', 'auto'],
content: html,
success: function (layero, index) {
var layerEl = layero && layero[0] ? layero[0] : layero;
if (layerEl && layerEl.classList) {
layerEl.classList.add('soon-layer--pay');
var layerContent = layerEl.querySelector('.layui-layer-content');
if (layerContent) {
layerContent.style.padding = '0';
layerContent.style.overflow = 'visible';
layerContent.style.maxHeight = 'none';
layerContent.style.background = 'transparent';
}
}
var sheet = layerEl.querySelector('.soon-pay-sheet');
payCtrl = bindPaySheet(sheet, {
planId: opts.planId,
planName: opts.planName,
priceDisplay: opts.priceDisplay,
orderNo: opts.orderNo,
channel: channel,
displayChannels: channels,
onPaid: function () {
layer.close(idx);
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
if (typeof opts.onPaid === 'function') opts.onPaid();
},
onBack: opts.onBack,
});
requestAnimationFrame(function () {
if (typeof layer.style === 'function') layer.style(index);
});
},
end: function () {
if (payCtrl) payCtrl.destroy();
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
if (typeof opts.onClose === 'function') opts.onClose();
},
});
return idx;
}
loadDisplayChannels();
window.SoonMemberPay = {
apiBase: apiBase,
apiGet: apiGet,
apiPost: apiPost,
esc: esc,
paySheetHtml: paySheetHtml,
bindPaySheet: bindPaySheet,
openPayModal: openPayModal,
payEmptyStateHtml: payEmptyStateHtml,
renderPayPayload: renderPayPayload,
submitAlipayForm: submitAlipayForm,
openAlipayPayWindow: openAlipayPayWindow,
loadDisplayChannels: loadDisplayChannels,
displayChannels: displayChannels,
defaultChannel: defaultChannel,
resolveChannel: resolveChannel,
POLL_TIMEOUT_MS: POLL_TIMEOUT_MS,
};
})();