@codyswann/lisa 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +867 -0
  3. package/all/copy-overwrite/.claude/README.md +205 -0
  4. package/all/copy-overwrite/.claude/agents/agent-architect.md +311 -0
  5. package/all/copy-overwrite/.claude/agents/codebase-analyzer.md +146 -0
  6. package/all/copy-overwrite/.claude/agents/codebase-locator.md +125 -0
  7. package/all/copy-overwrite/.claude/agents/codebase-pattern-finder.md +237 -0
  8. package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +183 -0
  9. package/all/copy-overwrite/.claude/agents/hooks-expert.md +74 -0
  10. package/all/copy-overwrite/.claude/agents/skill-evaluator.md +246 -0
  11. package/all/copy-overwrite/.claude/agents/slash-command-architect.md +87 -0
  12. package/all/copy-overwrite/.claude/agents/web-search-researcher.md +112 -0
  13. package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +8 -0
  14. package/all/copy-overwrite/.claude/commands/git/commit.md +44 -0
  15. package/all/copy-overwrite/.claude/commands/git/prune.md +34 -0
  16. package/all/copy-overwrite/.claude/commands/git/submit-pr.md +50 -0
  17. package/all/copy-overwrite/.claude/commands/jira/create.md +50 -0
  18. package/all/copy-overwrite/.claude/commands/jira/verify.md +34 -0
  19. package/all/copy-overwrite/.claude/commands/project/archive.md +8 -0
  20. package/all/copy-overwrite/.claude/commands/project/bootstrap.md +49 -0
  21. package/all/copy-overwrite/.claude/commands/project/complete-task.md +7 -0
  22. package/all/copy-overwrite/.claude/commands/project/debrief.md +65 -0
  23. package/all/copy-overwrite/.claude/commands/project/execute.md +94 -0
  24. package/all/copy-overwrite/.claude/commands/project/implement.md +42 -0
  25. package/all/copy-overwrite/.claude/commands/project/local-code-review.md +88 -0
  26. package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +74 -0
  27. package/all/copy-overwrite/.claude/commands/project/plan.md +314 -0
  28. package/all/copy-overwrite/.claude/commands/project/research.md +248 -0
  29. package/all/copy-overwrite/.claude/commands/project/review.md +63 -0
  30. package/all/copy-overwrite/.claude/commands/project/setup.md +19 -0
  31. package/all/copy-overwrite/.claude/commands/project/verify.md +38 -0
  32. package/all/copy-overwrite/.claude/commands/pull-request/review.md +12 -0
  33. package/all/copy-overwrite/.claude/commands/rules/format-md.md +72 -0
  34. package/all/copy-overwrite/.claude/commands/sonarqube/check.md +6 -0
  35. package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +3 -0
  36. package/all/copy-overwrite/.claude/hooks/README.md +301 -0
  37. package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +181 -0
  38. package/all/copy-overwrite/.claude/settings.json +41 -0
  39. package/all/copy-overwrite/.claude/settings.local.json.example +14 -0
  40. package/all/copy-overwrite/.claude/skills/coding-philosophy/SKILL.md +405 -0
  41. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +416 -0
  42. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +316 -0
  43. package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +118 -0
  44. package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +202 -0
  45. package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +210 -0
  46. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  47. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
  48. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
  49. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
  50. package/all/copy-overwrite/CLAUDE.md +77 -0
  51. package/all/copy-overwrite/HUMAN.md +17 -0
  52. package/all/copy-overwrite/specs/.keep +0 -0
  53. package/all/create-only/PROJECT_RULES.md +0 -0
  54. package/cdk/merge/package.json +20 -0
  55. package/dist/cli/index.d.ts +7 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +107 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/prompts.d.ts +45 -0
  60. package/dist/cli/prompts.d.ts.map +1 -0
  61. package/dist/cli/prompts.js +58 -0
  62. package/dist/cli/prompts.js.map +1 -0
  63. package/dist/core/config.d.ts +73 -0
  64. package/dist/core/config.d.ts.map +1 -0
  65. package/dist/core/config.js +36 -0
  66. package/dist/core/config.js.map +1 -0
  67. package/dist/core/index.d.ts +4 -0
  68. package/dist/core/index.d.ts.map +1 -0
  69. package/dist/core/index.js +4 -0
  70. package/dist/core/index.js.map +1 -0
  71. package/dist/core/lisa.d.ts +81 -0
  72. package/dist/core/lisa.d.ts.map +1 -0
  73. package/dist/core/lisa.js +459 -0
  74. package/dist/core/lisa.js.map +1 -0
  75. package/dist/core/manifest.d.ts +58 -0
  76. package/dist/core/manifest.d.ts.map +1 -0
  77. package/dist/core/manifest.js +104 -0
  78. package/dist/core/manifest.js.map +1 -0
  79. package/dist/detection/detector.interface.d.ts +15 -0
  80. package/dist/detection/detector.interface.d.ts.map +1 -0
  81. package/dist/detection/detector.interface.js +2 -0
  82. package/dist/detection/detector.interface.js.map +1 -0
  83. package/dist/detection/detectors/cdk.d.ts +10 -0
  84. package/dist/detection/detectors/cdk.d.ts.map +1 -0
  85. package/dist/detection/detectors/cdk.js +34 -0
  86. package/dist/detection/detectors/cdk.js.map +1 -0
  87. package/dist/detection/detectors/expo.d.ts +10 -0
  88. package/dist/detection/detectors/expo.d.ts.map +1 -0
  89. package/dist/detection/detectors/expo.js +30 -0
  90. package/dist/detection/detectors/expo.js.map +1 -0
  91. package/dist/detection/detectors/nestjs.d.ts +10 -0
  92. package/dist/detection/detectors/nestjs.d.ts.map +1 -0
  93. package/dist/detection/detectors/nestjs.js +34 -0
  94. package/dist/detection/detectors/nestjs.js.map +1 -0
  95. package/dist/detection/detectors/npm-package.d.ts +13 -0
  96. package/dist/detection/detectors/npm-package.d.ts.map +1 -0
  97. package/dist/detection/detectors/npm-package.js +30 -0
  98. package/dist/detection/detectors/npm-package.js.map +1 -0
  99. package/dist/detection/detectors/typescript.d.ts +10 -0
  100. package/dist/detection/detectors/typescript.d.ts.map +1 -0
  101. package/dist/detection/detectors/typescript.js +25 -0
  102. package/dist/detection/detectors/typescript.js.map +1 -0
  103. package/dist/detection/index.d.ts +24 -0
  104. package/dist/detection/index.d.ts.map +1 -0
  105. package/dist/detection/index.js +57 -0
  106. package/dist/detection/index.js.map +1 -0
  107. package/dist/errors/index.d.ts +69 -0
  108. package/dist/errors/index.d.ts.map +1 -0
  109. package/dist/errors/index.js +110 -0
  110. package/dist/errors/index.js.map +1 -0
  111. package/dist/index.d.ts +3 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +8 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/logging/console-logger.d.ts +12 -0
  116. package/dist/logging/console-logger.d.ts.map +1 -0
  117. package/dist/logging/console-logger.js +22 -0
  118. package/dist/logging/console-logger.js.map +1 -0
  119. package/dist/logging/index.d.ts +4 -0
  120. package/dist/logging/index.d.ts.map +1 -0
  121. package/dist/logging/index.js +3 -0
  122. package/dist/logging/index.js.map +1 -0
  123. package/dist/logging/logger.interface.d.ts +20 -0
  124. package/dist/logging/logger.interface.d.ts.map +1 -0
  125. package/dist/logging/logger.interface.js +2 -0
  126. package/dist/logging/logger.interface.js.map +1 -0
  127. package/dist/logging/silent-logger.d.ts +12 -0
  128. package/dist/logging/silent-logger.d.ts.map +1 -0
  129. package/dist/logging/silent-logger.js +21 -0
  130. package/dist/logging/silent-logger.js.map +1 -0
  131. package/dist/strategies/copy-contents.d.ts +14 -0
  132. package/dist/strategies/copy-contents.d.ts.map +1 -0
  133. package/dist/strategies/copy-contents.js +69 -0
  134. package/dist/strategies/copy-contents.js.map +1 -0
  135. package/dist/strategies/copy-overwrite.d.ts +14 -0
  136. package/dist/strategies/copy-overwrite.d.ts.map +1 -0
  137. package/dist/strategies/copy-overwrite.js +47 -0
  138. package/dist/strategies/copy-overwrite.js.map +1 -0
  139. package/dist/strategies/create-only.d.ts +13 -0
  140. package/dist/strategies/create-only.d.ts.map +1 -0
  141. package/dist/strategies/create-only.js +30 -0
  142. package/dist/strategies/create-only.js.map +1 -0
  143. package/dist/strategies/index.d.ts +31 -0
  144. package/dist/strategies/index.d.ts.map +1 -0
  145. package/dist/strategies/index.js +52 -0
  146. package/dist/strategies/index.js.map +1 -0
  147. package/dist/strategies/merge.d.ts +13 -0
  148. package/dist/strategies/merge.d.ts.map +1 -0
  149. package/dist/strategies/merge.js +60 -0
  150. package/dist/strategies/merge.js.map +1 -0
  151. package/dist/strategies/strategy.interface.d.ts +31 -0
  152. package/dist/strategies/strategy.interface.d.ts.map +1 -0
  153. package/dist/strategies/strategy.interface.js +2 -0
  154. package/dist/strategies/strategy.interface.js.map +1 -0
  155. package/dist/transaction/backup.d.ts +38 -0
  156. package/dist/transaction/backup.d.ts.map +1 -0
  157. package/dist/transaction/backup.js +97 -0
  158. package/dist/transaction/backup.js.map +1 -0
  159. package/dist/transaction/index.d.ts +4 -0
  160. package/dist/transaction/index.d.ts.map +1 -0
  161. package/dist/transaction/index.js +3 -0
  162. package/dist/transaction/index.js.map +1 -0
  163. package/dist/transaction/transaction.d.ts +34 -0
  164. package/dist/transaction/transaction.d.ts.map +1 -0
  165. package/dist/transaction/transaction.js +68 -0
  166. package/dist/transaction/transaction.js.map +1 -0
  167. package/dist/utils/file-operations.d.ts +29 -0
  168. package/dist/utils/file-operations.d.ts.map +1 -0
  169. package/dist/utils/file-operations.js +84 -0
  170. package/dist/utils/file-operations.js.map +1 -0
  171. package/dist/utils/index.d.ts +4 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +4 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/json-utils.d.ts +22 -0
  176. package/dist/utils/json-utils.d.ts.map +1 -0
  177. package/dist/utils/json-utils.js +57 -0
  178. package/dist/utils/json-utils.js.map +1 -0
  179. package/dist/utils/path-utils.d.ts +21 -0
  180. package/dist/utils/path-utils.d.ts.map +1 -0
  181. package/dist/utils/path-utils.js +35 -0
  182. package/dist/utils/path-utils.js.map +1 -0
  183. package/eslint-plugin-code-organization/README.md +149 -0
  184. package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
  185. package/eslint-plugin-code-organization/index.js +23 -0
  186. package/eslint-plugin-code-organization/package.json +10 -0
  187. package/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
  188. package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +238 -0
  189. package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +360 -0
  190. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +360 -0
  191. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +417 -0
  192. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +257 -0
  193. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +233 -0
  194. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +327 -0
  195. package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +299 -0
  196. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +749 -0
  197. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +318 -0
  198. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +198 -0
  199. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +207 -0
  200. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +268 -0
  201. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +619 -0
  202. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +340 -0
  203. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +276 -0
  204. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +414 -0
  205. package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +202 -0
  206. package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +443 -0
  207. package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +309 -0
  208. package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +417 -0
  209. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +431 -0
  210. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +290 -0
  211. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +169 -0
  212. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +411 -0
  213. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +343 -0
  214. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +307 -0
  215. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +300 -0
  216. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +354 -0
  217. package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +362 -0
  218. package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +505 -0
  219. package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +711 -0
  220. package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +446 -0
  221. package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +223 -0
  222. package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +319 -0
  223. package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +420 -0
  224. package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +556 -0
  225. package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +590 -0
  226. package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +291 -0
  227. package/expo/copy-overwrite/.easignore.extra +2 -0
  228. package/expo/copy-overwrite/.mcp.json +33 -0
  229. package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +234 -0
  230. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +84 -0
  231. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +196 -0
  232. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +289 -0
  233. package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +32 -0
  234. package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +10 -0
  235. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +230 -0
  236. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +91 -0
  237. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +178 -0
  238. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +238 -0
  239. package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +260 -0
  240. package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +29 -0
  241. package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +10 -0
  242. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +51 -0
  243. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +55 -0
  244. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +73 -0
  245. package/expo/copy-overwrite/eslint.config.mjs +560 -0
  246. package/expo/copy-overwrite/lighthouserc.js +194 -0
  247. package/expo/create-only/lighthouserc-config.json +28 -0
  248. package/expo/merge/package.json +132 -0
  249. package/lisa.sh +35 -0
  250. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +176 -0
  251. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +527 -0
  252. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +483 -0
  253. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +257 -0
  254. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +413 -0
  255. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +513 -0
  256. package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +536 -0
  257. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +275 -0
  258. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +487 -0
  259. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +450 -0
  260. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +536 -0
  261. package/nestjs/merge/package.json +75 -0
  262. package/package.json +124 -0
  263. package/typescript/copy-contents/.husky/commit-msg +91 -0
  264. package/typescript/copy-contents/.husky/pre-commit +96 -0
  265. package/typescript/copy-contents/.husky/pre-push +211 -0
  266. package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +74 -0
  267. package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +59 -0
  268. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +103 -0
  269. package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +388 -0
  270. package/typescript/copy-overwrite/.github/README.md +455 -0
  271. package/typescript/copy-overwrite/.github/dependabot.yml +40 -0
  272. package/typescript/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +129 -0
  273. package/typescript/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +354 -0
  274. package/typescript/copy-overwrite/.github/k6/README.md +386 -0
  275. package/typescript/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +264 -0
  276. package/typescript/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +115 -0
  277. package/typescript/copy-overwrite/.github/k6/examples/data-driven-test.js +268 -0
  278. package/typescript/copy-overwrite/.github/k6/scenarios/load.js +142 -0
  279. package/typescript/copy-overwrite/.github/k6/scenarios/load.json +27 -0
  280. package/typescript/copy-overwrite/.github/k6/scenarios/smoke.js +26 -0
  281. package/typescript/copy-overwrite/.github/k6/scenarios/smoke.json +20 -0
  282. package/typescript/copy-overwrite/.github/k6/scenarios/soak.js +244 -0
  283. package/typescript/copy-overwrite/.github/k6/scenarios/soak.json +29 -0
  284. package/typescript/copy-overwrite/.github/k6/scenarios/spike.js +180 -0
  285. package/typescript/copy-overwrite/.github/k6/scenarios/spike.json +32 -0
  286. package/typescript/copy-overwrite/.github/k6/scenarios/stress.js +206 -0
  287. package/typescript/copy-overwrite/.github/k6/scenarios/stress.json +38 -0
  288. package/typescript/copy-overwrite/.github/k6/scripts/api-test.js +452 -0
  289. package/typescript/copy-overwrite/.github/k6/scripts/default-test.js +185 -0
  290. package/typescript/copy-overwrite/.github/k6/thresholds/normal.json +30 -0
  291. package/typescript/copy-overwrite/.github/k6/thresholds/relaxed.json +21 -0
  292. package/typescript/copy-overwrite/.github/k6/thresholds/strict.json +29 -0
  293. package/typescript/copy-overwrite/.github/workflows/build.yml +72 -0
  294. package/typescript/copy-overwrite/.github/workflows/ci.yml +49 -0
  295. package/typescript/copy-overwrite/.github/workflows/claude.yml +51 -0
  296. package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +113 -0
  297. package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +195 -0
  298. package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +267 -0
  299. package/typescript/copy-overwrite/.github/workflows/deploy.yml +228 -0
  300. package/typescript/copy-overwrite/.github/workflows/k6-load-test-README.md +230 -0
  301. package/typescript/copy-overwrite/.github/workflows/lighthouse.yml +68 -0
  302. package/typescript/copy-overwrite/.github/workflows/load-test.yml +282 -0
  303. package/typescript/copy-overwrite/.github/workflows/quality.yml +1737 -0
  304. package/typescript/copy-overwrite/.github/workflows/release.yml +1599 -0
  305. package/typescript/copy-overwrite/.gitleaksignore +28 -0
  306. package/typescript/copy-overwrite/.nvmrc +1 -0
  307. package/typescript/copy-overwrite/.prettierignore +23 -0
  308. package/typescript/copy-overwrite/.prettierrc.json +22 -0
  309. package/typescript/copy-overwrite/.versionrc +42 -0
  310. package/typescript/copy-overwrite/.yamllint +20 -0
  311. package/typescript/copy-overwrite/commitlint.config.js +11 -0
  312. package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +149 -0
  313. package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
  314. package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +23 -0
  315. package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +10 -0
  316. package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
  317. package/typescript/copy-overwrite/eslint.config.mjs +390 -0
  318. package/typescript/copy-overwrite/eslint.ignore.config.json +57 -0
  319. package/typescript/copy-overwrite/eslint.thresholds.config.json +5 -0
  320. package/typescript/github-rulesets/base.json +106 -0
  321. package/typescript/merge/.claude/settings.json +28 -0
  322. package/typescript/merge/package.json +71 -0
