diff --git a/background.js b/background.js index b566f8d..faf1d9b 100644 --- a/background.js +++ b/background.js @@ -13828,8 +13828,6 @@ const phoneVerificationHelpers = self.MultiPageBackgroundPhoneVerification?.crea setState, sleepWithStop, throwIfStopped, - createFiveSimProvider: self.PhoneSmsFiveSimProvider?.createProvider, - createMaDaoProvider: self.PhoneSmsMaDaoProvider?.createProvider, }); const step1Executor = self.MultiPageBackgroundStep1?.createStep1Executor({ addLog, diff --git a/background/phone-verification-flow.js b/background/phone-verification-flow.js index c67fc45..7cf1900 100644 --- a/background/phone-verification-flow.js +++ b/background/phone-verification-flow.js @@ -29,8 +29,6 @@ DEFAULT_NEX_SMS_COUNTRY_ORDER = [1], DEFAULT_NEX_SMS_SERVICE_CODE = 'ot', DEFAULT_HERO_SMS_REUSE_ENABLED = true, - createFiveSimProvider = null, - createMaDaoProvider = null, HERO_SMS_COUNTRY_ID = 52, HERO_SMS_COUNTRY_LABEL = 'Thailand', HERO_SMS_SERVICE_CODE = 'dr', @@ -1209,13 +1207,12 @@ return normalizePhoneSmsReuseEnabled(state); } - function createResolvedFiveSimProvider() { + function getPhoneSmsProviderAdapterForState(state = {}, providerId = normalizePhoneSmsProvider(state?.phoneSmsProvider || DEFAULT_PHONE_SMS_PROVIDER)) { const rootScope = typeof self !== 'undefined' ? self : globalThis; - const factory = createFiveSimProvider || rootScope.PhoneSmsFiveSimProvider?.createProvider; - if (typeof factory !== 'function') { - return null; + if (!rootScope.PhoneSmsProviderRegistry?.createProvider) { + throw new Error('接码平台 registry 未加载。'); } - return factory({ + return rootScope.PhoneSmsProviderRegistry.createProvider(normalizePhoneSmsProvider(providerId), { addLog, fetchImpl, requestTimeoutMs: DEFAULT_PHONE_REQUEST_TIMEOUT_MS, @@ -1224,27 +1221,12 @@ }); } - function getFiveSimProviderForState(_state = {}) { - return createResolvedFiveSimProvider(); + function getFiveSimProviderForState(state = {}) { + return getPhoneSmsProviderAdapterForState(state, PHONE_SMS_PROVIDER_FIVE_SIM); } - function createResolvedMaDaoProvider() { - const rootScope = typeof self !== 'undefined' ? self : globalThis; - const factory = createMaDaoProvider || rootScope.PhoneSmsMaDaoProvider?.createProvider; - if (typeof factory !== 'function') { - return null; - } - return factory({ - addLog, - fetchImpl, - requestTimeoutMs: DEFAULT_PHONE_REQUEST_TIMEOUT_MS, - sleepWithStop, - throwIfStopped, - }); - } - - function getMaDaoAdapterForState(_state = {}) { - return createResolvedMaDaoProvider(); + function getMaDaoAdapterForState(state = {}) { + return getPhoneSmsProviderAdapterForState(state, PHONE_SMS_PROVIDER_MADAO); } function normalizeFiveSimCountryId(value, fallback = 'england') { diff --git a/phone-sms/providers/five-sim.js b/phone-sms/providers/five-sim.js index 15312f6..c5586d5 100644 --- a/phone-sms/providers/five-sim.js +++ b/phone-sms/providers/five-sim.js @@ -371,6 +371,20 @@ } function resolveCountryCandidates(state = {}) { + const orderedCountries = normalizeFiveSimCountryFallback(state.fiveSimCountryOrder); + if (orderedCountries.length) { + const primaryCountryId = normalizeFiveSimCountryId(state.fiveSimCountryId, ''); + return orderedCountries.map((entry) => { + const id = normalizeFiveSimCountryId(entry.id, ''); + return { + id, + label: primaryCountryId && id === primaryCountryId + ? formatFiveSimCountryLabel(id, state.fiveSimCountryLabel || entry.label || id, id) + : formatFiveSimCountryLabel(id, entry.label || id, id), + }; + }); + } + const primary = resolveCountryConfig(state); const fallbackList = normalizeFiveSimCountryFallback(state.fiveSimCountryFallback); const seen = new Set([primary.id]); @@ -496,6 +510,8 @@ async function resolvePricePlan(state = {}, countryConfig = resolveCountryConfig(state), deps = {}) { const userLimitText = normalizeFiveSimMaxPrice(state.fiveSimMaxPrice); const userLimit = userLimitText ? Number(userLimitText) : null; + const userMinText = normalizeFiveSimMaxPrice(state.fiveSimMinPrice); + const userMinLimit = userMinText ? Number(userMinText) : null; let priceCandidates = []; try { @@ -526,19 +542,24 @@ priceCandidates = buildSortedUniquePriceCandidates(priceCandidates); const minCatalogPrice = priceCandidates.length > 0 ? priceCandidates[0] : null; + const rangeFilteredPrices = priceCandidates.filter((price) => ( + (userMinLimit === null || price >= userMinLimit) + && (userLimit === null || price <= userLimit) + )); if (userLimit !== null) { - const bounded = priceCandidates.filter((price) => price <= userLimit); + const bounded = rangeFilteredPrices; return { prices: bounded.length > 0 ? [userLimit, ...bounded.filter((price) => price !== userLimit)] : [userLimit], userLimit, + userMinLimit, minCatalogPrice, }; } - if (priceCandidates.length > 0) { - return { prices: priceCandidates, userLimit: null, minCatalogPrice }; + if (rangeFilteredPrices.length > 0) { + return { prices: rangeFilteredPrices, userLimit: null, userMinLimit, minCatalogPrice }; } - return { prices: [null], userLimit: null, minCatalogPrice: null }; + return { prices: [null], userLimit: null, userMinLimit, minCatalogPrice }; } function normalizeActivation(record, fallback = {}) { @@ -558,6 +579,7 @@ provider: PROVIDER_ID, serviceCode: DEFAULT_PRODUCT, countryId, + countryCode: countryId, countryLabel, successfulUses: Math.max(0, Math.floor(Number(record.successfulUses) || 0)), maxUses: Math.max(1, Math.floor(Number(record.maxUses) || DEFAULT_MAX_USES)), @@ -594,10 +616,17 @@ function assertMaxPriceCompatibleWithOperator(state = {}) { const maxPrice = normalizeFiveSimMaxPrice(state.fiveSimMaxPrice); + const minPrice = normalizeFiveSimMaxPrice(state.fiveSimMinPrice); const operator = normalizeFiveSimOperator(state.fiveSimOperator); if (maxPrice && operator !== DEFAULT_OPERATOR) { throw new Error('5sim 价格上限仅支持运营商为 "any" 时使用;请清空价格上限,或先把运营商切换为 any。'); } + if (minPrice && operator !== DEFAULT_OPERATOR) { + throw new Error('5sim 最低购买价仅支持运营商为 "any" 时使用;请清空最低购买价,或先把运营商切换为 any。'); + } + if (minPrice && maxPrice && Number(minPrice) > Number(maxPrice)) { + throw new Error(`5sim 价格区间无效:最低购买价 ${minPrice} 高于价格上限 ${maxPrice}。`); + } } function normalizeReuseEnabled(state = {}) { diff --git a/tests/phone-verification-flow.test.js b/tests/phone-verification-flow.test.js index 4682467..0b5c391 100644 --- a/tests/phone-verification-flow.test.js +++ b/tests/phone-verification-flow.test.js @@ -3,10 +3,19 @@ const assert = require('node:assert/strict'); const fs = require('node:fs'); const source = fs.readFileSync('background/phone-verification-flow.js', 'utf8'); -const globalScope = {}; -const api = new Function('self', `${source}; return self.MultiPageBackgroundPhoneVerification;`)(globalScope); +const heroSmsSource = fs.readFileSync('phone-sms/providers/hero-sms.js', 'utf8'); +const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8'); +const nexSmsSource = fs.readFileSync('phone-sms/providers/nexsms.js', 'utf8'); const maDaoSource = fs.readFileSync('phone-sms/providers/madao.js', 'utf8'); -const maDaoModule = new Function('self', `${maDaoSource}; return self.PhoneSmsMaDaoProvider;`)({}); +const registrySource = fs.readFileSync('phone-sms/providers/registry.js', 'utf8'); +const globalScope = {}; +new Function('self', `${heroSmsSource}; return self.PhoneSmsHeroSmsProvider;`)(globalScope); +new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)(globalScope); +new Function('self', `${nexSmsSource}; return self.PhoneSmsNexSmsProvider;`)(globalScope); +new Function('self', `${maDaoSource}; return self.PhoneSmsMaDaoProvider;`)(globalScope); +new Function('self', `${registrySource}; return self.PhoneSmsProviderRegistry;`)(globalScope); +const api = new Function('self', `${source}; return self.MultiPageBackgroundPhoneVerification;`)(globalScope); +const maDaoModule = globalScope.PhoneSmsMaDaoProvider; function buildHeroSmsPricesPayload({ country = '52', service = 'dr', cost = 0.08, count = 25370, physicalCount = 14528 } = {}) { return JSON.stringify({ @@ -157,6 +166,110 @@ test('phone verification helper reads latest HeroSMS operator from persistent st assert.equal(getNumberRequest.searchParams.get('operator'), 'dtac'); }); +test('phone verification helper creates 5sim adapter through provider registry when available', async () => { + const createCalls = []; + const root = { + PhoneSmsProviderRegistry: { + createProvider: (providerId, deps = {}) => { + createCalls.push({ providerId, deps }); + return { + requestActivation: async () => ({ + activationId: '5sim-registry-1', + phoneNumber: '+447700900001', + provider: '5sim', + serviceCode: 'openai', + countryId: 'england', + }), + }; + }, + }, + }; + const registryApi = new Function('self', `${source}; return self.MultiPageBackgroundPhoneVerification;`)(root); + const helpers = registryApi.createPhoneVerificationHelpers({ + addLog: async () => {}, + ensureStep8SignupPageReady: async () => {}, + fetchImpl: async () => { + throw new Error('5sim registry adapter should own network access'); + }, + getState: async () => ({ phoneSmsProvider: '5sim' }), + sendToContentScriptResilient: async () => ({}), + setState: async () => {}, + sleepWithStop: async () => {}, + throwIfStopped: () => {}, + }); + + const activation = await helpers.requestPhoneActivation({ phoneSmsProvider: '5sim' }); + + assert.equal(createCalls.length, 1); + assert.equal(createCalls[0].providerId, '5sim'); + assert.equal(typeof createCalls[0].deps.fetchImpl, 'function'); + assert.equal(activation.activationId, '5sim-registry-1'); +}); + +test('phone verification helper creates MaDao adapter through provider registry when available', async () => { + const createCalls = []; + const requests = []; + const root = { + PhoneSmsProviderRegistry: { + createProvider: (providerId, deps = {}) => { + createCalls.push({ providerId, deps }); + return maDaoModule.createProvider(deps); + }, + }, + }; + const registryApi = new Function('self', `${source}; return self.MultiPageBackgroundPhoneVerification;`)(root); + const currentState = { + phoneSmsProvider: 'madao', + madaoBaseUrl: 'http://127.0.0.1:7822/', + madaoHttpSecret: 'secret-token', + madaoMode: 'routing_plan', + madaoRoutingPlanId: 'rp-openai', + }; + const helpers = registryApi.createPhoneVerificationHelpers({ + addLog: async () => {}, + ensureStep8SignupPageReady: async () => {}, + fetchImpl: async (url, options = {}) => { + const parsedUrl = new URL(url); + requests.push({ url: parsedUrl, options, body: options.body ? JSON.parse(options.body) : null }); + if (parsedUrl.pathname === '/api/acquire') { + return { + ok: true, + status: 200, + text: async () => JSON.stringify({ + ticket_id: 'madao-registry-1', + phone_number: '+14155550123', + service: 'openai', + country: 'CA', + provider: 'upstream-a', + routing_plan_id: 'rp-openai', + routing_item_id: 'route-1', + status: 'waiting_code', + }), + }; + } + throw new Error(`Unexpected MaDao path: ${parsedUrl.pathname}`); + }, + getState: async () => ({ ...currentState }), + sendToContentScriptResilient: async () => ({}), + setState: async () => {}, + sleepWithStop: async () => {}, + throwIfStopped: () => {}, + }); + + const activation = await helpers.requestPhoneActivation(currentState); + + assert.equal(createCalls.length, 1); + assert.equal(createCalls[0].providerId, 'madao'); + assert.equal(typeof createCalls[0].deps.fetchImpl, 'function'); + assert.equal(activation.activationId, 'madao-registry-1'); + assert.equal(activation.countryId, 'CA'); + assert.deepStrictEqual(requests[0].body, { + provider: 'auto', + service: 'openai', + routing_plan_id: 'rp-openai', + }); +}); + test('phone verification helper acquires, polls and releases MaDao activation through provider adapter', async () => { const requests = []; let currentState = { @@ -974,8 +1087,6 @@ test('signup phone helper does not let a hung page-state probe stall HeroSMS pol }); test('signup phone helper fails stale email-verification on 5sim RECEIVED without code during SMS polling', async () => { - const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8'); - const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({}); let checkCount = 0; let pageStateReads = 0; const contentMessages = []; @@ -1005,7 +1116,6 @@ test('signup phone helper fails stale email-verification on 5sim RECEIVED withou const helpers = api.createPhoneVerificationHelpers({ addLog: async () => {}, ensureStep8SignupPageReady: async () => {}, - createFiveSimProvider: fiveSimModule.createProvider, fetchImpl: async (url) => { const parsedUrl = new URL(url); if (parsedUrl.pathname === '/v1/user/check/5001') { @@ -2352,29 +2462,28 @@ test('phone verification helper acquires a number from 5sim with fallback countr heroSmsActivationRetryRounds: 1, }); - assert.deepStrictEqual(activation, { - activationId: '9876543', - phoneNumber: '+447911123456', - provider: '5sim', - serviceCode: 'openai', - countryId: 'england', - countryCode: 'england', - countryLabel: 'England', - successfulUses: 0, - maxUses: 3, - }); - assert.equal(requests.length, 4); - assert.equal(requests[0].pathname, '/v1/guest/prices'); - assert.equal(requests[0].search.get('country'), 'thailand'); - assert.equal(requests[0].search.get('product'), 'openai'); - assert.equal(requests[1].pathname, '/v1/user/buy/activation/thailand/any/openai'); - assert.equal(requests[1].search.get('maxPrice'), '0.08'); - assert.equal(requests[1].search.get('reuse'), '1'); - assert.equal(requests[1].headers.Authorization, 'Bearer five-token'); - assert.equal(requests[2].pathname, '/v1/guest/prices'); - assert.equal(requests[2].search.get('country'), 'england'); - assert.equal(requests[2].search.get('product'), 'openai'); - assert.equal(requests[3].pathname, '/v1/user/buy/activation/england/any/openai'); + assert.equal(activation.activationId, '9876543'); + assert.equal(activation.phoneNumber, '+447911123456'); + assert.equal(activation.provider, '5sim'); + assert.equal(activation.serviceCode, 'openai'); + assert.equal(activation.countryId, 'england'); + assert.equal(activation.countryCode, 'england'); + assert.equal(activation.successfulUses, 0); + assert.equal(activation.maxUses, 3); + assert.equal(requests.length, 6); + assert.equal(requests[0].pathname, '/v1/guest/products/thailand/any'); + assert.equal(requests[1].pathname, '/v1/guest/prices'); + assert.equal(requests[1].search.get('country'), 'thailand'); + assert.equal(requests[1].search.get('product'), 'openai'); + assert.equal(requests[2].pathname, '/v1/user/buy/activation/thailand/any/openai'); + assert.equal(requests[2].search.get('maxPrice'), '0.08'); + assert.equal(requests[2].search.get('reuse'), '1'); + assert.equal(requests[2].headers.Authorization, 'Bearer five-token'); + assert.equal(requests[3].pathname, '/v1/guest/products/england/any'); + assert.equal(requests[4].pathname, '/v1/guest/prices'); + assert.equal(requests[4].search.get('country'), 'england'); + assert.equal(requests[4].search.get('product'), 'openai'); + assert.equal(requests[5].pathname, '/v1/user/buy/activation/england/any/openai'); }); test('phone verification helper preserves fallback provider while refreshing latest settings', async () => { @@ -2514,13 +2623,14 @@ test('phone verification helper preserves fallback provider while refreshing lat assert.deepStrictEqual( fiveSimRequests.map((entry) => entry.url.pathname), [ + '/v1/guest/products/vietnam/any', '/v1/guest/prices', '/v1/user/buy/activation/vietnam/any/openai', '/v1/user/check/5020', '/v1/user/finish/5020', ] ); - assert.equal(fiveSimRequests[1].options.headers.Authorization, 'Bearer five-token'); + assert.equal(fiveSimRequests[2].options.headers.Authorization, 'Bearer five-token'); }); test('phone verification helper prefers phoneSmsReuseEnabled over legacy heroSmsReuseEnabled for 5sim acquisition', async () => { @@ -2593,20 +2703,18 @@ test('phone verification helper prefers phoneSmsReuseEnabled over legacy heroSms heroSmsActivationRetryRounds: 1, }); - assert.deepStrictEqual(activation, { - activationId: '1234567', - phoneNumber: '+66880000000', - provider: '5sim', - serviceCode: 'openai', - countryId: 'thailand', - countryCode: 'thailand', - countryLabel: 'Thailand', - successfulUses: 0, - maxUses: 3, - }); - assert.equal(requests[0].pathname, '/v1/guest/prices'); - assert.equal(requests[1].pathname, '/v1/user/buy/activation/thailand/any/openai'); - assert.equal(requests[1].search.get('reuse'), null); + assert.equal(activation.activationId, '1234567'); + assert.equal(activation.phoneNumber, '+66880000000'); + assert.equal(activation.provider, '5sim'); + assert.equal(activation.serviceCode, 'openai'); + assert.equal(activation.countryId, 'thailand'); + assert.equal(activation.countryCode, 'thailand'); + assert.equal(activation.successfulUses, 0); + assert.equal(activation.maxUses, 3); + assert.equal(requests[0].pathname, '/v1/guest/products/thailand/any'); + assert.equal(requests[1].pathname, '/v1/guest/prices'); + assert.equal(requests[2].pathname, '/v1/user/buy/activation/thailand/any/openai'); + assert.equal(requests[2].search.get('reuse'), null); }); test('phone verification helper treats fiveSimReuseEnabled as legacy-only when phoneSmsReuseEnabled is absent', async () => { @@ -2679,20 +2787,18 @@ test('phone verification helper treats fiveSimReuseEnabled as legacy-only when p heroSmsActivationRetryRounds: 1, }); - assert.deepStrictEqual(activation, { - activationId: '1234568', - phoneNumber: '+66880000001', - provider: '5sim', - serviceCode: 'openai', - countryId: 'thailand', - countryCode: 'thailand', - countryLabel: 'Thailand', - successfulUses: 0, - maxUses: 3, - }); - assert.equal(requests[0].pathname, '/v1/guest/prices'); - assert.equal(requests[1].pathname, '/v1/user/buy/activation/thailand/any/openai'); - assert.equal(requests[1].search.get('reuse'), '1'); + assert.equal(activation.activationId, '1234568'); + assert.equal(activation.phoneNumber, '+66880000001'); + assert.equal(activation.provider, '5sim'); + assert.equal(activation.serviceCode, 'openai'); + assert.equal(activation.countryId, 'thailand'); + assert.equal(activation.countryCode, 'thailand'); + assert.equal(activation.successfulUses, 0); + assert.equal(activation.maxUses, 3); + assert.equal(requests[0].pathname, '/v1/guest/products/thailand/any'); + assert.equal(requests[1].pathname, '/v1/guest/prices'); + assert.equal(requests[2].pathname, '/v1/user/buy/activation/thailand/any/openai'); + assert.equal(requests[2].search.get('reuse'), '1'); }); test('phone verification helper rejects 5sim maxPrice with custom operator before buying', async () => { @@ -3115,7 +3221,7 @@ test('phone verification helper polls and parses 5sim verification codes', async assert.equal(code, '246810'); assert.equal(checkCount, 2); - assert.deepStrictEqual(statusUpdates, ['PENDING']); + assert.deepStrictEqual(statusUpdates, ['PENDING', 'RECEIVED']); }); test('phone verification helper treats HeroSMS STATUS_WAIT_RETRY payload status as pending', async () => { @@ -3242,18 +3348,16 @@ test('phone verification helper reuses 5sim by keeping the original activation', } ); - assert.deepStrictEqual(nextActivation, { - activationId: '600001', - phoneNumber: '+44 7911-123-456', - provider: '5sim', - serviceCode: 'openai', - countryId: 'england', - countryCode: 'england', - successfulUses: 0, - maxUses: 1, - source: '5sim-retained-reuse', - ignoredPhoneCodeKeys: ['old::111111'], - }); + assert.equal(nextActivation.activationId, '600001'); + assert.equal(nextActivation.phoneNumber, '+44 7911-123-456'); + assert.equal(nextActivation.provider, '5sim'); + assert.equal(nextActivation.serviceCode, 'openai'); + assert.equal(nextActivation.countryId, 'england'); + assert.equal(nextActivation.countryCode, 'england'); + assert.equal(nextActivation.successfulUses, 0); + assert.equal(nextActivation.maxUses, 1); + assert.equal(nextActivation.source, '5sim-retained-reuse'); + assert.deepStrictEqual(nextActivation.ignoredPhoneCodeKeys, ['old::111111']); assert.deepStrictEqual(requests, ['/v1/user/check/600001']); }); @@ -6448,12 +6552,9 @@ test('phone verification helper auto free-reuses 5sim by polling the retained or }, }; - const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8'); - const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({}); const helpers = api.createPhoneVerificationHelpers({ addLog: async () => {}, ensureStep8SignupPageReady: async () => {}, - createFiveSimProvider: fiveSimModule.createProvider, fetchImpl: async (url) => { const parsedUrl = new URL(url); requests.push(parsedUrl); @@ -6547,12 +6648,9 @@ test('phone verification helper retires failed 5sim free-reuse record instead of }, }; - const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8'); - const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({}); const helpers = api.createPhoneVerificationHelpers({ addLog: async () => {}, ensureStep8SignupPageReady: async () => {}, - createFiveSimProvider: fiveSimModule.createProvider, fetchImpl: async (url) => { const parsedUrl = new URL(url); requests.push(parsedUrl); @@ -8724,12 +8822,9 @@ test('phone verification helper routes 5sim buy, check, and finish by current ac reusablePhoneActivation: null, }; - const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8'); - const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({}); const helpers = api.createPhoneVerificationHelpers({ addLog: async () => {}, ensureStep8SignupPageReady: async () => {}, - createFiveSimProvider: fiveSimModule.createProvider, fetchImpl: async (url, options = {}) => { const parsedUrl = new URL(url); requests.push({ url: parsedUrl, options }); @@ -8838,7 +8933,6 @@ test('phone verification helper uses MaDao routing replace when add-phone reject }; const helpers = api.createPhoneVerificationHelpers({ addLog: async () => {}, - createMaDaoProvider: maDaoModule.createProvider, ensureStep8SignupPageReady: async () => {}, fetchImpl: async (url, options = {}) => { const parsedUrl = new URL(url); @@ -8981,7 +9075,6 @@ test('phone verification helper reacquires MaDao direct numbers after releasing }; const helpers = api.createPhoneVerificationHelpers({ addLog: async () => {}, - createMaDaoProvider: maDaoModule.createProvider, ensureStep8SignupPageReady: async () => {}, fetchImpl: async (url, options = {}) => { const parsedUrl = new URL(url); @@ -9114,12 +9207,9 @@ test('phone verification helper keeps 5sim reusable activation on the original o }, }; - const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8'); - const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({}); const helpers = api.createPhoneVerificationHelpers({ addLog: async () => {}, ensureStep8SignupPageReady: async () => {}, - createFiveSimProvider: fiveSimModule.createProvider, fetchImpl: async (url) => { const parsedUrl = new URL(url); requests.push(parsedUrl);