Skip to content

Commit 68326d6

Browse files
authored
Merge pull request #125 from hkbertoson/script-changes
Script changes
2 parents 6372405 + 31d452f commit 68326d6

File tree

10 files changed

+121
-41
lines changed

10 files changed

+121
-41
lines changed

.changeset/quiet-readers-beg.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"astro-turnstile": minor
3+
---
4+
5+
Updated to use data attribute instead of comment

.changeset/rare-clouds-eat.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"astro-turnstile": minor
3+
---
4+
5+
Updated script to only inject on pages where component is rendered

package/lib/client.ts

-23
This file was deleted.

package/lib/components/TurnstileForm.astro

+5-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const siteUrl = Astro.site;
2929
id="at-turnstile-captcha-form"
3030
data-siteurl={siteUrl}
3131
data-captcha={config.endpointPath}
32+
data-turnstile-container
3233
action={action}
3334
method={method}
3435
enctype={enctype}
@@ -52,7 +53,7 @@ const siteUrl = Astro.site;
5253
<script>
5354
// Get the form element
5455
const form = document.getElementById(
55-
'at-turnstile-captcha-form'
56+
'at-turnstile-captcha-form',
5657
) as HTMLFormElement;
5758

5859
const turnstileConfig = form.dataset;
@@ -70,7 +71,9 @@ const siteUrl = Astro.site;
7071
if (typeof value === 'string') {
7172
urlEncodedFormData.append(key, value);
7273
} else {
73-
console.warn(`Invalid form data value for key: ${key}, Skipping...`);
74+
console.warn(
75+
`Invalid form data value for key: ${key}, Skipping...`,
76+
);
7477
}
7578
});
7679

package/lib/components/TurnstileWidget.astro

