@@ -29,6 +29,57 @@ import {remove} from 'unist-util-remove';
2929
3030import { rehypeExpandCodeTabs } from './rehype-expand-code-tabs.mjs' ;
3131
32+ // Default values for code keyword placeholders (e.g. ___PUBLIC_DSN___) that are
33+ // normally replaced client-side by codeKeywords.tsx. These must stay in sync with
34+ // DEFAULTS.PROJECT[0] in src/components/codeContext.tsx.
35+ // Values must be clearly non-functional so agents and LLMs know they need
36+ // to be replaced with real project-specific values. Avoid realistic-looking
37+ // values (e.g. valid DSN shapes) — agents will use them as-is.
38+ const MD_KEYWORD_DEFAULTS = {
39+ DSN : 'https://<key>@<org>.ingest.sentry.io/<project>' ,
40+ PUBLIC_DSN : 'https://<key>@<org>.ingest.sentry.io/<project>' ,
41+ PUBLIC_KEY : '<your-public-key>' ,
42+ SECRET_KEY : '<your-secret-key>' ,
43+ API_URL : 'https://sentry.io/api' ,
44+ PROJECT_ID : '<your-project-id>' ,
45+ PROJECT_SLUG : '<your-project-slug>' ,
46+ ORG_ID : '<your-org-id>' ,
47+ ORG_SLUG : '<your-org-slug>' ,
48+ ORG_INGEST_DOMAIN : '<org>.ingest.sentry.io' ,
49+ JS_SDK_LOADER_HOST : 'js.sentry-cdn.com' ,
50+ MINIDUMP_URL :
51+ 'https://<org>.ingest.sentry.io/api/<project>/minidump/?sentry_key=<key>' ,
52+ UNREAL_URL : 'https://<org>.ingest.sentry.io/api/<project>/unreal/<key>/' ,
53+ OTLP_URL : 'https://<org>.ingest.sentry.io/api/<project>/integration/otlp' ,
54+ OTLP_TRACES_URL :
55+ 'https://<org>.ingest.sentry.io/api/<project>/integration/otlp/v1/traces' ,
56+ OTLP_LOGS_URL :
57+ 'https://<org>.ingest.sentry.io/api/<project>/integration/otlp/v1/logs' ,
58+ VERCEL_LOG_DRAIN_URL :
59+ 'https://<org>.ingest.sentry.io/api/<project>/integration/vercel/logs/' ,
60+ } ;
61+
62+ /**
63+ * Replaces ___KEYWORD___ placeholders in generated markdown with clearly non-functional
64+ * placeholder values (e.g. <your-org-slug>) so agents and LLMs know they must be
65+ * substituted with real project-specific values before use.
66+ *
67+ * These tokens are replaced client-side by codeKeywords.tsx at runtime; the SSR HTML
68+ * (and therefore the generated markdown) contains them verbatim. Using obviously-fake
69+ * values — rather than realistic-looking defaults — prevents agents from silently
70+ * treating them as valid configuration.
71+ *
72+ * Applied after HTML→markdown conversion, outside the cache, so existing cache entries
73+ * remain valid.
74+ */
75+ function applyKeywordDefaults ( markdown ) {
76+ let result = markdown ;
77+ for ( const [ key , value ] of Object . entries ( MD_KEYWORD_DEFAULTS ) ) {
78+ result = result . replaceAll ( `___${ key } ___` , value ) ;
79+ }
80+ return result ;
81+ }
82+
3283const DOCS_ORIGIN = process . env . NEXT_PUBLIC_DEVELOPER_DOCS
3384 ? 'https://develop.sentry.dev'
3485 : 'https://docs.sentry.io' ;
@@ -1099,10 +1150,12 @@ async function processTaskList({id, tasks, cacheDir, noCache, usedCacheFiles}) {
10991150 cacheMisses . push ( relativePath ) ;
11001151 }
11011152
1102- // Keep metadata outside the cache so description-only changes don't invalidate
1103- // the expensive HTML → markdown conversion output.
1104- // Prepend YAML frontmatter and write to target
1105- const output = frontmatter ? formatYamlFrontmatter ( frontmatter ) + data : data ;
1153+ // Keep these transformations outside the cache so they don't invalidate the
1154+ // expensive HTML → markdown conversion output:
1155+ // - applyKeywordDefaults: replaces ___KEYWORD___ placeholders with defaults
1156+ // - formatYamlFrontmatter: prepends YAML metadata
1157+ const resolved = applyKeywordDefaults ( data ) ;
1158+ const output = frontmatter ? formatYamlFrontmatter ( frontmatter ) + resolved : resolved ;
11061159 await writeFile ( targetPath , output , { encoding : 'utf8' } ) ;
11071160
11081161 if ( r2Hash !== null && s3Client ) {
0 commit comments