fix(clerk-js,clerk-react,chrome-extension): Build and consume packages without remotely hosted code - #4551
Conversation
🦋 Changeset detectedLatest commit: d7f9da1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
!allow-major |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
| @@ -20,9 +23,3 @@ export * from './runtime'; | |||
| export * from './url'; | |||
| export * from './web3'; | |||
| export * from './windowNavigate'; | |||
There was a problem hiding this comment.
Removes a number of duplicated exports.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
royanger
left a comment
There was a problem hiding this comment.
I looked over the code and to my eye, with limited experience inside the JS repo, it looks good. Testing with 'dev' and 'build' version of a Chrome Extension I'm not seeing of the the various reported remote code issues.
16ba756 to
ee2664a
Compare
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
@jacekradko @brkalow Mind taking another once-over here? Once this is pulled into the upstream extension updates PR, we're planning on releasing this via that. |
ee2664a to
1b8d68a
Compare
without remotely hosted code
…ant 2.9.14 Chrome Web Store rejected v1.0.0 because the bundled JS contained externally hosted code references (Cloudflare Turnstile, Google GSI), which violates Manifest V3. Clerk fixed this in @clerk/chrome-extension 2.0+ via the no-RHC build (PR clerk/javascript#4551, #4899). - bump @clerk/chrome-extension 1.3.14 -> 2.9.14 - replace removed `syncSessionWithTab` with `syncHost` - bump extension version 1.0.0 -> 1.0.1 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* feat(frontend): integrations marketplace with tag filter and browser extension card Turns the dashboard integrations page into a typed catalog-driven marketplace. Analytics providers stay first-class (GA4, Matomo still connect/test/toggle/edit exactly as before) alongside two new kinds of entries: an external-link card for the Chrome extension pointing at the Web Store, and a coming-soon teaser for ChatGPT. - Static catalog at src/lib/integrations-catalog.ts drives what renders, typed as a discriminated union so new kinds (oauth, api-key, ...) slot in without touching the existing analytics module. - Components moved out of components/dashboard/analytics-integration/ into a single components/dashboard/integrations/ folder (flat, one kind per file). ProviderLogo collapsed into IntegrationLogo; logos now live as assets in public/images/integrations/ and are selected by integration id. - Tag filter bar for Analytics / Browser / AI. "More integrations on the way" teaser restored below the list. - Pro-plan badge shown inline on analytics cards for free users, linking to the billing page. - All nine locale dictionaries updated with the new catalog strings. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * formatting, refactoring * feat(integrations): InDesign UXP plugin + Pro-gated render API Adds an Adobe InDesign UXP panel that browses, creates, and places QR codes straight into layouts. Plugin lives under apps/integrations/indesign/ so future Figma/ChatGPT integrations can follow the same convention. Backend - New api-key module (apps/backend/src/modules/api-key) with thin wrappers around clerkClient.apiKeys; create is Pro-gated via request.user.plan. - New POST /api/v1/qr-code/render endpoint: takes {config, data, sizePx?, format?} and returns PNG/WebP/JPEG/SVG. Uses existing qr-code-styling pipeline → resvg-js → sharp (webp/jpeg). ETag + 304 so clients cache. - Dedicated QR_RENDER rate-limit policy (600/min auth, 1200/min Pro) keyed on user id. - Normalises url('#id') → url(#id) so Resvg resolves clip-paths instead of flooding black. - Integration tests cover Pro-gate, cross-user isolation, secret leakage, revoked-key invalidation, size/format validation, and Clerk metadata round-tripping. Frontend - API-keys dashboard now calls the backend endpoints (not Clerk client SDK), so the Pro-gate is enforced server-side — DevTools workaround closed. - Plans/Billing: free plan drops limitedApi, Pro plan drops unlimitedApi, analyticsIntegrations renamed to marketplaceIntegrations with the user-facing label "Third-party integrations". Feature rows render an info icon that deep-links to /docs/api or /dashboard/settings/integrations. - Integrations marketplace gains an Adobe InDesign entry with logo. - /content/api-index.mdx rewritten to document API keys as a Pro-only feature and point to the new dashboard settings location. - All 9 locale dictionaries updated. InDesign plugin (apps/integrations/indesign) - UXP v5 manifest targeting InDesign 18+ - Webpack build, React 18, flat screen router (Settings / List / Create) - Renders previews and placements via the backend render endpoint - API-key entered once, persisted via uxp.storage.secureStorage - Dynamic QR support with reserved short-code preview - Click-to-place normalises SVG dimensions to 30mm so InDesign places sensibly sized QRs. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: apply prettier formatting to plugin assets and lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(integrations): point plugin api-client at production base URL Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(integrations): set Adobe-assigned plugin id for indesign Ties the manifest id to the listing created in Adobe's Distribution console so uploads are accepted. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(integrations): bump indesign host minVersion to 18.5.0 Adobe Distribution rejects uploads with a partial minVersion (18.0); moving to a fully qualified, UXP-capable version fixes the HOSTAPP_VERSION_INVALID error during package validation. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(browser-extension): upgrade @clerk/chrome-extension to MV3-compliant 2.9.14 Chrome Web Store rejected v1.0.0 because the bundled JS contained externally hosted code references (Cloudflare Turnstile, Google GSI), which violates Manifest V3. Clerk fixed this in @clerk/chrome-extension 2.0+ via the no-RHC build (PR clerk/javascript#4551, #4899). - bump @clerk/chrome-extension 1.3.14 -> 2.9.14 - replace removed `syncSessionWithTab` with `syncHost` - bump extension version 1.0.0 -> 1.0.1 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(api-key,qr-code,integrations): security hardening & test fixes - api-key: revoke via backend from ui (previously called clerk directly, bypassing plan + rate-limit guards) - api-key: align description max length with clerk's 255-char limit - api-key: distinguish clerk 4xx (→ 404) from 5xx/network errors on revoke instead of masking every failure as not-found - api-key: scope list/revoke tanstack-query cache key by userId - qr-code/render: drop http fetch branch — only data: urls and s3 storage paths accepted, eliminating ssrf surface from user-provided config.image - qr-code/render: fix request generic so request.user is typed as required - qr-code/render: lint fix on if-none-match header access - fastify helpers: expose request.headers through request parser (prototype getter was lost by object spread, breaking if-none-match reads) - indesign plugin: gate webpack devtool by mode — no more source maps in production bundles shipped to adobe - indesign plugin: remove localhost:5001 from manifest network allowlist - indesign plugin: use TextEncoder/TextDecoder for secureStorage so api keys with non-ascii characters round-trip correctly - indesign plugin: await placement and surface errors in CreateScreen instead of fire-and-forget that swallowed failures - indesign plugin: drop unused fetchRemoteSvg helper - frontend integrations: switch chrome extension entry to coming-soon until the store id is assigned (was pointing at a placeholder url) - tests: seed a pro subscription for TEST_USER_PRO_ID in api-key tests and hard-delete (not revoke) clerk keys on setup/teardown so repeated runs don't hit clerk's token-creation-conflict (409) on duplicate names * chore: apply prettier formatting to browser-extension splash and lockfile --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
* Bump toshimaru/auto-author-assign from 2.1.1 to 3.0.1 Bumps [toshimaru/auto-author-assign](https://github.com/toshimaru/auto-author-assign) from 2.1.1 to 3.0.1. - [Release notes](https://github.com/toshimaru/auto-author-assign/releases) - [Changelog](https://github.com/toshimaru/auto-author-assign/blob/main/CHANGELOG.md) - [Commits](https://github.com/toshimaru/auto-author-assign/compare/v2.1.1...v3.0.1) --- updated-dependencies: - dependency-name: toshimaru/auto-author-assign dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> * Bump actions/upload-artifact from 4 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> * Bump actions/download-artifact from 4 to 8 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> * implement deadlock check with retry function * consolidate scan analytics into single endpoint and fix security issues - Replace 3 separate scan calls (Umami, clear-cache, track-scan) with single POST /record-scan endpoint behind internalApiAuthHandler - Fix IP attribution: forward real scanner IP via x-scanner-ip header, validated against internal API key in resolveClientIp before rate limiting - Add SCAN_LOOKUP (300/min) and SCAN_RECORD (200/min) rate limit policies - Protect GET /:shortCode with internalApiAuthHandler and return only minimal fields (destinationUrl, isActive, deletedAt) instead of full entity with createdBy, Cloudflare data, and DNS records - Add sendEvent() to UmamiAnalyticsService for backend-side Umami tracking Co-Authored-By: Claude Opus 4.6 <[email protected]> * redesign analytics section with animated progress bars and area chart Replace plain text analytics cards with animated progress bar charts (indigo gradient), add Recharts area chart for 30-day activity timeline, and extract summary cards into dedicated components with icons. Fix backend to fetch only last 30 days for time series data. Add translations for new keys across all 8 locales. Co-Authored-By: Claude Opus 4.6 <[email protected]> * update url-shortener tests for internal API security model Replace clear-views-cache tests with record-scan endpoint tests, rewrite get-short-url tests to verify minimal scan-lookup response and internal API key auth, and add Umami /api/send mock support. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix analytics responsive layout, empty labels, and time chart description - Change analytics cards grid breakpoint from md to lg (1024px) for single-column stacking - Fix card height issues in mobile/tablet by removing h-full from chart and summary cards - Replace empty metric labels with translated "Unknown" instead of filtering - Update time chart description to "Last 30 days" across all 8 locales Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix mobile responsiveness on dashboard detail pages Co-Authored-By: Claude Opus 4.6 <[email protected]> * add Portuguese locale, fix vCard CSV columns, URL link, and gradient fallback - Add Portuguese (pt) as 9th supported locale with full translations - Update vCard CSV factory to match expanded column schema - Add CSV template validation tests - Fix QR code detail URL to use short URL link - Add gradient string fallback in color picker utils Co-Authored-By: Claude Opus 4.6 <[email protected]> * chore: update schemas, controllers, FAQ page, accordion UI, and locales - Update Zod v4 schemas and DTOs across shared package - Refactor backend controllers with updated schema validation - Add dedicated FAQ page and components - Redesign accordion component - Update all 9 locale dictionaries - Update pnpm-lock.yaml Co-Authored-By: Claude Opus 4.6 <[email protected]> * deduplicate FAQs, add invoice FAQ, fix schema.org structure, and mobile padding - Remove 6 duplicate general FAQs across all 9 locales (13→7) - Add invoice/billing FAQ to pricing category (4→5) - Fix schema.org microdata: wrap answers in acceptedAnswer/Answer scope - Fix mobile padding consistency on FAQ page Co-Authored-By: Claude Opus 4.6 <[email protected]> * remove microdata from accordion FAQs, rely on JSON-LD instead Radix AccordionContent unmounts closed items, making microdata invisible to crawlers. The /faq page already has JSON-LD structured data covering all FAQs, which is always in the DOM regardless of accordion state. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Revert "remove microdata from accordion FAQs, rely on JSON-LD instead" This reverts commit 9cce0236ab5f0a087bd938e2d4899df924472c04. * fix lint errors in csv-template-validation test Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix CodeRabbit review issues: accessibility, Tailwind, accordion, FAQ copy - Fix invalid `text-md` Tailwind class → `text-base` in ProductUseCases - Escape `<` in JSON-LD output to prevent script injection - Use controlled Accordion (value/onValueChange) for hash deep-link support - Add aria-label to FAQ search input for screen readers - Fix formal "Sie" → informal "du" in German faqPage translations - Update privacy FAQ across all 9 locales (accurately mention analytics) - Replace "coming soon" bulk QR text with CSV import in all 9 locales Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: add user satisfaction survey (thumbs up/down with feedback) Full-stack implementation of a satisfaction survey for logged-in dashboard users whose account is older than 2 weeks. Includes backend module (entity, repository, use case, controller, migration, tests), shared DTOs, frontend dialog component with PostHog tracking, and i18n translations for all 9 locales. Co-Authored-By: Claude Opus 4.6 <[email protected]> * improve icon upload UX: dropzone, preview with filename, increase size limit to 1MB Replace plain file input with FileUpload dropzone (drag & drop), add icon preview showing thumbnail and filename, and raise image size limit from 0.5MB to 1MB in both schema and frontend validation. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix icon upload: reset FileUpload state on delete and show max size in error toast The FileUpload component's internal file store wasn't cleared when removing an icon via trash button, preventing subsequent uploads. Also added file format/size hint as toast description for rejected files. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix CodeRabbit review issues for user satisfaction survey - Make rating column NOT NULL in entity and regenerate migration - Add missing .execute() on repository update method - Remove unnecessary null coalescing on dto.rating in use case - Fix thank-you step visibility by separating markResponded from dismiss - Move markResponded to mutation onSuccess callbacks - Update null-rating test assertion Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix remaining CodeRabbit review issues - Add type="button" to remove icon button in SettingsForm (prevents form submit) - Guard iconName in IconPicker when SVG fetch fails - Validate Base64 decoded byte size instead of string length in QrCode schema Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix additional CodeRabbit review issues - Handle concurrent survey submissions gracefully via try/catch on unique constraint - Fix stale closure in FileReader.onload by using partial updateConfig Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix frontend ESLint for Next.js 16 and remove Turborepo - Rewrite eslint.config.js: remove FlatCompat + shared(), use eslint-config-next v16 native flat config directly - Fix @typescript-eslint plugin conflict with eslint-config-next - Add eslint-config-prettier/flat for Prettier integration - Fix scripts: replace removed `next lint` with `eslint .` - Remove Turborepo: replace turbo scripts with pnpm -r, remove turbo devDependency, clean up .gitignore/.dockerignore references Co-Authored-By: Claude Opus 4.6 <[email protected]> * chore: overhaul ESLint config with shared base and fix all warnings - Create shared ESLint base config (packages/eslint-config/base.js) used by frontend, backend, and desktop - Fix frontend ESLint for Next.js 16 (removed next lint, native flat config instead of FlatCompat) - Filter @typescript-eslint plugin from eslint-config-next to avoid "Cannot redefine plugin" error - Remove unused shared.js and node.js config files - Promote meaningful rules to error, disable noisy rules that don't improve code quality - Fix all lint violations across the monorepo to achieve 0 warnings: - Add void prefix for floating promises in event handlers - Fix exhaustive-deps with eslint-disable where deps are intentionally omitted - Convert catch(err) to bare catch where error is unused - Add eslint-disable for <img> tags used with dynamic content (data URIs, external URLs) - Convert inline import() type annotations to top-level type imports - Add varsIgnorePattern for _-prefixed destructuring variables - Add eslint-config-prettier to prevent formatting rule conflicts Co-Authored-By: Claude Opus 4.6 <[email protected]> * formatting lock * fix FAQ structured data: replace broken microdata with JSON-LD Radix UI accordion hides closed items with hidden attribute, preventing Google from reading microdata. Replace inline microdata with a reusable FaqJsonLd component that renders JSON-LD on the /faq page only. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix flaky tests: add per-file isolation, robust cleanup, and diagnostic matcher - Add custom `toHaveStatusCode` Jest matcher that includes response body in failure messages for better diagnostics (replaces 539 assertions) - Add `resetTestState()` (cache flush + DB cleanup) to beforeAll in all 70 HTTP integration test files for per-file isolation - Make `cleanUpMockData` robust with FOREIGN_KEY_CHECKS disable/enable and per-table error handling - Replace all `expect(response.statusCode).toBe(X)` with `expect(response).toHaveStatusCode(X)` across 71 files Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix config-template tests: truncate faker name to 32 chars faker.lorem.words(3) can generate strings longer than 32 characters, causing validation errors. Apply .substring(0, 32) like qr-code tests. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix mock cleanup: pin FK checks to single transaction Use db.transaction() so SET FOREIGN_KEY_CHECKS = 0/1 and all deletes run on the same connection, preventing leaked state across pooled sessions. Also remove per-table try/catch to fail fast on cleanup errors. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix CodeRabbit review issues - add --if-present to recursive pnpm scripts to prevent failures - remove direct prop mutation in template ListItem handleUpdate - add missing `t` dependency in QrCodeTagSelector handleOpenChange Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix GitHub code scanning security alerts - Replace Math.random() with randomInt() in load test payloads (insecure randomness) - Truncate error messages in token generator to avoid logging sensitive API responses - Add explicit permissions: contents: read to workflow files missing permissions Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix infinite loop in URL pagination sync when navigating via sidebar Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix review issues: stale resize handler, dead code, unused lint plugins - Fix QrPreviewModal resize handler using positionRef to avoid stale closure and constant listener re-attachment - Remove dead AddPaymentMethodForm (unused Clerk experimental billing) - Remove unused ESLint plugins (unicorn, lodash, unused-imports) - Remove unnecessary eslint-disable directive in ListItem.tsx - Add descriptive comment to use-toast.ts eslint-disable - Force legal footer links to English locale Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: QR code margin/border spacing bugs and size slider step - Convert pixel margin back to percentage when loading QR codes for editing - Round pixel margin to integer to prevent visual jitter on size change - Fix margin slider NaN by extracting number from Slider's array callback - Make logo image margin proportional to QR code size (3%) instead of fixed 30px - Change size slider step from 25px to 100px Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: add self-referencing hreflang alternate links for SEO Include the current locale in alternates.languages so each page has a self-referencing hreflang link, which Google requires for proper international SEO indexing. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: bulk import fails for QR codes with template image The config object was shared by reference across all CSV rows during bulk import. After the first QR code's image was uploaded, the mutation of config.image (from base64 to S3 path) caused all subsequent rows to fail with "Invalid base64 image format". Shallow-copying config per iteration ensures each QR code uploads its own independent image. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Increase maxAttempts from 5 to 30 * feat: add Help & Support button to dashboard header Add a help dropdown in the SiteHeader with links to FAQ, Documentation (always EN locale), and Email Support ([email protected]). Uses the same design pattern as the Products dropdown in the main header. Includes translations for all 9 locales and mobile drawer support. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: show help button text at all sizes with responsive font Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: make survey skip button more subtle to reduce skip rate Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: make table action column sticky so it's always accessible The three-dots action menu in dashboard tables was getting cut off on smaller screens and users didn't realize they could scroll horizontally. Now the actions column stays pinned to the right edge with reduced padding for a compact look. Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: rename isEditable to isDynamic for URL QR codes + add custom domain caching Unify naming: URL-type QR codes now use `isDynamic` (matching vCard) instead of `isEditable`. Includes DB migration to rename the field in existing JSON content, and updates across shared package, backend, frontend, browser extension, desktop app, and load tests. Also adds caching to custom domain repository lookups and a rate limit policy update. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: remove unnecessary type assertion in custom domain cache test Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add Cloudflare API retry logic and distinguish transient errors from domain issues Transient Cloudflare errors (5xx, error code 10001) were being returned as 400 DomainVerificationFailedError, misleading users into thinking their DNS config was wrong. Now retries transient errors with exponential backoff and returns 503 ServiceUnavailableError for upstream failures. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: redesign analytics page with new layout, world map, and mobile optimization - Redesign summary cards: 4 cards (Total Scans/Clicks, Visitors, Top Country, Top Device) with background icons, auto-fit text, and bold sub-values - Add interactive world map (cylindrical stereographic projection) with local GeoJSON, hover tooltips, and country heatmap coloring - Redesign device chart as donut/ring chart with hover effects and centered percentage - Redesign browser chart as 2x2 grid cards with expandable bar list - Country list as auto-filling scrollable list matching right column height - Add scroll hints (fade gradient + bouncing chevron) that hide when scrolled to bottom - Support scan vs click variant for QR code vs Short URL contexts - Add clicksUnit, totalClicks, showMore, showLess translations (9 locales) - Use bar width based on actual percentage share instead of relative to max - Show <1% instead of 0% for small values - Use slate gradient for bar charts - Mobile optimization: responsive card grid, icon-only buttons under lg, responsive card padding (px-4/sm:px-6) - Fix duplicate React keys in browser chart - Fix Short URL detail header mobile layout Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: format pnpm-lock.yaml Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: apply container max-width from xl instead of lg to avoid layout jump Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address CodeRabbit review feedback - Use raw percentage for bar width so <1% items show a visible 1% bar - Filter unknown country codes before passing to WorldMap - Remove unused variant prop from AnalyticsDeviceChart - Revert container.tsx max-width change back to main Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * remove umami from frontend * feat: add Google Ads conversion tracking (gtag.js) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add Google Ads conversion tracking for registration and subscription Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: use actual subscription price as Google Ads conversion value Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add Google Ads tag to docs and legal layouts Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: improve registration conversion tracking with dedicated signup-success page Replace timing-based heuristic with deterministic redirect via Clerk's signUpForceRedirectUrl to /signup-success page. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: optimize preview image generation (SVG → WebP, 800px) Generate QR code and config template preview images as optimized WebP instead of full-size SVGs. This reduces preview file sizes from 2-3 MB to under 100 KB. - Add sharp for SVG→WebP conversion and icon resizing - Generate previews at 800x800px (down from 1000x1000 default) - Resize embedded icons to max 400px before embedding in preview - Convert final SVG to WebP (quality 80, density 300) - Add unoptimized prop to frontend Image components (already optimized) - Update regenerate script with --force flag for bulk migration Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: use canvas-based preview generation and dynamic QR rendering - Replace SVG→WebP conversion with real Canvas API (node-canvas) for preview generation, fixing blurry embedded icons - Use DynamicQrCode on detail and share pages instead of preview images - Add canvas dependency for server-side QR code rendering - PNG→WebP conversion via sharp preserves icon quality Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * update preview image generation for qr codes * update preview image generation for qr codes * feat: add OpenTelemetry metrics, traces (prepared), and frontend web vitals Add comprehensive observability via OpenTelemetry to the backend and activate web vitals tracking in the frontend. Backend: - HTTP request metrics (duration histogram, request/error counters, active requests gauge) - Business metrics (QR codes created/deleted, short URLs created/deleted, scans, bulk imports) - Active sessions tracking via Redis sorted set with observable gauge - Node.js runtime metrics (CPU, memory, event loop, GC) via @opentelemetry/host-metrics - Rate limit hit counter - Traces infrastructure prepared with @fastify/otel (disabled by default, activate with OTEL_TRACES_DATASET) - Graceful OTel shutdown on process exit Frontend: - AxiomWebVitals component for LCP, CLS, INP, TTFB, FCP tracking Activation: set OTEL_METRICS_DATASET env var to enable metrics, set OTEL_TRACES_DATASET to enable traces (requires 3rd Axiom dataset). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: use delta temporality for OTel metrics and simplify active sessions - Switch OTLPMetricExporter to DELTA temporality so counters report increments per interval instead of cumulative totals (fixes inflated values when using sum aggregation in Axiom) - Replace UpDownCounter with synchronous Gauge for active sessions (reports absolute count directly via gauge.record()) - Remove registerActiveSessionsGauge from server.ts (no longer needed) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: load Clerk JS via same-origin rewrite to fix Firefox ETP blocking Firefox's Enhanced Tracking Protection with CNAME uncloaking blocks clerk-js loading from the clerk.qrcodly.de proxy domain, preventing users from seeing the sign-in button. Proxy clerk-js through a Next.js rewrite (/__clerk-js/) backed by jsdelivr CDN so the browser sees a same-origin request that can't be blocked. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add remote MCP server for QRcodly API Adds a new `apps/mcp-server` app that exposes the QRcodly REST API as MCP tools via Streamable HTTP transport. AI agents (Claude, ChatGPT, Cursor, VS Code) can connect to it at `mcp.qrcodly.de/mcp` using a Bearer token for authentication. - 31 MCP tools auto-generated from the bundled OpenAPI specification - Fastify server with `@modelcontextprotocol/sdk` StreamableHTTPServerTransport - Bearer token passthrough to the QRcodly API - Session management for concurrent users - ESLint, TypeScript strict mode, Prettier aligned with monorepo standards - Configurable API base URL via QRCODLY_API_BASE_URL env var - Default QR code styling injected when `config` is omitted on create Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add price increase promo for Pro plan (valid until April 30) Show current prices with strikethrough future prices and urgency badge on Plans page and Dashboard Billing. Prices auto-switch to new rates (6.99€/8.99€) after April 30, 2026. Translations added for all 9 locales. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * docs: add MCP server integration guide and README Adds beginner-friendly documentation for connecting QRcodly to AI tools: - Step-by-step setup for Claude Code, Claude Desktop, Cursor, VS Code, ChatGPT/OpenAI, and Windsurf - Example prompts showing what users can do (create QR codes, manage templates, view analytics) - Complete tool reference table and supported QR code types - Troubleshooting section for common issues - Quick link card on the docs landing page - Developer README for the mcp-server app Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add templateId support to QR code creation Adds an optional `templateId` parameter to the create QR code endpoint. When provided, the template's styling config (including logo) is applied as the base. User-provided config fields override template values. Config itself is now optional — defaults to QrCodeDefaults when neither templateId nor config is provided. This enables AI agents to create QR codes with templates in a single call (e.g., "Create a QR code with my Corporate template") instead of manually extracting and passing the template config. Backend changes: - CreateQrCodeDto: add optional templateId, make config optional - CreateQrCodeUseCase: resolve config from template/defaults, skip image re-upload for existing S3 paths from templates MCP server changes: - Remove client-side config default injection (backend handles it now) - Remove TOOL_OVERRIDES for create_qr_code Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(mcp-server): add landing page, robots.txt, health check, session TTL Production readiness improvements: - GET / returns a human-friendly HTML landing page with link to docs - GET /robots.txt blocks all crawlers (Disallow: /) - GET /health returns { status, sessions } for monitoring - Sessions expire after 30 min of inactivity (prevents memory leaks) - Background cleanup runs every 5 minutes Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor(mcp-server): Zod env validation, catalog deps, docs improvements - Replace config.ts with env.ts using Zod schema validation (like backend) - Align dependency versions with backend (fastify, @fastify/cors, tsx) - Use pnpm catalog for zod and typescript - Add .gitignore for build artifacts - Move MCP Server card after API Docs on docs landing page - Add template-based QR code creation examples to MCP docs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor: move shared deps to pnpm catalog Move fastify, @fastify/cors, tsx, @typescript-eslint/eslint-plugin, and @typescript-eslint/parser to the pnpm workspace catalog so versions are managed centrally. Updates backend, mcp-server, and frontend package.json files to use catalog: references. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add note field to vCard QR codes Adds an optional `note` field (max 300 chars) to the vCard content type. Includes schema update, frontend generator/detail components, backend tests, translations for all 9 locales, and vCard serialization support. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(mcp-server): bind MCP sessions to bearer token for auth verification Verify the API key on every request (POST, GET, DELETE) — not just on session creation — to prevent session ID replay attacks. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: add mcp-server root scripts and fix frontend type error - Add mcp-server:dev, mcp-server:build, mcp-server:start to root package.json - Fix SaveQrCodeBtn prop type to require config (always provided by the store, but optional in TCreateQrCodeDto since templateId was added) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(mcp-server): add MCP registry config, fix CORS and landing page - Add server.json for MCP registry publishing (de.qrcodly/mcp-server) - Add .well-known/mcp-registry-auth for domain verification - Fix CORS to include DELETE method for browser-based MCP clients - Fix setup guide link to point to qrcodly.de Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: bypass middleware for .well-known paths Allow .well-known/mcp-registry-auth to be served as a static file for MCP registry domain verification. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(frontend): improve error handling for network failures in API requests Catch fetch TypeError (network errors, ad blockers, DNS failures) in apiRequest() and screenshot API, showing a user-friendly message instead of raw "Failed to fetch". Also report network errors (code 0) to Sentry alongside server errors. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix QRcode URL content update * feat: add comprehensive use-case unit tests across all modules Add 25 new unit test files covering previously untested use cases: URL-Shortener: - delete-short-url.use-case.test.ts - list-short-urls.use-case.test.ts Tag: - set-qr-code-tags.use-case.test.ts - set-short-url-tags.use-case.test.ts Analytics-Integration: - create-analytics-integration.use-case.test.ts - delete-analytics-integration.use-case.test.ts - dispatch-tracking-event.use-case.test.ts - list-analytics-integrations.use-case.test.ts - test-analytics-integration.use-case.test.ts - update-analytics-integration.use-case.test.ts Custom-Domain (all 10 use cases): - clear-default-custom-domain.use-case.test.ts - create-custom-domain.use-case.test.ts - delete-custom-domain.use-case.test.ts - get-custom-domain.use-case.test.ts - get-default-custom-domain.use-case.test.ts - get-setup-instructions.use-case.test.ts - list-custom-domains.use-case.test.ts - resolve-custom-domain.use-case.test.ts - set-default-custom-domain.use-case.test.ts - verify-custom-domain.use-case.test.ts QR-Code Share: - create-qr-code-share.use-case.test.ts - delete-qr-code-share.use-case.test.ts - update-qr-code-share.use-case.test.ts - get-public-shared-qr-code.use-case.test.ts User-Survey: - submit-survey.use-case.test.ts Agent-Logs-Url: https://github.com/FloB95/qrcodly/sessions/4f01b0a6-99d7-4164-9584-2e688a98bfeb Co-authored-by: FloB95 <[email protected]> * fix: correct 2 failing tests and add billing/analytics event handler & cron job tests Fix update-analytics-integration test (schema validation after clearing required field) and update-qr-code-share test (objDiff returns {oldValue, newValue} format). Add 8 new test files covering billing event handlers (subscription-active, subscription-canceled, subscription-cancel-initiated, subscription-past-due), billing cron jobs (stripe-reconciliation, cancellation-reminder, process-expired-grace-periods), and scan-tracking event handler. Coverage improved from 86.5% to 90.2% statements. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * style: apply prettier formatting to test files Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor(db): extract Drizzle table definitions to @qrcodly/db package Move all 11 module entity definitions out of apps/backend into a new packages/db workspace package so the schema can be reused by external admin tooling without dragging in the backend's runtime. Backend entity files become thin re-exports from @qrcodly/db; everything else stays the same. The package uses explicit named re-exports in src/index.ts (rather than `export * from`) so the CommonJS build target can be statically analyzed by Node's ESM↔CJS interop layer — otherwise consumers with "type": "module" can't see any of the named exports. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(build): build @qrcodly/db before backend/frontend, clean stale dist The backend:build / frontend:build scripts only built @shared/schemas before invoking the app build. With @qrcodly/db extracted as a separate workspace package, the backend's tsc step couldn't find its declarations in CI (`Cannot find module '@qrcodly/db' or its corresponding type declarations`), even though local incremental builds worked. * Add a build:db-package script and a combined build:shared-packages step that builds @shared/schemas first (since @qrcodly/db depends on it) and then @qrcodly/db. Use it from both backend:build and frontend:build. * Make both shared package build scripts `clean && tsc` so a stale tsbuildinfo can never produce a half-emitted dist (which is what caused the first local repro — index.d.ts was missing while the rest of dist looked up to date). * Add rimraf as a devDependency to @shared/schemas so the new pnpm run clean step actually has the binary available. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(ui): increase Select dropdown z-index to render above modals Select dropdowns used z-50 while Dialog overlays/content used z-[110], causing dropdowns to be hidden behind modals. Increased to z-[120]. Fixes #361, fixes #362, fixes #363 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add legal pages (AGB, AVV/DPA, Privacy Policy) with DE/EN i18n support - Add Terms of Service (AGB) at /terms-of-service with German and English content - Add Data Processing Agreement (AVV) at /dpa with TOMs, sub-processor list, and processing description - Rewrite Privacy Policy with comprehensive coverage of all services (Clerk, Stripe, Sentry, PostHog, Umami, Cloudflare, Axiom) - Add German translations for Imprint page - Move legal pages into [locale] routing for proper i18n support (DE + EN, other locales fall back to EN) - Add print-optimized CSS with A4 layout, page breaks, and professional typography for PDF export - Add PrintHeader and PrintButton components for legal pages - Update Footer with links to new legal pages (Terms of Service, DPA) - Update middleware and sitemap to include legal pages in i18n routing - Add legal.printButton translation key to all 9 dictionaries - Kleinunternehmerregelung (§19 UStG) reflected in AGB pricing section - Strengthened liability limitation with 12-month cap and free-tier as-is clause Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: remove dpa from website * docs: document commit and branch naming conventions Codify Conventional Commits and branch naming so all contributors (and AI tooling) follow the same git hygiene already visible in history. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * Bump release-drafter/release-drafter from 6 to 7 (#346) Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 6 to 7. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v6...v7) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @tailwindcss/vite from 4.1.18 to 4.2.1 (#347) Bumps [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) from 4.1.18 to 4.2.1. - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.1/packages/@tailwindcss-vite) --- updated-dependencies: - dependency-name: "@tailwindcss/vite" dependency-version: 4.2.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): Bump actions/github-script from 7 to 9 (#366) Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 9. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v9) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): Bump softprops/action-gh-release from 2 to 3 (#367) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump next-intl from 4.7.0 to 4.8.3 (#349) Bumps [next-intl](https://github.com/amannn/next-intl) from 4.7.0 to 4.8.3. - [Release notes](https://github.com/amannn/next-intl/releases) - [Changelog](https://github.com/amannn/next-intl/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/next-intl/compare/v4.7.0...v4.8.3) --- updated-dependencies: - dependency-name: next-intl dependency-version: 4.8.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump electron-store from 10.1.0 to 11.0.2 (#350) Bumps [electron-store](https://github.com/sindresorhus/electron-store) from 10.1.0 to 11.0.2. - [Release notes](https://github.com/sindresorhus/electron-store/releases) - [Commits](https://github.com/sindresorhus/electron-store/compare/v10.1.0...v11.0.2) --- updated-dependencies: - dependency-name: electron-store dependency-version: 11.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump electron from 35.7.5 to 41.0.2 (#351) Bumps [electron](https://github.com/electron/electron) from 35.7.5 to 41.0.2. - [Release notes](https://github.com/electron/electron/releases) - [Commits](https://github.com/electron/electron/compare/v35.7.5...v41.0.2) --- updated-dependencies: - dependency-name: electron dependency-version: 41.0.2 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(storage): serve assets via public CDN URL (Cloudflare R2 prep) (#368) * feat(storage): serve assets via public CDN URL instead of signed URLs Switch image previews and user uploads from on-demand S3 pre-signed URLs to stable public CDN URLs, in preparation for the migration from Scaleway S3 to Cloudflare R2 fronted by cdn.qrcodly.de / cdn-stage.qrcodly.de. - Add required S3_PUBLIC_URL env var and ObjectStorage.getPublicUrl() - Thread getPublicUrl() through BaseImageStrategy and ImageService - Replace getSignedUrl() calls in qr-code and config-template read paths - Configure local MinIO bucket as public-read for dev and CI - Whitelist cdn.qrcodly.de hostnames in Next.js remotePatterns, drop the legacy Scaleway hostname getSignedUrl() is kept on the storage layer for future private-asset flows. DB still stores raw keys, so no data migration is needed on the app side — only the underlying bucket contents need to be rclone'd. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(ci): build @qrcodly/db before lint/typecheck After the Drizzle tables were extracted into @qrcodly/db, the backend's inferred entity types (TAnalyticsIntegration, TUserSubscription, etc.) resolve to 'error'/'any' unless the db package has been built first. CI only built @shared/schemas, so every backend lint run on a fresh checkout flagged 37 @typescript-eslint/no-redundant-type-constituents errors that don't reproduce locally (where the dist is cached). Use the existing build:shared-packages root script which builds both @shared/schemas and @qrcodly/db in the correct order. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * perf(frontend): enable Next.js image optimization for QR previews Drop the unoptimized flag on QR preview Image components so Next.js serves AVIF/WebP derivatives at the displayed size (56px thumbnail and 200px preview) instead of the full-resolution SVG/PNG from the CDN. Originals often exceed 100KB, which is wasteful for a thumbnail. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * fix(storage): cache-bust preview filenames with a timestamp suffix (#369) After the CDN migration, updating a QR code or config template regenerated the preview but kept uploading it under the stable key qr-codes/images/previews/<userId>/<id>.svg. The public URL therefore never changed, so the Cloudflare edge cache (1y TTL) and browsers kept serving the old preview even though R2 already held the new one. Switch the preview filename to '<id>-<timestamp>.svg' so every regeneration produces a new key, a new public URL, and a clean cache miss. The existing update-use-case deletion path already removes the old preview before firing the update event, so this does not leave orphans beyond what was already being cleaned up. Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * fix(frontend): narrow react-simple-maps projection type union (#371) `string | any` collapses to `any` and trips @typescript-eslint/no-redundant-type-constituents, breaking `pnpm run check`. Narrow the projection prop to `string | ((...args: any[]) => unknown)` so the union stays meaningful while still covering the named-projection and custom-projection-function forms accepted by `react-simple-maps`. Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * fix(frontend): stabilize google maps loader across locale changes (#370) The @react-google-maps/api `useJsApiLoader` wraps a global singleton that throws "Loader must not be called again with different options" when re-initialized with different args. Passing the current app locale as `language` caused the loader to be re-invoked whenever the user switched locale client-side, crashing the location generator. Drop the `language` option so the loader args stay stable. Places results fall back to the browser language, which avoids the reload requirement and matches Google's own default behavior. Fixes QRCODLY-5D Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * feat(browser-extension): align look & feel with main app, add tests (#372) Brings the popup in line with the refreshed qrcodly.de visual language and removes the artificial 1500ms splash delay so the extension is ready as soon as Clerk + i18n have loaded. - Header uses the shared QrcodlyLogo SVG component and a translucent backdrop, matching the web app's header treatment - Sign-in prompt uses the shadcn Button and QrcodlyLogo for a consistent idle state - Initial loading state reuses the frontend TableLoader (dark gradient card with pulsing QR dots) instead of a plain spinner; the matching keyframes are mirrored into the extension stylesheet - Splash markup in index.html rewritten to show the same loader design, and the dynamically generated qr-grid splash.js is removed - Wire up Vitest + jsdom and cover i18n helpers with a smoke suite Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * Feat/indesign plugin (#375) * feat(frontend): integrations marketplace with tag filter and browser extension card Turns the dashboard integrations page into a typed catalog-driven marketplace. Analytics providers stay first-class (GA4, Matomo still connect/test/toggle/edit exactly as before) alongside two new kinds of entries: an external-link card for the Chrome extension pointing at the Web Store, and a coming-soon teaser for ChatGPT. - Static catalog at src/lib/integrations-catalog.ts drives what renders, typed as a discriminated union so new kinds (oauth, api-key, ...) slot in without touching the existing analytics module. - Components moved out of components/dashboard/analytics-integration/ into a single components/dashboard/integrations/ folder (flat, one kind per file). ProviderLogo collapsed into IntegrationLogo; logos now live as assets in public/images/integrations/ and are selected by integration id. - Tag filter bar for Analytics / Browser / AI. "More integrations on the way" teaser restored below the list. - Pro-plan badge shown inline on analytics cards for free users, linking to the billing page. - All nine locale dictionaries updated with the new catalog strings. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * formatting, refactoring * feat(integrations): InDesign UXP plugin + Pro-gated render API Adds an Adobe InDesign UXP panel that browses, creates, and places QR codes straight into layouts. Plugin lives under apps/integrations/indesign/ so future Figma/ChatGPT integrations can follow the same convention. Backend - New api-key module (apps/backend/src/modules/api-key) with thin wrappers around clerkClient.apiKeys; create is Pro-gated via request.user.plan. - New POST /api/v1/qr-code/render endpoint: takes {config, data, sizePx?, format?} and returns PNG/WebP/JPEG/SVG. Uses existing qr-code-styling pipeline → resvg-js → sharp (webp/jpeg). ETag + 304 so clients cache. - Dedicated QR_RENDER rate-limit policy (600/min auth, 1200/min Pro) keyed on user id. - Normalises url('#id') → url(#id) so Resvg resolves clip-paths instead of flooding black. - Integration tests cover Pro-gate, cross-user isolation, secret leakage, revoked-key invalidation, size/format validation, and Clerk metadata round-tripping. Frontend - API-keys dashboard now calls the backend endpoints (not Clerk client SDK), so the Pro-gate is enforced server-side — DevTools workaround closed. - Plans/Billing: free plan drops limitedApi, Pro plan drops unlimitedApi, analyticsIntegrations renamed to marketplaceIntegrations with the user-facing label "Third-party integrations". Feature rows render an info icon that deep-links to /docs/api or /dashboard/settings/integrations. - Integrations marketplace gains an Adobe InDesign entry with logo. - /content/api-index.mdx rewritten to document API keys as a Pro-only feature and point to the new dashboard settings location. - All 9 locale dictionaries updated. InDesign plugin (apps/integrations/indesign) - UXP v5 manifest targeting InDesign 18+ - Webpack build, React 18, flat screen router (Settings / List / Create) - Renders previews and placements via the backend render endpoint - API-key entered once, persisted via uxp.storage.secureStorage - Dynamic QR support with reserved short-code preview - Click-to-place normalises SVG dimensions to 30mm so InDesign places sensibly sized QRs. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: apply prettier formatting to plugin assets and lockfile Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(integrations): point plugin api-client at production base URL Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(integrations): set Adobe-assigned plugin id for indesign Ties the manifest id to the listing created in Adobe's Distribution console so uploads are accepted. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(integrations): bump indesign host minVersion to 18.5.0 Adobe Distribution rejects uploads with a partial minVersion (18.0); moving to a fully qualified, UXP-capable version fixes the HOSTAPP_VERSION_INVALID error during package validation. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(browser-extension): upgrade @clerk/chrome-extension to MV3-compliant 2.9.14 Chrome Web Store rejected v1.0.0 because the bundled JS contained externally hosted code references (Cloudflare Turnstile, Google GSI), which violates Manifest V3. Clerk fixed this in @clerk/chrome-extension 2.0+ via the no-RHC build (PR clerk/javascript#4551, #4899). - bump @clerk/chrome-extension 1.3.14 -> 2.9.14 - replace removed `syncSessionWithTab` with `syncHost` - bump extension version 1.0.0 -> 1.0.1 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(api-key,qr-code,integrations): security hardening & test fixes - api-key: revoke via backend from ui (previously called clerk directly, bypassing plan + rate-limit guards) - api-key: align description max length with clerk's 255-char limit - api-key: distinguish clerk 4xx (→ 404) from 5xx/network errors on revoke instead of masking every failure as not-found - api-key: scope list/revoke tanstack-query cache key by userId - qr-code/render: drop http fetch branch — only data: urls and s3 storage paths accepted, eliminating ssrf surface from user-provided config.image - qr-code/render: fix request generic so request.user is typed as required - qr-code/render: lint fix on if-none-match header access - fastify helpers: expose request.headers through request parser (prototype getter was lost by object spread, breaking if-none-match reads) - indesign plugin: gate webpack devtool by mode — no more source maps in production bundles shipped to adobe - indesign plugin: remove localhost:5001 from manifest network allowlist - indesign plugin: use TextEncoder/TextDecoder for secureStorage so api keys with non-ascii characters round-trip correctly - indesign plugin: await placement and surface errors in CreateScreen instead of fire-and-forget that swallowed failures - indesign plugin: drop unused fetchRemoteSvg helper - frontend integrations: switch chrome extension entry to coming-soon until the store id is assigned (was pointing at a placeholder url) - tests: seed a pro subscription for TEST_USER_PRO_ID in api-key tests and hard-delete (not revoke) clerk keys on setup/teardown so repeated runs don't hit clerk's token-creation-conflict (409) on duplicate names * chore: apply prettier formatting to browser-extension splash and lockfile --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * fix(qr-code): resolve own cdn urls back to storage keys in render GET endpoints rewrite config.image from a storage key to a public cdn url via imageService.getPublicUrl. The render use case previously only accepted data: urls and raw storage keys, so any client that fed the same config back (the indesign plugin hits render right after listing) lost the center logo entirely. resolveImage now strips our own S3_PUBLIC_URL prefix and re-fetches the image from storage. Foreign http/https urls remain blocked — the ssrf surface stays closed. * fix(integrations/indesign): runtime robustness and svg placement - resolve indesign dom via module-scope require (webpack external) with globalThis.require fallback; previous call only checked globalThis which is not reliably populated in UXP, causing placements to fail with "InDesign DOM not available" - surface the actual reason when require("indesign") fails so future breakage is diagnosable from the thrown message - place qr codes as svg (vector, print-ready) instead of png raster; preview thumbnails still use png since UXP cannot render complex svg reliably at small sizes - replace FileReader-based blobToDataUrl with arrayBuffer + btoa — UXP ships no FileReader and preview rendering failed with "FileReader is not defined" - hand-roll utf-8 encode/decode in secureStorage helpers so api-key storage works on UXP builds without TextEncoder/TextDecoder - catch getStoredApiKey() failures in App so the panel never gets stuck on the loading screen; surface the error in the settings form - guard localStorage fallback behind typeof check — UXP has no localStorage and the previous fallback crashed at module evaluation * optimize indesign ratelimit * optimize indesign ratelimit * fix(integrations/indesign): add plugin icons for Adobe review compliance Adobe rejected the plugin due to missing custom icons showing default icons in the plugin panel. Add theme-aware icons for both panel toolbar (23x23) and plugin list (48x48) with proper @1x/@2x scale variants and species configuration per Adobe UXP manifest v5 spec. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(frontend): promote InDesign integration from coming-soon to active Link to published Adobe Exchange plugin, update description to mention Pro plan and API key requirement, and add localized CTA buttons across all 9 locales. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(frontend): add API key required badge to InDesign integration Show a teal "API Key Required" badge linking to the API keys settings page, matching the existing Pro badge pattern from analytics cards. Remove redundant requirement text from descriptions. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(frontend): hide domain edit icon on QR code view and edit pages Changing the custom domain after creation would invalidate the QR code, so the pencil icon linking to domain settings is now hidden on detail and edit pages while remaining available in the generator for new codes. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(frontend): update privacy policy with new DPO-provided content Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(frontend): make QR code and template preview images clickable Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore(frontend): remove print button from legal pages Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(frontend): update i18n wording for privacy policy and InDesign CTA Rename footer link from "Datenschutzrichtlinie" to "Datenschutzerklärung" (DE) and change InDesign plugin CTA from "get" to "install" across all 9 locales. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(backend): add UUID suffix to uploaded image filenames to bust CDN cache When updating a QR code or template logo, the new image was uploaded to the same S3 path as the old one (entityId.ext). Cloudflare CDN cached the old image at that URL, causing stale logos to appear in production even though S3 already contained the new file. Adding a random UUID to the filename ensures each upload gets a unique path and CDN serves fresh content. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat(qr-code): expand vCard bulk import with title, note and multi-line support Add `title` and `note` columns to the vCard bulk-import column map and to all 8 localised CSV templates so every vCard form field has a CSV counterpart. Encode newlines per RFC 6350 §3.4 before passing NOTE to the vcf library (which silently strips real LF/CR otherwise) and replace the line-based frontend CSV parser with a quote-aware state machine so multi-line note cells survive the round trip. Tests: a new schema-coverage test asserts each Input-Schema field is present in the bulk-import column map; csv-template-validation now reads the expected columns from the use-case directly so the two sources can no longer drift. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * chore(billing): lower Pro plan bulk import file size limit to 1 MB 50 vCard rows comfortably fit under 100 KB even with multi-line notes, so 2 MB was an unnecessarily generous safety cap. 1 MB still leaves plenty of headroom and brings the policy closer to actual upload sizes. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * chore(billing): remove promo pricing, update to new Pro prices (#381) Remove launch promo logic (PROMO_END, strikethrough prices, "Offer ends" badges). Pro plan now shows regular pricing: 8,99 €/month and 6,99 €/month (annual). Add legacy Stripe price ID env vars so existing subscribers on old prices still see correct billing interval and amount. Update Google Ads conversion tracking values. Add "Custom short URL paths (coming soon)" as new Pro feature across all 9 locales. Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * Feat/duplicate entities (#380) * feat: add duplicate functionality for QR codes, templates and short URLs Add POST /:id/duplicate endpoints for all three entity types with full S3 image copying, tag preservation and short URL generation for dynamic QR codes. Includes frontend action menu buttons with i18n (9 locales), PostHog + Sentry tracking, and integration tests. Also increases entity name max length from 32 to 50 characters across schemas, DB tables and frontend inputs. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(frontend): reorder action menu — move save-as-template before toggle Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(backend): make image cleanup best-effort in duplicate use cases Wrap deleteImage calls in catch blocks with try/catch so cleanup failures don't mask the original duplication error. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor(backend): harden duplicate use cases — UoW, smart copy-name, image-folder tests - Wrap DuplicateShortUrlUseCase writes in UnitOfWork to prevent orphan rows if tag assignment fails after short URL creation. - Wrap DuplicateConfigTemplateUseCase errors in UnhandledServerError / CustomApiError consistent with DuplicateQrCodeUseCase. - Log silent S3 cleanup failures instead of swallowing them. - Extract shared buildCopyName utility with smart "(Copy)"/"(Copy 2)" counter — replaces three duplicated implementations and stops prefix stacking on repeated duplications. - Fix base-image.strategy copyImage extension extraction edge case for paths without a dot. - Add integration tests verifying duplicated images land in the correct S3 folder per entity type, plus tag carry-over and counter behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * refactor(backend): move event/log/metric side effects out of duplicate transactions Events, logger.info, and metric increments previously ran inside the UnitOfWork callback — if the commit failed afterwards, observers and metrics would report a creation that rolled back. Side effects now run only after the transaction returns successfully. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * fix(backend): isolate cleanup catch and guard post-commit telemetry The image-cleanup catch in DuplicateQrCode/DuplicateConfigTemplate also wrapped the post-commit event/log/metric calls. A throw from those would delete the just-copied image even though the DB row had committed, leaving the duplicated entity pointing to a missing image. Move post-commit side effects out of the cleanup try block and wrap telemetry in its own swallowing try/catch so a logging or metric failure cannot turn a successful duplication into a 5xx response (which would trigger client retries and create real duplicates). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]> * feat(vcard): split vCard address into private and business vCards now carry two distinct addresses (streetPrivate/cityPrivate/... and streetBusiness/cityBusiness/...) that serialize as ADR;TYPE=HOME and ADR;TYPE=WORK in the generated VCF, so contact apps file them under "Home" and "Work" instead of a single untyped block. The Zod schema, VCF serializer, bulk-import column maps (frontend + backend) and the localized CSV templates (8 locales) are extended together. The vCard form renders both blocks as cards with Home / Office icons and a single-column layout on mobile. Detail and hover views distinguish the two addresses by icon as well. Translations added for all 9 locales. Existing data is migrated once via SQL migration 0030_split-vcard-addresses.sql: legacy street/city/zip/state/country keys are copied into their *Private counterparts and then dropped from the JSON content column. After deploy there are no more legacy fields and no compat shims in code. Note: qr_code_data on existing static vCards still contains the old untyped ADR string; it gets refreshed on the next user edit. A backfill script will follow if we want immediate consistency. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * chore(frontend): switch PostHog ingest host and pin defaults Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * feat(integrations/indesign): release v0.2.0 - Switch SVG→PNG placement (4096px, DPI-tagged) for reliable colors across all InDesign builds - Auto-size to active page format (~18% of shorter side, clamped 20–200mm) - Fix template image lost when creating from a predefined template (sent both templateId and config; resolveConfig spread overwrote the raw image storage key with a public URL the render endpoint then rejected as external) - Webpack DefinePlugin for API URL: dev=localhost:5001, prod=live - React 19 performance.measure shim for UXP runtime - Backend: optional printSizeMm on /qr-code/render embeds DPI metadata, bump sizePx max from 2048 to 4096 - Drop ~600 LOC dead SVG/sanitize/flatten code, qr-code-styling client-side dependency, and XMLSerializer polyfill - Add ESLint config inheriting workspace base Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * chore: reformat pnpm-lock.yaml to repo-standard prettier output Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * feat(api-key): add scopes (read/write/update/delete) and token-type r… (#383) * feat(api-key): add scopes (read/write/update/delete) and token-type restriction Scopes are selected per key on creation and editable afterwards. Default scope per HTTP method (GET=read, POST=write, PATCH/PUT=update, DELETE=delete) with per-route override via decorator config. Session-token requests bypass scope checks; only api_key tokens are enforced. Token-type restriction added so security-critical routes can opt out of api-key access entirely. Hidden routes (schema.hide=true) are now session- only by default rule, and api-key management routes are explicitly session- only — no key can mint, list, update, or revoke other keys. Frontend: checkbox group on create + edit dialogs; scope badges in list view (single "Full access" badge when all four scopes are granted, including legacy keys with empty scopes); i18n in all 9 locales. Tests: middleware unit tests, default-method-mapping unit test, integration tests for scope rejection, token-type rejection, and update flow on real Clerk-issued keys, plus scope-coverage matrix that introspects every registered route and asserts correct scope and token-type configuration. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * chore: trim verbose comments on api-key scope feature Function-level JSDoc kept short on enforce-scope, enforce-token-type, filterKnownScopes, resolveScopeForMethod. Property comments on the route decorator config collapsed to one line each. Inline narrative comments in helpers and tests reduced to one line. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * fix(api-key): handle Clerk errors on update + sanitize PostHog payloads CodeRabbit feedback on PR #383: - Wrap apiKeys.update() in the same try-catch as apiKeys.get(), so 4xx Clerk responses surface as ApiKeyNotFoundError instead of opaque 500. - Strip raw error objects from posthog.capture() calls in create/edit/ revoke flows; send only sanitized name + message + ids. Skipped: CodeRabbit's suggestion to extract UpdateApiKeyDto into a dedicated schema module — local convention in dtos/api-key/ already keeps small DTOs flat. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]> * perf(qr-code): cache rendered buffers in Redis + skip use case on If-None-Match Triple optimization on /qr-code/render — the hot path used by the InDesign plugin for high-res PNG renders (~420ms CPU per call before this). 1. Controller short-circuits on If-None-Match by computing the etag from the request body alone, returning 304 without invoking the use case at all. 2. Use case checks Redis (qr_render:<etag>) before rendering and writes the resulting buffer back with a 24h TTL. Identical render inputs across users now serve from cache in ~8ms instead of re-rendering. 3. Render path wrapped in a 5s timeout so a pathological input cannot pin a worker indefinitely. Cache key is content-derived (SHA1 of {config, data, format, sizePx, printSizeMm}) — any visual change produces a new key, the old one expires naturally. No explicit invalidation on update/delete needed. KeyCache gains getBuffer() since ioredis's default get() coerces to string, which corrupts binary payloads. Verified with 16 passing tests including spies on the use case prototype that prove cache hits skip the render path and matching ETags skip the use case entirely. Co-Authored-By: Claude Opus 4.7 (1M conte…
Description
Removes remotely hosted code (RHC) for Browser Extensions in the following fashion:
clerk-jsRemoves RHC via
__BUILD_DISABLE_RHC__and creates a new build under@clerk/clerk-js/no-rhc.clerk-reactIntroduces
__BUILD_DISABLE_RHC__into theclerk-reactbuild which, if unset, defaults tofalse.clerk-reactis bundled intochrome-extensionand in that process__BUILD_DISABLE_RHC__is set totruewhich ultimately removes the RHC.Checklist
npm testruns as expected.npm run buildruns as expected.Type of change