@codyswann/lisa 1.47.0 → 1.48.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 (216) hide show
  1. package/all/copy-overwrite/.claude/rules/lisa.md +23 -10
  2. package/all/copy-overwrite/.claude/settings.json +10 -230
  3. package/all/deletions.json +67 -1
  4. package/cdk/copy-overwrite/.claude/settings.json +80 -0
  5. package/cdk/create-only/.github/workflows/ci.yml +1 -1
  6. package/cdk/create-only/.github/workflows/deploy.yml +1 -1
  7. package/dist/core/lisa.d.ts +14 -0
  8. package/dist/core/lisa.d.ts.map +1 -1
  9. package/dist/core/lisa.js +47 -0
  10. package/dist/core/lisa.js.map +1 -1
  11. package/expo/copy-overwrite/.claude/settings.json +80 -0
  12. package/expo/copy-overwrite/eslint.expo.ts +2 -2
  13. package/expo/create-only/.github/workflows/ci.yml +1 -1
  14. package/expo/create-only/.github/workflows/deploy.yml +1 -1
  15. package/expo/deletions.json +33 -0
  16. package/expo/package-lisa/package.lisa.json +2 -2
  17. package/nestjs/copy-overwrite/.claude/settings.json +80 -0
  18. package/nestjs/create-only/.github/workflows/ci.yml +1 -1
  19. package/nestjs/create-only/.github/workflows/deploy.yml +1 -1
  20. package/nestjs/deletions.json +8 -0
  21. package/package.json +8 -4
  22. package/rails/copy-overwrite/.claude/settings.json +80 -0
  23. package/rails/create-only/.github/workflows/ci.yml +1 -1
  24. package/rails/deletions.json +11 -1
  25. package/typescript/copy-overwrite/.claude/settings.json +13 -231
  26. package/typescript/copy-overwrite/.github/workflows/claude-ci-auto-fix.yml +1 -0
  27. package/typescript/copy-overwrite/.github/workflows/claude-code-review-response.yml +11 -10
  28. package/typescript/copy-overwrite/.github/workflows/claude-deploy-auto-fix.yml +1 -0
  29. package/typescript/copy-overwrite/.github/workflows/claude-nightly-code-complexity.yml +1 -0
  30. package/typescript/copy-overwrite/.github/workflows/claude-nightly-test-coverage.yml +1 -0
  31. package/typescript/copy-overwrite/.github/workflows/claude-nightly-test-improvement.yml +2 -0
  32. package/typescript/copy-overwrite/.github/workflows/claude.yml +1 -0
  33. package/typescript/copy-overwrite/eslint.typescript.ts +1 -1
  34. package/typescript/create-only/.github/workflows/ci.yml +1 -1
  35. package/typescript/deletions.json +12 -1
  36. package/typescript/package-lisa/package.lisa.json +1 -1
  37. package/all/copy-overwrite/.claude/agents/agent-architect.md +0 -310
  38. package/all/copy-overwrite/.claude/agents/architecture-specialist.md +0 -53
  39. package/all/copy-overwrite/.claude/agents/debug-specialist.md +0 -204
  40. package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +0 -183
  41. package/all/copy-overwrite/.claude/agents/hooks-expert.md +0 -74
  42. package/all/copy-overwrite/.claude/agents/implementer.md +0 -54
  43. package/all/copy-overwrite/.claude/agents/learner.md +0 -44
  44. package/all/copy-overwrite/.claude/agents/performance-specialist.md +0 -95
  45. package/all/copy-overwrite/.claude/agents/product-specialist.md +0 -72
  46. package/all/copy-overwrite/.claude/agents/quality-specialist.md +0 -55
  47. package/all/copy-overwrite/.claude/agents/security-specialist.md +0 -58
  48. package/all/copy-overwrite/.claude/agents/skill-evaluator.md +0 -246
  49. package/all/copy-overwrite/.claude/agents/slash-command-architect.md +0 -87
  50. package/all/copy-overwrite/.claude/agents/test-specialist.md +0 -64
  51. package/all/copy-overwrite/.claude/agents/verification-specialist.md +0 -189
  52. package/all/copy-overwrite/.claude/agents/web-search-researcher.md +0 -112
  53. package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +0 -7
  54. package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-and-verify.md +0 -7
  55. package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-deploy-and-verify.md +0 -7
  56. package/all/copy-overwrite/.claude/commands/git/commit.md +0 -7
  57. package/all/copy-overwrite/.claude/commands/git/prune.md +0 -6
  58. package/all/copy-overwrite/.claude/commands/git/submit-pr.md +0 -7
  59. package/all/copy-overwrite/.claude/commands/jira/create.md +0 -7
  60. package/all/copy-overwrite/.claude/commands/jira/sync.md +0 -7
  61. package/all/copy-overwrite/.claude/commands/jira/verify.md +0 -7
  62. package/all/copy-overwrite/.claude/commands/lisa/review-implementation.md +0 -7
  63. package/all/copy-overwrite/.claude/commands/plan/add-test-coverage.md +0 -7
  64. package/all/copy-overwrite/.claude/commands/plan/create.md +0 -6
  65. package/all/copy-overwrite/.claude/commands/plan/execute.md +0 -7
  66. package/all/copy-overwrite/.claude/commands/plan/fix-linter-error.md +0 -7
  67. package/all/copy-overwrite/.claude/commands/plan/local-code-review.md +0 -6
  68. package/all/copy-overwrite/.claude/commands/plan/lower-code-complexity.md +0 -6
  69. package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines-per-function.md +0 -7
  70. package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines.md +0 -7
  71. package/all/copy-overwrite/.claude/commands/pull-request/review.md +0 -7
  72. package/all/copy-overwrite/.claude/commands/security/zap-scan.md +0 -6
  73. package/all/copy-overwrite/.claude/commands/sonarqube/check.md +0 -6
  74. package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +0 -6
  75. package/all/copy-overwrite/.claude/commands/tasks/load.md +0 -7
  76. package/all/copy-overwrite/.claude/commands/tasks/sync.md +0 -7
  77. package/all/copy-overwrite/.claude/hooks/check-tired-boss.sh +0 -61
  78. package/all/copy-overwrite/.claude/hooks/debug-hook.sh +0 -47
  79. package/all/copy-overwrite/.claude/hooks/enforce-plan-rules.sh +0 -15
  80. package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +0 -183
  81. package/all/copy-overwrite/.claude/hooks/setup-jira-cli.sh +0 -52
  82. package/all/copy-overwrite/.claude/hooks/sync-tasks.sh +0 -107
  83. package/all/copy-overwrite/.claude/hooks/ticket-sync-reminder.sh +0 -23
  84. package/all/copy-overwrite/.claude/hooks/track-plan-sessions.sh +0 -164
  85. package/all/copy-overwrite/.claude/rules/coding-philosophy.md +0 -428
  86. package/all/copy-overwrite/.claude/rules/verfication.md +0 -541
  87. package/all/copy-overwrite/.claude/skills/agent-design-best-practices/SKILL.md +0 -219
  88. package/all/copy-overwrite/.claude/skills/git-commit/SKILL.md +0 -48
  89. package/all/copy-overwrite/.claude/skills/git-commit-and-submit-pr/SKILL.md +0 -8
  90. package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-and-verify/SKILL.md +0 -7
  91. package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-deploy-and-verify/SKILL.md +0 -7
  92. package/all/copy-overwrite/.claude/skills/git-prune/SKILL.md +0 -35
  93. package/all/copy-overwrite/.claude/skills/git-submit-pr/SKILL.md +0 -44
  94. package/all/copy-overwrite/.claude/skills/jira-create/SKILL.md +0 -41
  95. package/all/copy-overwrite/.claude/skills/jira-sync/SKILL.md +0 -63
  96. package/all/copy-overwrite/.claude/skills/jira-verify/SKILL.md +0 -29
  97. package/all/copy-overwrite/.claude/skills/lisa-review-implementation/SKILL.md +0 -209
  98. package/all/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -44
  99. package/all/copy-overwrite/.claude/skills/plan-execute/SKILL.md +0 -89
  100. package/all/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
  101. package/all/copy-overwrite/.claude/skills/plan-local-code-review/SKILL.md +0 -88
  102. package/all/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -44
  103. package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -45
  104. package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
  105. package/all/copy-overwrite/.claude/skills/pull-request-review/SKILL.md +0 -68
  106. package/all/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
  107. package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +0 -202
  108. package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +0 -210
  109. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  110. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +0 -305
  111. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +0 -112
  112. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +0 -67
  113. package/all/copy-overwrite/.claude/skills/sonarqube-check/SKILL.md +0 -11
  114. package/all/copy-overwrite/.claude/skills/sonarqube-fix/SKILL.md +0 -8
  115. package/all/copy-overwrite/.claude/skills/tasks-load/SKILL.md +0 -88
  116. package/all/copy-overwrite/.claude/skills/tasks-sync/SKILL.md +0 -108
  117. package/eslint-plugin-code-organization/README.md +0 -149
  118. package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
  119. package/eslint-plugin-code-organization/index.js +0 -28
  120. package/eslint-plugin-code-organization/package.json +0 -10
  121. package/eslint-plugin-code-organization/rules/enforce-statement-order.js +0 -162
  122. package/expo/copy-overwrite/.claude/agents/ops-specialist.md +0 -124
  123. package/expo/copy-overwrite/.claude/rules/expo-verification.md +0 -261
  124. package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +0 -238
  125. package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +0 -360
  126. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +0 -360
  127. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +0 -417
  128. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +0 -257
  129. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +0 -233
  130. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +0 -329
  131. package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +0 -299
  132. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +0 -749
  133. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +0 -318
  134. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +0 -200
  135. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +0 -209
  136. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +0 -268
  137. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +0 -619
  138. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +0 -340
  139. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +0 -276
  140. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +0 -416
  141. package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +0 -202
  142. package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +0 -445
  143. package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +0 -309
  144. package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +0 -417
  145. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +0 -431
  146. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +0 -290
  147. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +0 -171
  148. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +0 -411
  149. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +0 -343
  150. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +0 -307
  151. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +0 -300
  152. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +0 -315
  153. package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +0 -362
  154. package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +0 -505
  155. package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +0 -711
  156. package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +0 -446
  157. package/expo/copy-overwrite/.claude/skills/ops-browser-uat/SKILL.md +0 -124
  158. package/expo/copy-overwrite/.claude/skills/ops-check-logs/SKILL.md +0 -211
  159. package/expo/copy-overwrite/.claude/skills/ops-db-ops/SKILL.md +0 -119
  160. package/expo/copy-overwrite/.claude/skills/ops-deploy/SKILL.md +0 -119
  161. package/expo/copy-overwrite/.claude/skills/ops-monitor-errors/SKILL.md +0 -99
  162. package/expo/copy-overwrite/.claude/skills/ops-performance/SKILL.md +0 -165
  163. package/expo/copy-overwrite/.claude/skills/ops-run-local/SKILL.md +0 -166
  164. package/expo/copy-overwrite/.claude/skills/ops-verify-health/SKILL.md +0 -101
  165. package/expo/copy-overwrite/.claude/skills/owasp-zap/SKILL.md +0 -56
  166. package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +0 -223
  167. package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +0 -314
  168. package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +0 -420
  169. package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +0 -556
  170. package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +0 -590
  171. package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +0 -291
  172. package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +0 -234
  173. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +0 -89
  174. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +0 -201
  175. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +0 -294
  176. package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +0 -37
  177. package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +0 -10
  178. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +0 -235
  179. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +0 -96
  180. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +0 -183
  181. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +0 -243
  182. package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +0 -192
  183. package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +0 -31
  184. package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +0 -10
  185. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +0 -56
  186. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +0 -60
  187. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +0 -176
  188. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +0 -527
  189. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +0 -483
  190. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +0 -257
  191. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +0 -413
  192. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +0 -513
  193. package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +0 -536
  194. package/nestjs/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
  195. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +0 -275
  196. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +0 -487
  197. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +0 -450
  198. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +0 -536
  199. package/rails/copy-overwrite/.claude/skills/action-controller-best-practices/SKILL.md +0 -374
  200. package/rails/copy-overwrite/.claude/skills/action-view-best-practices/SKILL.md +0 -335
  201. package/rails/copy-overwrite/.claude/skills/active-record-model-best-practices/SKILL.md +0 -166
  202. package/rails/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -45
  203. package/rails/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
  204. package/rails/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -48
  205. package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -46
  206. package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
  207. package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +0 -76
  208. package/typescript/copy-overwrite/.claude/hooks/install-pkgs.sh +0 -64
  209. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +0 -105
  210. package/typescript/copy-overwrite/.claude/hooks/sg-scan-on-edit.sh +0 -68
  211. package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +0 -432
  212. package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +0 -149
  213. package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
  214. package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +0 -28
  215. package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +0 -10
  216. package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +0 -162
