Files
openclaude/scripts/validate-externals.ts
2c71e09394 chore(build): clean up external dependency validation warnings (#1124)
* chore(build): clean up external dependency validation warnings

Remove 2 unused externals (@opentelemetry/sdk-trace-node, ink) and add
12 missing packages to package.json that are dynamically imported at
runtime but weren't declared as dependencies. Also remove the unused
@opentelemetry/sdk-trace-node dependency.

This eliminates all 13 build validation warnings:
- 8 missing OTel exporter deps (http, proto, grpc variants + prometheus)
- 4 missing AWS SDK deps (bedrock, bedrock-runtime, sts, credential-providers)
- 1 missing Azure dep (@azure/identity)
- ink external pointed to local reimplementation, not npm package
- sdk-trace-node was declared external but never imported

Build validation now passes cleanly with 0 warnings.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* chore(build): eliminate external validation warnings

Remove unused @opentelemetry/sdk-trace-node from externals and package.json
(it's not imported anywhere in src/). Remove ink from SDK_ONLY_EXTERNALS
(the project reimplements ink locally at src/ink/). Add OPTIONAL_RUNTIME_EXTERNALS
list for packages that are dynamically imported but intentionally not direct
deps — OTel protocol exporters and cloud provider SDKs are resolved from
transitive deps or installed by users who need them.

Validation now passes with 0 warnings instead of 13.

Co-Authored-By: OpenClaude <openclaude@gitlawb.com>

* feat(telemetry): full OpenTelemetry purge — remove all tracking dependencies

Replace all @opentelemetry/* runtime dependencies with no-op stubs,
delete OTel-only source modules, and remove 10 @opentelemetry packages
from package.json plus @growthbook/growthbook.

Key changes:
- Delete 5 OTel-only modules (instrumentation, betaSessionTracing,
  bigqueryExporter, logger, firstPartyEventLoggingExporter)
- Replace 9 modules with no-op stubs (sessionTracing, events,
  telemetryAttributes, firstPartyEventLogger, growthbook, index,
  sink, datadog, sinkKillswitch, perfettoTracing)
- Remove all @opentelemetry/* imports from bootstrap/state.ts,
  entrypoints/init.ts, and ~20 caller files
- Remove all OTel counter types, meter/provider state from state.ts
- Clean externals.ts: remove 27 @opentelemetry/* entries
- Clean build.ts: remove OTel native-stub namespace exports
- Simplify no-telemetry-plugin.ts: remove redundant source-level stubs
- Remove 10 @opentelemetry/* + @growthbook/growthbook from package.json
- GrowthBook stub reads local ~/.claude/feature-flags.json for overrides

Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com>

* fix format

* chore: regenerate lockfile after OTel dependency removal

Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com>

* fix(growthbook): route gate helpers through local flag overrides

checkStatsigFeatureGate_CACHED_MAY_BE_STALE() and
checkGate_CACHED_OR_BLOCKING() now resolve from
~/.claude/feature-flags.json like getFeatureValue_* does,
so gates like tengu_thinkback, tengu_ccr_bridge, and
VS Code upsells can be flipped on locally. Security gates
(checkSecurityRestrictionGate) remain hard-false.

Also adds 5 tests covering gate helper override behavior
and unifies JSDoc wording for _getFlagValue-routed functions.

Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com>

---------

Co-authored-by: OpenClaude <openclaude@gitlawb.com>
2026-05-13 17:07:45 +08:00

104 lines
3.7 KiB
TypeScript

/**
* Validates that all package.json dependencies are accounted for
* in the external lists or explicitly marked as intentionally bundled.
*
* Run as part of the build to catch missing externals early.
*/
import { readFileSync } from 'fs'
import { CLI_EXTERNALS, SDK_EXTERNALS, INTENTIONALLY_BUNDLED, OPTIONAL_RUNTIME_EXTERNALS } from './externals.js'
const pkg = JSON.parse(readFileSync('package.json', 'utf8'))
const allDeps = new Set([
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
])
function validate(bundleName: string, externals: string[]): boolean {
const externalSet = new Set(externals)
const intentionallyBundledSet = new Set(INTENTIONALLY_BUNDLED)
const missing = [...allDeps].filter(
d => !externalSet.has(d) && !intentionallyBundledSet.has(d),
)
if (missing.length > 0) {
console.error(`❌ ${bundleName}: Dependencies missing from externals:`)
for (const dep of missing) {
console.error(` - ${dep}`)
}
console.error(
`\n Either add them to scripts/externals.ts or to INTENTIONALLY_BUNDLED.`,
)
return false
}
const optionalSet = new Set(OPTIONAL_RUNTIME_EXTERNALS)
const extra = [...externalSet].filter(d => !allDeps.has(d) && !optionalSet.has(d))
if (extra.length > 0) {
console.warn(`⚠️ ${bundleName}: External entries not in package.json (may be ok):`)
for (const dep of extra) {
console.warn(` - ${dep}`)
}
}
console.log(`✓ ${bundleName}: All dependencies accounted for (${missing.length} missing, ${externalSet.size} external)`)
return true
}
const cliOk = validate('CLI bundle', CLI_EXTERNALS)
const sdkOk = validate('SDK bundle', SDK_EXTERNALS)
if (!cliOk || !sdkOk) {
console.error(`\n❌ External list validation failed. Fix scripts/externals.ts before committing.`)
process.exit(1)
}
console.log('\n✓ All external lists valid.')
// ============================================================================
// Validate sdk.d.ts ↔ index.ts export drift
// ============================================================================
const SDK_DTS_PATH = 'src/entrypoints/sdk.d.ts'
const SDK_INDEX_PATH = 'src/entrypoints/sdk/index.ts'
function extractExportNames(filePath: string): Set<string> {
const content = readFileSync(filePath, 'utf8')
const names = new Set<string>()
// Match: export { name1, name2 } / export type { name1 } / export class/function/interface/const/type Name
for (const match of content.matchAll(/export\s+(?:type\s+)?\{([^}]+)\}/g)) {
for (const name of match[1].split(',')) {
const trimmed = name.trim().split(/\s+as\s+/)[0].trim()
if (trimmed) names.add(trimmed)
}
}
for (const match of content.matchAll(
/export\s+(?:type\s+)?(?:class|function|interface|const|type)\s+(\w+)/g,
)) {
names.add(match[1])
}
return names
}
const dtsExports = extractExportNames(SDK_DTS_PATH)
const indexExports = extractExportNames(SDK_INDEX_PATH)
const inDtsNotIndex = [...dtsExports].filter(n => !indexExports.has(n))
const inIndexNotDts = [...indexExports].filter(n => !dtsExports.has(n))
if (inDtsNotIndex.length > 0 || inIndexNotDts.length > 0) {
console.error(`\n❌ SDK type declaration drift detected:`)
if (inDtsNotIndex.length > 0) {
console.error(` In sdk.d.ts but not in index.ts:`)
for (const name of inDtsNotIndex) console.error(` - ${name}`)
}
if (inIndexNotDts.length > 0) {
console.error(` In index.ts but not in sdk.d.ts:`)
for (const name of inIndexNotDts) console.error(` - ${name}`)
}
console.error(`\n Keep sdk.d.ts in sync with src/entrypoints/sdk/index.ts.`)
process.exit(1)
}
console.log(`✓ SDK type declarations in sync (${dtsExports.size} exports match).`)