+5-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const getStyleSettings = (size: string): Dimensions => {
5454
// If the style settings do not exist, throw an error
5555
throw new AstroError(
5656
`Invalid size: ${size}`,
57-
`'size' must be one of the following: ${Object.keys(sizes).join(', ')}.`
57+
`'size' must be one of the following: ${Object.keys(sizes).join(', ')}.`,
5858
);
5959
};
6060
@@ -71,6 +71,7 @@ const {
7171
data-sitekey={TURNSTILE_SITE_KEY}
7272
data-theme={theme}
7373
data-size={size}
74+
data-turnstile-container
7475
>
7576
</div>
7677

@@ -96,7 +97,9 @@ const {
9697
// see https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#explicitly-render-the-turnstile-widget
9798
function onloadTurnstileCallback() {
9899
// Get the Turnstile configuration from the Turnstile form element
99-
const {dataset: TConfig} = document.getElementById('at-turnstile-captcha');
100+
const {dataset: TConfig} = document.getElementById(
101+
'at-turnstile-captcha',
102+
);
100103

101104
// Render the Turnstile widget with the configuration
102105
turnstile.render('#at-turnstile-captcha', {

package/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
"default": "./src/index.ts"
3131
},
3232
"./schema": "./src/schema.ts",
33-
"./client": "./lib/client.ts",
3433
"./server": "./lib/server.ts",
3534
"./components/TurnstileWidget": "./lib/components/TurnstileWidget.astro",
3635
"./components/TurnstileForm": "./lib/components/TurnstileForm.astro"

package/src/integration.ts

+5-8
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const astroTurnstile = defineIntegration({
4141
command,
4242
config,
4343
addDevToolbarApp,
44+
addMiddleware,
4445
} = params;
4546

4647
// Log startup message
@@ -122,16 +123,10 @@ export const astroTurnstile = defineIntegration({
122123

123124
// Inject the required Turnstile client-side script if not disabled
124125
if (!disableClientScript) {
125-
verbose && logger.info(loggerStrings.injectScript);
126-
injectScript('page', `import '${name}/client'`);
126+
verbose && logger.info(loggerStrings.injectMiddleware);
127+
addMiddleware({entrypoint: resolve('./middleware.ts'), order: 'post'});
127128
}
128129

129-
/*
130-
131-
name: 'Astro Turnstile',
132-
id: 'astro-turnstile-dev-toolbar',
133-
icon: svgIcons.turnstile,
134-
*/
135130
// Add Development Toolbar App for Astro Turnstile testing
136131
if (!disableDevToolbar) {
137132
verbose && logger.info(loggerStrings.addDevToolbarApp);
@@ -164,6 +159,8 @@ export const astroTurnstile = defineIntegration({
164159
},
165160
});
166161

162+
163+
167164
// Log completion message
168165
verbose && logger.info(loggerStrings.setupComplete);
169166
},

package/src/middleware.ts

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import type { MiddlewareHandler } from "astro";
2+
3+
const COMPONENT_IDENTIFIER = 'data-turnstile-container';
4+
const TURNSTILE_SCRIPT = `
5+
<script
6+
src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback"
7+
async
8+
defer
9+
id="turnstile-script">
10+
</script>`;
11+
12+
const MAX_SCAN_BYTES = 16384; // 16KB
13+
let bytesScanned = 0;
14+
15+
export const onRequest: MiddlewareHandler = async (_, next) => {
16+
const response = await next();
17+
18+
const contentType = response.headers.get('Content-Type');
19+
if (!contentType?.includes('text/html') || !response.body) {
20+
return response;
21+
}
22+
23+
const transformedBody = response.body
24+
.pipeThrough(new TextDecoderStream())
25+
.pipeThrough(createFastScriptInjectionTransform())
26+
.pipeThrough(new TextEncoderStream());
27+
28+
return new Response(transformedBody, {
29+
status: response.status,
30+
statusText: response.statusText,
31+
headers: response.headers
32+
});
33+
};
34+
35+
function createFastScriptInjectionTransform(): TransformStream<string, string> {
36+
let hasInjected = false;
37+
let hasFoundComponent = false;
38+
let buffer = '';
39+
40+
return new TransformStream({
41+
transform(chunk: string, controller) {
42+
// Fast path: already injected or scanned too much
43+
if (hasInjected || bytesScanned > MAX_SCAN_BYTES) {
44+
controller.enqueue(chunk);
45+
return;
46+
}
47+
48+
bytesScanned += chunk.length;
49+
50+
// Fast path: haven't found component yet
51+
if (!hasFoundComponent) {
52+
// Look for the data attribute
53+
if (chunk.includes(COMPONENT_IDENTIFIER)) {
54+
hasFoundComponent = true;
55+
buffer = chunk;
56+
} else {
57+
controller.enqueue(chunk);
58+
return;
59+
}
60+
} else {
61+
buffer += chunk;
62+
}
63+
64+
const headCloseIndex = buffer.indexOf('</head>');
65+
if (headCloseIndex === -1) {
66+
if (buffer.length > MAX_SCAN_BYTES) {
67+
controller.enqueue(buffer);
68+
buffer = '';
69+
hasInjected = true;
70+
}
71+
return;
72+
}
73+
74+
const injectedContent =
75+
buffer.slice(0, headCloseIndex) +
76+
TURNSTILE_SCRIPT +
77+
buffer.slice(headCloseIndex);
78+
79+
controller.enqueue(injectedContent);
80+
hasInjected = true;
81+
buffer = '';
82+
},
83+
84+
flush(controller) {
85+
if (buffer) {
86+
controller.enqueue(buffer);
87+
}
88+
bytesScanned = 0;
89+
}
90+
});
91+
}

package/src/strings.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export const loggerStrings = {
22
setup: 'Turnstile integration setup...',
33
configSiteMissing: `Astro Config Error: 'site' is not defined, it is recommended to define 'site' in your Astro config. (https://docs.astro.build/en/reference/configuration-reference/#site)\nFalling back to 'window.location.origin'.`,
44
updateConfig: 'Updating Astro config with Turnstile environment variables...',
5-
injectScript: 'Injecting Turnstile client script...',
5+
injectMiddleware: 'Injecting Turnstile middleware...',
66
injectRoute: (value: string) => `Injecting Turnstile route at ${value}...`,
77
virtualImports: 'Adding Virtual Import modules...',
88
setupComplete: 'Turnstile integration setup complete.',

playground/src/pages/index.astro

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ if (Astro.request.method === 'POST') {
1010

1111
<Layout title="Welcome to Astro.">
1212
<main>
13-
<!-- <form>
13+
<form>
1414
<label>
1515
Username:
1616
<input type="text" name="username" required />
1717
</label>
1818
<button type="submit">Submit</button>
1919
<TurnstileWidget />
20-
</form> -->
21-
<main>
20+
</form>
21+
<!-- <main>
2222
<TurnstileForm action="/" method={'POST'} enctype={'submit'}>
2323
<label>
2424
Username:
2525
<input type="text" name="username" required />
2626
</label>
2727
</TurnstileForm>
28-
</main>
28+
</main> -->
2929
</main>
3030

3131
<style is:global>

0 commit comments

Comments
 (0)