cfsa-antigravity 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/bin/cli.mjs +324 -0
- package/package.json +34 -0
- package/template/.agent/instructions/commands.md +48 -0
- package/template/.agent/instructions/patterns.md +61 -0
- package/template/.agent/instructions/structure.md +29 -0
- package/template/.agent/instructions/tech-stack.md +43 -0
- package/template/.agent/instructions/workflow.md +41 -0
- package/template/.agent/kit-sync.md +15 -0
- package/template/.agent/rules/boundary-not-placeholder.md +146 -0
- package/template/.agent/rules/completion-checklist.md +48 -0
- package/template/.agent/rules/decision-classification.md +103 -0
- package/template/.agent/rules/extensibility.md +47 -0
- package/template/.agent/rules/question-vs-command.md +81 -0
- package/template/.agent/rules/security-first.md +43 -0
- package/template/.agent/rules/specificity-standards.md +54 -0
- package/template/.agent/rules/tdd-contract-first.md +57 -0
- package/template/.agent/rules/vertical-slices.md +42 -0
- package/template/.agent/skill-library/MANIFEST.md +480 -0
- package/template/.agent/skill-library/README.md +38 -0
- package/template/.agent/skill-library/meta/brand-guidelines/SKILL.md +73 -0
- package/template/.agent/skill-library/meta/claude-code/README.md +9 -0
- package/template/.agent/skill-library/meta/claude-code/agent-development/SKILL.md +415 -0
- package/template/.agent/skill-library/meta/claude-code/hook-development/SKILL.md +712 -0
- package/template/.agent/skill-library/meta/claude-code/plugin-structure/SKILL.md +476 -0
- package/template/.agent/skill-library/meta/git-advanced/SKILL.md +972 -0
- package/template/.agent/skill-library/meta/mcp-builder/SKILL.md +236 -0
- package/template/.agent/skill-library/meta/product-marketing-context/SKILL.md +241 -0
- package/template/.agent/skill-library/meta/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skill-library/meta/tmux-processes/SKILL.md +210 -0
- package/template/.agent/skill-library/meta/using-tmux-for-interactive-commands/SKILL.md +178 -0
- package/template/.agent/skill-library/stack/3d/threejs-pro/SKILL.md +300 -0
- package/template/.agent/skill-library/stack/ai/ai-sdk/SKILL.md +77 -0
- package/template/.agent/skill-library/stack/ai/langchain/SKILL.md +530 -0
- package/template/.agent/skill-library/stack/ai/ollama/SKILL.md +321 -0
- package/template/.agent/skill-library/stack/ai/openai-sdk/SKILL.md +549 -0
- package/template/.agent/skill-library/stack/analytics/google-analytics/SKILL.md +153 -0
- package/template/.agent/skill-library/stack/api/graphql/SKILL.md +1061 -0
- package/template/.agent/skill-library/stack/api/trpc/SKILL.md +576 -0
- package/template/.agent/skill-library/stack/auth/authjs/SKILL.md +569 -0
- package/template/.agent/skill-library/stack/auth/clerk/SKILL.md +590 -0
- package/template/.agent/skill-library/stack/auth/firebase-auth/SKILL.md +734 -0
- package/template/.agent/skill-library/stack/cms/payload-cms/SKILL.md +573 -0
- package/template/.agent/skill-library/stack/cms/shopify/SKILL.md +1193 -0
- package/template/.agent/skill-library/stack/cms/wordpress/SKILL.md +1104 -0
- package/template/.agent/skill-library/stack/css/sass-scss/SKILL.md +1121 -0
- package/template/.agent/skill-library/stack/css/tailwind-css-patterns/SKILL.md +863 -0
- package/template/.agent/skill-library/stack/css/tailwind-design-system/SKILL.md +490 -0
- package/template/.agent/skill-library/stack/css/vanilla-css/SKILL.md +1078 -0
- package/template/.agent/skill-library/stack/databases/clickhouse/SKILL.md +311 -0
- package/template/.agent/skill-library/stack/databases/influxdb/SKILL.md +280 -0
- package/template/.agent/skill-library/stack/databases/lancedb/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/databases/mongodb/SKILL.md +1169 -0
- package/template/.agent/skill-library/stack/databases/neo4j/SKILL.md +839 -0
- package/template/.agent/skill-library/stack/databases/pgvector/SKILL.md +241 -0
- package/template/.agent/skill-library/stack/databases/pinecone/SKILL.md +212 -0
- package/template/.agent/skill-library/stack/databases/postgresql/SKILL.md +658 -0
- package/template/.agent/skill-library/stack/databases/qdrant/SKILL.md +312 -0
- package/template/.agent/skill-library/stack/databases/redis/SKILL.md +1079 -0
- package/template/.agent/skill-library/stack/databases/spacetimedb/SKILL.md +532 -0
- package/template/.agent/skill-library/stack/databases/sqlite/SKILL.md +1132 -0
- package/template/.agent/skill-library/stack/databases/supabase/SKILL.md +640 -0
- package/template/.agent/skill-library/stack/databases/surrealdb-expert/SKILL.md +945 -0
- package/template/.agent/skill-library/stack/databases/timescaledb/SKILL.md +745 -0
- package/template/.agent/skill-library/stack/databases/weaviate/SKILL.md +218 -0
- package/template/.agent/skill-library/stack/devops/github-actions/SKILL.md +554 -0
- package/template/.agent/skill-library/stack/devops/kubernetes/SKILL.md +950 -0
- package/template/.agent/skill-library/stack/devops/nginx/SKILL.md +841 -0
- package/template/.agent/skill-library/stack/devops/terraform/SKILL.md +860 -0
- package/template/.agent/skill-library/stack/email/resend/SKILL.md +391 -0
- package/template/.agent/skill-library/stack/engines/godot/SKILL.md +488 -0
- package/template/.agent/skill-library/stack/extensions/chrome-extension/SKILL.md +375 -0
- package/template/.agent/skill-library/stack/extensions/vscode-extension/SKILL.md +453 -0
- package/template/.agent/skill-library/stack/frameworks/astro-framework/SKILL.md +162 -0
- package/template/.agent/skill-library/stack/frameworks/electron/SKILL.md +1286 -0
- package/template/.agent/skill-library/stack/frameworks/fastapi/SKILL.md +650 -0
- package/template/.agent/skill-library/stack/frameworks/hono/SKILL.md +90 -0
- package/template/.agent/skill-library/stack/frameworks/nestjs/SKILL.md +878 -0
- package/template/.agent/skill-library/stack/frameworks/nextjs/SKILL.md +635 -0
- package/template/.agent/skill-library/stack/frameworks/nuxt/SKILL.md +564 -0
- package/template/.agent/skill-library/stack/frameworks/sveltekit/SKILL.md +614 -0
- package/template/.agent/skill-library/stack/frameworks/tauri/SKILL.md +920 -0
- package/template/.agent/skill-library/stack/gamedev/godot/SKILL.md +1032 -0
- package/template/.agent/skill-library/stack/gamedev/unity/SKILL.md +1175 -0
- package/template/.agent/skill-library/stack/hosting/aws/SKILL.md +467 -0
- package/template/.agent/skill-library/stack/hosting/cloudflare/SKILL.md +201 -0
- package/template/.agent/skill-library/stack/hosting/docker-expert/SKILL.md +409 -0
- package/template/.agent/skill-library/stack/hosting/vercel/SKILL.md +484 -0
- package/template/.agent/skill-library/stack/languages/bash-scripting/SKILL.md +773 -0
- package/template/.agent/skill-library/stack/languages/c-cpp/SKILL.md +712 -0
- package/template/.agent/skill-library/stack/languages/gdscript/SKILL.md +789 -0
- package/template/.agent/skill-library/stack/languages/go/SKILL.md +664 -0
- package/template/.agent/skill-library/stack/languages/java/SKILL.md +778 -0
- package/template/.agent/skill-library/stack/languages/kotlin/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/languages/python/SKILL.md +678 -0
- package/template/.agent/skill-library/stack/languages/rust/SKILL.md +673 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/SKILL.md +141 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/advanced-generics.md +90 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/branded-types.md +57 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/builder-pattern.md +71 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/common-pitfalls.md +135 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/conditional-types.md +27 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/decorators.md +98 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/discriminated-unions.md +62 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/mapped-types.md +53 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/performance-best-practices.md +104 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/template-literal-types.md +49 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/testing-types.md +112 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-guards.md +70 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-inference.md +101 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/utility-types.md +98 -0
- package/template/.agent/skill-library/stack/languages/vanilla-javascript/SKILL.md +803 -0
- package/template/.agent/skill-library/stack/messaging/kafka/SKILL.md +235 -0
- package/template/.agent/skill-library/stack/mobile/expo-react-native/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/mobile/flutter/SKILL.md +316 -0
- package/template/.agent/skill-library/stack/mobile/react-native/SKILL.md +337 -0
- package/template/.agent/skill-library/stack/monitoring/posthog/SKILL.md +396 -0
- package/template/.agent/skill-library/stack/monitoring/sentry/SKILL.md +509 -0
- package/template/.agent/skill-library/stack/observability/datadog/SKILL.md +179 -0
- package/template/.agent/skill-library/stack/observability/distributed-tracing/SKILL.md +140 -0
- package/template/.agent/skill-library/stack/observability/logging-best-practices/SKILL.md +168 -0
- package/template/.agent/skill-library/stack/observability/opentelemetry/SKILL.md +164 -0
- package/template/.agent/skill-library/stack/observability/prometheus-grafana/SKILL.md +246 -0
- package/template/.agent/skill-library/stack/observability/python-observability/SKILL.md +158 -0
- package/template/.agent/skill-library/stack/orm/drizzle-orm/SKILL.md +613 -0
- package/template/.agent/skill-library/stack/orm/prisma/SKILL.md +744 -0
- package/template/.agent/skill-library/stack/payments/lemonsqueezy/SKILL.md +393 -0
- package/template/.agent/skill-library/stack/payments/stripe-integration/SKILL.md +457 -0
- package/template/.agent/skill-library/stack/queue/bullmq/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/queue/inngest/SKILL.md +438 -0
- package/template/.agent/skill-library/stack/realtime/socketio/SKILL.md +595 -0
- package/template/.agent/skill-library/stack/search/elasticsearch/SKILL.md +248 -0
- package/template/.agent/skill-library/stack/search/meilisearch/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/security/crypto-patterns/SKILL.md +437 -0
- package/template/.agent/skill-library/stack/security/csp-cors-headers/SKILL.md +588 -0
- package/template/.agent/skill-library/stack/security/dependency-auditing/SKILL.md +560 -0
- package/template/.agent/skill-library/stack/security/input-sanitization/SKILL.md +430 -0
- package/template/.agent/skill-library/stack/security/owasp-web-security/SKILL.md +421 -0
- package/template/.agent/skill-library/stack/state/tanstack-query/SKILL.md +637 -0
- package/template/.agent/skill-library/stack/state/zustand/SKILL.md +483 -0
- package/template/.agent/skill-library/stack/storage/aws-s3/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/testing/playwright/SKILL.md +641 -0
- package/template/.agent/skill-library/stack/testing/storybook/SKILL.md +923 -0
- package/template/.agent/skill-library/stack/testing/testing-library/SKILL.md +872 -0
- package/template/.agent/skill-library/stack/testing/vitest/SKILL.md +714 -0
- package/template/.agent/skill-library/stack/ui/react-best-practices/SKILL.md +877 -0
- package/template/.agent/skill-library/stack/ui/react-composition-patterns/SKILL.md +1107 -0
- package/template/.agent/skill-library/stack/ui/react-flow/SKILL.md +425 -0
- package/template/.agent/skill-library/stack/ui/shadcn-ui/SKILL.md +703 -0
- package/template/.agent/skill-library/surface/api/api-caching/SKILL.md +458 -0
- package/template/.agent/skill-library/surface/api/api-documentation-openapi/SKILL.md +697 -0
- package/template/.agent/skill-library/surface/api/api-error-handling/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/api-security-checklist/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/api-versioning/SKILL.md +420 -0
- package/template/.agent/skill-library/surface/api/email-best-practices/SKILL.md +59 -0
- package/template/.agent/skill-library/surface/api/rate-limiting-abuse-protection/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/webhook-design/SKILL.md +752 -0
- package/template/.agent/skill-library/surface/cli/cli-configuration-management/SKILL.md +445 -0
- package/template/.agent/skill-library/surface/cli/cli-error-diagnostics/SKILL.md +515 -0
- package/template/.agent/skill-library/surface/cli/cli-shell-integration/SKILL.md +479 -0
- package/template/.agent/skill-library/surface/cli/cli-ux-design/SKILL.md +477 -0
- package/template/.agent/skill-library/surface/desktop/desktop-app-distribution/SKILL.md +416 -0
- package/template/.agent/skill-library/surface/desktop/desktop-security-sandboxing/SKILL.md +407 -0
- package/template/.agent/skill-library/surface/desktop/desktop-ux-conventions/SKILL.md +361 -0
- package/template/.agent/skill-library/surface/desktop/native-os-integration/SKILL.md +563 -0
- package/template/.agent/skill-library/surface/extension/browser-extension-patterns/SKILL.md +482 -0
- package/template/.agent/skill-library/surface/extension/plugin-architecture-design/SKILL.md +632 -0
- package/template/.agent/skill-library/surface/extension/vscode-extension-development/SKILL.md +728 -0
- package/template/.agent/skill-library/surface/mobile/app-store-submission/SKILL.md +304 -0
- package/template/.agent/skill-library/surface/mobile/mobile-offline-sync/SKILL.md +443 -0
- package/template/.agent/skill-library/surface/mobile/mobile-responsive-patterns/SKILL.md +432 -0
- package/template/.agent/skill-library/surface/mobile/push-notifications/SKILL.md +495 -0
- package/template/.agent/skill-library/surface/web/accessibility-compliance/SKILL.md +827 -0
- package/template/.agent/skill-library/surface/web/ai-seo/SKILL.md +398 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/content-patterns.md +285 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/platform-ranking-factors.md +152 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/SKILL.md +309 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/event-library.md +260 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/ga4-implementation.md +300 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/gtm-implementation.md +390 -0
- package/template/.agent/skill-library/surface/web/authentication-ui-flows/SKILL.md +530 -0
- package/template/.agent/skill-library/surface/web/dark-mode-theming/SKILL.md +516 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/SKILL.md +105 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/charts.csv +26 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/colors.csv +97 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/landing.csv +31 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/styles.csv +59 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/typography.csv +58 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/ux-guidelines.csv +100 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/core.py +258 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/design_system.py +1067 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/search.py +106 -0
- package/template/.agent/skill-library/surface/web/form-handling-validation/SKILL.md +675 -0
- package/template/.agent/skill-library/surface/web/frontend-design/SKILL.md +1393 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/cppn-hero.tsx +299 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/wave-hero.tsx +875 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/SKILL.md +111 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/scripts/ux_audit.py +739 -0
- package/template/.agent/skill-library/surface/web/i18n-localization/SKILL.md +154 -0
- package/template/.agent/skill-library/surface/web/offline-first-pwa/SKILL.md +657 -0
- package/template/.agent/skill-library/surface/web/page-cro/SKILL.md +182 -0
- package/template/.agent/skill-library/surface/web/page-cro/references/experiments.md +248 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/SKILL.md +238 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/references/playbooks.md +308 -0
- package/template/.agent/skill-library/surface/web/schema-markup/SKILL.md +179 -0
- package/template/.agent/skill-library/surface/web/schema-markup/references/schema-examples.md +398 -0
- package/template/.agent/skill-library/surface/web/seo-audit/SKILL.md +394 -0
- package/template/.agent/skill-library/surface/web/seo-audit/references/ai-writing-detection.md +200 -0
- package/template/.agent/skill-library/surface/web/web-performance-optimization/SKILL.md +646 -0
- package/template/.agent/skill-library/surface/web/web-scraping/SKILL.md +58 -0
- package/template/.agent/skills/accessibility/SKILL.md +522 -0
- package/template/.agent/skills/accessibility/references/WCAG.md +162 -0
- package/template/.agent/skills/adversarial-review/SKILL.md +90 -0
- package/template/.agent/skills/antigravity-workflows/SKILL.md +81 -0
- package/template/.agent/skills/antigravity-workflows/resources/implementation-playbook.md +36 -0
- package/template/.agent/skills/api-design-principles/SKILL.md +37 -0
- package/template/.agent/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/template/.agent/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/template/.agent/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/template/.agent/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/template/.agent/skills/api-design-principles/resources/implementation-playbook.md +513 -0
- package/template/.agent/skills/api-versioning/SKILL.md +420 -0
- package/template/.agent/skills/architecture-mapping/SKILL.md +219 -0
- package/template/.agent/skills/bootstrap-agents/SKILL.md +259 -0
- package/template/.agent/skills/brainstorming/SKILL.md +236 -0
- package/template/.agent/skills/brand-guidelines/SKILL.md +44 -0
- package/template/.agent/skills/clean-code/SKILL.md +94 -0
- package/template/.agent/skills/code-review-pro/SKILL.md +152 -0
- package/template/.agent/skills/concise-planning/SKILL.md +68 -0
- package/template/.agent/skills/cross-layer-consistency/SKILL.md +117 -0
- package/template/.agent/skills/database-schema-design/SKILL.md +429 -0
- package/template/.agent/skills/deployment-procedures/SKILL.md +241 -0
- package/template/.agent/skills/design-anti-cliche/SKILL.md +159 -0
- package/template/.agent/skills/design-direction/SKILL.md +45 -0
- package/template/.agent/skills/error-handling-patterns/SKILL.md +721 -0
- package/template/.agent/skills/find-skills/SKILL.md +145 -0
- package/template/.agent/skills/git-advanced/SKILL.md +972 -0
- package/template/.agent/skills/git-workflow/SKILL.md +420 -0
- package/template/.agent/skills/idea-extraction/SKILL.md +271 -0
- package/template/.agent/skills/logging-best-practices/SKILL.md +851 -0
- package/template/.agent/skills/migration-management/SKILL.md +384 -0
- package/template/.agent/skills/minimalist-surgical-development/SKILL.md +69 -0
- package/template/.agent/skills/parallel-agents/SKILL.md +165 -0
- package/template/.agent/skills/parallel-debugging/SKILL.md +135 -0
- package/template/.agent/skills/parallel-feature-development/SKILL.md +166 -0
- package/template/.agent/skills/performance-budgeting/SKILL.md +144 -0
- package/template/.agent/skills/pipeline-rubrics/SKILL.md +51 -0
- package/template/.agent/skills/pipeline-rubrics/references/architecture-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +21 -0
- package/template/.agent/skills/pipeline-rubrics/references/fe-rubric.md +20 -0
- package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/scoring.md +28 -0
- package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +11 -0
- package/template/.agent/skills/prd-templates/SKILL.md +88 -0
- package/template/.agent/skills/prd-templates/references/architecture-design-template.md +88 -0
- package/template/.agent/skills/prd-templates/references/be-spec-template.md +101 -0
- package/template/.agent/skills/prd-templates/references/data-placement-template.md +74 -0
- package/template/.agent/skills/prd-templates/references/decomposition-templates.md +211 -0
- package/template/.agent/skills/prd-templates/references/design-system-decisions.md +198 -0
- package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +124 -0
- package/template/.agent/skills/prd-templates/references/fe-classification-procedures.md +47 -0
- package/template/.agent/skills/prd-templates/references/fe-spec-template.md +84 -0
- package/template/.agent/skills/prd-templates/references/infrastructure-report-template.md +71 -0
- package/template/.agent/skills/prd-templates/references/operational-templates.md +116 -0
- package/template/.agent/skills/prd-templates/references/placeholder-guard-template.md +21 -0
- package/template/.agent/skills/prd-templates/references/surface-model.md +61 -0
- package/template/.agent/skills/prd-templates/references/vision-template.md +66 -0
- package/template/.agent/skills/prompt-engineer/README.md +659 -0
- package/template/.agent/skills/prompt-engineer/SKILL.md +249 -0
- package/template/.agent/skills/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skills/resolve-ambiguity/SKILL.md +278 -0
- package/template/.agent/skills/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skills/security-scanning-security-hardening/SKILL.md +231 -0
- package/template/.agent/skills/session-continuity/SKILL.md +730 -0
- package/template/.agent/skills/session-continuity/protocols/01-session-resumption.md +38 -0
- package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +85 -0
- package/template/.agent/skills/session-continuity/protocols/03-progress-update.md +70 -0
- package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +60 -0
- package/template/.agent/skills/session-continuity/protocols/05-session-close.md +37 -0
- package/template/.agent/skills/session-continuity/protocols/06-decision-analysis.md +84 -0
- package/template/.agent/skills/session-continuity/protocols/07-spec-pipeline-generation.md +48 -0
- package/template/.agent/skills/session-continuity/protocols/08-spec-pipeline-update.md +43 -0
- package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +122 -0
- package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +104 -0
- package/template/.agent/skills/session-continuity/protocols/ambiguity-gates.md +48 -0
- package/template/.agent/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.agent/skills/skill-creator/README.md +270 -0
- package/template/.agent/skills/skill-creator/SKILL.md +590 -0
- package/template/.agent/skills/skill-creator/references/output-patterns.md +82 -0
- package/template/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/template/.agent/skills/skill-creator/scripts/init_skill.py +303 -0
- package/template/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/template/.agent/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/template/.agent/skills/spec-writing/SKILL.md +110 -0
- package/template/.agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/template/.agent/skills/systematic-debugging/SKILL.md +297 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/template/.agent/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/template/.agent/skills/systematic-debugging/find-polluter.sh +63 -0
- package/template/.agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/template/.agent/skills/systematic-debugging/test-academic.md +14 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/template/.agent/skills/tdd-workflow/SKILL.md +409 -0
- package/template/.agent/skills/tech-stack-catalog/SKILL.md +49 -0
- package/template/.agent/skills/tech-stack-catalog/references/constraint-questions.md +21 -0
- package/template/.agent/skills/tech-stack-catalog/references/dev-tooling-decisions.md +37 -0
- package/template/.agent/skills/tech-stack-catalog/references/surface-decision-tables.md +69 -0
- package/template/.agent/skills/technical-writer/SKILL.md +242 -0
- package/template/.agent/skills/testing-strategist/SKILL.md +932 -0
- package/template/.agent/skills/verification-before-completion/SKILL.md +145 -0
- package/template/.agent/skills/workflow-automation/SKILL.md +73 -0
- package/template/.agent/workflows/audit-ambiguity-execute.md +165 -0
- package/template/.agent/workflows/audit-ambiguity-rubrics.md +83 -0
- package/template/.agent/workflows/audit-ambiguity.md +64 -0
- package/template/.agent/workflows/bootstrap-agents-fill.md +201 -0
- package/template/.agent/workflows/bootstrap-agents-provision.md +197 -0
- package/template/.agent/workflows/bootstrap-agents.md +66 -0
- package/template/.agent/workflows/create-prd-architecture.md +119 -0
- package/template/.agent/workflows/create-prd-compile.md +138 -0
- package/template/.agent/workflows/create-prd-design-system.md +135 -0
- package/template/.agent/workflows/create-prd-security.md +113 -0
- package/template/.agent/workflows/create-prd-stack.md +91 -0
- package/template/.agent/workflows/create-prd.md +168 -0
- package/template/.agent/workflows/decompose-architecture-structure.md +82 -0
- package/template/.agent/workflows/decompose-architecture-validate.md +119 -0
- package/template/.agent/workflows/decompose-architecture.md +111 -0
- package/template/.agent/workflows/evolve-contract.md +98 -0
- package/template/.agent/workflows/evolve-feature-cascade.md +140 -0
- package/template/.agent/workflows/evolve-feature-classify.md +116 -0
- package/template/.agent/workflows/evolve-feature.md +56 -0
- package/template/.agent/workflows/ideate-discover.md +144 -0
- package/template/.agent/workflows/ideate-extract.md +129 -0
- package/template/.agent/workflows/ideate-validate.md +117 -0
- package/template/.agent/workflows/ideate.md +113 -0
- package/template/.agent/workflows/implement-slice-setup.md +113 -0
- package/template/.agent/workflows/implement-slice-tdd.md +198 -0
- package/template/.agent/workflows/implement-slice.md +50 -0
- package/template/.agent/workflows/plan-phase.md +202 -0
- package/template/.agent/workflows/propagate-decision-apply.md +135 -0
- package/template/.agent/workflows/propagate-decision-scan.md +147 -0
- package/template/.agent/workflows/propagate-decision.md +56 -0
- package/template/.agent/workflows/remediate-pipeline-assess.md +138 -0
- package/template/.agent/workflows/remediate-pipeline-execute.md +135 -0
- package/template/.agent/workflows/remediate-pipeline.md +55 -0
- package/template/.agent/workflows/resolve-ambiguity.md +82 -0
- package/template/.agent/workflows/sync-kit.md +209 -0
- package/template/.agent/workflows/update-architecture-map.md +74 -0
- package/template/.agent/workflows/validate-phase.md +219 -0
- package/template/.agent/workflows/verify-infrastructure.md +207 -0
- package/template/.agent/workflows/write-architecture-spec-deepen.md +139 -0
- package/template/.agent/workflows/write-architecture-spec-design.md +202 -0
- package/template/.agent/workflows/write-architecture-spec.md +63 -0
- package/template/.agent/workflows/write-be-spec-classify.md +165 -0
- package/template/.agent/workflows/write-be-spec-write.md +98 -0
- package/template/.agent/workflows/write-be-spec.md +76 -0
- package/template/.agent/workflows/write-fe-spec-classify.md +170 -0
- package/template/.agent/workflows/write-fe-spec-write.md +94 -0
- package/template/.agent/workflows/write-fe-spec.md +71 -0
- package/template/AGENTS.md +176 -0
- package/template/GEMINI.md +177 -0
- package/template/docs/README.md +187 -0
- package/template/docs/audits/.gitkeep +0 -0
- package/template/docs/audits/README.md +10 -0
- package/template/docs/plans/.gitkeep +0 -0
- package/template/docs/plans/README.md +21 -0
- package/template/docs/plans/be/.gitkeep +0 -0
- package/template/docs/plans/be/README.md +11 -0
- package/template/docs/plans/fe/.gitkeep +0 -0
- package/template/docs/plans/fe/README.md +11 -0
- package/template/docs/plans/ia/.gitkeep +0 -0
- package/template/docs/plans/ia/README.md +17 -0
- package/template/docs/plans/ia/deep-dives/.gitkeep +0 -0
- package/template/docs/plans/ia/deep-dives/README.md +5 -0
- package/template/docs/plans/phases/.gitkeep +0 -0
- package/template/docs/plans/phases/README.md +11 -0
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: offline-first-pwa
|
|
3
|
+
description: "Build Progressive Web Apps with offline-first architecture, service workers, cache strategies, background sync, push notifications, and IndexedDB persistence. Use when adding PWA features, offline support, or installable web app capabilities."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Offline-First PWA
|
|
8
|
+
|
|
9
|
+
Build web applications that work reliably regardless of network conditions. Progressive Web Apps combine the reach of the web with the capabilities of native apps.
|
|
10
|
+
|
|
11
|
+
## Web App Manifest
|
|
12
|
+
|
|
13
|
+
The manifest defines how the app appears when installed on a device.
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"name": "My Application",
|
|
18
|
+
"short_name": "MyApp",
|
|
19
|
+
"description": "A brief description of what the app does",
|
|
20
|
+
"start_url": "/",
|
|
21
|
+
"display": "standalone",
|
|
22
|
+
"orientation": "any",
|
|
23
|
+
"theme_color": "#1a1a2e",
|
|
24
|
+
"background_color": "#1a1a2e",
|
|
25
|
+
"icons": [
|
|
26
|
+
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
|
27
|
+
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
|
|
28
|
+
{ "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
|
29
|
+
],
|
|
30
|
+
"screenshots": [
|
|
31
|
+
{ "src": "/screenshots/desktop.png", "sizes": "1280x720", "type": "image/png", "form_factor": "wide" },
|
|
32
|
+
{ "src": "/screenshots/mobile.png", "sizes": "750x1334", "type": "image/png", "form_factor": "narrow" }
|
|
33
|
+
],
|
|
34
|
+
"categories": ["productivity"],
|
|
35
|
+
"prefer_related_applications": false
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Link in HTML:**
|
|
40
|
+
```html
|
|
41
|
+
<link rel="manifest" href="/manifest.json" />
|
|
42
|
+
<meta name="theme-color" content="#1a1a2e" />
|
|
43
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
44
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
45
|
+
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Display Modes
|
|
49
|
+
|
|
50
|
+
| Mode | Behavior |
|
|
51
|
+
|------|----------|
|
|
52
|
+
| `standalone` | Looks like a native app (no browser chrome). Most common. |
|
|
53
|
+
| `fullscreen` | No browser chrome, no system status bar. For games/immersive. |
|
|
54
|
+
| `minimal-ui` | Standalone with minimal browser controls. |
|
|
55
|
+
| `browser` | Normal browser tab. Not recommended for PWA. |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Service Worker Lifecycle
|
|
60
|
+
|
|
61
|
+
### Registration
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// src/lib/register-sw.ts
|
|
65
|
+
export async function registerServiceWorker(): Promise<ServiceWorkerRegistration | null> {
|
|
66
|
+
if (!('serviceWorker' in navigator)) {
|
|
67
|
+
console.warn('Service workers not supported');
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const registration = await navigator.serviceWorker.register('/sw.js', {
|
|
73
|
+
scope: '/',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
registration.addEventListener('updatefound', () => {
|
|
77
|
+
const newWorker = registration.installing;
|
|
78
|
+
if (!newWorker) return;
|
|
79
|
+
|
|
80
|
+
newWorker.addEventListener('statechange', () => {
|
|
81
|
+
if (newWorker.state === 'activated' && navigator.serviceWorker.controller) {
|
|
82
|
+
// New version available --- notify the user
|
|
83
|
+
showUpdateNotification();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return registration;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('Service worker registration failed:', error);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Service Worker Structure
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// public/sw.js
|
|
100
|
+
const CACHE_NAME = 'app-v1';
|
|
101
|
+
const PRECACHE_URLS = [
|
|
102
|
+
'/',
|
|
103
|
+
'/offline.html',
|
|
104
|
+
'/styles/main.css',
|
|
105
|
+
'/scripts/main.js',
|
|
106
|
+
'/icons/icon-192.png',
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
// INSTALL: precache essential assets
|
|
110
|
+
self.addEventListener('install', (event: ExtendableEvent) => {
|
|
111
|
+
event.waitUntil(
|
|
112
|
+
caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS))
|
|
113
|
+
);
|
|
114
|
+
// Activate immediately without waiting for existing clients to close
|
|
115
|
+
self.skipWaiting();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ACTIVATE: clean up old caches
|
|
119
|
+
self.addEventListener('activate', (event: ExtendableEvent) => {
|
|
120
|
+
event.waitUntil(
|
|
121
|
+
caches.keys().then((cacheNames) =>
|
|
122
|
+
Promise.all(
|
|
123
|
+
cacheNames
|
|
124
|
+
.filter((name) => name !== CACHE_NAME)
|
|
125
|
+
.map((name) => caches.delete(name))
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
// Take control of all clients immediately
|
|
130
|
+
self.clients.claim();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// FETCH: serve from cache or network
|
|
134
|
+
self.addEventListener('fetch', (event: FetchEvent) => {
|
|
135
|
+
// Handle fetch with chosen strategy (see next section)
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Cache Strategies
|
|
142
|
+
|
|
143
|
+
### Cache First (Static Assets)
|
|
144
|
+
|
|
145
|
+
Best for versioned assets that rarely change (images, fonts, CSS/JS bundles).
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Cache first, fall back to network
|
|
149
|
+
self.addEventListener('fetch', (event: FetchEvent) => {
|
|
150
|
+
if (isStaticAsset(event.request.url)) {
|
|
151
|
+
event.respondWith(
|
|
152
|
+
caches.match(event.request).then((cached) => {
|
|
153
|
+
return cached || fetch(event.request).then((response) => {
|
|
154
|
+
const clone = response.clone();
|
|
155
|
+
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
|
156
|
+
return response;
|
|
157
|
+
});
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
function isStaticAsset(url: string): boolean {
|
|
164
|
+
return /\.(js|css|png|jpg|webp|avif|woff2|svg)$/.test(url);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Network First (API Data)
|
|
169
|
+
|
|
170
|
+
Best for data that must be fresh when online but can fall back to cached data offline.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
async function networkFirst(request: Request, cacheName: string): Promise<Response> {
|
|
174
|
+
try {
|
|
175
|
+
const networkResponse = await fetch(request);
|
|
176
|
+
// Cache the fresh response
|
|
177
|
+
const cache = await caches.open(cacheName);
|
|
178
|
+
cache.put(request, networkResponse.clone());
|
|
179
|
+
return networkResponse;
|
|
180
|
+
} catch {
|
|
181
|
+
// Network failed, try cache
|
|
182
|
+
const cached = await caches.match(request);
|
|
183
|
+
if (cached) return cached;
|
|
184
|
+
// Nothing in cache either --- return offline fallback
|
|
185
|
+
return caches.match('/offline.html') as Promise<Response>;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Stale-While-Revalidate (Semi-Fresh Content)
|
|
191
|
+
|
|
192
|
+
Best for content that can be slightly stale (blog posts, user profiles, catalogs).
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
async function staleWhileRevalidate(request: Request, cacheName: string): Promise<Response> {
|
|
196
|
+
const cache = await caches.open(cacheName);
|
|
197
|
+
const cached = await cache.match(request);
|
|
198
|
+
|
|
199
|
+
// Fetch fresh version in the background
|
|
200
|
+
const networkPromise = fetch(request).then((response) => {
|
|
201
|
+
cache.put(request, response.clone());
|
|
202
|
+
return response;
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Return cached immediately if available, otherwise wait for network
|
|
206
|
+
return cached || networkPromise;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Strategy Decision Table
|
|
211
|
+
|
|
212
|
+
| Content Type | Strategy | Why |
|
|
213
|
+
|-------------|----------|-----|
|
|
214
|
+
| App shell (HTML layout) | Cache first | Instant load, update in background |
|
|
215
|
+
| Static assets (JS, CSS, fonts) | Cache first | Versioned via filename hash |
|
|
216
|
+
| API data (user-specific) | Network first | Must be fresh, offline fallback |
|
|
217
|
+
| API data (public/catalog) | Stale-while-revalidate | Fast display, refresh in background |
|
|
218
|
+
| Images (user uploads) | Cache first | Immutable once uploaded |
|
|
219
|
+
| Page navigation | Network first | HTML should be fresh |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Install Prompts
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
let deferredPrompt: BeforeInstallPromptEvent | null = null;
|
|
227
|
+
|
|
228
|
+
window.addEventListener('beforeinstallprompt', (event: BeforeInstallPromptEvent) => {
|
|
229
|
+
// Prevent the browser's default mini-infobar
|
|
230
|
+
event.preventDefault();
|
|
231
|
+
deferredPrompt = event;
|
|
232
|
+
showInstallButton();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
async function handleInstallClick(): Promise<void> {
|
|
236
|
+
if (!deferredPrompt) return;
|
|
237
|
+
|
|
238
|
+
deferredPrompt.prompt();
|
|
239
|
+
const { outcome } = await deferredPrompt.userChoice;
|
|
240
|
+
|
|
241
|
+
if (outcome === 'accepted') {
|
|
242
|
+
console.log('User accepted install');
|
|
243
|
+
} else {
|
|
244
|
+
console.log('User dismissed install');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
deferredPrompt = null;
|
|
248
|
+
hideInstallButton();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Detect if already installed
|
|
252
|
+
window.addEventListener('appinstalled', () => {
|
|
253
|
+
hideInstallButton();
|
|
254
|
+
deferredPrompt = null;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Check if running as installed PWA
|
|
258
|
+
function isInstalledPWA(): boolean {
|
|
259
|
+
return window.matchMedia('(display-mode: standalone)').matches
|
|
260
|
+
|| (navigator as any).standalone === true; // iOS Safari
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Background Sync
|
|
267
|
+
|
|
268
|
+
Queue actions taken offline and replay them when connectivity returns.
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// In the main app: register a sync
|
|
272
|
+
async function saveFormOffline(data: FormData): Promise<void> {
|
|
273
|
+
// Save to IndexedDB
|
|
274
|
+
await saveToQueue('form-submissions', data);
|
|
275
|
+
|
|
276
|
+
// Register background sync
|
|
277
|
+
const registration = await navigator.serviceWorker.ready;
|
|
278
|
+
await registration.sync.register('sync-forms');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// In service worker: handle the sync event
|
|
282
|
+
self.addEventListener('sync', (event: SyncEvent) => {
|
|
283
|
+
if (event.tag === 'sync-forms') {
|
|
284
|
+
event.waitUntil(replayFormSubmissions());
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
async function replayFormSubmissions(): Promise<void> {
|
|
289
|
+
const queue = await getQueue('form-submissions');
|
|
290
|
+
|
|
291
|
+
for (const item of queue) {
|
|
292
|
+
try {
|
|
293
|
+
await fetch('/api/submit', {
|
|
294
|
+
method: 'POST',
|
|
295
|
+
headers: { 'Content-Type': 'application/json' },
|
|
296
|
+
body: JSON.stringify(item.data),
|
|
297
|
+
});
|
|
298
|
+
await removeFromQueue('form-submissions', item.id);
|
|
299
|
+
} catch {
|
|
300
|
+
// Will retry on next sync event
|
|
301
|
+
throw new Error('Sync failed, will retry');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Push Notifications
|
|
310
|
+
|
|
311
|
+
### Subscription
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
async function subscribeToPush(): Promise<PushSubscription | null> {
|
|
315
|
+
const registration = await navigator.serviceWorker.ready;
|
|
316
|
+
|
|
317
|
+
// Check permission
|
|
318
|
+
const permission = await Notification.requestPermission();
|
|
319
|
+
if (permission !== 'granted') return null;
|
|
320
|
+
|
|
321
|
+
const subscription = await registration.pushManager.subscribe({
|
|
322
|
+
userVisibleOnly: true, // Required by browsers
|
|
323
|
+
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Send subscription to your server
|
|
327
|
+
await fetch('/api/push/subscribe', {
|
|
328
|
+
method: 'POST',
|
|
329
|
+
headers: { 'Content-Type': 'application/json' },
|
|
330
|
+
body: JSON.stringify(subscription),
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
return subscription;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function urlBase64ToUint8Array(base64String: string): Uint8Array {
|
|
337
|
+
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
|
338
|
+
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
|
|
339
|
+
const rawData = atob(base64);
|
|
340
|
+
return Uint8Array.from(rawData, (char) => char.charCodeAt(0));
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Display and Click Handling (Service Worker)
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// Handle incoming push
|
|
348
|
+
self.addEventListener('push', (event: PushEvent) => {
|
|
349
|
+
const data = event.data?.json() ?? {};
|
|
350
|
+
|
|
351
|
+
const options: NotificationOptions = {
|
|
352
|
+
body: data.body ?? 'You have a new notification',
|
|
353
|
+
icon: '/icons/icon-192.png',
|
|
354
|
+
badge: '/icons/badge-72.png',
|
|
355
|
+
data: { url: data.url ?? '/' },
|
|
356
|
+
actions: data.actions ?? [],
|
|
357
|
+
tag: data.tag, // Replaces existing notification with same tag
|
|
358
|
+
renotify: !!data.tag, // Vibrate again even if replacing
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
event.waitUntil(
|
|
362
|
+
self.registration.showNotification(data.title ?? 'Notification', options)
|
|
363
|
+
);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Handle notification click
|
|
367
|
+
self.addEventListener('notificationclick', (event: NotificationEvent) => {
|
|
368
|
+
event.notification.close();
|
|
369
|
+
|
|
370
|
+
const targetUrl = event.notification.data?.url ?? '/';
|
|
371
|
+
|
|
372
|
+
event.waitUntil(
|
|
373
|
+
self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clients) => {
|
|
374
|
+
// Focus existing window if already open
|
|
375
|
+
for (const client of clients) {
|
|
376
|
+
if (client.url === targetUrl && 'focus' in client) {
|
|
377
|
+
return client.focus();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Otherwise open a new window
|
|
381
|
+
return self.clients.openWindow(targetUrl);
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## IndexedDB for Local Persistence
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
// src/lib/db.ts
|
|
393
|
+
import { openDB, type DBSchema, type IDBPDatabase } from 'idb';
|
|
394
|
+
|
|
395
|
+
interface AppDB extends DBSchema {
|
|
396
|
+
'offline-queue': {
|
|
397
|
+
key: string;
|
|
398
|
+
value: {
|
|
399
|
+
id: string;
|
|
400
|
+
type: string;
|
|
401
|
+
data: unknown;
|
|
402
|
+
createdAt: number;
|
|
403
|
+
retryCount: number;
|
|
404
|
+
};
|
|
405
|
+
indexes: { 'by-type': string };
|
|
406
|
+
};
|
|
407
|
+
'cached-data': {
|
|
408
|
+
key: string;
|
|
409
|
+
value: {
|
|
410
|
+
key: string;
|
|
411
|
+
data: unknown;
|
|
412
|
+
expiresAt: number;
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let dbPromise: Promise<IDBPDatabase<AppDB>>;
|
|
418
|
+
|
|
419
|
+
export function getDB(): Promise<IDBPDatabase<AppDB>> {
|
|
420
|
+
if (!dbPromise) {
|
|
421
|
+
dbPromise = openDB<AppDB>('app-db', 1, {
|
|
422
|
+
upgrade(db) {
|
|
423
|
+
const queue = db.createObjectStore('offline-queue', { keyPath: 'id' });
|
|
424
|
+
queue.createIndex('by-type', 'type');
|
|
425
|
+
|
|
426
|
+
db.createObjectStore('cached-data', { keyPath: 'key' });
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return dbPromise;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Queue operations
|
|
434
|
+
export async function enqueue(type: string, data: unknown): Promise<string> {
|
|
435
|
+
const db = await getDB();
|
|
436
|
+
const id = crypto.randomUUID();
|
|
437
|
+
await db.put('offline-queue', { id, type, data, createdAt: Date.now(), retryCount: 0 });
|
|
438
|
+
return id;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export async function dequeue(id: string): Promise<void> {
|
|
442
|
+
const db = await getDB();
|
|
443
|
+
await db.delete('offline-queue', id);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export async function getQueueByType(type: string): Promise<AppDB['offline-queue']['value'][]> {
|
|
447
|
+
const db = await getDB();
|
|
448
|
+
return db.getAllFromIndex('offline-queue', 'by-type', type);
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Update Flow
|
|
455
|
+
|
|
456
|
+
### "New Version Available" Prompt
|
|
457
|
+
|
|
458
|
+
```tsx
|
|
459
|
+
function UpdatePrompt() {
|
|
460
|
+
const [showPrompt, setShowPrompt] = useState(false);
|
|
461
|
+
const [waitingWorker, setWaitingWorker] = useState<ServiceWorker | null>(null);
|
|
462
|
+
|
|
463
|
+
useEffect(() => {
|
|
464
|
+
async function checkForUpdate() {
|
|
465
|
+
const registration = await navigator.serviceWorker?.ready;
|
|
466
|
+
if (!registration) return;
|
|
467
|
+
|
|
468
|
+
registration.addEventListener('updatefound', () => {
|
|
469
|
+
const newWorker = registration.installing;
|
|
470
|
+
if (!newWorker) return;
|
|
471
|
+
|
|
472
|
+
newWorker.addEventListener('statechange', () => {
|
|
473
|
+
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
|
474
|
+
setWaitingWorker(newWorker);
|
|
475
|
+
setShowPrompt(true);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
checkForUpdate();
|
|
482
|
+
}, []);
|
|
483
|
+
|
|
484
|
+
function handleUpdate() {
|
|
485
|
+
waitingWorker?.postMessage({ type: 'SKIP_WAITING' });
|
|
486
|
+
// Reload once the new worker takes over
|
|
487
|
+
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
488
|
+
window.location.reload();
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (!showPrompt) return null;
|
|
493
|
+
|
|
494
|
+
return (
|
|
495
|
+
<div role="alert" className="update-banner">
|
|
496
|
+
<p>A new version is available.</p>
|
|
497
|
+
<button onClick={handleUpdate}>Update now</button>
|
|
498
|
+
<button onClick={() => setShowPrompt(false)} aria-label="Dismiss update notification">
|
|
499
|
+
Later
|
|
500
|
+
</button>
|
|
501
|
+
</div>
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**In the service worker:**
|
|
507
|
+
```typescript
|
|
508
|
+
self.addEventListener('message', (event) => {
|
|
509
|
+
if (event.data?.type === 'SKIP_WAITING') {
|
|
510
|
+
self.skipWaiting();
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## Workbox Patterns
|
|
518
|
+
|
|
519
|
+
[Workbox](https://developer.chrome.com/docs/workbox/) simplifies service worker development with tested, production-ready modules.
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
// sw.ts (compiled with workbox-cli or webpack plugin)
|
|
523
|
+
import { precacheAndRoute } from 'workbox-precaching';
|
|
524
|
+
import { registerRoute } from 'workbox-routing';
|
|
525
|
+
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
|
|
526
|
+
import { ExpirationPlugin } from 'workbox-expiration';
|
|
527
|
+
import { BackgroundSyncPlugin } from 'workbox-background-sync';
|
|
528
|
+
|
|
529
|
+
// Precache app shell (injected by build tool)
|
|
530
|
+
precacheAndRoute(self.__WB_MANIFEST);
|
|
531
|
+
|
|
532
|
+
// Static assets: cache first
|
|
533
|
+
registerRoute(
|
|
534
|
+
({ request }) => request.destination === 'image' || request.destination === 'font',
|
|
535
|
+
new CacheFirst({
|
|
536
|
+
cacheName: 'static-assets',
|
|
537
|
+
plugins: [
|
|
538
|
+
new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 }),
|
|
539
|
+
],
|
|
540
|
+
})
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
// API data: network first with cache fallback
|
|
544
|
+
registerRoute(
|
|
545
|
+
({ url }) => url.pathname.startsWith('/api/'),
|
|
546
|
+
new NetworkFirst({
|
|
547
|
+
cacheName: 'api-data',
|
|
548
|
+
plugins: [
|
|
549
|
+
new ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 24 * 60 * 60 }),
|
|
550
|
+
],
|
|
551
|
+
})
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
// Page navigations: network first
|
|
555
|
+
registerRoute(
|
|
556
|
+
({ request }) => request.mode === 'navigate',
|
|
557
|
+
new NetworkFirst({
|
|
558
|
+
cacheName: 'pages',
|
|
559
|
+
plugins: [
|
|
560
|
+
new ExpirationPlugin({ maxEntries: 25 }),
|
|
561
|
+
],
|
|
562
|
+
})
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
// Background sync for form submissions
|
|
566
|
+
const bgSyncPlugin = new BackgroundSyncPlugin('form-queue', {
|
|
567
|
+
maxRetentionTime: 24 * 60, // Retry for up to 24 hours (in minutes)
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
registerRoute(
|
|
571
|
+
({ url }) => url.pathname === '/api/submit',
|
|
572
|
+
new NetworkFirst({ plugins: [bgSyncPlugin] }),
|
|
573
|
+
'POST'
|
|
574
|
+
);
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Offline UI Patterns
|
|
580
|
+
|
|
581
|
+
### Online/Offline Indicator
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
function useOnlineStatus(): boolean {
|
|
585
|
+
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
|
586
|
+
|
|
587
|
+
useEffect(() => {
|
|
588
|
+
const handleOnline = () => setIsOnline(true);
|
|
589
|
+
const handleOffline = () => setIsOnline(false);
|
|
590
|
+
|
|
591
|
+
window.addEventListener('online', handleOnline);
|
|
592
|
+
window.addEventListener('offline', handleOffline);
|
|
593
|
+
|
|
594
|
+
return () => {
|
|
595
|
+
window.removeEventListener('online', handleOnline);
|
|
596
|
+
window.removeEventListener('offline', handleOffline);
|
|
597
|
+
};
|
|
598
|
+
}, []);
|
|
599
|
+
|
|
600
|
+
return isOnline;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function OfflineBanner() {
|
|
604
|
+
const isOnline = useOnlineStatus();
|
|
605
|
+
|
|
606
|
+
if (isOnline) return null;
|
|
607
|
+
|
|
608
|
+
return (
|
|
609
|
+
<div role="status" aria-live="polite" className="offline-banner">
|
|
610
|
+
You are offline. Some features may be unavailable.
|
|
611
|
+
</div>
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Queued Actions Display
|
|
617
|
+
|
|
618
|
+
```tsx
|
|
619
|
+
function PendingActions() {
|
|
620
|
+
const [pending, setPending] = useState<QueueItem[]>([]);
|
|
621
|
+
|
|
622
|
+
return pending.length > 0 ? (
|
|
623
|
+
<div role="status" aria-live="polite" className="pending-actions">
|
|
624
|
+
<p>{pending.length} action{pending.length > 1 ? 's' : ''} will sync when you are back online.</p>
|
|
625
|
+
</div>
|
|
626
|
+
) : null;
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## Debugging Service Workers
|
|
633
|
+
|
|
634
|
+
| Tool | Purpose |
|
|
635
|
+
|------|---------|
|
|
636
|
+
| Chrome DevTools > Application > Service Workers | View registration, state, lifecycle events |
|
|
637
|
+
| Chrome DevTools > Application > Cache Storage | Inspect cached assets |
|
|
638
|
+
| Chrome DevTools > Application > IndexedDB | Inspect local data |
|
|
639
|
+
| chrome://serviceworker-internals | Low-level SW debugging |
|
|
640
|
+
| Lighthouse PWA audit | Full PWA compliance check |
|
|
641
|
+
| `navigator.serviceWorker.controller` | Verify which SW is controlling the page |
|
|
642
|
+
|
|
643
|
+
**Force update during development:**
|
|
644
|
+
```typescript
|
|
645
|
+
// In DevTools console
|
|
646
|
+
navigator.serviceWorker.getRegistration().then((reg) => reg?.unregister());
|
|
647
|
+
caches.keys().then((names) => names.forEach((name) => caches.delete(name)));
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
## References
|
|
651
|
+
|
|
652
|
+
- [web.dev: Progressive Web Apps](https://web.dev/progressive-web-apps/)
|
|
653
|
+
- [Workbox Documentation](https://developer.chrome.com/docs/workbox/)
|
|
654
|
+
- [Service Worker API (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
|
|
655
|
+
- [Web App Manifest (MDN)](https://developer.mozilla.org/en-US/docs/Web/Manifest)
|
|
656
|
+
- [idb library](https://github.com/jakearchibald/idb)
|
|
657
|
+
- [Push API (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Push_API)
|