@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.
- package/LICENSE +21 -0
- package/README.md +867 -0
- package/all/copy-overwrite/.claude/README.md +205 -0
- package/all/copy-overwrite/.claude/agents/agent-architect.md +311 -0
- package/all/copy-overwrite/.claude/agents/codebase-analyzer.md +146 -0
- package/all/copy-overwrite/.claude/agents/codebase-locator.md +125 -0
- package/all/copy-overwrite/.claude/agents/codebase-pattern-finder.md +237 -0
- package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +183 -0
- package/all/copy-overwrite/.claude/agents/hooks-expert.md +74 -0
- package/all/copy-overwrite/.claude/agents/skill-evaluator.md +246 -0
- package/all/copy-overwrite/.claude/agents/slash-command-architect.md +87 -0
- package/all/copy-overwrite/.claude/agents/web-search-researcher.md +112 -0
- package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +8 -0
- package/all/copy-overwrite/.claude/commands/git/commit.md +44 -0
- package/all/copy-overwrite/.claude/commands/git/prune.md +34 -0
- package/all/copy-overwrite/.claude/commands/git/submit-pr.md +50 -0
- package/all/copy-overwrite/.claude/commands/jira/create.md +50 -0
- package/all/copy-overwrite/.claude/commands/jira/verify.md +34 -0
- package/all/copy-overwrite/.claude/commands/project/archive.md +8 -0
- package/all/copy-overwrite/.claude/commands/project/bootstrap.md +49 -0
- package/all/copy-overwrite/.claude/commands/project/complete-task.md +7 -0
- package/all/copy-overwrite/.claude/commands/project/debrief.md +65 -0
- package/all/copy-overwrite/.claude/commands/project/execute.md +94 -0
- package/all/copy-overwrite/.claude/commands/project/implement.md +42 -0
- package/all/copy-overwrite/.claude/commands/project/local-code-review.md +88 -0
- package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +74 -0
- package/all/copy-overwrite/.claude/commands/project/plan.md +314 -0
- package/all/copy-overwrite/.claude/commands/project/research.md +248 -0
- package/all/copy-overwrite/.claude/commands/project/review.md +63 -0
- package/all/copy-overwrite/.claude/commands/project/setup.md +19 -0
- package/all/copy-overwrite/.claude/commands/project/verify.md +38 -0
- package/all/copy-overwrite/.claude/commands/pull-request/review.md +12 -0
- package/all/copy-overwrite/.claude/commands/rules/format-md.md +72 -0
- package/all/copy-overwrite/.claude/commands/sonarqube/check.md +6 -0
- package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +3 -0
- package/all/copy-overwrite/.claude/hooks/README.md +301 -0
- package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +181 -0
- package/all/copy-overwrite/.claude/settings.json +41 -0
- package/all/copy-overwrite/.claude/settings.local.json.example +14 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/SKILL.md +405 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +416 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +316 -0
- package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +118 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +210 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/all/copy-overwrite/CLAUDE.md +77 -0
- package/all/copy-overwrite/HUMAN.md +17 -0
- package/all/copy-overwrite/specs/.keep +0 -0
- package/all/create-only/PROJECT_RULES.md +0 -0
- package/cdk/merge/package.json +20 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +107 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +45 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +58 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/core/config.d.ts +73 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +36 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lisa.d.ts +81 -0
- package/dist/core/lisa.d.ts.map +1 -0
- package/dist/core/lisa.js +459 -0
- package/dist/core/lisa.js.map +1 -0
- package/dist/core/manifest.d.ts +58 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +104 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/detection/detector.interface.d.ts +15 -0
- package/dist/detection/detector.interface.d.ts.map +1 -0
- package/dist/detection/detector.interface.js +2 -0
- package/dist/detection/detector.interface.js.map +1 -0
- package/dist/detection/detectors/cdk.d.ts +10 -0
- package/dist/detection/detectors/cdk.d.ts.map +1 -0
- package/dist/detection/detectors/cdk.js +34 -0
- package/dist/detection/detectors/cdk.js.map +1 -0
- package/dist/detection/detectors/expo.d.ts +10 -0
- package/dist/detection/detectors/expo.d.ts.map +1 -0
- package/dist/detection/detectors/expo.js +30 -0
- package/dist/detection/detectors/expo.js.map +1 -0
- package/dist/detection/detectors/nestjs.d.ts +10 -0
- package/dist/detection/detectors/nestjs.d.ts.map +1 -0
- package/dist/detection/detectors/nestjs.js +34 -0
- package/dist/detection/detectors/nestjs.js.map +1 -0
- package/dist/detection/detectors/npm-package.d.ts +13 -0
- package/dist/detection/detectors/npm-package.d.ts.map +1 -0
- package/dist/detection/detectors/npm-package.js +30 -0
- package/dist/detection/detectors/npm-package.js.map +1 -0
- package/dist/detection/detectors/typescript.d.ts +10 -0
- package/dist/detection/detectors/typescript.d.ts.map +1 -0
- package/dist/detection/detectors/typescript.js +25 -0
- package/dist/detection/detectors/typescript.js.map +1 -0
- package/dist/detection/index.d.ts +24 -0
- package/dist/detection/index.d.ts.map +1 -0
- package/dist/detection/index.js +57 -0
- package/dist/detection/index.js.map +1 -0
- package/dist/errors/index.d.ts +69 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +110 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/console-logger.d.ts +12 -0
- package/dist/logging/console-logger.d.ts.map +1 -0
- package/dist/logging/console-logger.js +22 -0
- package/dist/logging/console-logger.js.map +1 -0
- package/dist/logging/index.d.ts +4 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +3 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/logger.interface.d.ts +20 -0
- package/dist/logging/logger.interface.d.ts.map +1 -0
- package/dist/logging/logger.interface.js +2 -0
- package/dist/logging/logger.interface.js.map +1 -0
- package/dist/logging/silent-logger.d.ts +12 -0
- package/dist/logging/silent-logger.d.ts.map +1 -0
- package/dist/logging/silent-logger.js +21 -0
- package/dist/logging/silent-logger.js.map +1 -0
- package/dist/strategies/copy-contents.d.ts +14 -0
- package/dist/strategies/copy-contents.d.ts.map +1 -0
- package/dist/strategies/copy-contents.js +69 -0
- package/dist/strategies/copy-contents.js.map +1 -0
- package/dist/strategies/copy-overwrite.d.ts +14 -0
- package/dist/strategies/copy-overwrite.d.ts.map +1 -0
- package/dist/strategies/copy-overwrite.js +47 -0
- package/dist/strategies/copy-overwrite.js.map +1 -0
- package/dist/strategies/create-only.d.ts +13 -0
- package/dist/strategies/create-only.d.ts.map +1 -0
- package/dist/strategies/create-only.js +30 -0
- package/dist/strategies/create-only.js.map +1 -0
- package/dist/strategies/index.d.ts +31 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +52 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/strategies/merge.d.ts +13 -0
- package/dist/strategies/merge.d.ts.map +1 -0
- package/dist/strategies/merge.js +60 -0
- package/dist/strategies/merge.js.map +1 -0
- package/dist/strategies/strategy.interface.d.ts +31 -0
- package/dist/strategies/strategy.interface.d.ts.map +1 -0
- package/dist/strategies/strategy.interface.js +2 -0
- package/dist/strategies/strategy.interface.js.map +1 -0
- package/dist/transaction/backup.d.ts +38 -0
- package/dist/transaction/backup.d.ts.map +1 -0
- package/dist/transaction/backup.js +97 -0
- package/dist/transaction/backup.js.map +1 -0
- package/dist/transaction/index.d.ts +4 -0
- package/dist/transaction/index.d.ts.map +1 -0
- package/dist/transaction/index.js +3 -0
- package/dist/transaction/index.js.map +1 -0
- package/dist/transaction/transaction.d.ts +34 -0
- package/dist/transaction/transaction.d.ts.map +1 -0
- package/dist/transaction/transaction.js +68 -0
- package/dist/transaction/transaction.js.map +1 -0
- package/dist/utils/file-operations.d.ts +29 -0
- package/dist/utils/file-operations.d.ts.map +1 -0
- package/dist/utils/file-operations.js +84 -0
- package/dist/utils/file-operations.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/json-utils.d.ts +22 -0
- package/dist/utils/json-utils.d.ts.map +1 -0
- package/dist/utils/json-utils.js +57 -0
- package/dist/utils/json-utils.js.map +1 -0
- package/dist/utils/path-utils.d.ts +21 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/path-utils.js +35 -0
- package/dist/utils/path-utils.js.map +1 -0
- package/eslint-plugin-code-organization/README.md +149 -0
- package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
- package/eslint-plugin-code-organization/index.js +23 -0
- package/eslint-plugin-code-organization/package.json +10 -0
- package/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
- package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +238 -0
- package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +360 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +360 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +417 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +257 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +233 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +327 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +299 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +749 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +318 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +198 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +207 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +268 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +619 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +340 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +276 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +414 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +202 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +443 -0
- package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +309 -0
- package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +417 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +431 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +290 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +169 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +411 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +343 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +307 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +300 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +354 -0
- package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +362 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +505 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +711 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +446 -0
- package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +223 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +319 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +420 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +556 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +590 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +291 -0
- package/expo/copy-overwrite/.easignore.extra +2 -0
- package/expo/copy-overwrite/.mcp.json +33 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +234 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +84 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +196 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +289 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +32 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +10 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +230 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +91 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +178 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +238 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +260 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +29 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +10 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +51 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +55 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +73 -0
- package/expo/copy-overwrite/eslint.config.mjs +560 -0
- package/expo/copy-overwrite/lighthouserc.js +194 -0
- package/expo/create-only/lighthouserc-config.json +28 -0
- package/expo/merge/package.json +132 -0
- package/lisa.sh +35 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +176 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +527 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +483 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +257 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +413 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +513 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +536 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +275 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +487 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +450 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +536 -0
- package/nestjs/merge/package.json +75 -0
- package/package.json +124 -0
- package/typescript/copy-contents/.husky/commit-msg +91 -0
- package/typescript/copy-contents/.husky/pre-commit +96 -0
- package/typescript/copy-contents/.husky/pre-push +211 -0
- package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +74 -0
- package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +59 -0
- package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +103 -0
- package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +388 -0
- package/typescript/copy-overwrite/.github/README.md +455 -0
- package/typescript/copy-overwrite/.github/dependabot.yml +40 -0
- package/typescript/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +129 -0
- package/typescript/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +354 -0
- package/typescript/copy-overwrite/.github/k6/README.md +386 -0
- package/typescript/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +264 -0
- package/typescript/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +115 -0
- package/typescript/copy-overwrite/.github/k6/examples/data-driven-test.js +268 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/load.js +142 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/load.json +27 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/smoke.js +26 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/smoke.json +20 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/soak.js +244 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/soak.json +29 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/spike.js +180 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/spike.json +32 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/stress.js +206 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/stress.json +38 -0
- package/typescript/copy-overwrite/.github/k6/scripts/api-test.js +452 -0
- package/typescript/copy-overwrite/.github/k6/scripts/default-test.js +185 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/normal.json +30 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/relaxed.json +21 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/strict.json +29 -0
- package/typescript/copy-overwrite/.github/workflows/build.yml +72 -0
- package/typescript/copy-overwrite/.github/workflows/ci.yml +49 -0
- package/typescript/copy-overwrite/.github/workflows/claude.yml +51 -0
- package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +113 -0
- package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +195 -0
- package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +267 -0
- package/typescript/copy-overwrite/.github/workflows/deploy.yml +228 -0
- package/typescript/copy-overwrite/.github/workflows/k6-load-test-README.md +230 -0
- package/typescript/copy-overwrite/.github/workflows/lighthouse.yml +68 -0
- package/typescript/copy-overwrite/.github/workflows/load-test.yml +282 -0
- package/typescript/copy-overwrite/.github/workflows/quality.yml +1737 -0
- package/typescript/copy-overwrite/.github/workflows/release.yml +1599 -0
- package/typescript/copy-overwrite/.gitleaksignore +28 -0
- package/typescript/copy-overwrite/.nvmrc +1 -0
- package/typescript/copy-overwrite/.prettierignore +23 -0
- package/typescript/copy-overwrite/.prettierrc.json +22 -0
- package/typescript/copy-overwrite/.versionrc +42 -0
- package/typescript/copy-overwrite/.yamllint +20 -0
- package/typescript/copy-overwrite/commitlint.config.js +11 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +149 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +23 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +10 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
- package/typescript/copy-overwrite/eslint.config.mjs +390 -0
- package/typescript/copy-overwrite/eslint.ignore.config.json +57 -0
- package/typescript/copy-overwrite/eslint.thresholds.config.json +5 -0
- package/typescript/github-rulesets/base.json +106 -0
- package/typescript/merge/.claude/settings.json +28 -0
- package/typescript/merge/package.json +71 -0
|
@@ -0,0 +1,505 @@
|
|
|
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
|
+
```
|