package-dependency-policy.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /** Explicit exceptions and Host packages for the published dependency policy. */
  2. /** Packages treated as Client/Host packages without declaring `dsh.client`. */
  3. const CLIENT_FACE_INCLUDE: readonly string[] = []
  4. /** Packages exempted from automatic Client/Host treatment despite declaring `dsh.client`. */
  5. const CLIENT_FACE_EXCLUDE: readonly string[] = [
  6. '@deepseek-ai/dsh-api-session-controller',
  7. '@deepseek-ai/dsh-api-workspace-controller',
  8. ]
  9. /** Host-only packages whose peer relays are deliberately flattened. */
  10. const HOST_DEPENDENCY_PACKAGES: readonly string[] = [
  11. '@deepseek-ai/dsh-llm',
  12. '@deepseek-ai/dsh-session',
  13. ]
  14. /** Development-only package relationships not represented by source imports. */
  15. const CONFIGURATION_ONLY_DEV_DEPENDENCIES = {
  16. '@deepseek-ai/dsh-client-locale': ['@deepseek-ai/dsh-api-remotes'],
  17. '@deepseek-ai/dsh-client-ui-conversation': [
  18. '@deepseek-ai/dsh-api-remotes',
  19. '@deepseek-ai/dsh-client-ui-workspace',
  20. ],
  21. '@deepseek-ai/dsh-client-ui-model-selection': ['@deepseek-ai/dsh-client-ui-input-trigger'],
  22. '@deepseek-ai/dsh-client-ui-sidebar': ['@deepseek-ai/dsh-client-ui-workspace'],
  23. '@deepseek-ai/dsh-client-ui-subagent': ['@deepseek-ai/dsh-client-ui-input-trigger'],
  24. '@deepseek-ai/dsh-client-ui-theme': ['@deepseek-ai/dsh-api-remotes'],
  25. '@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'],
  26. } as const satisfies Readonly<Record<string, readonly string[]>>
  27. /** Workspace packages whose complete runtime surface is safe across duplicate installations. */
  28. const DUPLICATE_SAFE_PACKAGES: readonly string[] = [
  29. '@deepseek-ai/dsh-brand',
  30. '@deepseek-ai/dsh-lazy-require',
  31. '@deepseek-ai/dsh-typert-protocol',
  32. '@deepseek-ai/dsh-util-crypto',
  33. '@deepseek-ai/dsh-util-values',
  34. ]
  35. /**
  36. * Runtime exports whose values remain valid when npm installs another package copy.
  37. * New entries are forbidden by default. Automated agents must not add an
  38. * exception; every addition requires explicit human review and a dedicated,
  39. * prominent heading in the pull request description.
  40. */
  41. const SAFE_HOST_DEPENDENCY_EXPORTS = {
  42. '@deepseek-ai/dsh-credentials': ['credentialKey'],
  43. '@deepseek-ai/dsh-deque': ['Deque'],
  44. '@deepseek-ai/dsh-llm': ['callConfigEquals'],
  45. '@deepseek-ai/dsh-session-format': ['sessionFormatLogFilename'],
  46. '@deepseek-ai/dsh-timeout': ['MAX_TIMER_DELAY_MS'],
  47. '@deepseek-ai/schemastery': ['default'],
  48. } as const satisfies HostDependencyExports
  49. /** Runtime exports that require every consumer to resolve the provider's shared peer instance. */
  50. const PEER_REQUIRED_HOST_EXPORTS = {
  51. '@deepseek-ai/dsh-subprocess': ['SubprocessExecutableNotFoundError'],
  52. '@deepseek-ai/dsh-scope': ['carrierKeyOf', 'scopeOf', 'scopeTarget'],
  53. '@deepseek-ai/dsh-session': ['SESSION_FORMAT_VERSION'],
  54. '@deepseek-ai/dsh-session-persistence': ['SessionPersistenceNotFoundError'],
  55. } as const satisfies HostDependencyExports
  56. /** Exact import specifier to reviewed runtime exports. */
  57. type HostDependencyExports = Readonly<Record<string, readonly string[]>>
  58. /** Complete configurable input to package dependency classification. */
  59. export interface PackageDependencyPolicy {
  60. readonly clientFaceInclude: readonly string[]
  61. readonly clientFaceExclude: readonly string[]
  62. readonly hostPackages: readonly string[]
  63. readonly configurationOnlyDevDependencies: Readonly<Record<string, readonly string[]>>
  64. readonly duplicateSafePackages?: readonly string[]
  65. readonly safeHostDependencyExports: HostDependencyExports
  66. readonly peerRequiredHostExports: HostDependencyExports
  67. }
  68. /** Repository dependency policy consumed by verification and benchmarking. */
  69. export const PACKAGE_DEPENDENCY_POLICY: PackageDependencyPolicy = {
  70. clientFaceInclude: CLIENT_FACE_INCLUDE,
  71. clientFaceExclude: CLIENT_FACE_EXCLUDE,
  72. hostPackages: HOST_DEPENDENCY_PACKAGES,
  73. configurationOnlyDevDependencies: CONFIGURATION_ONLY_DEV_DEPENDENCIES,
  74. duplicateSafePackages: DUPLICATE_SAFE_PACKAGES,
  75. safeHostDependencyExports: SAFE_HOST_DEPENDENCY_EXPORTS,
  76. peerRequiredHostExports: PEER_REQUIRED_HOST_EXPORTS,
  77. }
  78. function isRecord(value: unknown): value is Record<string, unknown> {
  79. return typeof value === 'object' && value !== null && !Array.isArray(value)
  80. }
  81. /** Whether a package manifest declares a dynamically loaded Client entry. */
  82. export function hasClientDeclaration(dshField: unknown): boolean {
  83. return isRecord(dshField) && Object.hasOwn(dshField, 'client')
  84. }
  85. /** Whether the repository policy flattens one package's non-Cordis peers. */
  86. export function usesFlattenedPackageDependencies(
  87. manifestPath: string,
  88. packageName: string,
  89. dshField: unknown,
  90. policy: PackageDependencyPolicy = PACKAGE_DEPENDENCY_POLICY,
  91. ): boolean {
  92. if (!manifestPath.startsWith('packages/') || manifestPath.startsWith('packages/experimental/')) return false
  93. if (policy.hostPackages.includes(packageName)) return true
  94. if (manifestPath.startsWith('packages/client/')) return true
  95. const included = hasClientDeclaration(dshField) || policy.clientFaceInclude.includes(packageName)
  96. return included && !policy.clientFaceExclude.includes(packageName)
  97. }