@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,527 @@
|
|
|
1
|
+
# Advanced Features
|
|
2
|
+
|
|
3
|
+
Guide to field middleware, query complexity, plugins, subscriptions, and extensions.
|
|
4
|
+
|
|
5
|
+
## Extensions
|
|
6
|
+
|
|
7
|
+
Extensions allow attaching metadata to GraphQL schema elements.
|
|
8
|
+
|
|
9
|
+
### @Extensions Decorator
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Extensions, Field, ObjectType } from "@nestjs/graphql";
|
|
13
|
+
|
|
14
|
+
@ObjectType()
|
|
15
|
+
export class User {
|
|
16
|
+
@Field()
|
|
17
|
+
@Extensions({ role: "ADMIN" })
|
|
18
|
+
secretField: string;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Accessing Extensions
|
|
23
|
+
|
|
24
|
+
Extensions are available on field config in schema transformers:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { mapSchema, MapperKind } from "@graphql-tools/utils";
|
|
28
|
+
|
|
29
|
+
const transformer = (schema: GraphQLSchema) => {
|
|
30
|
+
return mapSchema(schema, {
|
|
31
|
+
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
|
|
32
|
+
const extensions = fieldConfig.extensions;
|
|
33
|
+
if (extensions?.role === "ADMIN") {
|
|
34
|
+
// Wrap resolver with auth check
|
|
35
|
+
}
|
|
36
|
+
return fieldConfig;
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Using Extensions for Auth
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// Decorator
|
|
46
|
+
export const AUTH_EXTENSION_KEY = "auth";
|
|
47
|
+
|
|
48
|
+
export const Public = () =>
|
|
49
|
+
Extensions({ [AUTH_EXTENSION_KEY]: { rules: [{ allow: "public" }] } });
|
|
50
|
+
|
|
51
|
+
export const Authed = () =>
|
|
52
|
+
Extensions({ [AUTH_EXTENSION_KEY]: { rules: [{ allow: "authed" }] } });
|
|
53
|
+
|
|
54
|
+
// Usage
|
|
55
|
+
@Query(() => String)
|
|
56
|
+
@Public()
|
|
57
|
+
hello(): string {
|
|
58
|
+
return "Hello World";
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Field Middleware
|
|
63
|
+
|
|
64
|
+
Middleware that runs during field resolution.
|
|
65
|
+
|
|
66
|
+
### Creating Middleware
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { FieldMiddleware, MiddlewareContext, NextFn } from "@nestjs/graphql";
|
|
70
|
+
|
|
71
|
+
export const loggingMiddleware: FieldMiddleware = async (
|
|
72
|
+
ctx: MiddlewareContext,
|
|
73
|
+
next: NextFn
|
|
74
|
+
) => {
|
|
75
|
+
const start = Date.now();
|
|
76
|
+
const result = await next();
|
|
77
|
+
const duration = Date.now() - start;
|
|
78
|
+
console.log(`Field ${ctx.info.fieldName} took ${duration}ms`);
|
|
79
|
+
return result;
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Applying Middleware to Field
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
@ObjectType()
|
|
87
|
+
export class User {
|
|
88
|
+
@Field({ middleware: [loggingMiddleware] })
|
|
89
|
+
email: string;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Global Middleware
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
97
|
+
buildSchemaOptions: {
|
|
98
|
+
fieldMiddleware: [loggingMiddleware],
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Middleware Context
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
interface MiddlewareContext<TSource = unknown, TContext = unknown> {
|
|
107
|
+
source: TSource; // Parent object
|
|
108
|
+
args: Record<string, unknown>; // Field arguments
|
|
109
|
+
context: TContext; // GraphQL context
|
|
110
|
+
info: GraphQLResolveInfo; // Resolve info
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Transform Middleware
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const upperCaseMiddleware: FieldMiddleware = async (ctx, next) => {
|
|
118
|
+
const value = await next();
|
|
119
|
+
return typeof value === "string" ? value.toUpperCase() : value;
|
|
120
|
+
};
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Auth Middleware
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const authMiddleware: FieldMiddleware = async (ctx, next) => {
|
|
127
|
+
const { extensions } = ctx.info.parentType.getFields()[ctx.info.fieldName];
|
|
128
|
+
|
|
129
|
+
if (extensions?.auth === "admin") {
|
|
130
|
+
const user = ctx.context.req.user;
|
|
131
|
+
if (!user?.isAdmin) {
|
|
132
|
+
return null; // Hide field
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return next();
|
|
137
|
+
};
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Query Complexity
|
|
141
|
+
|
|
142
|
+
Prevent expensive queries from overwhelming the server.
|
|
143
|
+
|
|
144
|
+
### Installation
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
bun add graphql-query-complexity
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Complexity Plugin
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { Plugin } from "@nestjs/apollo";
|
|
154
|
+
import { GraphQLSchemaHost } from "@nestjs/graphql";
|
|
155
|
+
import {
|
|
156
|
+
ApolloServerPlugin,
|
|
157
|
+
GraphQLRequestListener,
|
|
158
|
+
} from "@apollo/server";
|
|
159
|
+
import {
|
|
160
|
+
fieldExtensionsEstimator,
|
|
161
|
+
getComplexity,
|
|
162
|
+
simpleEstimator,
|
|
163
|
+
} from "graphql-query-complexity";
|
|
164
|
+
|
|
165
|
+
@Plugin()
|
|
166
|
+
export class ComplexityPlugin implements ApolloServerPlugin {
|
|
167
|
+
constructor(private readonly gqlSchemaHost: GraphQLSchemaHost) {}
|
|
168
|
+
|
|
169
|
+
async requestDidStart(): Promise<GraphQLRequestListener<unknown>> {
|
|
170
|
+
const maxComplexity = 100;
|
|
171
|
+
const { schema } = this.gqlSchemaHost;
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
didResolveOperation: async ({ request, document }) => {
|
|
175
|
+
const complexity = getComplexity({
|
|
176
|
+
schema,
|
|
177
|
+
operationName: request.operationName,
|
|
178
|
+
query: document,
|
|
179
|
+
variables: request.variables,
|
|
180
|
+
estimators: [
|
|
181
|
+
fieldExtensionsEstimator(),
|
|
182
|
+
simpleEstimator({ defaultComplexity: 1 }),
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (complexity > maxComplexity) {
|
|
187
|
+
throw new Error(
|
|
188
|
+
`Query too complex: ${complexity}. Maximum: ${maxComplexity}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Field-Level Complexity
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
@ObjectType()
|
|
201
|
+
export class User {
|
|
202
|
+
// Static complexity
|
|
203
|
+
@Field(() => String, { complexity: 1 })
|
|
204
|
+
name: string;
|
|
205
|
+
|
|
206
|
+
// Higher complexity for relations
|
|
207
|
+
@Field(() => [Post], { complexity: 10 })
|
|
208
|
+
posts: Post[];
|
|
209
|
+
|
|
210
|
+
// Dynamic complexity based on arguments
|
|
211
|
+
@Field(() => [Post], {
|
|
212
|
+
complexity: (options) => {
|
|
213
|
+
const limit = options.args.limit ?? 20;
|
|
214
|
+
return 1 + limit * 2;
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
recentPosts: Post[];
|
|
218
|
+
|
|
219
|
+
// Complexity with child multiplication
|
|
220
|
+
@Field(() => [Comment], {
|
|
221
|
+
complexity: (options) => {
|
|
222
|
+
return options.childComplexity * (options.args.first ?? 10);
|
|
223
|
+
},
|
|
224
|
+
})
|
|
225
|
+
comments: Comment[];
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Complexity Guidelines
|
|
230
|
+
|
|
231
|
+
| Field Type | Suggested Complexity |
|
|
232
|
+
|------------|---------------------|
|
|
233
|
+
| Scalar fields | 1 |
|
|
234
|
+
| Enum fields | 1 |
|
|
235
|
+
| Simple object fields | 2-5 |
|
|
236
|
+
| Relation fields (belongsTo) | 5-10 |
|
|
237
|
+
| Collection fields (hasMany) | 10-20 |
|
|
238
|
+
| Aggregation fields | 5-15 |
|
|
239
|
+
| Search/filter fields | 10-30 |
|
|
240
|
+
|
|
241
|
+
## Plugins
|
|
242
|
+
|
|
243
|
+
Apollo Server plugins for request lifecycle hooks.
|
|
244
|
+
|
|
245
|
+
### Creating a Plugin
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { Plugin } from "@nestjs/apollo";
|
|
249
|
+
import {
|
|
250
|
+
ApolloServerPlugin,
|
|
251
|
+
GraphQLRequestListener,
|
|
252
|
+
GraphQLRequestContext,
|
|
253
|
+
} from "@apollo/server";
|
|
254
|
+
|
|
255
|
+
@Plugin()
|
|
256
|
+
export class LoggingPlugin implements ApolloServerPlugin {
|
|
257
|
+
async requestDidStart(
|
|
258
|
+
requestContext: GraphQLRequestContext<unknown>
|
|
259
|
+
): Promise<GraphQLRequestListener<unknown>> {
|
|
260
|
+
console.log("Request started");
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
async willSendResponse() {
|
|
264
|
+
console.log("Response sent");
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
async didEncounterErrors({ errors }) {
|
|
268
|
+
console.log("Errors:", errors);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Request Lifecycle Hooks
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
interface GraphQLRequestListener {
|
|
279
|
+
// Before parsing
|
|
280
|
+
parsingDidStart?(): Promise<void>;
|
|
281
|
+
|
|
282
|
+
// Before validation
|
|
283
|
+
validationDidStart?(): Promise<void>;
|
|
284
|
+
|
|
285
|
+
// After operation resolved
|
|
286
|
+
didResolveOperation?(): Promise<void>;
|
|
287
|
+
|
|
288
|
+
// Before response sent
|
|
289
|
+
willSendResponse?(): Promise<void>;
|
|
290
|
+
|
|
291
|
+
// On errors
|
|
292
|
+
didEncounterErrors?(): Promise<void>;
|
|
293
|
+
|
|
294
|
+
// Before execution
|
|
295
|
+
executionDidStart?(): Promise<void>;
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Registering Plugins
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
@Module({
|
|
303
|
+
imports: [GraphQLModule.forRoot<ApolloDriverConfig>({ /* ... */ })],
|
|
304
|
+
providers: [LoggingPlugin, ComplexityPlugin],
|
|
305
|
+
})
|
|
306
|
+
export class AppModule {}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Performance Monitoring Plugin
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
@Plugin()
|
|
313
|
+
export class PerformancePlugin implements ApolloServerPlugin {
|
|
314
|
+
async requestDidStart(): Promise<GraphQLRequestListener<unknown>> {
|
|
315
|
+
const start = Date.now();
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
async willSendResponse({ response }) {
|
|
319
|
+
const duration = Date.now() - start;
|
|
320
|
+
response.http.headers.set("X-Response-Time", `${duration}ms`);
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Subscriptions
|
|
328
|
+
|
|
329
|
+
Real-time updates via WebSocket.
|
|
330
|
+
|
|
331
|
+
### Installation
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
bun add graphql-subscriptions
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### PubSub Setup
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import { PubSub } from "graphql-subscriptions";
|
|
341
|
+
|
|
342
|
+
// Simple in-memory PubSub (for development only)
|
|
343
|
+
export const pubSub = new PubSub();
|
|
344
|
+
|
|
345
|
+
// For production, use Redis:
|
|
346
|
+
// import { RedisPubSub } from "graphql-redis-subscriptions";
|
|
347
|
+
// export const pubSub = new RedisPubSub({ /* redis config */ });
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Subscription Resolver
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import { Args, Mutation, Resolver, Subscription } from "@nestjs/graphql";
|
|
354
|
+
import { pubSub } from "./pubsub";
|
|
355
|
+
|
|
356
|
+
const POST_CREATED = "postCreated";
|
|
357
|
+
|
|
358
|
+
@Resolver(() => Post)
|
|
359
|
+
export class PostResolver {
|
|
360
|
+
@Mutation(() => Post)
|
|
361
|
+
async createPost(@Args("input") input: CreatePostInput): Promise<Post> {
|
|
362
|
+
const post = await this.postService.create(input);
|
|
363
|
+
pubSub.publish(POST_CREATED, { postCreated: post });
|
|
364
|
+
return post;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
@Subscription(() => Post, {
|
|
368
|
+
description: "Subscribe to new posts",
|
|
369
|
+
})
|
|
370
|
+
postCreated() {
|
|
371
|
+
return pubSub.asyncIterator(POST_CREATED);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Filtered Subscriptions
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
@Subscription(() => Post, {
|
|
380
|
+
filter: (payload, variables) => {
|
|
381
|
+
return payload.postCreated.authorId === variables.authorId;
|
|
382
|
+
},
|
|
383
|
+
})
|
|
384
|
+
postCreated(@Args("authorId") authorId: string) {
|
|
385
|
+
return pubSub.asyncIterator(POST_CREATED);
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Subscription with Resolver
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
@Subscription(() => Post, {
|
|
393
|
+
resolve: (payload) => {
|
|
394
|
+
// Transform the payload before sending
|
|
395
|
+
return {
|
|
396
|
+
...payload.postCreated,
|
|
397
|
+
isNew: true,
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
})
|
|
401
|
+
postCreated() {
|
|
402
|
+
return pubSub.asyncIterator(POST_CREATED);
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Module Configuration for Subscriptions
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
410
|
+
driver: ApolloDriver,
|
|
411
|
+
autoSchemaFile: true,
|
|
412
|
+
subscriptions: {
|
|
413
|
+
"graphql-ws": true, // Modern WebSocket protocol
|
|
414
|
+
"subscriptions-transport-ws": true, // Legacy protocol (deprecated)
|
|
415
|
+
},
|
|
416
|
+
context: ({ req, connection }) => {
|
|
417
|
+
// HTTP request
|
|
418
|
+
if (req) {
|
|
419
|
+
return { req };
|
|
420
|
+
}
|
|
421
|
+
// WebSocket connection
|
|
422
|
+
if (connection) {
|
|
423
|
+
return { user: connection.context.user };
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
})
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Subscription Authentication
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
433
|
+
subscriptions: {
|
|
434
|
+
"graphql-ws": {
|
|
435
|
+
onConnect: (context) => {
|
|
436
|
+
const { connectionParams } = context;
|
|
437
|
+
const token = connectionParams?.authorization;
|
|
438
|
+
|
|
439
|
+
if (!token) {
|
|
440
|
+
throw new Error("Missing auth token");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Validate token and return user
|
|
444
|
+
const user = validateToken(token);
|
|
445
|
+
return { user };
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
})
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Schema Transformation
|
|
453
|
+
|
|
454
|
+
Transform the generated schema before use.
|
|
455
|
+
|
|
456
|
+
### Using transformSchema
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
460
|
+
transformSchema: (schema) => {
|
|
461
|
+
return mapSchema(schema, {
|
|
462
|
+
[MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
|
|
463
|
+
// Transform field config
|
|
464
|
+
return fieldConfig;
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
})
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Combining Transformers
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
const combinedTransformer = (schema: GraphQLSchema): GraphQLSchema => {
|
|
475
|
+
const withAuth = authTransformer(schema);
|
|
476
|
+
const withLogging = loggingTransformer(withAuth);
|
|
477
|
+
return withLogging;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
481
|
+
transformSchema: combinedTransformer,
|
|
482
|
+
})
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Directives
|
|
486
|
+
|
|
487
|
+
Custom schema directives (schema-first approach).
|
|
488
|
+
|
|
489
|
+
### Defining Directive
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
import { Directive, Field, ObjectType } from "@nestjs/graphql";
|
|
493
|
+
|
|
494
|
+
@Directive("@upper")
|
|
495
|
+
@ObjectType()
|
|
496
|
+
export class User {
|
|
497
|
+
@Directive("@deprecated(reason: \"Use name instead\")")
|
|
498
|
+
@Field()
|
|
499
|
+
firstName: string;
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Registering Directive Transformer
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
import { mapSchema, getDirective, MapperKind } from "@graphql-tools/utils";
|
|
507
|
+
|
|
508
|
+
function upperDirectiveTransformer(schema: GraphQLSchema) {
|
|
509
|
+
return mapSchema(schema, {
|
|
510
|
+
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
|
|
511
|
+
const upperDirective = getDirective(schema, fieldConfig, "upper")?.[0];
|
|
512
|
+
|
|
513
|
+
if (upperDirective) {
|
|
514
|
+
const { resolve = defaultFieldResolver } = fieldConfig;
|
|
515
|
+
fieldConfig.resolve = async (source, args, context, info) => {
|
|
516
|
+
const result = await resolve(source, args, context, info);
|
|
517
|
+
return typeof result === "string" ? result.toUpperCase() : result;
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return fieldConfig;
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**Note:** For code-first approach, prefer using `@Extensions()` decorator over directives. Extensions are simpler to implement and don't require SDL directive definitions.
|