@@ -0,0 +1,749 @@
1
+ # Container/View Pattern - Complete Examples
2
+
3
+ ## Example 1: Simple Button Component
4
+
5
+ ### Directory Structure
6
+
7
+ ```
8
+ AddButton/
9
+ ├── AddButtonContainer.tsx
10
+ ├── AddButtonView.tsx
11
+ └── index.tsx
12
+ ```
13
+
14
+ ### AddButtonContainer.tsx
15
+
16
+ ```tsx
17
+ import { useCallback } from "react";
18
+
19
+ import AddButtonView from "./AddButtonView";
20
+
21
+ /**
22
+ * Props for the AddButton component.
23
+ */
24
+ interface AddButtonProps {
25
+ /** Callback when button is pressed */
26
+ readonly onAdd: () => void;
27
+ /** Whether the button is disabled */
28
+ readonly isDisabled?: boolean;
29
+ }
30
+
31
+ /**
32
+ * Container component that manages the add button logic.
33
+ * @param props - Component properties
34
+ * @param props.onAdd - Callback when button is pressed
35
+ * @param props.isDisabled - Whether the button is disabled
36
+ */
37
+ const AddButtonContainer = ({ onAdd, isDisabled = false }: AddButtonProps) => {
38
+ const handlePress = useCallback(() => {
39
+ if (!isDisabled) {
40
+ onAdd();
41
+ }
42
+ }, [onAdd, isDisabled]);
43
+
44
+ return <AddButtonView onPress={handlePress} isDisabled={isDisabled} />;
45
+ };
46
+
47
+ export default AddButtonContainer;
48
+ ```
49
+
50
+ ### AddButtonView.tsx
51
+
52
+ ```tsx
53
+ import { memo } from "react";
54
+ import { Plus } from "lucide-react-native";
55
+
56
+ import { Button, ButtonIcon, ButtonText } from "@/components/ui/button";
57
+
58
+ /**
59
+ * Props for the AddButtonView component.
60
+ */
61
+ interface AddButtonViewProps {
62
+ /** Handler for button press */
63
+ readonly onPress: () => void;
64
+ /** Whether the button is disabled */
65
+ readonly isDisabled: boolean;
66
+ }
67
+
68
+ /**
69
+ * View component that renders the add button UI.
70
+ * @param props - Component properties
71
+ * @param props.onPress - Handler for button press
72
+ * @param props.isDisabled - Whether the button is disabled
73
+ */
74
+ const AddButtonView = ({ onPress, isDisabled }: AddButtonViewProps) => (
75
+ <Button
76
+ testID="ADD_BUTTON.BUTTON"
77
+ onPress={onPress}
78
+ isDisabled={isDisabled}
79
+ className="flex-row items-center gap-2"
80
+ >
81
+ <ButtonIcon as={Plus} />
82
+ <ButtonText>Add</ButtonText>
83
+ </Button>
84
+ );
85
+
86
+ AddButtonView.displayName = "AddButtonView";
87
+
88
+ export default memo(AddButtonView);
89
+ ```
90
+
91
+ ### index.tsx
92
+
93
+ ```tsx
94
+ export { default } from "./AddButtonContainer";
95
+ ```
96
+
97
+ ## Example 2: List with Loading/Empty States
98
+
99
+ ### Directory Structure
100
+
101
+ ```
102
+ PlayerList/
103
+ ├── PlayerListContainer.tsx
104
+ ├── PlayerListView.tsx
105
+ └── index.tsx
106
+ ```
107
+
108
+ ### PlayerListContainer.tsx
109
+
110
+ ```tsx
111
+ import { useCallback, useMemo } from "react";
112
+ import { useRouter } from "expo-router";
113
+
114
+ import { useListPlayersQuery } from "@/generated/graphql";
115
+ import PlayerListView from "./PlayerListView";
116
+
117
+ /**
118
+ * Props for the PlayerList component.
119
+ */
120
+ interface PlayerListProps {
121
+ /** Optional filter for player position */
122
+ readonly positionFilter?: string;
123
+ }
124
+
125
+ /**
126
+ * Container component that manages player list data and navigation.
127
+ * @param props - Component properties
128
+ * @param props.positionFilter - Optional filter for player position
129
+ */
130
+ const PlayerListContainer = ({ positionFilter }: PlayerListProps) => {
131
+ const router = useRouter();
132
+ const { data, loading, error, refetch } = useListPlayersQuery();
133
+
134
+ const players = useMemo(() => {
135
+ const allPlayers = data?.listPlayers?.edges ?? [];
136
+ if (!positionFilter) {
137
+ return allPlayers;
138
+ }
139
+ return allPlayers.filter(p => p.position === positionFilter);
140
+ }, [data?.listPlayers?.edges, positionFilter]);
141
+
142
+ const isEmpty = useMemo(
143
+ () => !loading && !error && players.length === 0,
144
+ [loading, error, players.length]
145
+ );
146
+
147
+ const handlePlayerPress = useCallback(
148
+ (playerId: string) => {
149
+ if (!playerId) {
150
+ console.error("Cannot navigate: player ID is missing");
151
+ return;
152
+ }
153
+ router.push(`/players/${playerId}`);
154
+ },
155
+ [router]
156
+ );
157
+
158
+ const handleRefresh = useCallback(() => {
159
+ refetch();
160
+ }, [refetch]);
161
+
162
+ return (
163
+ <PlayerListView
164
+ players={players}
165
+ isLoading={loading}
166
+ hasError={!!error}
167
+ isEmpty={isEmpty}
168
+ onPlayerPress={handlePlayerPress}
169
+ onRefresh={handleRefresh}
170
+ />
171
+ );
172
+ };
173
+
174
+ export default PlayerListContainer;
175
+ ```
176
+
177
+ ### PlayerListView.tsx
178
+
179
+ ```tsx
180
+ import { memo } from "react";
181
+ import { FlashList, ListRenderItem } from "@shopify/flash-list";
182
+
183
+ import { Box } from "@/components/ui/box";
184
+ import { Text } from "@/components/ui/text";
185
+ import { Pressable } from "@/components/ui/pressable";
186
+ import { Spinner } from "@/components/ui/spinner";
187
+ import { PlayerFragment } from "@/generated/graphql";
188
+
189
+ /**
190
+ * Props for the PlayerListView component.
191
+ */
192
+ interface PlayerListViewProps {
193
+ /** List of players to display */
194
+ readonly players: readonly PlayerFragment[];
195
+ /** Whether the list is loading */
196
+ readonly isLoading: boolean;
197
+ /** Whether there was an error loading */
198
+ readonly hasError: boolean;
199
+ /** Whether the list is empty */
200
+ readonly isEmpty: boolean;
201
+ /** Handler for player item press */
202
+ readonly onPlayerPress: (playerId: string) => void;
203
+ /** Handler for pull-to-refresh */
204
+ readonly onRefresh: () => void;
205
+ }
206
+
207
+ /**
208
+ * Renders a single player item.
209
+ * @param props - Render item props
210
+ * @param props.player - The player data
211
+ * @param props.onPress - Press handler
212
+ */
213
+ function renderPlayerItem(props: {
214
+ readonly player: PlayerFragment;
215
+ readonly onPress: (id: string) => void;
216
+ }) {
217
+ const { player, onPress } = props;
218
+ return (
219
+ <Pressable
220
+ testID={`PLAYER_LIST.ITEM.${player.id}`}
221
+ onPress={() => onPress(player.id)}
222
+ className="flex-row items-center gap-4 p-4"
223
+ >
224
+ <Text className="text-lg font-medium">{player.name}</Text>
225
+ <Text className="text-sm text-gray-500">{player.position}</Text>
226
+ </Pressable>
227
+ );
228
+ }
229
+
230
+ /**
231
+ * Renders the loading state.
232
+ */
233
+ function renderLoading() {
234
+ return (
235
+ <Box className="flex-1 items-center justify-center">
236
+ <Spinner size="large" />
237
+ </Box>
238
+ );
239
+ }
240
+
241
+ /**
242
+ * Renders the error state.
243
+ */
244
+ function renderError() {
245
+ return (
246
+ <Box className="flex-1 items-center justify-center p-8">
247
+ <Text className="text-center text-red-500">
248
+ Failed to load players. Pull to refresh.
249
+ </Text>
250
+ </Box>
251
+ );
252
+ }
253
+
254
+ /**
255
+ * Renders the empty state.
256
+ */
257
+ function renderEmpty() {
258
+ return (
259
+ <Box className="flex-1 items-center justify-center p-8">
260
+ <Text className="text-center text-gray-500">No players found</Text>
261
+ </Box>
262
+ );
263
+ }
264
+
265
+ /**
266
+ * View component that renders the player list UI.
267
+ * @param props - Component properties
268
+ * @param props.players - List of players to display
269
+ * @param props.isLoading - Whether the list is loading
270
+ * @param props.hasError - Whether there was an error loading
271
+ * @param props.isEmpty - Whether the list is empty
272
+ * @param props.onPlayerPress - Handler for player item press
273
+ * @param props.onRefresh - Handler for pull-to-refresh
274
+ */
275
+ const PlayerListView = ({
276
+ players,
277
+ isLoading,
278
+ hasError,
279
+ isEmpty,
280
+ onPlayerPress,
281
+ onRefresh,
282
+ }: PlayerListViewProps) => (
283
+ <Box testID="PLAYER_LIST.CONTAINER" className="flex-1">
284
+ {isLoading ? (
285
+ renderLoading()
286
+ ) : hasError ? (
287
+ renderError()
288
+ ) : isEmpty ? (
289
+ renderEmpty()
290
+ ) : (
291
+ <FlashList
292
+ data={players}
293
+ renderItem={({ item }) =>
294
+ renderPlayerItem({ player: item, onPress: onPlayerPress })
295
+ }
296
+ estimatedItemSize={72}
297
+ onRefresh={onRefresh}
298
+ refreshing={isLoading}
299
+ />
300
+ )}
301
+ </Box>
302
+ );
303
+
304
+ PlayerListView.displayName = "PlayerListView";
305
+
306
+ export default memo(PlayerListView);
307
+ ```
308
+
309
+ ### index.tsx
310
+
311
+ ```tsx
312
+ export { default } from "./PlayerListContainer";
313
+ ```
314
+
315
+ ## Example 3: Form with Validation
316
+
317
+ ### Directory Structure
318
+
319
+ ```
320
+ EditProfile/
321
+ ├── EditProfileContainer.tsx
322
+ ├── EditProfileView.tsx
323
+ └── index.tsx
324
+ ```
325
+
326
+ ### EditProfileContainer.tsx
327
+
328
+ ```tsx
329
+ import { useCallback, useMemo, useState } from "react";
330
+ import { useForm } from "react-hook-form";
331
+ import { yupResolver } from "@hookform/resolvers/yup";
332
+ import * as yup from "yup";
333
+
334
+ import { useUpdateProfileMutation } from "@/generated/graphql";
335
+ import EditProfileView from "./EditProfileView";
336
+
337
+ /**
338
+ * Form data shape for profile editing.
339
+ */
340
+ interface ProfileFormData {
341
+ readonly name: string;
342
+ readonly email: string;
343
+ readonly bio?: string;
344
+ }
345
+
346
+ const schema = yup.object({
347
+ name: yup.string().required("Name is required"),
348
+ email: yup.string().email("Invalid email").required("Email is required"),
349
+ bio: yup.string().max(500, "Bio must be 500 characters or less"),
350
+ });
351
+
352
+ /**
353
+ * Props for the EditProfile component.
354
+ */
355
+ interface EditProfileProps {
356
+ /** Initial profile data */
357
+ readonly initialData: ProfileFormData;
358
+ /** Callback when profile is saved */
359
+ readonly onSaved: () => void;
360
+ }
361
+
362
+ /**
363
+ * Container component that manages profile editing logic.
364
+ * @param props - Component properties
365
+ * @param props.initialData - Initial profile data
366
+ * @param props.onSaved - Callback when profile is saved
367
+ */
368
+ const EditProfileContainer = ({ initialData, onSaved }: EditProfileProps) => {
369
+ const [isSubmitting, setIsSubmitting] = useState(false);
370
+ const [updateProfile] = useUpdateProfileMutation();
371
+
372
+ const {
373
+ control,
374
+ handleSubmit,
375
+ formState: { errors, isDirty },
376
+ } = useForm<ProfileFormData>({
377
+ resolver: yupResolver(schema),
378
+ defaultValues: initialData,
379
+ });
380
+
381
+ const errorMessages = useMemo(
382
+ () => ({
383
+ name: errors.name?.message,
384
+ email: errors.email?.message,
385
+ bio: errors.bio?.message,
386
+ }),
387
+ [errors.name?.message, errors.email?.message, errors.bio?.message]
388
+ );
389
+
390
+ const handleFormSubmit = useCallback(
391
+ async (data: ProfileFormData) => {
392
+ setIsSubmitting(true);
393
+ try {
394
+ await updateProfile({ variables: { input: data } });
395
+ onSaved();
396
+ } catch (error) {
397
+ console.error("Failed to update profile:", error);
398
+ } finally {
399
+ setIsSubmitting(false);
400
+ }
401
+ },
402
+ [updateProfile, onSaved]
403
+ );
404
+
405
+ const onSubmit = useMemo(
406
+ () => handleSubmit(handleFormSubmit),
407
+ [handleSubmit, handleFormSubmit]
408
+ );
409
+
410
+ return (
411
+ <EditProfileView
412
+ control={control}
413
+ errors={errorMessages}
414
+ isSubmitting={isSubmitting}
415
+ isDirty={isDirty}
416
+ onSubmit={onSubmit}
417
+ />
418
+ );
419
+ };
420
+
421
+ export default EditProfileContainer;
422
+ ```
423
+
424
+ ### EditProfileView.tsx
425
+
426
+ ```tsx
427
+ import { memo } from "react";
428
+ import { Control, Controller } from "react-hook-form";
429
+
430
+ import { Box } from "@/components/ui/box";
431
+ import { VStack } from "@/components/ui/vstack";
432
+ import { Text } from "@/components/ui/text";
433
+ import { Input, InputField } from "@/components/ui/input";
434
+ import { Textarea, TextareaInput } from "@/components/ui/textarea";
435
+ import { Button, ButtonText, ButtonSpinner } from "@/components/ui/button";
436
+
437
+ /**
438
+ * Error messages for form fields.
439
+ */
440
+ interface FormErrors {
441
+ readonly name?: string;
442
+ readonly email?: string;
443
+ readonly bio?: string;
444
+ }
445
+
446
+ /**
447
+ * Props for the EditProfileView component.
448
+ */
449
+ interface EditProfileViewProps {
450
+ /** React Hook Form control object */
451
+ readonly control: Control<any>;
452
+ /** Validation error messages */
453
+ readonly errors: FormErrors;
454
+ /** Whether the form is submitting */
455
+ readonly isSubmitting: boolean;
456
+ /** Whether the form has changes */
457
+ readonly isDirty: boolean;
458
+ /** Handler for form submission */
459
+ readonly onSubmit: () => void;
460
+ }
461
+
462
+ /**
463
+ * View component that renders the profile edit form UI.
464
+ * @param props - Component properties
465
+ * @param props.control - React Hook Form control object
466
+ * @param props.errors - Validation error messages
467
+ * @param props.isSubmitting - Whether the form is submitting
468
+ * @param props.isDirty - Whether the form has changes
469
+ * @param props.onSubmit - Handler for form submission
470
+ */
471
+ const EditProfileView = ({
472
+ control,
473
+ errors,
474
+ isSubmitting,
475
+ isDirty,
476
+ onSubmit,
477
+ }: EditProfileViewProps) => (
478
+ <Box testID="EDIT_PROFILE.CONTAINER" className="flex-1 p-4">
479
+ <VStack space="lg">
480
+ <VStack space="xs">
481
+ <Text className="font-medium">Name</Text>
482
+ <Controller
483
+ control={control}
484
+ name="name"
485
+ render={({ field: { onChange, onBlur, value } }) => (
486
+ <Input isInvalid={!!errors.name}>
487
+ <InputField
488
+ testID="EDIT_PROFILE.NAME_INPUT"
489
+ placeholder="Enter your name"
490
+ value={value}
491
+ onChangeText={onChange}
492
+ onBlur={onBlur}
493
+ />
494
+ </Input>
495
+ )}
496
+ />
497
+ {errors.name && (
498
+ <Text className="text-sm text-red-500">{errors.name}</Text>
499
+ )}
500
+ </VStack>
501
+
502
+ <VStack space="xs">
503
+ <Text className="font-medium">Email</Text>
504
+ <Controller
505
+ control={control}
506
+ name="email"
507
+ render={({ field: { onChange, onBlur, value } }) => (
508
+ <Input isInvalid={!!errors.email}>
509
+ <InputField
510
+ testID="EDIT_PROFILE.EMAIL_INPUT"
511
+ placeholder="Enter your email"
512
+ keyboardType="email-address"
513
+ autoCapitalize="none"
514
+ value={value}
515
+ onChangeText={onChange}
516
+ onBlur={onBlur}
517
+ />
518
+ </Input>
519
+ )}
520
+ />
521
+ {errors.email && (
522
+ <Text className="text-sm text-red-500">{errors.email}</Text>
523
+ )}
524
+ </VStack>
525
+
526
+ <VStack space="xs">
527
+ <Text className="font-medium">Bio</Text>
528
+ <Controller
529
+ control={control}
530
+ name="bio"
531
+ render={({ field: { onChange, onBlur, value } }) => (
532
+ <Textarea isInvalid={!!errors.bio}>
533
+ <TextareaInput
534
+ testID="EDIT_PROFILE.BIO_INPUT"
535
+ placeholder="Tell us about yourself"
536
+ value={value}
537
+ onChangeText={onChange}
538
+ onBlur={onBlur}
539
+ />
540
+ </Textarea>
541
+ )}
542
+ />
543
+ {errors.bio && (
544
+ <Text className="text-sm text-red-500">{errors.bio}</Text>
545
+ )}
546
+ </VStack>
547
+
548
+ <Button
549
+ testID="EDIT_PROFILE.SUBMIT_BUTTON"
550
+ onPress={onSubmit}
551
+ isDisabled={!isDirty || isSubmitting}
552
+ >
553
+ {isSubmitting ? (
554
+ <ButtonSpinner />
555
+ ) : (
556
+ <ButtonText>Save Changes</ButtonText>
557
+ )}
558
+ </Button>
559
+ </VStack>
560
+ </Box>
561
+ );
562
+
563
+ EditProfileView.displayName = "EditProfileView";
564
+
565
+ export default memo(EditProfileView);
566
+ ```
567
+
568
+ ### index.tsx
569
+
570
+ ```tsx
571
+ export { default } from "./EditProfileContainer";
572
+ ```
573
+
574
+ ## Example 4: Modal with Confirmation
575
+
576
+ ### Directory Structure
577
+
578
+ ```
579
+ DeleteConfirmModal/
580
+ ├── DeleteConfirmModalContainer.tsx
581
+ ├── DeleteConfirmModalView.tsx
582
+ └── index.tsx
583
+ ```
584
+
585
+ ### DeleteConfirmModalContainer.tsx
586
+
587
+ ```tsx
588
+ import { useCallback, useState } from "react";
589
+
590
+ import DeleteConfirmModalView from "./DeleteConfirmModalView";
591
+
592
+ /**
593
+ * Props for the DeleteConfirmModal component.
594
+ */
595
+ interface DeleteConfirmModalProps {
596
+ /** Whether the modal is visible */
597
+ readonly isOpen: boolean;
598
+ /** Name of the item being deleted */
599
+ readonly itemName: string;
600
+ /** Handler for delete confirmation */
601
+ readonly onConfirm: () => Promise<void>;
602
+ /** Handler for modal close */
603
+ readonly onClose: () => void;
604
+ }
605
+
606
+ /**
607
+ * Container component that manages delete confirmation modal logic.
608
+ * @param props - Component properties
609
+ * @param props.isOpen - Whether the modal is visible
610
+ * @param props.itemName - Name of the item being deleted
611
+ * @param props.onConfirm - Handler for delete confirmation
612
+ * @param props.onClose - Handler for modal close
613
+ */
614
+ const DeleteConfirmModalContainer = ({
615
+ isOpen,
616
+ itemName,
617
+ onConfirm,
618
+ onClose,
619
+ }: DeleteConfirmModalProps) => {
620
+ const [isDeleting, setIsDeleting] = useState(false);
621
+
622
+ const handleConfirm = useCallback(async () => {
623
+ setIsDeleting(true);
624
+ try {
625
+ await onConfirm();
626
+ onClose();
627
+ } catch (error) {
628
+ console.error("Delete failed:", error);
629
+ } finally {
630
+ setIsDeleting(false);
631
+ }
632
+ }, [onConfirm, onClose]);
633
+
634
+ const handleCancel = useCallback(() => {
635
+ if (!isDeleting) {
636
+ onClose();
637
+ }
638
+ }, [isDeleting, onClose]);
639
+
640
+ return (
641
+ <DeleteConfirmModalView
642
+ isOpen={isOpen}
643
+ itemName={itemName}
644
+ isDeleting={isDeleting}
645
+ onConfirm={handleConfirm}
646
+ onCancel={handleCancel}
647
+ />
648
+ );
649
+ };
650
+
651
+ export default DeleteConfirmModalContainer;
652
+ ```
653
+
654
+ ### DeleteConfirmModalView.tsx
655
+
656
+ ```tsx
657
+ import { memo } from "react";
658
+
659
+ import {
660
+ AlertDialog,
661
+ AlertDialogBackdrop,
662
+ AlertDialogContent,
663
+ AlertDialogHeader,
664
+ AlertDialogBody,
665
+ AlertDialogFooter,
666
+ } from "@/components/ui/alert-dialog";
667
+ import { Heading } from "@/components/ui/heading";
668
+ import { Text } from "@/components/ui/text";
669
+ import { Button, ButtonText, ButtonSpinner } from "@/components/ui/button";
670
+ import { HStack } from "@/components/ui/hstack";
671
+
672
+ /**
673
+ * Props for the DeleteConfirmModalView component.
674
+ */
675
+ interface DeleteConfirmModalViewProps {
676
+ /** Whether the modal is visible */
677
+ readonly isOpen: boolean;
678
+ /** Name of the item being deleted */
679
+ readonly itemName: string;
680
+ /** Whether deletion is in progress */
681
+ readonly isDeleting: boolean;
682
+ /** Handler for confirm button */
683
+ readonly onConfirm: () => void;
684
+ /** Handler for cancel button */
685
+ readonly onCancel: () => void;
686
+ }
687
+
688
+ /**
689
+ * View component that renders the delete confirmation modal UI.
690
+ * @param props - Component properties
691
+ * @param props.isOpen - Whether the modal is visible
692
+ * @param props.itemName - Name of the item being deleted
693
+ * @param props.isDeleting - Whether deletion is in progress
694
+ * @param props.onConfirm - Handler for confirm button
695
+ * @param props.onCancel - Handler for cancel button
696
+ */
697
+ const DeleteConfirmModalView = ({
698
+ isOpen,
699
+ itemName,
700
+ isDeleting,
701
+ onConfirm,
702
+ onCancel,
703
+ }: DeleteConfirmModalViewProps) => (
704
+ <AlertDialog isOpen={isOpen} onClose={onCancel}>
705
+ <AlertDialogBackdrop />
706
+ <AlertDialogContent testID="DELETE_CONFIRM_MODAL.CONTENT">
707
+ <AlertDialogHeader>
708
+ <Heading size="lg">Delete {itemName}?</Heading>
709
+ </AlertDialogHeader>
710
+ <AlertDialogBody>
711
+ <Text>
712
+ This action cannot be undone. Are you sure you want to delete{" "}
713
+ <Text className="font-bold">{itemName}</Text>?
714
+ </Text>
715
+ </AlertDialogBody>
716
+ <AlertDialogFooter>
717
+ <HStack space="md">
718
+ <Button
719
+ testID="DELETE_CONFIRM_MODAL.CANCEL_BUTTON"
720
+ variant="outline"
721
+ onPress={onCancel}
722
+ isDisabled={isDeleting}
723
+ >
724
+ <ButtonText>Cancel</ButtonText>
725
+ </Button>
726
+ <Button
727
+ testID="DELETE_CONFIRM_MODAL.CONFIRM_BUTTON"
728
+ action="negative"
729
+ onPress={onConfirm}
730
+ isDisabled={isDeleting}
731
+ >
732
+ {isDeleting ? <ButtonSpinner /> : <ButtonText>Delete</ButtonText>}
733
+ </Button>
734
+ </HStack>
735
+ </AlertDialogFooter>
736
+ </AlertDialogContent>
737
+ </AlertDialog>
738
+ );
739
+
740
+ DeleteConfirmModalView.displayName = "DeleteConfirmModalView";
741
+
742
+ export default memo(DeleteConfirmModalView);
743
+ ```
744
+
745
+ ### index.tsx
746
+
747
+ ```tsx
748
+ export { default } from "./DeleteConfirmModalContainer";
749
+ ```