@@ -1,505 +0,0 @@
1
- # AsyncStorage Reference
2
-
3
- ## Overview
4
-
5
- AsyncStorage is an asynchronous, unencrypted, persistent key-value storage system for React Native. The community package `@react-native-async-storage/async-storage` is the standard solution for Expo projects.
6
-
7
- ## Key Characteristics
8
-
9
- - **Asynchronous**: All operations return Promises (non-blocking)
10
- - **String-only**: Stores only string values (JSON serialization required for objects)
11
- - **Persistent**: Data survives app restarts
12
- - **Unencrypted**: Not suitable for sensitive data
13
- - **Size limits**: ~6 MB total storage, ~2 MB per record (varies by platform)
14
-
15
- ## Core API
16
-
17
- ### Basic Operations
18
-
19
- ```typescript
20
- import AsyncStorage from "@react-native-async-storage/async-storage";
21
-
22
- // Set a value
23
- await AsyncStorage.setItem("@app:key", "string value");
24
-
25
- // Get a value (returns string | null)
26
- const value = await AsyncStorage.getItem("@app:key");
27
-
28
- // Remove a value
29
- await AsyncStorage.removeItem("@app:key");
30
-
31
- // Get all keys
32
- const allKeys = await AsyncStorage.getAllKeys();
33
-
34
- // Clear all storage (use with caution)
35
- await AsyncStorage.clear();
36
- ```
37
-
38
- ### Batch Operations
39
-
40
- More efficient than multiple single operations:
41
-
42
- ```typescript
43
- // Get multiple values
44
- const keyValuePairs = await AsyncStorage.multiGet([
45
- "@app:user-prefs",
46
- "@app:filters",
47
- "@app:theme",
48
- ]);
49
- // Returns: [["@app:user-prefs", "..."], ["@app:filters", "..."], ...]
50
-
51
- // Set multiple values
52
- await AsyncStorage.multiSet([
53
- ["@app:user-prefs", JSON.stringify(prefs)],
54
- ["@app:filters", JSON.stringify(filters)],
55
- ]);
56
-
57
- // Remove multiple keys
58
- await AsyncStorage.multiRemove(["@app:temp-data", "@app:cache"]);
59
-
60
- // Merge values (shallow merge for JSON objects)
61
- await AsyncStorage.multiMerge([
62
- ["@app:user", JSON.stringify({ name: "John" })],
63
- ["@app:user", JSON.stringify({ age: 30 })],
64
- ]);
65
- // Result: { name: "John", age: 30 }
66
- ```
67
-
68
- ## Type-Safe Utilities
69
-
70
- ### Generic Storage Functions
71
-
72
- ```typescript
73
- import AsyncStorage from "@react-native-async-storage/async-storage";
74
-
75
- /**
76
- * Save typed data to AsyncStorage with error handling
77
- * @param key - Storage key (should include namespace prefix)
78
- * @param value - Value to store (will be JSON stringified)
79
- */
80
- export const saveToStorage = async <T>(
81
- key: string,
82
- value: T
83
- ): Promise<void> => {
84
- try {
85
- const jsonValue = JSON.stringify(value);
86
- await AsyncStorage.setItem(key, jsonValue);
87
- } catch (error) {
88
- const message = error instanceof Error ? error.message : "Unknown error";
89
- console.error(`Failed to save ${key}:`, message);
90
- throw error;
91
- }
92
- };
93
-
94
- /**
95
- * Load typed data from AsyncStorage with error handling
96
- * @param key - Storage key
97
- * @param defaultValue - Default value if key doesn't exist or parsing fails
98
- * @returns Parsed value or default
99
- */
100
- export const loadFromStorage = async <T>(
101
- key: string,
102
- defaultValue: T
103
- ): Promise<T> => {
104
- try {
105
- const jsonValue = await AsyncStorage.getItem(key);
106
- if (jsonValue === null) {
107
- return defaultValue;
108
- }
109
- return JSON.parse(jsonValue) as T;
110
- } catch (error) {
111
- const message = error instanceof Error ? error.message : "Unknown error";
112
- console.error(`Failed to load ${key}:`, message);
113
- return defaultValue;
114
- }
115
- };
116
-
117
- /**
118
- * Remove data from AsyncStorage
119
- * @param key - Storage key to remove
120
- */
121
- export const removeFromStorage = async (key: string): Promise<void> => {
122
- try {
123
- await AsyncStorage.removeItem(key);
124
- } catch (error) {
125
- const message = error instanceof Error ? error.message : "Unknown error";
126
- console.error(`Failed to remove ${key}:`, message);
127
- }
128
- };
129
- ```
130
-
131
- ### Result-Based Error Handling
132
-
133
- For operations where you need to know if they succeeded:
134
-
135
- ```typescript
136
- interface StorageResult<T> {
137
- readonly success: boolean;
138
- readonly data?: T;
139
- readonly error?: string;
140
- }
141
-
142
- /**
143
- * Safely get item with success/failure result
144
- */
145
- export const safeGetItem = async <T>(
146
- key: string,
147
- defaultValue: T
148
- ): Promise<StorageResult<T>> => {
149
- try {
150
- const jsonValue = await AsyncStorage.getItem(key);
151
- if (jsonValue === null) {
152
- return { success: true, data: defaultValue };
153
- }
154
- const parsed = JSON.parse(jsonValue) as T;
155
- return { success: true, data: parsed };
156
- } catch (error) {
157
- const errorMessage =
158
- error instanceof Error ? error.message : "Unknown error";
159
- return { success: false, error: errorMessage, data: defaultValue };
160
- }
161
- };
162
-
163
- /**
164
- * Safely set item with success/failure result
165
- */
166
- export const safeSetItem = async <T>(
167
- key: string,
168
- value: T
169
- ): Promise<StorageResult<void>> => {
170
- try {
171
- await AsyncStorage.setItem(key, JSON.stringify(value));
172
- return { success: true };
173
- } catch (error) {
174
- const errorMessage =
175
- error instanceof Error ? error.message : "Unknown error";
176
- return { success: false, error: errorMessage };
177
- }
178
- };
179
-
180
- // Usage
181
- const result = await safeGetItem<UserPrefs>("@app:prefs", DEFAULT_PREFS);
182
- if (!result.success) {
183
- console.warn("Storage read failed:", result.error);
184
- }
185
- const prefs = result.data;
186
- ```
187
-
188
- ## Key Naming Conventions
189
-
190
- ### Namespace Prefix
191
-
192
- Always use a namespace prefix to avoid collisions with other libraries:
193
-
194
- ```typescript
195
- // Project namespace
196
- const STORAGE_PREFIX = "@whatever";
197
-
198
- // Key examples
199
- const KEYS = {
200
- USER_PREFERENCES: `${STORAGE_PREFIX}:user-preferences`,
201
- FILTER_VALUES: `${STORAGE_PREFIX}:filter-values`,
202
- FEATURE_FLAGS: `${STORAGE_PREFIX}:feature-flags`,
203
- ONBOARDING_COMPLETE: `${STORAGE_PREFIX}:onboarding-complete`,
204
- } as const;
205
- ```
206
-
207
- ### Key Organization
208
-
209
- ```typescript
210
- // Feature-scoped keys
211
- const PLAYER_KEYS = {
212
- FAVORITES: "@whatever:players:favorites",
213
- RECENT_VIEWS: "@whatever:players:recent-views",
214
- COMPARISONS: "@whatever:players:comparisons",
215
- } as const;
216
-
217
- const SETTINGS_KEYS = {
218
- THEME: "@whatever:settings:theme",
219
- LANGUAGE: "@whatever:settings:language",
220
- NOTIFICATIONS: "@whatever:settings:notifications",
221
- } as const;
222
- ```
223
-
224
- ## Error Handling Patterns
225
-
226
- ### Always Use Try/Catch
227
-
228
- AsyncStorage operations can fail for various reasons (storage full, permissions, corrupted data):
229
-
230
- ```typescript
231
- // CORRECT - proper error handling
232
- const loadSettings = async (): Promise<Settings> => {
233
- try {
234
- const stored = await AsyncStorage.getItem(SETTINGS_KEY);
235
- if (stored) {
236
- return JSON.parse(stored) as Settings;
237
- }
238
- return DEFAULT_SETTINGS;
239
- } catch (error) {
240
- const message = error instanceof Error ? error.message : "Unknown error";
241
- console.error("Failed to load settings:", message);
242
- return DEFAULT_SETTINGS; // Graceful fallback
243
- }
244
- };
245
-
246
- // INCORRECT - unhandled errors can crash the app
247
- const loadSettings = async (): Promise<Settings> => {
248
- const stored = await AsyncStorage.getItem(SETTINGS_KEY);
249
- return stored ? JSON.parse(stored) : DEFAULT_SETTINGS;
250
- };
251
- ```
252
-
253
- ### Handle JSON Parse Errors Separately
254
-
255
- ```typescript
256
- const loadData = async <T>(key: string, defaultValue: T): Promise<T> => {
257
- try {
258
- const jsonValue = await AsyncStorage.getItem(key);
259
- if (jsonValue === null) {
260
- return defaultValue;
261
- }
262
-
263
- try {
264
- return JSON.parse(jsonValue) as T;
265
- } catch (parseError) {
266
- // Corrupted data - clear and return default
267
- console.warn(`Corrupted data for ${key}, resetting to default`);
268
- await AsyncStorage.removeItem(key);
269
- return defaultValue;
270
- }
271
- } catch (storageError) {
272
- const message =
273
- storageError instanceof Error ? storageError.message : "Unknown error";
274
- console.error(`Storage error for ${key}:`, message);
275
- return defaultValue;
276
- }
277
- };
278
- ```
279
-
280
- ## Security Considerations
281
-
282
- ### Never Store Sensitive Data
283
-
284
- AsyncStorage is unencrypted and readable by anyone with device access:
285
-
286
- ```typescript
287
- // NEVER store these in AsyncStorage:
288
- // - Access tokens
289
- // - Refresh tokens
290
- // - Passwords
291
- // - API keys
292
- // - Personal identification numbers
293
- // - Financial data
294
-
295
- // Use expo-secure-store instead:
296
- import * as SecureStore from "expo-secure-store";
297
-
298
- const saveToken = async (token: string): Promise<void> => {
299
- await SecureStore.setItemAsync("accessToken", token);
300
- };
301
-
302
- const getToken = async (): Promise<string | null> => {
303
- return await SecureStore.getItemAsync("accessToken");
304
- };
305
- ```
306
-
307
- ### What IS Safe for AsyncStorage
308
-
309
- - User preferences (theme, language, notifications)
310
- - Feature flags
311
- - UI state (last viewed tab, collapsed sections)
312
- - Non-sensitive cache data
313
- - Onboarding completion flags
314
- - Search history (non-sensitive queries)
315
-
316
- ## Performance Best Practices
317
-
318
- ### Use Batch Operations
319
-
320
- ```typescript
321
- // EFFICIENT - single batch operation
322
- await AsyncStorage.multiSet([
323
- ["@app:pref1", JSON.stringify(pref1)],
324
- ["@app:pref2", JSON.stringify(pref2)],
325
- ["@app:pref3", JSON.stringify(pref3)],
326
- ]);
327
-
328
- // INEFFICIENT - multiple sequential operations
329
- await AsyncStorage.setItem("@app:pref1", JSON.stringify(pref1));
330
- await AsyncStorage.setItem("@app:pref2", JSON.stringify(pref2));
331
- await AsyncStorage.setItem("@app:pref3", JSON.stringify(pref3));
332
- ```
333
-
334
- ### Debounce Frequent Writes
335
-
336
- For values that change frequently (e.g., form inputs):
337
-
338
- ```typescript
339
- import { useDebouncedCallback } from "use-debounce";
340
-
341
- const usePersistedFilter = () => {
342
- const [filter, setFilter] = useState("");
343
-
344
- const persistFilter = useDebouncedCallback(
345
- async (value: string) => {
346
- try {
347
- await AsyncStorage.setItem("@app:filter", value);
348
- } catch (error) {
349
- console.error("Failed to persist filter");
350
- }
351
- },
352
- 500 // 500ms debounce
353
- );
354
-
355
- const updateFilter = useCallback(
356
- (value: string) => {
357
- setFilter(value);
358
- persistFilter(value);
359
- },
360
- [persistFilter]
361
- );
362
-
363
- return { filter, updateFilter };
364
- };
365
- ```
366
-
367
- ### Load Only What You Need
368
-
369
- ```typescript
370
- // EFFICIENT - load specific keys
371
- const loadUserContext = async () => {
372
- const keys = ["@app:user-prefs", "@app:theme"];
373
- const results = await AsyncStorage.multiGet(keys);
374
- return results.reduce(
375
- (acc, [key, value]) => ({
376
- ...acc,
377
- [key]: value ? JSON.parse(value) : null,
378
- }),
379
- {} as Record<string, unknown>
380
- );
381
- };
382
-
383
- // INEFFICIENT - loading all keys
384
- const loadEverything = async () => {
385
- const allKeys = await AsyncStorage.getAllKeys();
386
- const allData = await AsyncStorage.multiGet(allKeys);
387
- // Processing potentially hundreds of keys...
388
- };
389
- ```
390
-
391
- ### Consider Storage Limits
392
-
393
- ```typescript
394
- // Check data size before storing
395
- const safeStore = async <T>(key: string, data: T): Promise<boolean> => {
396
- const serialized = JSON.stringify(data);
397
- const sizeInBytes = new Blob([serialized]).size;
398
-
399
- // Warn if approaching 2MB limit per key
400
- if (sizeInBytes > 1_500_000) {
401
- console.warn(
402
- `Data for ${key} is ${(sizeInBytes / 1_000_000).toFixed(2)}MB`
403
- );
404
- }
405
-
406
- if (sizeInBytes > 2_000_000) {
407
- console.error(`Data for ${key} exceeds 2MB limit, not storing`);
408
- return false;
409
- }
410
-
411
- try {
412
- await AsyncStorage.setItem(key, serialized);
413
- return true;
414
- } catch (error) {
415
- console.error("Storage failed:", error);
416
- return false;
417
- }
418
- };
419
- ```
420
-
421
- ## Migration Patterns
422
-
423
- ### Versioned Storage Keys
424
-
425
- ```typescript
426
- const STORAGE_VERSION = "v2";
427
- const PREFS_KEY = `@app:preferences:${STORAGE_VERSION}`;
428
-
429
- // Migration on app start
430
- const migrateStorageIfNeeded = async (): Promise<void> => {
431
- const allKeys = await AsyncStorage.getAllKeys();
432
-
433
- // Find old version keys
434
- const oldKeys = allKeys.filter(
435
- key => key.startsWith("@app:preferences:") && !key.includes(STORAGE_VERSION)
436
- );
437
-
438
- if (oldKeys.length === 0) return;
439
-
440
- // Migrate each old key
441
- const migrations = oldKeys.map(async oldKey => {
442
- try {
443
- const oldData = await AsyncStorage.getItem(oldKey);
444
- if (oldData) {
445
- const parsed = JSON.parse(oldData);
446
- const migrated = migratePreferences(parsed); // Your migration logic
447
- await AsyncStorage.setItem(PREFS_KEY, JSON.stringify(migrated));
448
- }
449
- await AsyncStorage.removeItem(oldKey);
450
- } catch (error) {
451
- console.error(`Failed to migrate ${oldKey}:`, error);
452
- }
453
- });
454
-
455
- await Promise.all(migrations);
456
- };
457
- ```
458
-
459
- ### Schema Validation
460
-
461
- ```typescript
462
- interface StoredPreferences {
463
- readonly theme: "light" | "dark";
464
- readonly language: string;
465
- readonly schemaVersion: number;
466
- }
467
-
468
- const CURRENT_SCHEMA_VERSION = 2;
469
-
470
- const isValidPreferences = (data: unknown): data is StoredPreferences => {
471
- if (typeof data !== "object" || data === null) return false;
472
- const obj = data as Record<string, unknown>;
473
- return (
474
- (obj.theme === "light" || obj.theme === "dark") &&
475
- typeof obj.language === "string" &&
476
- typeof obj.schemaVersion === "number"
477
- );
478
- };
479
-
480
- const loadPreferences = async (): Promise<StoredPreferences> => {
481
- try {
482
- const stored = await AsyncStorage.getItem(PREFS_KEY);
483
- if (!stored) return DEFAULT_PREFERENCES;
484
-
485
- const parsed = JSON.parse(stored);
486
-
487
- if (!isValidPreferences(parsed)) {
488
- console.warn("Invalid preferences schema, resetting");
489
- await AsyncStorage.removeItem(PREFS_KEY);
490
- return DEFAULT_PREFERENCES;
491
- }
492
-
493
- if (parsed.schemaVersion < CURRENT_SCHEMA_VERSION) {
494
- const migrated = migrateToCurrentSchema(parsed);
495
- await AsyncStorage.setItem(PREFS_KEY, JSON.stringify(migrated));
496
- return migrated;
497
- }
498
-
499
- return parsed;
500
- } catch (error) {
501
- console.error("Failed to load preferences:", error);
502
- return DEFAULT_PREFERENCES;
503
- }
504
- };
505
- ```