@luxexchange/sessions 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.depcheckrc +20 -0
- package/.eslintrc.js +21 -0
- package/README.md +1 -0
- package/env.d.ts +12 -0
- package/package.json +50 -0
- package/project.json +42 -0
- package/src/challenge-solvers/createChallengeSolverService.ts +64 -0
- package/src/challenge-solvers/createHashcashMockSolver.ts +39 -0
- package/src/challenge-solvers/createHashcashSolver.test.ts +385 -0
- package/src/challenge-solvers/createHashcashSolver.ts +255 -0
- package/src/challenge-solvers/createNoneMockSolver.ts +11 -0
- package/src/challenge-solvers/createTurnstileMockSolver.ts +30 -0
- package/src/challenge-solvers/createTurnstileSolver.ts +353 -0
- package/src/challenge-solvers/hashcash/core.native.ts +34 -0
- package/src/challenge-solvers/hashcash/core.test.ts +314 -0
- package/src/challenge-solvers/hashcash/core.ts +35 -0
- package/src/challenge-solvers/hashcash/core.web.ts +123 -0
- package/src/challenge-solvers/hashcash/createWorkerHashcashSolver.test.ts +195 -0
- package/src/challenge-solvers/hashcash/createWorkerHashcashSolver.ts +120 -0
- package/src/challenge-solvers/hashcash/shared.ts +70 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashMultiWorkerChannel.native.ts +22 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashMultiWorkerChannel.ts +22 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashMultiWorkerChannel.web.ts +212 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashWorkerChannel.native.ts +16 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashWorkerChannel.ts +16 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashWorkerChannel.web.ts +97 -0
- package/src/challenge-solvers/hashcash/worker/hashcash.worker.ts +91 -0
- package/src/challenge-solvers/hashcash/worker/types.ts +69 -0
- package/src/challenge-solvers/turnstileErrors.ts +49 -0
- package/src/challenge-solvers/turnstileScriptLoader.ts +325 -0
- package/src/challenge-solvers/turnstileSolver.integration.test.ts +331 -0
- package/src/challenge-solvers/types.ts +55 -0
- package/src/challengeFlow.integration.test.ts +627 -0
- package/src/device-id/createDeviceIdService.ts +19 -0
- package/src/device-id/types.ts +11 -0
- package/src/index.ts +137 -0
- package/src/lux-identifier/createLuxIdentifierService.ts +19 -0
- package/src/lux-identifier/luxIdentifierQuery.ts +20 -0
- package/src/lux-identifier/types.ts +11 -0
- package/src/oauth-service/createOAuthService.ts +125 -0
- package/src/oauth-service/types.ts +104 -0
- package/src/performance/createNoopPerformanceTracker.ts +12 -0
- package/src/performance/createPerformanceTracker.ts +43 -0
- package/src/performance/index.ts +7 -0
- package/src/performance/types.ts +11 -0
- package/src/session-initialization/createSessionInitializationService.test.ts +557 -0
- package/src/session-initialization/createSessionInitializationService.ts +193 -0
- package/src/session-initialization/sessionErrors.ts +32 -0
- package/src/session-repository/createSessionClient.ts +10 -0
- package/src/session-repository/createSessionRepository.test.ts +313 -0
- package/src/session-repository/createSessionRepository.ts +242 -0
- package/src/session-repository/errors.ts +22 -0
- package/src/session-repository/types.ts +289 -0
- package/src/session-service/createNoopSessionService.ts +24 -0
- package/src/session-service/createSessionService.test.ts +388 -0
- package/src/session-service/createSessionService.ts +61 -0
- package/src/session-service/types.ts +59 -0
- package/src/session-storage/createSessionStorage.ts +28 -0
- package/src/session-storage/types.ts +15 -0
- package/src/session.integration.test.ts +480 -0
- package/src/sessionLifecycle.integration.test.ts +264 -0
- package/src/test-utils/createLocalCookieTransport.ts +52 -0
- package/src/test-utils/createLocalHeaderTransport.ts +45 -0
- package/src/test-utils/mocks.ts +122 -0
- package/src/test-utils.ts +200 -0
- package/tsconfig.json +19 -0
- package/tsconfig.lint.json +8 -0
- package/tsconfig.spec.json +8 -0
- package/vitest.config.ts +20 -0
- package/vitest.integration.config.ts +14 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/// @vitest-environment happy-dom
|
|
2
|
+
import { ChallengeType } from '@uniswap/client-platform-service/dist/uniswap/platformservice/v1/sessionService_pb'
|
|
3
|
+
import { createTurnstileSolver } from '@luxexchange/sessions/src/challenge-solvers/createTurnstileSolver'
|
|
4
|
+
import { resetTurnstileState } from '@luxexchange/sessions/src/challenge-solvers/turnstileScriptLoader'
|
|
5
|
+
import type { PerformanceTracker } from '@luxexchange/sessions/src/performance/types'
|
|
6
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
+
|
|
8
|
+
// Mock performance tracker for testing
|
|
9
|
+
function createMockPerformanceTracker(): PerformanceTracker {
|
|
10
|
+
let time = 0
|
|
11
|
+
return {
|
|
12
|
+
now: (): number => {
|
|
13
|
+
time += 100
|
|
14
|
+
return time
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Mock window.turnstile API
|
|
20
|
+
const mockTurnstileAPI = {
|
|
21
|
+
render: vi.fn(),
|
|
22
|
+
remove: vi.fn(),
|
|
23
|
+
reset: vi.fn(),
|
|
24
|
+
getResponse: vi.fn(),
|
|
25
|
+
ready: vi.fn(),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Setup DOM mocks
|
|
29
|
+
beforeAll(() => {
|
|
30
|
+
const originalCreateElement = document.createElement.bind(document)
|
|
31
|
+
vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
|
|
32
|
+
const element = originalCreateElement(tagName)
|
|
33
|
+
if (tagName === 'div') {
|
|
34
|
+
// Track created divs for assertions
|
|
35
|
+
// eslint-disable-next-line no-extra-semi
|
|
36
|
+
;(element as any)._testCreated = true
|
|
37
|
+
}
|
|
38
|
+
return element
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
vi.spyOn(document.head, 'appendChild').mockImplementation((node) => {
|
|
42
|
+
if (node instanceof HTMLScriptElement && node.src.includes('challenges.cloudflare.com')) {
|
|
43
|
+
// Simulate script load immediately
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
// Set up the mock turnstile API
|
|
46
|
+
// eslint-disable-next-line no-extra-semi
|
|
47
|
+
;(window as any).turnstile = mockTurnstileAPI
|
|
48
|
+
if (node.onload) {
|
|
49
|
+
node.onload({} as Event)
|
|
50
|
+
}
|
|
51
|
+
}, 0)
|
|
52
|
+
}
|
|
53
|
+
return node
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const originalBodyAppendChild = document.body.appendChild.bind(document.body)
|
|
57
|
+
vi.spyOn(document.body, 'appendChild').mockImplementation((node) => {
|
|
58
|
+
// eslint-disable-next-line no-extra-semi
|
|
59
|
+
;(node as any)._testAppended = true
|
|
60
|
+
// Actually append to the DOM so we can query it later
|
|
61
|
+
return originalBodyAppendChild(node)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
vi.spyOn(Element.prototype, 'removeChild').mockImplementation(function (this: Element, child: Node) {
|
|
65
|
+
// eslint-disable-next-line no-extra-semi
|
|
66
|
+
;(child as any)._testRemoved = true
|
|
67
|
+
return child
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
afterAll(() => {
|
|
72
|
+
vi.restoreAllMocks()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('Turnstile Solver Integration Tests', () => {
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
// Configure mock Turnstile API behavior
|
|
78
|
+
mockTurnstileAPI.ready.mockImplementation((callback: () => void) => {
|
|
79
|
+
// Call the callback immediately
|
|
80
|
+
callback()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
mockTurnstileAPI.render.mockImplementation((container: string | HTMLElement, options: any) => {
|
|
84
|
+
// Simulate successful render and call the callback with a test token
|
|
85
|
+
if (options.callback) {
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
options.callback('test-turnstile-solution-token')
|
|
88
|
+
}, 10) // Small delay to simulate async behavior
|
|
89
|
+
}
|
|
90
|
+
return 'widget-123' // Return a mock widget ID
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
afterEach(() => {
|
|
95
|
+
// Clean up DOM
|
|
96
|
+
document.querySelectorAll('div[id^="turnstile-"]').forEach((el) => el.remove())
|
|
97
|
+
|
|
98
|
+
// Reset mocks to default successful behavior
|
|
99
|
+
vi.clearAllMocks()
|
|
100
|
+
;(window as any).turnstile = undefined
|
|
101
|
+
|
|
102
|
+
// Reset turnstile script loader state for next test
|
|
103
|
+
resetTurnstileState()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('verifies Turnstile solver basic functionality', async () => {
|
|
107
|
+
// Create a challenge solver directly to test
|
|
108
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
109
|
+
|
|
110
|
+
// Create challenge data with proper structure
|
|
111
|
+
const challengeData = {
|
|
112
|
+
challengeId: 'dom-test-challenge-123',
|
|
113
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
114
|
+
extra: {
|
|
115
|
+
challengeData: JSON.stringify({
|
|
116
|
+
siteKey: '0x4AAAAAABiAHneWOWZHzZtO',
|
|
117
|
+
action: 'session_verification',
|
|
118
|
+
}),
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Execute the solver and wait for solution
|
|
123
|
+
const solution = await turnstileSolver.solve(challengeData)
|
|
124
|
+
|
|
125
|
+
// Verify solution was returned
|
|
126
|
+
expect(solution).toBe('test-turnstile-solution-token')
|
|
127
|
+
|
|
128
|
+
// Verify script injection was attempted
|
|
129
|
+
expect(document.head.appendChild).toHaveBeenCalledWith(
|
|
130
|
+
expect.objectContaining({
|
|
131
|
+
src: 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit',
|
|
132
|
+
}),
|
|
133
|
+
)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('handles Turnstile solver errors properly', async () => {
|
|
137
|
+
// Configure mock to simulate an error with proper timing
|
|
138
|
+
mockTurnstileAPI.render.mockImplementation(async (container: string | HTMLElement, options: any) => {
|
|
139
|
+
if (options['error-callback']) {
|
|
140
|
+
// Use microtask to ensure promise handlers are set up
|
|
141
|
+
await Promise.resolve().then(() => {
|
|
142
|
+
options['error-callback']('NETWORK_ERROR')
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
return 'widget-error-123'
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
149
|
+
const challengeData = {
|
|
150
|
+
challengeId: 'error-test-123',
|
|
151
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
152
|
+
extra: {
|
|
153
|
+
challengeData: JSON.stringify({
|
|
154
|
+
siteKey: 'test-site-key',
|
|
155
|
+
action: 'test-action',
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Should reject with Turnstile error
|
|
161
|
+
await expect(turnstileSolver.solve(challengeData)).rejects.toThrow('Turnstile error: NETWORK_ERROR')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('handles expired tokens', async () => {
|
|
165
|
+
// Configure mock to simulate token expiration
|
|
166
|
+
mockTurnstileAPI.render.mockImplementation(async (container: string | HTMLElement, options: any) => {
|
|
167
|
+
if (options['expired-callback']) {
|
|
168
|
+
// Use microtask to ensure promise handlers are set up
|
|
169
|
+
await Promise.resolve().then(() => {
|
|
170
|
+
options['expired-callback']()
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
return 'widget-expired-123'
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
177
|
+
const challengeData = {
|
|
178
|
+
challengeId: 'expired-test-123',
|
|
179
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
180
|
+
extra: {
|
|
181
|
+
challengeData: JSON.stringify({
|
|
182
|
+
siteKey: 'test-site-key',
|
|
183
|
+
action: 'test-action',
|
|
184
|
+
}),
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Should reject with expiration error
|
|
189
|
+
await expect(turnstileSolver.solve(challengeData)).rejects.toThrow('Turnstile token expired')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('handles timeout scenarios', async () => {
|
|
193
|
+
// Configure mock to never call any callbacks
|
|
194
|
+
mockTurnstileAPI.render.mockImplementation(() => {
|
|
195
|
+
// Don't call any callbacks - simulate timeout
|
|
196
|
+
return 'widget-timeout-123'
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// Use a short timeout for testing (100ms instead of 30s)
|
|
200
|
+
const turnstileSolver = createTurnstileSolver({
|
|
201
|
+
performanceTracker: createMockPerformanceTracker(),
|
|
202
|
+
timeoutMs: 100,
|
|
203
|
+
})
|
|
204
|
+
const challengeData = {
|
|
205
|
+
challengeId: 'timeout-test-123',
|
|
206
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
207
|
+
extra: {
|
|
208
|
+
challengeData: JSON.stringify({
|
|
209
|
+
siteKey: 'test-site-key',
|
|
210
|
+
action: 'test-action',
|
|
211
|
+
}),
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Should reject with timeout error after 100ms
|
|
216
|
+
await expect(turnstileSolver.solve(challengeData)).rejects.toThrow('Turnstile challenge timed out after')
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('handles missing challenge data', async () => {
|
|
220
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
221
|
+
const challengeData = {
|
|
222
|
+
challengeId: 'missing-data-123',
|
|
223
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
224
|
+
extra: {}, // Missing challengeData
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Should reject with missing data error
|
|
228
|
+
await expect(turnstileSolver.solve(challengeData)).rejects.toThrow('Missing challengeData in challenge extra')
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('handles invalid challenge data JSON', async () => {
|
|
232
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
233
|
+
const challengeData = {
|
|
234
|
+
challengeId: 'invalid-json-123',
|
|
235
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
236
|
+
extra: {
|
|
237
|
+
challengeData: 'invalid-json-{',
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Should reject with JSON parse error
|
|
242
|
+
await expect(turnstileSolver.solve(challengeData)).rejects.toThrow('Failed to parse challengeData')
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('handles missing siteKey in challenge data', async () => {
|
|
246
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
247
|
+
const challengeData = {
|
|
248
|
+
challengeId: 'missing-sitekey-123',
|
|
249
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
250
|
+
extra: {
|
|
251
|
+
challengeData: JSON.stringify({
|
|
252
|
+
action: 'test-action',
|
|
253
|
+
// Missing siteKey
|
|
254
|
+
}),
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Should reject with missing siteKey error
|
|
259
|
+
await expect(turnstileSolver.solve(challengeData)).rejects.toThrow('Missing siteKey in challengeData')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('handles script loading failures', async () => {
|
|
263
|
+
// Mock script loading failure
|
|
264
|
+
vi.spyOn(document.head, 'appendChild').mockImplementationOnce((node) => {
|
|
265
|
+
if (node instanceof HTMLScriptElement && node.src.includes('challenges.cloudflare.com')) {
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
if (node.onerror) {
|
|
268
|
+
node.onerror({} as Event)
|
|
269
|
+
}
|
|
270
|
+
}, 0)
|
|
271
|
+
}
|
|
272
|
+
return node
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
276
|
+
const challengeData = {
|
|
277
|
+
challengeId: 'script-fail-123',
|
|
278
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
279
|
+
extra: {
|
|
280
|
+
challengeData: JSON.stringify({
|
|
281
|
+
siteKey: 'test-site-key',
|
|
282
|
+
action: 'test-action',
|
|
283
|
+
}),
|
|
284
|
+
},
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Should reject with script loading error
|
|
288
|
+
await expect(turnstileSolver.solve(challengeData)).rejects.toThrow('Failed to load Turnstile script')
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('handles multiple concurrent solve requests', async () => {
|
|
292
|
+
const turnstileSolver = createTurnstileSolver({ performanceTracker: createMockPerformanceTracker() })
|
|
293
|
+
|
|
294
|
+
// Create multiple challenge data objects
|
|
295
|
+
const challenges = Array.from({ length: 3 }, (_, i) => ({
|
|
296
|
+
challengeId: `concurrent-test-${i}`,
|
|
297
|
+
challengeType: ChallengeType.TURNSTILE,
|
|
298
|
+
extra: {
|
|
299
|
+
challengeData: JSON.stringify({
|
|
300
|
+
siteKey: `test-site-key-${i}`,
|
|
301
|
+
action: 'test-action',
|
|
302
|
+
}),
|
|
303
|
+
},
|
|
304
|
+
}))
|
|
305
|
+
|
|
306
|
+
// Configure mock to return different tokens for each widget
|
|
307
|
+
let widgetCounter = 0
|
|
308
|
+
mockTurnstileAPI.render.mockImplementation((container: string | HTMLElement, options: any) => {
|
|
309
|
+
const widgetId = `widget-${widgetCounter++}`
|
|
310
|
+
if (options.callback) {
|
|
311
|
+
setTimeout(() => {
|
|
312
|
+
options.callback(`solution-for-${widgetId}`)
|
|
313
|
+
}, 10)
|
|
314
|
+
}
|
|
315
|
+
return widgetId
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// Execute all solvers concurrently
|
|
319
|
+
const solutionPromises = challenges.map((challenge) => turnstileSolver.solve(challenge))
|
|
320
|
+
|
|
321
|
+
// Wait for all solutions
|
|
322
|
+
const solutions = await Promise.all(solutionPromises)
|
|
323
|
+
|
|
324
|
+
// Verify all solutions are unique
|
|
325
|
+
expect(solutions).toHaveLength(3)
|
|
326
|
+
expect(new Set(solutions).size).toBe(3)
|
|
327
|
+
solutions.forEach((solution) => {
|
|
328
|
+
expect(solution).toMatch(/^solution-for-widget-\d+$/)
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { TypedChallengeData } from '@luxexchange/sessions/src/session-repository/types'
|
|
2
|
+
import type { ChallengeType } from '@luxexchange/sessions/src/session-service/types'
|
|
3
|
+
|
|
4
|
+
interface ChallengeData {
|
|
5
|
+
challengeId: string
|
|
6
|
+
challengeType: ChallengeType
|
|
7
|
+
/** @deprecated Use challengeData instead */
|
|
8
|
+
extra?: Record<string, string>
|
|
9
|
+
/** Type-safe challenge-specific data (replaces extra) */
|
|
10
|
+
challengeData?: TypedChallengeData
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ChallengeSolver {
|
|
14
|
+
solve(challengeData: ChallengeData): Promise<string>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ChallengeSolverService {
|
|
18
|
+
getSolver(type: ChallengeType): ChallengeSolver | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Script injection options for CSP compliance and customization
|
|
23
|
+
*/
|
|
24
|
+
interface TurnstileScriptOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Custom nonce for the injected script (for CSP compliance)
|
|
27
|
+
*/
|
|
28
|
+
nonce?: string
|
|
29
|
+
/**
|
|
30
|
+
* Whether to set the script as defer
|
|
31
|
+
* @default false (Turnstile requires synchronous loading for ready())
|
|
32
|
+
*/
|
|
33
|
+
defer?: boolean
|
|
34
|
+
/**
|
|
35
|
+
* Whether to set the script as async
|
|
36
|
+
* @default false (Turnstile requires synchronous loading for ready())
|
|
37
|
+
*/
|
|
38
|
+
async?: boolean
|
|
39
|
+
/**
|
|
40
|
+
* Custom crossOrigin for the injected script
|
|
41
|
+
*/
|
|
42
|
+
crossOrigin?: string
|
|
43
|
+
/**
|
|
44
|
+
* Custom ID for the injected script
|
|
45
|
+
* @default "cf-turnstile-script"
|
|
46
|
+
*/
|
|
47
|
+
id?: string
|
|
48
|
+
/**
|
|
49
|
+
* Custom name for the onload callback
|
|
50
|
+
* @default "onloadTurnstileCallback"
|
|
51
|
+
*/
|
|
52
|
+
onLoadCallbackName?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type { ChallengeData, ChallengeSolver, ChallengeSolverService, TurnstileScriptOptions }
|