patch-rows.ts 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. /**
  2. * The one walk over the rows a patch list introduces. Composition, ownership,
  3. * the package probe, and patch loading all read those rows and their group
  4. * children; sharing the walk keeps them reading the same tree.
  5. * @module @deepseek-ai/dsh-app-boot/patch-rows
  6. */
  7. import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
  8. import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
  9. /** How a patch introduced a row: inserted it, or set it as a group's `config`. */
  10. export type PatchRowSource = 'insert' | 'config'
  11. /** Whether a config item is a row: a plain object naming a module. */
  12. function isRowLike(value: unknown): value is EntryOptions {
  13. return typeof value === 'object' && value !== null && !Array.isArray(value) && typeof (value as { name?: unknown }).name === 'string'
  14. }
  15. /**
  16. * Visit one row and, for a group with a list config, each of its children,
  17. * parents before children, in written order.
  18. * @param row - the row.
  19. * @param visit - called once per row.
  20. */
  21. export function visitRowTree(row: EntryOptions, visit: (row: EntryOptions) => void): void {
  22. visit(row)
  23. if (row.group && Array.isArray(row.config)) {
  24. for (const child of row.config as EntryOptions[]) visitRowTree(child, visit)
  25. }
  26. }
  27. /**
  28. * Visit every row a patch list introduces, at any depth, in written order:
  29. * the rows a patch inserts, and the rows an id-targeted patch sets as a
  30. * group's `config`, which mount as that group's children the same way an
  31. * insert's do.
  32. * @param patches - the patch list.
  33. * @param visit - called once per row, with how the patch introduced it.
  34. */
  35. export function visitPatchRows(patches: readonly PatchOptions[], visit: (row: EntryOptions, source: PatchRowSource) => void): void {
  36. for (const patch of patches) {
  37. if (patch.insert !== undefined) {
  38. for (const row of patch.insert) visitRowTree(row, (entry) => { visit(entry, 'insert') })
  39. continue
  40. }
  41. if (patch.id === undefined || !Array.isArray(patch.config)) continue
  42. for (const item of patch.config as unknown[]) {
  43. if (isRowLike(item)) visitRowTree(item, (entry) => { visit(entry, 'config') })
  44. }
  45. }
  46. }