From aaca64cbab60e5c83c4153c3f33853245dd56e0f Mon Sep 17 00:00:00 2001 From: Juraj Uhlar Date: Thu, 23 Apr 2026 11:50:42 +0100 Subject: [PATCH 1/2] fix: align scopes.yaml with v4 schema paths The SDK sync action (dx-team-toolkit/update-sdk-schema) filters the downloaded schema by allowed scopes using scopes.yaml. Paths still listed v3 names (/events/{request_id}, /events/search) while the released fingerprint-server-api-v4.yaml uses /events/{event_id} and /events, so the filter deleted every events path and left only /visitors/{visitor_id}. All 5 open SDK sync PRs for v3.2.0 (node #239, go #46, python #188, java #20, php #211) are truncated identically; node-sdk's generated types collapsed from 1421 lines to 3. Updates events/events-search/visitors to the v4 paths and methods (get+patch on /events/{event_id}, get on /events, delete on /visitors/{visitor_id}). webhook and related-visitors scopes keep their names and now-inert paths - they don't exist in the v4 schema but the filter ignores unmatched scopes, and removing them would just produce noise in the filter logs and break scope names referenced by scripts/changeset.js. --- config/scopes.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config/scopes.yaml b/config/scopes.yaml index 09a4392f..c40dd1d7 100644 --- a/config/scopes.yaml +++ b/config/scopes.yaml @@ -1,21 +1,20 @@ events: - path: /events/{request_id} + path: /events/{event_id} methods: - get - - put + - patch events-search: - path: /events/search + path: /events methods: - get visitors: path: /visitors/{visitor_id} methods: - - get - delete webhook: path: /webhook methods: - - trace + - post related-visitors: path: /related-visitors methods: From 5398fef0e26ebcfb9be333a907709e3f09b64aa9 Mon Sep 17 00:00:00 2001 From: Juraj Uhlar Date: Thu, 23 Apr 2026 12:01:00 +0100 Subject: [PATCH 2/2] feat: enhance changeset linting to validate scopes against config Added functionality to the lintChangesets script to check for valid scopes defined in config/scopes.yaml. The script now flags changesets with invalid scopes and accepts changesets without a scope line. Updated tests to cover these new validations. --- scripts/lintChangesets.js | 14 ++++++++++++++ scripts/lintChangesets.spec.js | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/scripts/lintChangesets.js b/scripts/lintChangesets.js index 5ba51c61..992220e0 100644 --- a/scripts/lintChangesets.js +++ b/scripts/lintChangesets.js @@ -1,7 +1,12 @@ import fs from 'node:fs'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import yaml from 'js-yaml'; const DIR = process.argv[2] ?? '.changeset'; +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const SCOPES_PATH = path.resolve(scriptDir, '../config/scopes.yaml'); +const validScopes = new Set(Object.keys(yaml.load(fs.readFileSync(SCOPES_PATH, 'utf8')))); const files = fs.readdirSync(DIR).filter((f) => f.endsWith('.md') && f !== 'README.md'); @@ -26,6 +31,15 @@ for (const file of files) { continue; } + const scopeMatch = body.match(/^\*\*([^*]+)\*\*:\s*/); + if (scopeMatch) { + const scope = scopeMatch[1]; + if (!validScopes.has(scope)) { + const allowed = [...validScopes].sort().join(', '); + issues.push(`${full}: invalid scope "${scope}" — use one of: ${allowed} (or omit the scope line)`); + } + } + const withoutScope = body.replace(/^\*\*[^*]+\*\*:\s*/, ''); const firstChar = withoutScope[0]; diff --git a/scripts/lintChangesets.spec.js b/scripts/lintChangesets.spec.js index ec6906af..355c6578 100644 --- a/scripts/lintChangesets.spec.js +++ b/scripts/lintChangesets.spec.js @@ -55,4 +55,26 @@ describe('lintChangesets', () => { expect(status).toBe(1); expect(stderr).toMatch(/should end with a period/); }); + + it('flags a scope that is not in config/scopes.yaml', () => { + const { status, stderr } = run({ + 'bad.md': `${frontmatter}**unknown-scope**: Add \`x\` to \`Event\`\n`, + }); + expect(status).toBe(1); + expect(stderr).toMatch(/invalid scope "unknown-scope"/); + }); + + it('accepts a hyphenated scope from config/scopes.yaml', () => { + const { status } = run({ + 'good.md': `${frontmatter}**events-search**: Add \`q\` query parameter to search\n`, + }); + expect(status).toBe(0); + }); + + it('accepts a changeset with no scope line', () => { + const { status } = run({ + 'good.md': `${frontmatter}Add \`x\` to \`Event\`\n`, + }); + expect(status).toBe(0); + }); });