Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ec2ce70
ci: replace hardcoded account_id in wrangler.toml for fork CI
mmcintosh Jan 30, 2026
b702309
ci: configure KV namespace and R2 bucket for fork CI
mmcintosh Jan 30, 2026
596a092
ci: fix KV namespace listing parse error in CI configure step
mmcintosh Jan 30, 2026
0718944
ci: fix wrangler account resolution for fork CI
mmcintosh Jan 30, 2026
f5fc48d
Merge branch 'SonicJs-Org:main' into main
mmcintosh Jan 31, 2026
5ddbaa0
Merge upstream SonicJs-Org/main into fork
mmcintosh Feb 4, 2026
ea9c8e0
feat: add FTS5 full-text search, Vectorize auto-indexing, and relevan…
mmcintosh Feb 8, 2026
92b3d6f
test: add comprehensive E2E tests for AI Search v3
mmcintosh Feb 8, 2026
0521567
feat: add hybrid search with RRF, query rewriting, and AI reranking
mmcintosh Feb 9, 2026
1c2169a
Merge branch 'feature/search-phase2'
mmcintosh Feb 9, 2026
93a56d2
fix: persist reranking_enabled and query_rewriting_enabled settings
mmcintosh Feb 9, 2026
46ec795
fix: seed data plugin routes not working and settings not reflected
mmcintosh Jan 6, 2026
a196659
feat: overhaul seed data plugin with rich content, forms, and submiss…
mmcintosh Feb 9, 2026
c4101e9
fix: show actual content/FTS5 counts in index status when Vectorize u…
mmcintosh Feb 9, 2026
b5ca8da
fix: correct FTS5 index count query and sanitize search result HTML
mmcintosh Feb 9, 2026
c4b6d26
added npm audit to the pretest workflow
mmcintosh Feb 16, 2026
10018f7
ci: split E2E tests into 3 parallel shards
mmcintosh Feb 16, 2026
4822797
feat: AI Search plugin with FTS5, hybrid search, faceted search, and …
mmcintosh Feb 17, 2026
70ef979
added dependabot to our fork for security
mmcintosh Feb 17, 2026
ae2522d
test: add search quality agent E2E tests (25 tests)
mmcintosh Feb 17, 2026
d9e2672
feat: API key authentication middleware for search API
mmcintosh Feb 17, 2026
a2f2ddf
test: add related searches, trending, and experiments E2E tests (60 t…
mmcintosh Feb 17, 2026
f0cf691
feat: add Related Searches admin UI to Relevance tab
mmcintosh Feb 17, 2026
5abdf60
fix: ensure registration enabled in 02b auth tests beforeAll
mmcintosh Feb 17, 2026
4439f87
Merge pull request #36 from mmcintosh/fix/ui-1-related-searches-admin
mmcintosh Feb 18, 2026
64137c6
feat: add A/B test templates, visual editor, and recommendations
mmcintosh Feb 18, 2026
645d423
Merge pull request #38 from mmcintosh/feat/ab-tests-v2-templates-editor
mmcintosh Feb 18, 2026
64a0db2
feat: add one-way synonym support and fix agent tab rendering (#39)
mmcintosh Feb 19, 2026
223faca
feat: CSV/TXT synonym dictionary import with corpus filtering (#40)
mmcintosh Feb 19, 2026
a0580ec
feat: add documentation page and color-coded nav buttons
mmcintosh Feb 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1 @@
{
"hooks": {
"userPromptSubmitHook": {
"command": "echo '\n\n---\n\n📋 TESTING REMINDER: After completing this task, create and run E2E tests to verify the changes work correctly. Tests should go in tests/e2e/ using Playwright.\n\n---\n'",
"description": "Remind Claude to create E2E tests for all changes"
}
}
}

40 changes: 40 additions & 0 deletions .github/dependatbaot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Dependabot — Automated dependency update PRs
# https://docs.github.com/en/code-security/dependabot
#
# GitHub reads this file automatically — no workflow, no secrets needed.
# Dependabot will open PRs for:
# 1. npm packages with known vulnerabilities (security updates — always immediate)
# 2. npm packages with new versions (version updates — weekly batched)
# 3. GitHub Actions with new versions (keeps CI workflows current)

version: 2

updates:
# npm dependencies
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
# Group minor + patch updates into a single PR to reduce noise
groups:
minor-and-patch:
update-types:
- "minor"
- "patch"
labels:
- "dependencies"
# Limit open PRs to avoid overwhelming the PR list
open-pull-requests-limit: 10
# Security updates are always immediate regardless of schedule

# GitHub Actions versions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
labels:
- "dependencies"
- "ci"
open-pull-requests-limit: 5
58 changes: 58 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# GitHub CodeQL — Static Application Security Testing (SAST)
# https://github.com/github/codeql-action
#
# Analyzes JavaScript/TypeScript for:
# - SQL injection patterns
# - Prototype pollution
# - Path traversal
# - Cross-site scripting (XSS)
# - Insecure cryptography
# - Regular expression denial of service (ReDoS)
# - Missing authentication / authorization
# - Hardcoded credentials
# - And 100+ more security rules
#
# Results appear in GitHub Security tab → Code Scanning Alerts.
# Free for public repositories.

name: CodeQL

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
# Weekly on Wednesdays at 04:15 UTC (catches new CVE patterns in query packs)
- cron: '15 4 * * 3'

jobs:
analyze:
name: Analyze JavaScript/TypeScript
runs-on: ubuntu-latest
timeout-minutes: 30

permissions:
security-events: write # Upload SARIF results to Code Scanning
actions: read # Read workflow details
contents: read # Read repo contents

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
build-mode: none

# security-and-quality includes all security queries PLUS code quality
# checks (dead code, unused variables, etc). Use 'security-extended'
# if you only want security findings without quality noise.
queries: security-and-quality

- name: Run CodeQL analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:javascript-typescript"
86 changes: 66 additions & 20 deletions .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,18 @@ jobs:
echo "PR Head Repo: ${{ github.event.pull_request.head.repo.full_name || 'N/A' }}"
echo "Is Fork: ${{ github.event.pull_request.head.repo.full_name != github.repository }}"

test:
# Build, unit test, and deploy preview
build-and-deploy:
needs: authorize
runs-on: ubuntu-latest
timeout-minutes: 60
timeout-minutes: 30
outputs:
preview_url: ${{ steps.deploy.outputs.preview_url }}

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# For pull_request_target, we need to checkout the PR head
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}

- name: Setup Node.js
Expand All @@ -45,6 +47,10 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Security audit (npm)
run: npm audit --audit-level=high
continue-on-error: true

# TODO: Re-enable type checking after fixing remaining TypeScript errors
# Tracked in follow-up issue
# - name: Type check
Expand All @@ -65,7 +71,27 @@ jobs:
- name: Build core package
run: npm run build:core

# Skip Cloudflare deployment and E2E tests for Dependabot PRs (no access to secrets)
- name: Configure wrangler for CI account
if: github.actor != 'dependabot[bot]'
run: |
cd my-sonicjs-app

# Remove hardcoded account_id — the CLOUDFLARE_API_TOKEN is already
# scoped to the correct account, so wrangler derives it from the token.
# A wrong account_id in wrangler.toml causes auth failures on forks.
sed -i '/^account_id/d' wrangler.toml

# Remove KV namespace config if present — the KV ID may reference a
# namespace on a different account. KV is optional for CI testing.
sed -i '/^\[\[kv_namespaces\]\]/,/^$/d' wrangler.toml
sed -i '/^binding = "CACHE_KV"/d' wrangler.toml
sed -i '/^id = "/d' wrangler.toml

echo "=== wrangler.toml configured for CI ==="
cat wrangler.toml
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

- name: Create fresh D1 database for PR
if: github.actor != 'dependabot[bot]'
id: create-db
Expand Down Expand Up @@ -135,15 +161,9 @@ jobs:
id: deploy
run: |
cd my-sonicjs-app
# Deploy to preview with unique name based on PR/branch
# Cloudflare Workers has a 54 character limit for script names with previews
# Prefix "sonicjs-pr-" is 11 chars, so max branch name is 43 chars
BRANCH_NAME="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}"
# Limit to 43 chars to allow for "sonicjs-pr-" prefix (11 chars) = 54 total
# Remove trailing hyphens to avoid invalid worker names
SAFE_BRANCH=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9-]/-/g' | cut -c1-43 | sed 's/-*$//')

# Deploy and capture the URL (don't exit on error)
set +e
echo "Deploying to preview environment: sonicjs-pr-${SAFE_BRANCH}"
DEPLOY_OUTPUT=$(npx wrangler deploy --name "sonicjs-pr-${SAFE_BRANCH}" 2>&1)
Expand All @@ -159,7 +179,6 @@ jobs:
exit 1
fi

# Extract the URL from wrangler output
PREVIEW_URL=$(echo "$DEPLOY_OUTPUT" | grep -oP 'https://[^\s]+\.workers\.dev' | head -1)

if [ -z "$PREVIEW_URL" ]; then
Expand Down Expand Up @@ -188,30 +207,57 @@ jobs:
echo "Preview failed to become ready"
exit 1

# E2E tests — split into 3 parallel shards for faster execution
e2e:
needs: build-and-deploy
if: github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build core package
run: npm run build:core

- name: Install Playwright browsers
if: github.actor != 'dependabot[bot]'
run: npx playwright install --with-deps chromium

- name: Run E2E tests against preview
if: github.actor != 'dependabot[bot]'
run: npm run e2e
- name: Run E2E tests (shard ${{ matrix.shard }}/3)
run: npm run e2e -- --shard=${{ matrix.shard }}/3
env:
CI: true
BASE_URL: ${{ steps.deploy.outputs.preview_url }}
BASE_URL: ${{ needs.build-and-deploy.outputs.preview_url }}

- name: Upload test results
if: always() && github.actor != 'dependabot[bot]'
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
name: playwright-report-shard-${{ matrix.shard }}
path: playwright-report/
retention-days: 7

- name: Upload test videos
if: always() && github.actor != 'dependabot[bot]'
if: always()
uses: actions/upload-artifact@v4
with:
name: test-videos
name: test-videos-shard-${{ matrix.shard }}
path: |
test-results/**/*.webm
playwright-report/**/*.webm
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ node_modules/
# Development
.wrangler/
.dev.vars
.benchmark-temp/

# Environment variables
.env
Expand Down
16 changes: 14 additions & 2 deletions my-sonicjs-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { Hono } from 'hono'
import { createSonicJSApp, registerCollections } from '@sonicjs-cms/core'
import { createSonicJSApp, registerCollections, ExperimentService } from '@sonicjs-cms/core'
import type { SonicJSConfig } from '@sonicjs-cms/core'

// Import custom collections
Expand Down Expand Up @@ -53,4 +53,16 @@ if (contactFormPlugin.routes) {
// Mount core app last (catch-all)
app.route('/', coreApp)

export default app
export default {
fetch: app.fetch,
async scheduled(event: ScheduledEvent, env: any, ctx: ExecutionContext) {
const expService = new ExperimentService(env.DB, env.CACHE_KV, env.SEARCH_EXPERIMENTS)
const active = await expService.getActiveExperiment()
if (active) {
const result = await expService.evaluateExperiment(active.id)
if (result?.auto_completed) {
console.log('[Cron] Experiment ' + active.id + ' auto-completed: winner=' + result.winner)
}
}
}
}
44 changes: 35 additions & 9 deletions my-sonicjs-app/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,59 @@ compatibility_date = "2025-05-05"
compatibility_flags = ["nodejs_compat"]

# Cloudflare account
account_id = "f9d6328dc3115e621758a741dda3d5c4"
account_id = "f61c658f1de7911b0a529f38308adb21"

# Cloudflare Workers settings
workers_dev = true

# D1 Database
# Note: database_name and database_id are automatically updated by GitHub Actions
# D1 Database — persistent preview (not used by CI)
[[d1_databases]]
binding = "DB"
database_name = "sonicjs-worktree-lane711-otp-email-branding"
database_id = "68223153-215a-4c8f-a9bf-797f11236758"
database_name = "sonicjs-preview-db"
database_id = "0526b411-83d4-4e59-b23a-4e9f81011319"
migrations_dir = "./migrations"

# R2 Bucket for media storage (using CI bucket)
# R2 Bucket for media storage
[[r2_buckets]]
binding = "MEDIA_BUCKET"
bucket_name = "sonicjs-ci-media"
bucket_name = "sonicjs-preview-media"

# KV Cache (using CI namespace)
# KV Cache
[[kv_namespaces]]
binding = "CACHE_KV"
id = "a16f8246fc294d809c90b0fb2df6d363"
id = "6cd51bcf46d246798e54af26929134f9"

# Workers AI (for embeddings)
[ai]
binding = "AI"

# Vectorize index (for semantic/hybrid search)
[[vectorize]]
binding = "VECTORIZE_INDEX"
index_name = "sonicjs-ai-search"

# Vectorize index (for benchmark evaluation — isolated from production data)
[[vectorize]]
binding = "VECTORIZE_BENCHMARK_INDEX"
index_name = "sonicjs-benchmark"

# Environment variables
[vars]
ENVIRONMENT = "development"

# Extended CPU time for benchmark seeding (57K+ docs) and large evaluations
[limits]
cpu_ms = 300000

# Analytics Engine (for A/B experiment event tracking)
[[analytics_engine_datasets]]
binding = "SEARCH_EXPERIMENTS"
dataset = "sonicjs_search_experiments"

# Cron triggers (experiment auto-evaluation every 6 hours)
[triggers]
crons = ["0 */6 * * *"]

# Observability
[observability]
enabled = true
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface Bindings {
ENVIRONMENT?: string;
BUCKET_NAME?: string;
GOOGLE_MAPS_API_KEY?: string;
REQUIRE_API_KEY?: string;
}
interface Variables {
user?: {
Expand All @@ -32,6 +33,12 @@ interface Variables {
requestId?: string;
startTime?: number;
appVersion?: string;
apiKey?: {
id: string;
name: string;
scopes: string[];
userId: string;
};
}
interface SonicJSConfig {
collections?: {
Expand Down
Loading
Loading