@codyswann/lisa 1.47.1 → 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.
- package/all/copy-overwrite/.claude/rules/lisa.md +23 -10
- package/all/copy-overwrite/.claude/settings.json +10 -252
- package/all/deletions.json +67 -1
- package/cdk/copy-overwrite/.claude/settings.json +80 -0
- package/cdk/create-only/.github/workflows/ci.yml +1 -1
- package/cdk/create-only/.github/workflows/deploy.yml +1 -1
- package/dist/core/lisa.d.ts +14 -0
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +47 -0
- package/dist/core/lisa.js.map +1 -1
- package/expo/copy-overwrite/.claude/settings.json +80 -0
- package/expo/copy-overwrite/eslint.expo.ts +2 -2
- package/expo/create-only/.github/workflows/ci.yml +1 -1
- package/expo/create-only/.github/workflows/deploy.yml +1 -1
- package/expo/deletions.json +33 -0
- package/expo/package-lisa/package.lisa.json +2 -2
- package/nestjs/copy-overwrite/.claude/settings.json +80 -0
- package/nestjs/create-only/.github/workflows/ci.yml +1 -1
- package/nestjs/create-only/.github/workflows/deploy.yml +1 -1
- package/nestjs/deletions.json +8 -0
- package/package.json +8 -4
- package/rails/copy-overwrite/.claude/settings.json +80 -0
- package/rails/create-only/.github/workflows/ci.yml +1 -1
- package/rails/deletions.json +11 -1
- package/typescript/copy-overwrite/.claude/settings.json +13 -253
- package/typescript/copy-overwrite/eslint.typescript.ts +1 -1
- package/typescript/create-only/.github/workflows/ci.yml +1 -1
- package/typescript/deletions.json +12 -1
- package/typescript/package-lisa/package.lisa.json +1 -1
- package/all/copy-overwrite/.claude/agents/agent-architect.md +0 -310
- package/all/copy-overwrite/.claude/agents/architecture-specialist.md +0 -53
- package/all/copy-overwrite/.claude/agents/debug-specialist.md +0 -204
- package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +0 -183
- package/all/copy-overwrite/.claude/agents/hooks-expert.md +0 -74
- package/all/copy-overwrite/.claude/agents/implementer.md +0 -54
- package/all/copy-overwrite/.claude/agents/learner.md +0 -44
- package/all/copy-overwrite/.claude/agents/performance-specialist.md +0 -95
- package/all/copy-overwrite/.claude/agents/product-specialist.md +0 -72
- package/all/copy-overwrite/.claude/agents/quality-specialist.md +0 -55
- package/all/copy-overwrite/.claude/agents/security-specialist.md +0 -58
- package/all/copy-overwrite/.claude/agents/skill-evaluator.md +0 -246
- package/all/copy-overwrite/.claude/agents/slash-command-architect.md +0 -87
- package/all/copy-overwrite/.claude/agents/test-specialist.md +0 -64
- package/all/copy-overwrite/.claude/agents/verification-specialist.md +0 -189
- package/all/copy-overwrite/.claude/agents/web-search-researcher.md +0 -112
- package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +0 -7
- package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-and-verify.md +0 -7
- package/all/copy-overwrite/.claude/commands/git/commit-submit-pr-deploy-and-verify.md +0 -7
- package/all/copy-overwrite/.claude/commands/git/commit.md +0 -7
- package/all/copy-overwrite/.claude/commands/git/prune.md +0 -6
- package/all/copy-overwrite/.claude/commands/git/submit-pr.md +0 -7
- package/all/copy-overwrite/.claude/commands/jira/create.md +0 -7
- package/all/copy-overwrite/.claude/commands/jira/sync.md +0 -7
- package/all/copy-overwrite/.claude/commands/jira/verify.md +0 -7
- package/all/copy-overwrite/.claude/commands/lisa/review-implementation.md +0 -7
- package/all/copy-overwrite/.claude/commands/plan/add-test-coverage.md +0 -7
- package/all/copy-overwrite/.claude/commands/plan/create.md +0 -6
- package/all/copy-overwrite/.claude/commands/plan/execute.md +0 -7
- package/all/copy-overwrite/.claude/commands/plan/fix-linter-error.md +0 -7
- package/all/copy-overwrite/.claude/commands/plan/local-code-review.md +0 -6
- package/all/copy-overwrite/.claude/commands/plan/lower-code-complexity.md +0 -6
- package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines-per-function.md +0 -7
- package/all/copy-overwrite/.claude/commands/plan/reduce-max-lines.md +0 -7
- package/all/copy-overwrite/.claude/commands/pull-request/review.md +0 -7
- package/all/copy-overwrite/.claude/commands/security/zap-scan.md +0 -6
- package/all/copy-overwrite/.claude/commands/sonarqube/check.md +0 -6
- package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +0 -6
- package/all/copy-overwrite/.claude/commands/tasks/load.md +0 -7
- package/all/copy-overwrite/.claude/commands/tasks/sync.md +0 -7
- package/all/copy-overwrite/.claude/hooks/check-tired-boss.sh +0 -61
- package/all/copy-overwrite/.claude/hooks/debug-hook.sh +0 -47
- package/all/copy-overwrite/.claude/hooks/enforce-plan-rules.sh +0 -15
- package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +0 -183
- package/all/copy-overwrite/.claude/hooks/setup-jira-cli.sh +0 -52
- package/all/copy-overwrite/.claude/hooks/sync-tasks.sh +0 -107
- package/all/copy-overwrite/.claude/hooks/ticket-sync-reminder.sh +0 -23
- package/all/copy-overwrite/.claude/hooks/track-plan-sessions.sh +0 -164
- package/all/copy-overwrite/.claude/hooks/verify-completion.sh +0 -77
- package/all/copy-overwrite/.claude/rules/coding-philosophy.md +0 -428
- package/all/copy-overwrite/.claude/rules/verfication.md +0 -596
- package/all/copy-overwrite/.claude/skills/agent-design-best-practices/SKILL.md +0 -219
- package/all/copy-overwrite/.claude/skills/git-commit/SKILL.md +0 -48
- package/all/copy-overwrite/.claude/skills/git-commit-and-submit-pr/SKILL.md +0 -8
- package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-and-verify/SKILL.md +0 -7
- package/all/copy-overwrite/.claude/skills/git-commit-submit-pr-deploy-and-verify/SKILL.md +0 -7
- package/all/copy-overwrite/.claude/skills/git-prune/SKILL.md +0 -35
- package/all/copy-overwrite/.claude/skills/git-submit-pr/SKILL.md +0 -44
- package/all/copy-overwrite/.claude/skills/jira-create/SKILL.md +0 -41
- package/all/copy-overwrite/.claude/skills/jira-sync/SKILL.md +0 -63
- package/all/copy-overwrite/.claude/skills/jira-verify/SKILL.md +0 -29
- package/all/copy-overwrite/.claude/skills/lisa-review-implementation/SKILL.md +0 -209
- package/all/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -44
- package/all/copy-overwrite/.claude/skills/plan-execute/SKILL.md +0 -89
- package/all/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
- package/all/copy-overwrite/.claude/skills/plan-local-code-review/SKILL.md +0 -88
- package/all/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -44
- package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -45
- package/all/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
- package/all/copy-overwrite/.claude/skills/pull-request-review/SKILL.md +0 -68
- package/all/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
- package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +0 -202
- package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +0 -210
- 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 +0 -305
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +0 -112
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +0 -67
- package/all/copy-overwrite/.claude/skills/sonarqube-check/SKILL.md +0 -11
- package/all/copy-overwrite/.claude/skills/sonarqube-fix/SKILL.md +0 -8
- package/all/copy-overwrite/.claude/skills/tasks-load/SKILL.md +0 -88
- package/all/copy-overwrite/.claude/skills/tasks-sync/SKILL.md +0 -108
- package/eslint-plugin-code-organization/README.md +0 -149
- package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
- package/eslint-plugin-code-organization/index.js +0 -28
- package/eslint-plugin-code-organization/package.json +0 -10
- package/eslint-plugin-code-organization/rules/enforce-statement-order.js +0 -162
- package/expo/copy-overwrite/.claude/agents/ops-specialist.md +0 -124
- package/expo/copy-overwrite/.claude/rules/expo-verification.md +0 -261
- package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +0 -238
- package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +0 -360
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +0 -360
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +0 -417
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +0 -257
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +0 -233
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +0 -329
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +0 -299
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +0 -749
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +0 -318
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +0 -200
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +0 -209
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +0 -268
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +0 -619
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +0 -340
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +0 -276
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +0 -416
- package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +0 -202
- package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +0 -445
- package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +0 -309
- package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +0 -417
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +0 -431
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +0 -290
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +0 -171
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +0 -411
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +0 -343
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +0 -307
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +0 -300
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +0 -315
- package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +0 -362
- package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +0 -505
- package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +0 -711
- package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +0 -446
- package/expo/copy-overwrite/.claude/skills/ops-browser-uat/SKILL.md +0 -124
- package/expo/copy-overwrite/.claude/skills/ops-check-logs/SKILL.md +0 -211
- package/expo/copy-overwrite/.claude/skills/ops-db-ops/SKILL.md +0 -119
- package/expo/copy-overwrite/.claude/skills/ops-deploy/SKILL.md +0 -119
- package/expo/copy-overwrite/.claude/skills/ops-monitor-errors/SKILL.md +0 -99
- package/expo/copy-overwrite/.claude/skills/ops-performance/SKILL.md +0 -165
- package/expo/copy-overwrite/.claude/skills/ops-run-local/SKILL.md +0 -166
- package/expo/copy-overwrite/.claude/skills/ops-verify-health/SKILL.md +0 -101
- package/expo/copy-overwrite/.claude/skills/owasp-zap/SKILL.md +0 -56
- package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +0 -223
- package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +0 -314
- package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +0 -420
- package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +0 -556
- package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +0 -590
- package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +0 -291
- package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +0 -234
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +0 -89
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +0 -201
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +0 -294
- package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +0 -37
- package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +0 -10
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +0 -235
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +0 -96
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +0 -183
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +0 -243
- package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +0 -192
- package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +0 -31
- package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +0 -10
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +0 -56
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +0 -60
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +0 -176
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +0 -527
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +0 -483
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +0 -257
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +0 -413
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +0 -513
- package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +0 -536
- package/nestjs/copy-overwrite/.claude/skills/security-zap-scan/SKILL.md +0 -33
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +0 -275
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +0 -487
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +0 -450
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +0 -536
- package/rails/copy-overwrite/.claude/skills/action-controller-best-practices/SKILL.md +0 -374
- package/rails/copy-overwrite/.claude/skills/action-view-best-practices/SKILL.md +0 -335
- package/rails/copy-overwrite/.claude/skills/active-record-model-best-practices/SKILL.md +0 -166
- package/rails/copy-overwrite/.claude/skills/plan-add-test-coverage/SKILL.md +0 -45
- package/rails/copy-overwrite/.claude/skills/plan-fix-linter-error/SKILL.md +0 -45
- package/rails/copy-overwrite/.claude/skills/plan-lower-code-complexity/SKILL.md +0 -48
- package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines/SKILL.md +0 -46
- package/rails/copy-overwrite/.claude/skills/plan-reduce-max-lines-per-function/SKILL.md +0 -46
- package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +0 -76
- package/typescript/copy-overwrite/.claude/hooks/install-pkgs.sh +0 -64
- package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +0 -81
- package/typescript/copy-overwrite/.claude/hooks/sg-scan-on-edit.sh +0 -68
- package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +0 -432
- package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +0 -149
- package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +0 -473
- package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +0 -28
- package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +0 -10
- package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +0 -162
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ops-run-local
|
|
3
|
-
description: Manage the local development environment for Expo + serverless backend projects. Supports start, stop, restart, and status for the full stack or individual services.
|
|
4
|
-
allowed-tools:
|
|
5
|
-
- Bash
|
|
6
|
-
- Read
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Ops: Run Local
|
|
10
|
-
|
|
11
|
-
Manage the local development environment.
|
|
12
|
-
|
|
13
|
-
**Argument**: `$ARGUMENTS` — `start`, `stop`, `restart`, `status`, `start-frontend`, `start-backend` (default: `start`)
|
|
14
|
-
|
|
15
|
-
## Path Convention
|
|
16
|
-
|
|
17
|
-
- **Frontend**: Current project directory (`.`)
|
|
18
|
-
- **Backend**: `${BACKEND_DIR:-../backend-v2}` — set `BACKEND_DIR` in `.claude/settings.local.json` if your backend is elsewhere
|
|
19
|
-
|
|
20
|
-
## Prerequisites (run before any operation)
|
|
21
|
-
|
|
22
|
-
1. Verify backend directory exists:
|
|
23
|
-
```bash
|
|
24
|
-
BACKEND_DIR="${BACKEND_DIR:-../backend-v2}"
|
|
25
|
-
test -d "$BACKEND_DIR" && echo "Backend dir OK: $BACKEND_DIR" || echo "ERROR: Backend dir not found at $BACKEND_DIR — set BACKEND_DIR"
|
|
26
|
-
```
|
|
27
|
-
2. Check port availability:
|
|
28
|
-
```bash
|
|
29
|
-
lsof -i :8081 2>/dev/null | grep LISTEN
|
|
30
|
-
lsof -i :3000 2>/dev/null | grep LISTEN
|
|
31
|
-
```
|
|
32
|
-
3. Verify bun is installed:
|
|
33
|
-
```bash
|
|
34
|
-
which bun && bun --version
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Discovery
|
|
38
|
-
|
|
39
|
-
Read the frontend `package.json` to find available start scripts (e.g., `start:local`, `start:dev`, `start:staging`). Read the backend `package.json` to find backend start scripts (e.g., `start:local`, `start:dev`).
|
|
40
|
-
|
|
41
|
-
## Operations
|
|
42
|
-
|
|
43
|
-
### start (full stack)
|
|
44
|
-
|
|
45
|
-
Start both backend and frontend for local development.
|
|
46
|
-
|
|
47
|
-
1. **Start backend** (background):
|
|
48
|
-
```bash
|
|
49
|
-
cd "${BACKEND_DIR:-../backend-v2}" && IS_OFFLINE=true bun run start:local
|
|
50
|
-
```
|
|
51
|
-
Run this in the background using the Bash tool with `run_in_background: true`.
|
|
52
|
-
|
|
53
|
-
2. **Wait for backend** (up to 30 seconds):
|
|
54
|
-
```bash
|
|
55
|
-
for i in $(seq 1 30); do
|
|
56
|
-
curl -sf http://localhost:3000/graphql -X POST \
|
|
57
|
-
-H "Content-Type: application/json" \
|
|
58
|
-
-d '{"query":"{ __typename }"}' > /dev/null 2>&1 && echo "Backend ready" && break
|
|
59
|
-
sleep 1
|
|
60
|
-
done
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
3. **Start frontend** (background):
|
|
64
|
-
```bash
|
|
65
|
-
bun run start:local
|
|
66
|
-
```
|
|
67
|
-
Run this in the background using the Bash tool with `run_in_background: true`.
|
|
68
|
-
|
|
69
|
-
4. **Verify frontend** (up to 60 seconds — Metro bundler can be slow):
|
|
70
|
-
```bash
|
|
71
|
-
for i in $(seq 1 60); do
|
|
72
|
-
curl -sf http://localhost:8081 > /dev/null 2>&1 && echo "Frontend ready" && break
|
|
73
|
-
sleep 1
|
|
74
|
-
done
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
5. Report status table.
|
|
78
|
-
|
|
79
|
-
### start-frontend (frontend only, pointing at remote backend)
|
|
80
|
-
|
|
81
|
-
Use when the backend is already deployed and you only need the frontend.
|
|
82
|
-
|
|
83
|
-
1. ```bash
|
|
84
|
-
bun run start:dev
|
|
85
|
-
```
|
|
86
|
-
Run in background.
|
|
87
|
-
|
|
88
|
-
2. Verify:
|
|
89
|
-
```bash
|
|
90
|
-
for i in $(seq 1 60); do
|
|
91
|
-
curl -sf http://localhost:8081 > /dev/null 2>&1 && echo "Frontend ready" && break
|
|
92
|
-
sleep 1
|
|
93
|
-
done
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### start-backend (backend only)
|
|
97
|
-
|
|
98
|
-
1. Check AWS credentials (discover profile from backend `package.json` `aws:signin:*` scripts):
|
|
99
|
-
```bash
|
|
100
|
-
aws sts get-caller-identity --profile {aws-profile} 2>/dev/null
|
|
101
|
-
```
|
|
102
|
-
If expired, run the backend's `aws:signin:{env}` script.
|
|
103
|
-
|
|
104
|
-
2. Start:
|
|
105
|
-
```bash
|
|
106
|
-
cd "${BACKEND_DIR:-../backend-v2}" && IS_OFFLINE=true bun run start:local
|
|
107
|
-
```
|
|
108
|
-
Run in background.
|
|
109
|
-
|
|
110
|
-
3. Verify:
|
|
111
|
-
```bash
|
|
112
|
-
for i in $(seq 1 30); do
|
|
113
|
-
curl -sf http://localhost:3000/graphql -X POST \
|
|
114
|
-
-H "Content-Type: application/json" \
|
|
115
|
-
-d '{"query":"{ __typename }"}' > /dev/null 2>&1 && echo "Backend ready" && break
|
|
116
|
-
sleep 1
|
|
117
|
-
done
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### stop
|
|
121
|
-
|
|
122
|
-
Kill all local services.
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
# Kill frontend (Metro bundler)
|
|
126
|
-
lsof -ti :8081 | xargs kill -9 2>/dev/null || echo "No frontend process on :8081"
|
|
127
|
-
|
|
128
|
-
# Kill backend (Serverless Offline)
|
|
129
|
-
lsof -ti :3000 | xargs kill -9 2>/dev/null || echo "No backend process on :3000"
|
|
130
|
-
|
|
131
|
-
# Stop Docker if running
|
|
132
|
-
cd "${BACKEND_DIR:-../backend-v2}" && docker compose down 2>/dev/null || true
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### restart
|
|
136
|
-
|
|
137
|
-
1. Run **stop** (above).
|
|
138
|
-
2. Wait 2 seconds: `sleep 2`
|
|
139
|
-
3. Run **start** (above).
|
|
140
|
-
4. Verify both services respond.
|
|
141
|
-
|
|
142
|
-
### status
|
|
143
|
-
|
|
144
|
-
Check what is currently running and responsive.
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
echo "=== Port Check ==="
|
|
148
|
-
echo -n "Frontend :8081 — "; lsof -i :8081 2>/dev/null | grep LISTEN > /dev/null && echo "LISTENING" || echo "NOT LISTENING"
|
|
149
|
-
echo -n "Backend :3000 — "; lsof -i :3000 2>/dev/null | grep LISTEN > /dev/null && echo "LISTENING" || echo "NOT LISTENING"
|
|
150
|
-
|
|
151
|
-
echo ""
|
|
152
|
-
echo "=== Health Check ==="
|
|
153
|
-
echo -n "Frontend :8081 — "; curl -sf -o /dev/null -w "%{http_code}" http://localhost:8081 2>/dev/null || echo "UNREACHABLE"
|
|
154
|
-
echo -n "Backend :3000 — "; curl -sf -o /dev/null -w "%{http_code}" http://localhost:3000/graphql -X POST -H "Content-Type: application/json" -d '{"query":"{ __typename }"}' 2>/dev/null || echo "UNREACHABLE"
|
|
155
|
-
|
|
156
|
-
echo ""
|
|
157
|
-
echo "=== Docker ==="
|
|
158
|
-
docker compose -f "${BACKEND_DIR:-../backend-v2}/compose.yaml" ps 2>/dev/null || echo "No Docker services running"
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Report results as a table:
|
|
162
|
-
|
|
163
|
-
| Service | Port | Listening | Responsive |
|
|
164
|
-
|---------|------|-----------|------------|
|
|
165
|
-
| Frontend (Metro) | 8081 | YES/NO | YES/NO |
|
|
166
|
-
| Backend (Serverless Offline) | 3000 | YES/NO | YES/NO |
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ops-verify-health
|
|
3
|
-
description: Health check all services across environments. Checks frontend URLs, backend GraphQL endpoints, and reports response times.
|
|
4
|
-
allowed-tools:
|
|
5
|
-
- Bash
|
|
6
|
-
- Read
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Ops: Verify Health
|
|
10
|
-
|
|
11
|
-
Health check all services across environments.
|
|
12
|
-
|
|
13
|
-
**Argument**: `$ARGUMENTS` — environment(s) to check (default: `all`). Options: `local`, `dev`, `staging`, `production`, `all`
|
|
14
|
-
|
|
15
|
-
## Discovery
|
|
16
|
-
|
|
17
|
-
Read these files to build the environment URL table:
|
|
18
|
-
|
|
19
|
-
1. `e2e/constants.ts` — frontend URLs per environment
|
|
20
|
-
2. `.env.localhost`, `.env.development`, `.env.staging`, `.env.production` — `EXPO_PUBLIC_GRAPHQL_BASE_URL` for backend GraphQL URLs
|
|
21
|
-
|
|
22
|
-
## Health Checks
|
|
23
|
-
|
|
24
|
-
For each environment, run these checks:
|
|
25
|
-
|
|
26
|
-
### Frontend Check
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
curl -sf -o /dev/null -w "HTTP %{http_code} in %{time_total}s" {frontend_url}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Verify the response contains Expo/React markers:
|
|
33
|
-
```bash
|
|
34
|
-
curl -sf {frontend_url} | head -20
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Backend GraphQL Check
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
curl -sf {graphql_url} -X POST \
|
|
41
|
-
-H "Content-Type: application/json" \
|
|
42
|
-
-d '{"query":"{ __typename }"}' \
|
|
43
|
-
-w "\nHTTP %{http_code} in %{time_total}s\n"
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### GraphQL Introspection (detailed check)
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
curl -sf {graphql_url} -X POST \
|
|
50
|
-
-H "Content-Type: application/json" \
|
|
51
|
-
-d '{"query":"{ __schema { queryType { name } } }"}' \
|
|
52
|
-
-w "\nHTTP %{http_code} in %{time_total}s\n"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Full Health Check Script
|
|
56
|
-
|
|
57
|
-
Run all checks for the specified environment(s):
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
check_env() {
|
|
61
|
-
local ENV=$1
|
|
62
|
-
local FE_URL=$2
|
|
63
|
-
local BE_URL=$3
|
|
64
|
-
|
|
65
|
-
echo "=== $ENV ==="
|
|
66
|
-
|
|
67
|
-
# Frontend
|
|
68
|
-
FE_STATUS=$(curl -sf -o /dev/null -w "%{http_code}" --max-time 10 "$FE_URL" 2>/dev/null || echo "000")
|
|
69
|
-
FE_TIME=$(curl -sf -o /dev/null -w "%{time_total}" --max-time 10 "$FE_URL" 2>/dev/null || echo "timeout")
|
|
70
|
-
echo "Frontend: HTTP $FE_STATUS ($FE_TIME s)"
|
|
71
|
-
|
|
72
|
-
# Backend
|
|
73
|
-
BE_RESULT=$(curl -sf --max-time 10 "$BE_URL" -X POST \
|
|
74
|
-
-H "Content-Type: application/json" \
|
|
75
|
-
-d '{"query":"{ __typename }"}' \
|
|
76
|
-
-w "\n%{http_code} %{time_total}" 2>/dev/null || echo -e "\n000 timeout")
|
|
77
|
-
BE_STATUS=$(echo "$BE_RESULT" | tail -1 | awk '{print $1}')
|
|
78
|
-
BE_TIME=$(echo "$BE_RESULT" | tail -1 | awk '{print $2}')
|
|
79
|
-
echo "Backend: HTTP $BE_STATUS ($BE_TIME s)"
|
|
80
|
-
echo ""
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## EAS Update Status
|
|
85
|
-
|
|
86
|
-
Check the latest OTA updates deployed to each branch:
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
eas update:list --branch {env} --limit 3
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Output Format
|
|
93
|
-
|
|
94
|
-
Report results as a table:
|
|
95
|
-
|
|
96
|
-
| Service | Environment | Status | Response Time | Details |
|
|
97
|
-
|---------|-------------|--------|---------------|---------|
|
|
98
|
-
| Frontend | dev | UP (200) | 0.45s | HTML contains React root |
|
|
99
|
-
| Backend | dev | UP (200) | 0.32s | GraphQL responds `__typename` |
|
|
100
|
-
|
|
101
|
-
Flag any service with status != 200 or response time > 5s as a concern.
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# OWASP ZAP Baseline Scanning
|
|
2
|
-
|
|
3
|
-
OWASP ZAP (Zed Attack Proxy) performs DAST (Dynamic Application Security Testing) by scanning a running application for common security vulnerabilities from the OWASP Top 10.
|
|
4
|
-
|
|
5
|
-
## When to Use
|
|
6
|
-
|
|
7
|
-
- After making changes to HTTP headers, authentication, or security middleware
|
|
8
|
-
- Before deploying to staging or production
|
|
9
|
-
- When reviewing security scan results from CI
|
|
10
|
-
- When triaging ZAP findings from pull request checks
|
|
11
|
-
|
|
12
|
-
## Running Locally
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
# Requires Docker to be installed and running
|
|
16
|
-
bash scripts/zap-baseline.sh
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
The scan builds the Expo web export, serves it locally, and runs ZAP against it. Reports are saved to `zap-report.html`, `zap-report.json`, and `zap-report.md`.
|
|
20
|
-
|
|
21
|
-
## Interpreting Results
|
|
22
|
-
|
|
23
|
-
ZAP findings are categorized by risk level:
|
|
24
|
-
|
|
25
|
-
| Risk | Action |
|
|
26
|
-
|------|--------|
|
|
27
|
-
| **High** | Fix immediately — indicates exploitable vulnerability |
|
|
28
|
-
| **Medium** | Fix before deployment — security best practice violation |
|
|
29
|
-
| **Low** | Fix when convenient — minor security improvement |
|
|
30
|
-
| **Informational** | Review — may be false positive or acceptable risk |
|
|
31
|
-
|
|
32
|
-
## Common Findings and Fixes
|
|
33
|
-
|
|
34
|
-
### Infrastructure-Level (fix at CDN/hosting, not in code)
|
|
35
|
-
|
|
36
|
-
- **CSP Header Not Set**: Configure Content-Security-Policy at CDN or hosting platform. Expo web exports need `script-src 'self' 'unsafe-inline'` for hydration.
|
|
37
|
-
- **HSTS Not Set**: Configure Strict-Transport-Security at CDN/load balancer.
|
|
38
|
-
- **X-Frame-Options**: Use `frame-ancestors` in CSP at CDN level.
|
|
39
|
-
|
|
40
|
-
### Application-Level (fix in code)
|
|
41
|
-
|
|
42
|
-
- **Cookie flags missing**: Ensure all cookies set `HttpOnly`, `Secure`, and `SameSite` attributes.
|
|
43
|
-
- **Debug error messages**: Ensure error boundaries don't leak stack traces in production.
|
|
44
|
-
- **Server version disclosure**: Remove or mask the `Server` response header.
|
|
45
|
-
|
|
46
|
-
## Configuration
|
|
47
|
-
|
|
48
|
-
ZAP scan rules are configured in `.zap/baseline.conf`. Each line controls how ZAP treats a specific rule:
|
|
49
|
-
|
|
50
|
-
- `IGNORE`: Skip the rule entirely
|
|
51
|
-
- `WARN`: Report finding but don't fail the build
|
|
52
|
-
- `FAIL`: Fail the build if this finding is detected
|
|
53
|
-
|
|
54
|
-
## CI Integration
|
|
55
|
-
|
|
56
|
-
ZAP runs automatically in CI via the `zap-baseline.yml` workflow. Results are uploaded as artifacts and the build fails on medium+ severity findings.
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: playwright-selectors
|
|
3
|
-
description: Best practices for adding testID and aria-label selectors for Playwright E2E testing in Expo web applications. This skill should be used when adding E2E test coverage, creating new components that need test selectors, or reviewing code for testability.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Playwright Selectors Best Practices
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
This skill provides guidance for adding effective selectors (testID, aria-labels) to Expo/React Native components for Playwright E2E testing. Proper selector strategy ensures tests are reliable, maintainable, and accessible.
|
|
11
|
-
|
|
12
|
-
## Selector Priority
|
|
13
|
-
|
|
14
|
-
Choose selectors based on their reliability and accessibility impact, in this priority order:
|
|
15
|
-
|
|
16
|
-
| Priority | Selector Method | When to Use |
|
|
17
|
-
| -------- | --------------- | ----------------------------------------- |
|
|
18
|
-
| 1 | `getByRole` | Interactive elements with semantic roles |
|
|
19
|
-
| 2 | `getByText` | Visible text content |
|
|
20
|
-
| 3 | `getByLabel` | Form elements with labels |
|
|
21
|
-
| 4 | `getByTestId` | Fallback for elements without semantics |
|
|
22
|
-
|
|
23
|
-
### getByRole (Preferred)
|
|
24
|
-
|
|
25
|
-
Use semantic roles when possible - they improve both accessibility and test resilience.
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
// E2E test - preferred selector
|
|
29
|
-
const submitButton = page.getByRole("button", { name: "Submit" });
|
|
30
|
-
await expect(submitButton).toBeVisible();
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### getByTestId (Fallback)
|
|
34
|
-
|
|
35
|
-
Use testID when semantic selectors are not available, particularly for structural containers.
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
// E2E test - fallback selector
|
|
39
|
-
const container = page.getByTestId("home:container");
|
|
40
|
-
await expect(container).toBeVisible();
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## testID Naming Convention
|
|
44
|
-
|
|
45
|
-
Use a namespaced pattern with colons as separators: `screen:element`
|
|
46
|
-
|
|
47
|
-
### Format
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
{screen}:{element}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
- **screen**: lowercase screen/feature name (e.g., `home`, `profile`, `settings`)
|
|
54
|
-
- **element**: lowercase element identifier (e.g., `container`, `title`, `submit-button`)
|
|
55
|
-
|
|
56
|
-
### Examples
|
|
57
|
-
|
|
58
|
-
| testID | Description |
|
|
59
|
-
| -------------------------- | -------------------------------- |
|
|
60
|
-
| `home:container` | Main container on home screen |
|
|
61
|
-
| `home:title` | Title text on home screen |
|
|
62
|
-
| `profile:avatar` | User avatar on profile screen |
|
|
63
|
-
| `settings:dark-mode-toggle`| Dark mode toggle in settings |
|
|
64
|
-
| `auth:login-button` | Login button on auth screen |
|
|
65
|
-
|
|
66
|
-
### Rules
|
|
67
|
-
|
|
68
|
-
1. Use lowercase only
|
|
69
|
-
2. Use colons (`:`) to separate screen from element
|
|
70
|
-
3. Use hyphens (`-`) for multi-word elements
|
|
71
|
-
4. Be descriptive but concise
|
|
72
|
-
5. Avoid redundant words (e.g., `home:home-title` should be `home:title`)
|
|
73
|
-
|
|
74
|
-
## React Native to HTML Mapping
|
|
75
|
-
|
|
76
|
-
Understanding how testID propagates from React Native to the web is essential.
|
|
77
|
-
|
|
78
|
-
### How It Works
|
|
79
|
-
|
|
80
|
-
1. React Native's `testID` prop is for native testing (XCUITest, Espresso)
|
|
81
|
-
2. On web (via react-native-web), `testID` renders as `data-testid` in HTML
|
|
82
|
-
3. Playwright's `getByTestId()` queries `data-testid` by default
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
// React Native component
|
|
86
|
-
<Box testID="home:container">...</Box>
|
|
87
|
-
|
|
88
|
-
// Rendered HTML on web
|
|
89
|
-
<div data-testid="home:container">...</div>
|
|
90
|
-
|
|
91
|
-
// Playwright locator
|
|
92
|
-
page.getByTestId("home:container")
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Gluestack UI Components
|
|
96
|
-
|
|
97
|
-
Gluestack UI web components (Box, Text, etc.) require explicit testID handling because they use native HTML elements instead of react-native-web components. The web versions have been updated to:
|
|
98
|
-
|
|
99
|
-
1. Accept a `testID` prop in the TypeScript type
|
|
100
|
-
2. Map `testID` to `data-testid` on the rendered HTML element
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
// Gluestack Box web implementation
|
|
104
|
-
const Box = ({ testID, ...props }) => (
|
|
105
|
-
<div data-testid={testID} {...props} />
|
|
106
|
-
);
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## When to Add testID
|
|
110
|
-
|
|
111
|
-
### Add testID To
|
|
112
|
-
|
|
113
|
-
1. **Interactive elements** that E2E tests will click/interact with
|
|
114
|
-
2. **Key structural containers** for page load verification
|
|
115
|
-
3. **Dynamic content areas** that change based on state
|
|
116
|
-
4. **Form elements** that lack semantic labels
|
|
117
|
-
|
|
118
|
-
### Do Not Add testID To
|
|
119
|
-
|
|
120
|
-
1. Every element (over-testing creates maintenance burden)
|
|
121
|
-
2. Elements with good semantic selectors (use getByRole instead)
|
|
122
|
-
3. Decorative elements not needed for testing
|
|
123
|
-
4. Elements inside third-party components (may not propagate)
|
|
124
|
-
|
|
125
|
-
## Accessibility Best Practices
|
|
126
|
-
|
|
127
|
-
Prefer semantic selectors and aria-labels over testID when possible.
|
|
128
|
-
|
|
129
|
-
### aria-label for Testing and Accessibility
|
|
130
|
-
|
|
131
|
-
When adding labels for testing, use `aria-label` or `accessibilityLabel` to benefit screen reader users too.
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
// Correct - benefits both testing and accessibility
|
|
135
|
-
<Pressable
|
|
136
|
-
accessibilityLabel="Close dialog"
|
|
137
|
-
onPress={handleClose}
|
|
138
|
-
>
|
|
139
|
-
<XIcon />
|
|
140
|
-
</Pressable>
|
|
141
|
-
|
|
142
|
-
// E2E test uses accessible name
|
|
143
|
-
await page.getByRole("button", { name: "Close dialog" }).click();
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### accessibilityRole for Semantic Elements
|
|
147
|
-
|
|
148
|
-
Use `accessibilityRole` to provide semantic meaning on web.
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// Correct - semantic role for assistive technology
|
|
152
|
-
<Box accessibilityRole="banner" testID="header:container">
|
|
153
|
-
<Text accessibilityRole="heading">Welcome</Text>
|
|
154
|
-
</Box>
|
|
155
|
-
|
|
156
|
-
// E2E test can use role
|
|
157
|
-
await expect(page.getByRole("banner")).toBeVisible();
|
|
158
|
-
await expect(page.getByRole("heading", { name: "Welcome" })).toBeVisible();
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
## Implementation Checklist
|
|
162
|
-
|
|
163
|
-
When adding E2E test coverage to a component:
|
|
164
|
-
|
|
165
|
-
- [ ] Identify elements that need selectors for testing
|
|
166
|
-
- [ ] Prefer semantic selectors (role, text, label) when available
|
|
167
|
-
- [ ] Use namespaced testID pattern for elements without semantics
|
|
168
|
-
- [ ] Verify testID propagates to `data-testid` on web (check Gluestack components)
|
|
169
|
-
- [ ] Add accessibility labels where beneficial
|
|
170
|
-
- [ ] Document testIDs in component JSDoc preamble
|
|
171
|
-
|
|
172
|
-
## Example Component
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
/**
|
|
176
|
-
* Profile screen component.
|
|
177
|
-
*
|
|
178
|
-
* Test IDs for E2E testing:
|
|
179
|
-
* - `profile:container` - Main container
|
|
180
|
-
* - `profile:avatar` - User avatar image
|
|
181
|
-
* - `profile:name` - User display name
|
|
182
|
-
*
|
|
183
|
-
* @module features/profile/screens/Main
|
|
184
|
-
*/
|
|
185
|
-
export const ProfileScreen = () => (
|
|
186
|
-
<Box testID="profile:container" className="flex-1 p-4">
|
|
187
|
-
<Image
|
|
188
|
-
testID="profile:avatar"
|
|
189
|
-
source={{ uri: user.avatarUrl }}
|
|
190
|
-
accessibilityLabel={`${user.name}'s profile photo`}
|
|
191
|
-
/>
|
|
192
|
-
<Text testID="profile:name" accessibilityRole="heading">
|
|
193
|
-
{user.name}
|
|
194
|
-
</Text>
|
|
195
|
-
<Pressable
|
|
196
|
-
accessibilityLabel="Edit profile"
|
|
197
|
-
onPress={handleEdit}
|
|
198
|
-
>
|
|
199
|
-
<Text>Edit</Text>
|
|
200
|
-
</Pressable>
|
|
201
|
-
</Box>
|
|
202
|
-
);
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Corresponding E2E Test
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
test.describe("Profile Screen", () => {
|
|
209
|
-
test("displays user information", async ({ page }) => {
|
|
210
|
-
await page.goto("/profile");
|
|
211
|
-
|
|
212
|
-
// Verify structural container
|
|
213
|
-
await expect(page.getByTestId("profile:container")).toBeVisible();
|
|
214
|
-
|
|
215
|
-
// Prefer accessible queries when available
|
|
216
|
-
await expect(page.getByRole("heading")).toHaveText("John Doe");
|
|
217
|
-
await expect(page.getByRole("button", { name: "Edit profile" })).toBeVisible();
|
|
218
|
-
|
|
219
|
-
// Use testID for elements without semantic roles
|
|
220
|
-
await expect(page.getByTestId("profile:avatar")).toBeVisible();
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
```
|