⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Conversation

@Slashgear
Copy link
Member

Summary

  • Add fuzzy search across all 95+ events using Fuse.js (dynamically imported, ~15KB gzipped)
  • Static search index generated at build time via scripts/generate-search-index.mjs
  • Search dialog accessible via header button or ⌘K / Ctrl+K shortcut
  • Searches by event title, talk title, speaker name, sponsor name, and description

How it works

  1. Build step: generate-search-index.mjs fetches events from Meetup GraphQL API, enriches with data-override.ts (talks, speakers, sponsors), writes public/search-index.json
  2. Client: On first search open, lazily loads the index + Fuse.js. Debounced fuzzy search (200ms) with weighted keys (title 2x, talks/speakers 1.5x, sponsor 0.8x, description 0.5x)
  3. UI: Modal dialog with overlay, scroll lock, Escape/click-outside to close, auto-close on navigation

Test plan

  • pnpm build succeeds (generates index then builds Next.js)
  • Open search via button or ⌘K
  • Type a speaker name → relevant events appear
  • Type a talk title → relevant events appear
  • Fuzzy search with typos → still finds results
  • Click a result → navigates to event page, dialog closes
  • Close with Escape, click outside overlay
  • Test on mobile and desktop layouts

🤖 Generated with Claude Code

Add a search feature that lets users search across all past and upcoming
events by title, talk name, speaker name, or sponsor. Uses a static JSON
index generated at build time and Fuse.js for fuzzy matching on the client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lyonjs-website Ready Ready Preview, Comment Feb 7, 2026 8:05pm

Request Review


const index = allEvents.map((event) => {
const override = overrides[event.eventUrl] || {};
const description = (event.description || '').replace(/<[^>]*>/g, '').slice(0, 200);

Check failure

Code scanning / CodeQL

Incomplete multi-character sanitization High

This string may still contain
<script
, which may cause an HTML element injection vulnerability.

Copilot Autofix

AI 4 days ago

In general, the best way to fix this is to replace the custom regex-based stripping with a robust HTML sanitization mechanism that safely converts arbitrary HTML input into plain text (or safely allowed HTML) and does not rely on a single multi-character regex pass. For this search index, the intended behavior appears to be: “strip HTML and keep a short text snippet,” so converting the HTML description to plain text is appropriate and preserves functionality.

The single best fix here, without changing the rest of the script’s behavior, is to replace the .replace(/<[^>]*>/g, '') with a safe HTML-to-text conversion function. Because this is a Node script, we can use a well-known library such as striptags, which is designed to remove HTML tags correctly and does not suffer from the incomplete multi-character sanitization problem. Concretely:

  • Add an import for striptags at the top of scripts/generate-search-index.mjs.
  • Replace the current computation of description at line 193 with a call to striptags, followed by .slice(0, 200) as before.

No other logic needs to change: we still limit the text to 200 characters and keep the same structure of entry. The only functional difference is that descriptions are now stripped using a robust algorithm instead of a brittle regex, which addresses the CodeQL warning about incomplete sanitization.

Suggested changeset 2
scripts/generate-search-index.mjs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/scripts/generate-search-index.mjs b/scripts/generate-search-index.mjs
--- a/scripts/generate-search-index.mjs
+++ b/scripts/generate-search-index.mjs
@@ -1,6 +1,7 @@
 import { writeFileSync } from 'node:fs';
 import { resolve, dirname } from 'node:path';
 import { fileURLToPath } from 'node:url';
+import striptags from 'striptags';
 
 const MEETUP_GQL_URL = 'https://www.meetup.com/gql2';
 const LYONJS_MEETUP_ID = 18305583;
@@ -190,7 +191,7 @@
 
   const index = allEvents.map((event) => {
     const override = overrides[event.eventUrl] || {};
-    const description = (event.description || '').replace(/<[^>]*>/g, '').slice(0, 200);
+    const description = striptags(event.description || '').slice(0, 200);
 
     const entry = {
       id: event.id,
EOF
@@ -1,6 +1,7 @@
import { writeFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import striptags from 'striptags';

const MEETUP_GQL_URL = 'https://www.meetup.com/gql2';
const LYONJS_MEETUP_ID = 18305583;
@@ -190,7 +191,7 @@

const index = allEvents.map((event) => {
const override = overrides[event.eventUrl] || {};
const description = (event.description || '').replace(/<[^>]*>/g, '').slice(0, 200);
const description = striptags(event.description || '').slice(0, 200);

const entry = {
id: event.id,
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -23,7 +23,8 @@
     "react-markdown": "^10.0.0",
     "sharp": "^0.34.0",
     "temporal-polyfill": "^0.3.0",
-    "yet-another-react-lightbox": "^3.21.4"
+    "yet-another-react-lightbox": "^3.21.4",
+    "striptags": "^3.2.0"
   },
   "devDependencies": {
     "@playwright/test": "1.58.2",
EOF
@@ -23,7 +23,8 @@
"react-markdown": "^10.0.0",
"sharp": "^0.34.0",
"temporal-polyfill": "^0.3.0",
"yet-another-react-lightbox": "^3.21.4"
"yet-another-react-lightbox": "^3.21.4",
"striptags": "^3.2.0"
},
"devDependencies": {
"@playwright/test": "1.58.2",
This fix introduces these dependencies
Package Version Security advisories
striptags (npm) 3.2.0 None
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant