dreamcontext 0.5.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 +523 -0
- package/agents/dreamcontext-explore.md +137 -0
- package/agents/dreamcontext-initializer.md +169 -0
- package/agents/sleep-product.md +268 -0
- package/agents/sleep-state.md +270 -0
- package/agents/sleep-tasks.md +134 -0
- package/dist/agents/dreamcontext-explore.md +137 -0
- package/dist/agents/dreamcontext-initializer.md +169 -0
- package/dist/agents/sleep-product.md +268 -0
- package/dist/agents/sleep-state.md +270 -0
- package/dist/agents/sleep-tasks.md +134 -0
- package/dist/dashboard/assets/BrainCanvas3D-BLJ4_SqE.js +5126 -0
- package/dist/dashboard/assets/_baseUniq-DpaDAx_H.js +1 -0
- package/dist/dashboard/assets/arc-JvK3Ik1p.js +1 -0
- package/dist/dashboard/assets/architectureDiagram-Q4EWVU46-CCvw4XFg.js +36 -0
- package/dist/dashboard/assets/blockDiagram-DXYQGD6D-DMobz1n7.js +132 -0
- package/dist/dashboard/assets/c4Diagram-AHTNJAMY-FwcHT5er.js +10 -0
- package/dist/dashboard/assets/channel-D6954IHZ.js +1 -0
- package/dist/dashboard/assets/chunk-4BX2VUAB-B5kYwmBa.js +1 -0
- package/dist/dashboard/assets/chunk-4TB4RGXK-0ot1eS0J.js +206 -0
- package/dist/dashboard/assets/chunk-55IACEB6-24ngcLgH.js +1 -0
- package/dist/dashboard/assets/chunk-EDXVE4YY-DATt1OUl.js +1 -0
- package/dist/dashboard/assets/chunk-FMBD7UC4-BprbGSJw.js +15 -0
- package/dist/dashboard/assets/chunk-OYMX7WX6-CJJhpKWP.js +231 -0
- package/dist/dashboard/assets/chunk-QZHKN3VN-Cisp65Vq.js +1 -0
- package/dist/dashboard/assets/chunk-YZCP3GAM-DtMk33tU.js +1 -0
- package/dist/dashboard/assets/classDiagram-6PBFFD2Q-Bk4KDqBj.js +1 -0
- package/dist/dashboard/assets/classDiagram-v2-HSJHXN6E-Bk4KDqBj.js +1 -0
- package/dist/dashboard/assets/clone-C9Yhti5q.js +1 -0
- package/dist/dashboard/assets/cose-bilkent-S5V4N54A-BxYomDLe.js +1 -0
- package/dist/dashboard/assets/cytoscape.esm-D_LviqZs.js +331 -0
- package/dist/dashboard/assets/dagre-KV5264BT-CsX1ZayG.js +4 -0
- package/dist/dashboard/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/dashboard/assets/diagram-5BDNPKRD-B2G4mPPw.js +10 -0
- package/dist/dashboard/assets/diagram-G4DWMVQ6-C8nxN9ZB.js +24 -0
- package/dist/dashboard/assets/diagram-MMDJMWI5-DaYymOrR.js +43 -0
- package/dist/dashboard/assets/diagram-TYMM5635-BpiYFv-I.js +24 -0
- package/dist/dashboard/assets/erDiagram-SMLLAGMA-C6pE7F61.js +85 -0
- package/dist/dashboard/assets/flowDiagram-DWJPFMVM-jdNEPVFq.js +162 -0
- package/dist/dashboard/assets/ganttDiagram-T4ZO3ILL-C8GoRj1C.js +292 -0
- package/dist/dashboard/assets/gitGraphDiagram-UUTBAWPF-SiRn7RJ8.js +106 -0
- package/dist/dashboard/assets/graph-9wbTW7ld.js +1 -0
- package/dist/dashboard/assets/index-BHp63EMw.js +475 -0
- package/dist/dashboard/assets/index-CdnDt_7U.css +1 -0
- package/dist/dashboard/assets/infoDiagram-42DDH7IO-DcDC8M1a.js +2 -0
- package/dist/dashboard/assets/ishikawaDiagram-UXIWVN3A-UjyrPeaS.js +70 -0
- package/dist/dashboard/assets/journeyDiagram-VCZTEJTY-CXJPYMxN.js +139 -0
- package/dist/dashboard/assets/kanban-definition-6JOO6SKY-Cm1n9eat.js +89 -0
- package/dist/dashboard/assets/katex-DkKDou_j.js +257 -0
- package/dist/dashboard/assets/layout-w8zmQGXp.js +1 -0
- package/dist/dashboard/assets/linear-CMNvIisH.js +1 -0
- package/dist/dashboard/assets/min-BqXwiqEr.js +1 -0
- package/dist/dashboard/assets/mindmap-definition-QFDTVHPH-tksxnjhx.js +96 -0
- package/dist/dashboard/assets/pieDiagram-DEJITSTG-lIVvnPyq.js +30 -0
- package/dist/dashboard/assets/quadrantDiagram-34T5L4WZ-DSMB57t5.js +7 -0
- package/dist/dashboard/assets/requirementDiagram-MS252O5E-NG99tgmc.js +84 -0
- package/dist/dashboard/assets/sankeyDiagram-XADWPNL6-C6EkbQKo.js +10 -0
- package/dist/dashboard/assets/sequenceDiagram-FGHM5R23-ASU7Zp6_.js +157 -0
- package/dist/dashboard/assets/stateDiagram-FHFEXIEX-DHklUzce.js +1 -0
- package/dist/dashboard/assets/stateDiagram-v2-QKLJ7IA2-BZXFb2Fh.js +1 -0
- package/dist/dashboard/assets/timeline-definition-GMOUNBTQ-B37xNhjS.js +120 -0
- package/dist/dashboard/assets/vennDiagram-DHZGUBPP-D28OvWbm.js +34 -0
- package/dist/dashboard/assets/wardley-RL74JXVD-BQdaLyVb.js +162 -0
- package/dist/dashboard/assets/wardleyDiagram-NUSXRM2D-D0vChrnT.js +20 -0
- package/dist/dashboard/assets/xychartDiagram-5P7HB3ND-BzSx7EpJ.js +7 -0
- package/dist/dashboard/favicon.svg +14 -0
- package/dist/dashboard/index.html +18 -0
- package/dist/hooks/marketing-binary-guard.sh +18 -0
- package/dist/index.js +15881 -0
- package/dist/skill-packs/agents/biv-customer-analyst.md +140 -0
- package/dist/skill-packs/agents/biv-decision-gate.md +147 -0
- package/dist/skill-packs/agents/biv-financial-analyst.md +128 -0
- package/dist/skill-packs/agents/biv-market-analyst.md +103 -0
- package/dist/skill-packs/agents/biv-researcher.md +140 -0
- package/dist/skill-packs/agents/biv-strategist.md +164 -0
- package/dist/skill-packs/agents/council-persona.md +142 -0
- package/dist/skill-packs/agents/council-synthesizer.md +208 -0
- package/dist/skill-packs/agents/discover-brand.md +216 -0
- package/dist/skill-packs/agents/goal-implementer.md +70 -0
- package/dist/skill-packs/agents/goal-plan-reviewer.md +68 -0
- package/dist/skill-packs/agents/goal-planner.md +75 -0
- package/dist/skill-packs/agents/goal-validator.md +68 -0
- package/dist/skill-packs/agents/marketing-creative.md +85 -0
- package/dist/skill-packs/agents/marketing-monitor.md +143 -0
- package/dist/skill-packs/agents/marketing-strategy.md +139 -0
- package/dist/skill-packs/agents/review-cloud-functions.md +158 -0
- package/dist/skill-packs/agents/review-edge-cases.md +147 -0
- package/dist/skill-packs/agents/review-frontend.md +134 -0
- package/dist/skill-packs/agents/review-router.md +165 -0
- package/dist/skill-packs/agents/review-security.md +139 -0
- package/dist/skill-packs/agents/reviewer.md +152 -0
- package/dist/skill-packs/brand-voice/SKILL.md +115 -0
- package/dist/skill-packs/brand-voice/discover-brand.md +126 -0
- package/dist/skill-packs/brand-voice/guideline-generation.md +154 -0
- package/dist/skill-packs/brand-voice/references/before-after-examples.md +194 -0
- package/dist/skill-packs/brand-voice/references/confidence-scoring.md +128 -0
- package/dist/skill-packs/brand-voice/references/guideline-template.md +241 -0
- package/dist/skill-packs/brand-voice/references/search-strategies.md +271 -0
- package/dist/skill-packs/brand-voice/references/source-ranking.md +248 -0
- package/dist/skill-packs/brand-voice/references/voice-constant-tone-flexes.md +115 -0
- package/dist/skill-packs/business-idea-discovery/SKILL.md +452 -0
- package/dist/skill-packs/business-idea-validation/SKILL.md +209 -0
- package/dist/skill-packs/business-idea-validation/stage-definitions.md +658 -0
- package/dist/skill-packs/catalog.json +657 -0
- package/dist/skill-packs/council/SKILL.md +134 -0
- package/dist/skill-packs/council/debate-protocol.md +90 -0
- package/dist/skill-packs/design/SKILL.md +301 -0
- package/dist/skill-packs/design/design-mobile.md +207 -0
- package/dist/skill-packs/design/design-web.md +148 -0
- package/dist/skill-packs/design/frontend-principles.md +157 -0
- package/dist/skill-packs/design/onboarding-design.md +230 -0
- package/dist/skill-packs/engineering/SKILL.md +155 -0
- package/dist/skill-packs/engineering/backend-principles.md +233 -0
- package/dist/skill-packs/engineering/firebase-cloud-functions/SKILL.md +44 -0
- package/dist/skill-packs/engineering/firebase-cloud-functions/references/gen_comparison.md +45 -0
- package/dist/skill-packs/engineering/firebase-cloud-functions/references/idempotency.md +145 -0
- package/dist/skill-packs/engineering/firebase-cloud-functions/references/local_testing.md +218 -0
- package/dist/skill-packs/engineering/firebase-cloud-functions/references/scaling.md +128 -0
- package/dist/skill-packs/engineering/firebase-cloud-functions/references/secrets.md +70 -0
- package/dist/skill-packs/engineering/firebase-cloud-functions/references/triggers_and_deployment.md +139 -0
- package/dist/skill-packs/engineering/firebase-firestore/SKILL.md +50 -0
- package/dist/skill-packs/engineering/firebase-firestore/references/indexes.md +96 -0
- package/dist/skill-packs/engineering/firebase-firestore/references/provisioning.md +101 -0
- package/dist/skill-packs/engineering/firebase-firestore/references/query_mechanics.md +182 -0
- package/dist/skill-packs/engineering/firebase-firestore/references/security_rules.md +299 -0
- package/dist/skill-packs/engineering/firebase-firestore/references/web_sdk_usage.md +265 -0
- package/dist/skill-packs/engineering/web-app-frontend.md +187 -0
- package/dist/skill-packs/goal-skill/SKILL.md +203 -0
- package/dist/skill-packs/growth/SKILL.md +480 -0
- package/dist/skill-packs/growth/lean-analytics-experiments.md +341 -0
- package/dist/skill-packs/growth/lean-analytics-metrics.md +295 -0
- package/dist/skill-packs/growth/performance-marketing.md +337 -0
- package/dist/skill-packs/meta-marketing/SKILL.md +423 -0
- package/dist/skill-packs/meta-marketing/account-ops.md +190 -0
- package/dist/skill-packs/meta-marketing/api-reference.md +535 -0
- package/dist/skill-packs/meta-marketing/copy-formulas.md +123 -0
- package/dist/skill-packs/meta-marketing/council-personas/creative-director.md +76 -0
- package/dist/skill-packs/meta-marketing/council-personas/performance-monitor.md +71 -0
- package/dist/skill-packs/meta-marketing/council-personas/risk-officer.md +79 -0
- package/dist/skill-packs/meta-marketing/council-personas/strategy-optimizer.md +76 -0
- package/dist/skill-packs/meta-marketing/creative-frameworks.md +176 -0
- package/dist/skill-packs/meta-marketing/mistakes.md +154 -0
- package/dist/skill-packs/meta-marketing/platform-state.md +63 -0
- package/dist/skill-packs/multi-review/REVIEWER_SHARED.md +143 -0
- package/dist/skill-packs/multi-review/SKILL.md +182 -0
- package/dist/skill-packs/system-prompts/SKILL.md +472 -0
- package/dist/templates/AGENTS.md +84 -0
- package/dist/templates/CLAUDE.md +84 -0
- package/dist/templates/council-debate.md +20 -0
- package/dist/templates/council-final-report.md +34 -0
- package/dist/templates/council-persona.md +10 -0
- package/dist/templates/council-report.md +6 -0
- package/dist/templates/feature.md +38 -0
- package/dist/templates/init/0.soul.md +33 -0
- package/dist/templates/init/1.user.md +29 -0
- package/dist/templates/init/2.memory.md +21 -0
- package/dist/templates/init/3.style_guide_and_branding.md +18 -0
- package/dist/templates/init/4.tech_stack.md +22 -0
- package/dist/templates/init/CHANGELOG.json +1 -0
- package/dist/templates/init/RELEASES.json +1 -0
- package/dist/templates/init/data-structures/default.md +35 -0
- package/dist/templates/knowledge.md +10 -0
- package/dist/templates/obsidian/app.json +15 -0
- package/dist/templates/obsidian/appearance.json +4 -0
- package/dist/templates/obsidian/graph.json +58 -0
- package/dist/templates/task.md +70 -0
- package/install.sh +73 -0
- package/package.json +58 -0
- package/skill/SKILL.md +529 -0
- package/skill-packs/agents/biv-customer-analyst.md +140 -0
- package/skill-packs/agents/biv-decision-gate.md +147 -0
- package/skill-packs/agents/biv-financial-analyst.md +128 -0
- package/skill-packs/agents/biv-market-analyst.md +103 -0
- package/skill-packs/agents/biv-researcher.md +140 -0
- package/skill-packs/agents/biv-strategist.md +164 -0
- package/skill-packs/agents/council-persona.md +142 -0
- package/skill-packs/agents/council-synthesizer.md +208 -0
- package/skill-packs/agents/discover-brand.md +216 -0
- package/skill-packs/agents/goal-implementer.md +70 -0
- package/skill-packs/agents/goal-plan-reviewer.md +68 -0
- package/skill-packs/agents/goal-planner.md +75 -0
- package/skill-packs/agents/goal-validator.md +68 -0
- package/skill-packs/agents/marketing-creative.md +85 -0
- package/skill-packs/agents/marketing-monitor.md +143 -0
- package/skill-packs/agents/marketing-strategy.md +139 -0
- package/skill-packs/agents/review-cloud-functions.md +158 -0
- package/skill-packs/agents/review-edge-cases.md +147 -0
- package/skill-packs/agents/review-frontend.md +134 -0
- package/skill-packs/agents/review-router.md +165 -0
- package/skill-packs/agents/review-security.md +139 -0
- package/skill-packs/agents/reviewer.md +152 -0
- package/skill-packs/brand-voice/SKILL.md +115 -0
- package/skill-packs/brand-voice/discover-brand.md +126 -0
- package/skill-packs/brand-voice/guideline-generation.md +154 -0
- package/skill-packs/brand-voice/references/before-after-examples.md +194 -0
- package/skill-packs/brand-voice/references/confidence-scoring.md +128 -0
- package/skill-packs/brand-voice/references/guideline-template.md +241 -0
- package/skill-packs/brand-voice/references/search-strategies.md +271 -0
- package/skill-packs/brand-voice/references/source-ranking.md +248 -0
- package/skill-packs/brand-voice/references/voice-constant-tone-flexes.md +115 -0
- package/skill-packs/business-idea-discovery/SKILL.md +452 -0
- package/skill-packs/business-idea-validation/SKILL.md +209 -0
- package/skill-packs/business-idea-validation/stage-definitions.md +658 -0
- package/skill-packs/catalog.json +657 -0
- package/skill-packs/council/SKILL.md +134 -0
- package/skill-packs/council/debate-protocol.md +90 -0
- package/skill-packs/design/SKILL.md +301 -0
- package/skill-packs/design/design-mobile.md +207 -0
- package/skill-packs/design/design-web.md +148 -0
- package/skill-packs/design/frontend-principles.md +157 -0
- package/skill-packs/design/onboarding-design.md +230 -0
- package/skill-packs/engineering/SKILL.md +155 -0
- package/skill-packs/engineering/backend-principles.md +233 -0
- package/skill-packs/engineering/firebase-cloud-functions/SKILL.md +44 -0
- package/skill-packs/engineering/firebase-cloud-functions/references/gen_comparison.md +45 -0
- package/skill-packs/engineering/firebase-cloud-functions/references/idempotency.md +145 -0
- package/skill-packs/engineering/firebase-cloud-functions/references/local_testing.md +218 -0
- package/skill-packs/engineering/firebase-cloud-functions/references/scaling.md +128 -0
- package/skill-packs/engineering/firebase-cloud-functions/references/secrets.md +70 -0
- package/skill-packs/engineering/firebase-cloud-functions/references/triggers_and_deployment.md +139 -0
- package/skill-packs/engineering/firebase-firestore/SKILL.md +50 -0
- package/skill-packs/engineering/firebase-firestore/references/indexes.md +96 -0
- package/skill-packs/engineering/firebase-firestore/references/provisioning.md +101 -0
- package/skill-packs/engineering/firebase-firestore/references/query_mechanics.md +182 -0
- package/skill-packs/engineering/firebase-firestore/references/security_rules.md +299 -0
- package/skill-packs/engineering/firebase-firestore/references/web_sdk_usage.md +265 -0
- package/skill-packs/engineering/web-app-frontend.md +187 -0
- package/skill-packs/goal-skill/SKILL.md +203 -0
- package/skill-packs/growth/SKILL.md +480 -0
- package/skill-packs/growth/lean-analytics-experiments.md +341 -0
- package/skill-packs/growth/lean-analytics-metrics.md +295 -0
- package/skill-packs/growth/performance-marketing.md +337 -0
- package/skill-packs/meta-marketing/SKILL.md +423 -0
- package/skill-packs/meta-marketing/account-ops.md +190 -0
- package/skill-packs/meta-marketing/api-reference.md +535 -0
- package/skill-packs/meta-marketing/copy-formulas.md +123 -0
- package/skill-packs/meta-marketing/council-personas/creative-director.md +76 -0
- package/skill-packs/meta-marketing/council-personas/performance-monitor.md +71 -0
- package/skill-packs/meta-marketing/council-personas/risk-officer.md +79 -0
- package/skill-packs/meta-marketing/council-personas/strategy-optimizer.md +76 -0
- package/skill-packs/meta-marketing/creative-frameworks.md +176 -0
- package/skill-packs/meta-marketing/mistakes.md +154 -0
- package/skill-packs/meta-marketing/platform-state.md +63 -0
- package/skill-packs/multi-review/REVIEWER_SHARED.md +143 -0
- package/skill-packs/multi-review/SKILL.md +182 -0
- package/skill-packs/system-prompts/SKILL.md +472 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Firestore Security Rules Structure
|
|
2
|
+
|
|
3
|
+
Security rules determine who has read and write access to your database.
|
|
4
|
+
|
|
5
|
+
## Service and Database Declaration
|
|
6
|
+
|
|
7
|
+
All Firestore rules begin with the service declaration and a match block for the database (usually default).
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
rules_version = '2';
|
|
11
|
+
|
|
12
|
+
service cloud.firestore {
|
|
13
|
+
match /databases/{database}/documents {
|
|
14
|
+
// Rules go here
|
|
15
|
+
// {database} wildcard represents the database name
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Basic Read/Write Operations
|
|
21
|
+
|
|
22
|
+
Rules describe **conditions** that must be true to allow an operation.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
match /cities/{city} {
|
|
26
|
+
allow read: if <condition>;
|
|
27
|
+
allow write: if <condition>;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Common Patterns
|
|
32
|
+
|
|
33
|
+
### Locked Mode (Deny All)
|
|
34
|
+
Good for starting development or private data.
|
|
35
|
+
```
|
|
36
|
+
match /{document=**} {
|
|
37
|
+
allow read, write: if false;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Test Mode (Allow All)
|
|
42
|
+
**WARNING: insecure.** Only for quick prototyping. Unsafe to deploy for production apps.
|
|
43
|
+
```
|
|
44
|
+
match /{document=**} {
|
|
45
|
+
allow read, write: if true;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Auth Required
|
|
50
|
+
Allow access only to authenticated users. This allows any logged in user access to all data.
|
|
51
|
+
```
|
|
52
|
+
match /{document=**} {
|
|
53
|
+
allow read, write: if request.auth != null;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### User-Specific Data
|
|
58
|
+
Allow users to access only their own data.
|
|
59
|
+
```
|
|
60
|
+
match /users/{userId} {
|
|
61
|
+
allow read, write: if request.auth != null && request.auth.uid == userId;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Allow only verified emails
|
|
66
|
+
Requires users to verify ownership of the email address before using it to read or write data
|
|
67
|
+
```
|
|
68
|
+
// Allow access based on email domain
|
|
69
|
+
match /some_collection/{document} {
|
|
70
|
+
allow read: if request.auth != null
|
|
71
|
+
&& request.auth.email_verified
|
|
72
|
+
&& request.auth.email.endsWith('@example.com');
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Validate data in write operations
|
|
77
|
+
```
|
|
78
|
+
// Example for creating a user profile
|
|
79
|
+
match /users/{userId} {
|
|
80
|
+
allow create: if request.auth.uid == userId &&
|
|
81
|
+
request.resource.data.email is string &&
|
|
82
|
+
request.resource.data.createdAt == request.time;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Granular Operations
|
|
87
|
+
|
|
88
|
+
You can break down `read` and `write` into more specific operations:
|
|
89
|
+
|
|
90
|
+
* **read**
|
|
91
|
+
* `get`: Retrieval of a single document.
|
|
92
|
+
* `list`: Queries and collection reads.
|
|
93
|
+
* **write**
|
|
94
|
+
* `create`: Writing to a nonexistent document.
|
|
95
|
+
* `update`: Writing to an existing document.
|
|
96
|
+
* `delete`: Removing a document.
|
|
97
|
+
|
|
98
|
+
```firestore
|
|
99
|
+
match /cities/{city} {
|
|
100
|
+
allow get: if <condition>;
|
|
101
|
+
allow list: if <condition>;
|
|
102
|
+
allow create: if <condition>;
|
|
103
|
+
allow update: if <condition>;
|
|
104
|
+
allow delete: if <condition>;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Hierarchical Data
|
|
109
|
+
|
|
110
|
+
Rules applied to a parent collection **do not** cascade to subcollections. You must explicitly match subcollections.
|
|
111
|
+
|
|
112
|
+
### Nested Match Statements
|
|
113
|
+
|
|
114
|
+
Inner matches are relative to the outer match path.
|
|
115
|
+
|
|
116
|
+
```firestore
|
|
117
|
+
match /cities/{city} {
|
|
118
|
+
allow read, write: if <condition>;
|
|
119
|
+
|
|
120
|
+
// Explicitly match the subcollection 'landmarks'
|
|
121
|
+
match /landmarks/{landmark} {
|
|
122
|
+
allow read, write: if <condition>;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Recursive Wildcards (`{name=**}`)
|
|
128
|
+
|
|
129
|
+
Use recursive wildcards to apply rules to an arbitrarily deep hierarchy.
|
|
130
|
+
|
|
131
|
+
* **Version 2** (recommended): `{path=**}` matches zero or more path segments.
|
|
132
|
+
|
|
133
|
+
```firestore
|
|
134
|
+
// Allow read access to ANY document in the 'cities' collection or its subcollections
|
|
135
|
+
match /cities/{document=**} {
|
|
136
|
+
allow read: if true;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Controlling Field Access
|
|
141
|
+
|
|
142
|
+
### Read Limitations
|
|
143
|
+
|
|
144
|
+
Reads in Firestore are **document-level**. You cannot retrieve a partial document.
|
|
145
|
+
* **Allowed**: Read the entire document.
|
|
146
|
+
* **Denied**: logical failure, no data returned.
|
|
147
|
+
|
|
148
|
+
To secure specific fields (e.g., private user data), you must **split them into a separate document** (e.g., a `private` subcollection).
|
|
149
|
+
|
|
150
|
+
### Write Restrictions
|
|
151
|
+
|
|
152
|
+
You can strictly control which fields can be written or updated.
|
|
153
|
+
|
|
154
|
+
#### On Creation
|
|
155
|
+
Use `request.resource.data.keys()` to validate fields.
|
|
156
|
+
|
|
157
|
+
```firestore
|
|
158
|
+
match /restaurant/{restId} {
|
|
159
|
+
allow create: if request.resource.data.keys().hasAll(['name', 'location']) &&
|
|
160
|
+
request.resource.data.keys().hasOnly(['name', 'location', 'city', 'address']);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### On Update
|
|
165
|
+
Use `diff()` to see what changed between the existing document (`resource.data`) and the incoming data (`request.resource.data`).
|
|
166
|
+
|
|
167
|
+
```firestore
|
|
168
|
+
match /restaurant/{restId} {
|
|
169
|
+
allow update: if request.resource.data.diff(resource.data).affectedKeys()
|
|
170
|
+
.hasOnly(['name', 'location', 'city']); // Prevent others from changing
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Enforcing Field Types
|
|
175
|
+
Use the `is` operator to validate data types.
|
|
176
|
+
|
|
177
|
+
```firestore
|
|
178
|
+
allow create: if request.resource.data.score is int &&
|
|
179
|
+
request.resource.data.active is bool &&
|
|
180
|
+
request.resource.data.tags is list;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Understanding Rule Evaluation
|
|
184
|
+
|
|
185
|
+
### Overlapping Matches -> OR Logic
|
|
186
|
+
|
|
187
|
+
If a document matches more than one rule statement, access is allowed if **ANY** of the matching rules allow it.
|
|
188
|
+
|
|
189
|
+
```firestore
|
|
190
|
+
// Document: /cities/SF
|
|
191
|
+
|
|
192
|
+
match /cities/{city} {
|
|
193
|
+
allow read: if false; // Deny
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
match /cities/{document=**} {
|
|
197
|
+
allow read: if true; // Allow
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Result: ALLOWED (because one rule returned true)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Common Limits
|
|
204
|
+
|
|
205
|
+
* **Call Depth**: Maximum call depth for custom functions is 20.
|
|
206
|
+
* **Document Access**:
|
|
207
|
+
* 10 access calls for single-doc requests/queries.
|
|
208
|
+
* 20 access calls for multi-doc reads/transactions/batches.
|
|
209
|
+
* **Size**: Ruleset source max 256 KB. Compiled max 250 KB.
|
|
210
|
+
|
|
211
|
+
## Deploying
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
firebase deploy --only firestore:rules
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Security Rules Development Workflow
|
|
218
|
+
|
|
219
|
+
For complex applications, follow this structured 6-phase workflow to ensure your rules are secure and comprehensive.
|
|
220
|
+
|
|
221
|
+
### Phase 1: Codebase Analysis
|
|
222
|
+
|
|
223
|
+
Before writing rules, scan your codebase to identify:
|
|
224
|
+
1. **Collections & Paths**: List all collections and document structures.
|
|
225
|
+
2. **Data Models**: Define required fields, data types, and constraints (e.g., string length, regex patterns).
|
|
226
|
+
3. **Access Patterns**: Document who can read/write what and under what conditions (e.g., exact ownership, role-based).
|
|
227
|
+
4. **Authentication**: Identify if you use Firebase Auth, anonymous auth, or custom tokens.
|
|
228
|
+
|
|
229
|
+
### Phase 2: Security Rules Generation
|
|
230
|
+
|
|
231
|
+
Write your rules following these core principles:
|
|
232
|
+
* **Default Deny**: Start with `allow read, write: if false;` and whitelist specific operations.
|
|
233
|
+
* **Least Privilege**: Grant only the minimum permissions required.
|
|
234
|
+
* **Validate Data**: Check types (e.g., `is string`), required fields, and values on `create` and `update`.
|
|
235
|
+
* **UID Protection**: Ensure users cannot create documents with another user's UID or change ownership.
|
|
236
|
+
|
|
237
|
+
#### Recommended Structure
|
|
238
|
+
|
|
239
|
+
It is helpful to define a `User` type or similar helper functions at the top of your rules file.
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
// Helper Functions
|
|
243
|
+
function isAuthenticated() {
|
|
244
|
+
return request.auth != null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function isOwner(userId) {
|
|
248
|
+
return isAuthenticated() && request.auth.uid == userId;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Validate data types and required fields
|
|
252
|
+
function isValidUser() {
|
|
253
|
+
let user = request.resource.data;
|
|
254
|
+
return user.keys().hasAll(['name', 'email', 'createdAt']) &&
|
|
255
|
+
user.name is string && user.name.size() > 0 &&
|
|
256
|
+
user.email is string && user.email.matches('.+@.+\\..+') &&
|
|
257
|
+
user.createdAt is timestamp;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Prevent UID tampering
|
|
261
|
+
function isUidUnchanged() {
|
|
262
|
+
return request.resource.data.uid == resource.data.uid;
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Phase 3: Devil's Advocate Attack
|
|
267
|
+
|
|
268
|
+
Attempt to mentally "break" your rules by checking for common vulnerabilities:
|
|
269
|
+
1. Can I read data I shouldn't?
|
|
270
|
+
2. Can I create a document with someone else's UID?
|
|
271
|
+
3. Can I update a document and steal ownership (change the `uid` field)?
|
|
272
|
+
4. Can I send a massive string to a field with no length limit?
|
|
273
|
+
5. Can I delete a document I don't own?
|
|
274
|
+
6. Can I bypass validation by sending `null` or missing fields?
|
|
275
|
+
|
|
276
|
+
If *any* of these succeed, fix the rule and repeat.
|
|
277
|
+
|
|
278
|
+
### Phase 4: Syntactic Validation
|
|
279
|
+
|
|
280
|
+
Use `firebase deploy --only firestore:rules --dry-run` to validate syntax.
|
|
281
|
+
|
|
282
|
+
### Phase 5: Test Suite Generation
|
|
283
|
+
|
|
284
|
+
Create a comprehensive test suite using `@firebase/rules-unit-testing`. Ideally, create a dedicated `rules_test/` directory.
|
|
285
|
+
|
|
286
|
+
**Test Coverage Checklist:**
|
|
287
|
+
* [ ] **Authorized Operations**: Users *can* do what they are supposed to.
|
|
288
|
+
* [ ] **Unauthorized Operations**: Users *cannot* do what is forbidden.
|
|
289
|
+
* [ ] **UID Tampering**: Users cannot create/update data with another's UID.
|
|
290
|
+
* [ ] **Data Validation**: Invalid types, missing fields, or malformed data (bad emails, URLs) must fail.
|
|
291
|
+
* [ ] **Immutable Fields**: Fields like `createdAt` or `authorId` cannot be changed on update.
|
|
292
|
+
|
|
293
|
+
### Phase 6: Test Validation Loop
|
|
294
|
+
|
|
295
|
+
1. Start the emulator: `firebase emulators:start --only firestore`
|
|
296
|
+
2. Run tests: `npm test` (inside your test directory)
|
|
297
|
+
3. If tests fail due to **rules**: Fix the rules.
|
|
298
|
+
4. If tests fail due to **test bugs**: Fix the tests.
|
|
299
|
+
5. Repeat until 100% pass rate.
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Firestore Web SDK Usage Guide
|
|
2
|
+
|
|
3
|
+
This guide focuses on the **Modular Web SDK** (v9+), which is tree-shakeable and efficient.
|
|
4
|
+
|
|
5
|
+
## Initialization
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { initializeApp } from "firebase/app";
|
|
9
|
+
import { getFirestore } from "firebase/firestore";
|
|
10
|
+
|
|
11
|
+
// If running in Firebase App Hosting, you can skip Firebase Config and instead use:
|
|
12
|
+
// const app = initializeApp();
|
|
13
|
+
|
|
14
|
+
const firebaseConfig = {
|
|
15
|
+
// Your config options. Get the values by running 'firebase apps:sdkconfig <platform> <app-id>'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const app = initializeApp(firebaseConfig);
|
|
19
|
+
const db = getFirestore(app);
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Writing Data
|
|
24
|
+
|
|
25
|
+
### Set a Document (`setDoc`)
|
|
26
|
+
Creates a document if it doesn't exist, or overwrites it if it does.
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import { doc, setDoc } from "firebase/firestore";
|
|
30
|
+
|
|
31
|
+
// Create/Overwrite document with ID "LA"
|
|
32
|
+
await setDoc(doc(db, "cities", "LA"), {
|
|
33
|
+
name: "Los Angeles",
|
|
34
|
+
state: "CA",
|
|
35
|
+
country: "USA"
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// To merge with existing data instead of overwriting:
|
|
39
|
+
await setDoc(doc(db, "cities", "LA"), { population: 3900000 }, { merge: true });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Add a Document with Auto-ID (`addDoc`)
|
|
43
|
+
Use when you don't care about the document ID.
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
import { collection, addDoc } from "firebase/firestore";
|
|
47
|
+
|
|
48
|
+
const docRef = await addDoc(collection(db, "cities"), {
|
|
49
|
+
name: "Tokyo",
|
|
50
|
+
country: "Japan"
|
|
51
|
+
});
|
|
52
|
+
console.log("Document written with ID: ", docRef.id);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Update a Document (`updateDoc`)
|
|
56
|
+
Update some fields of an existing document without overwriting the entire document. Fails if the document doesn't exist.
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
import { doc, updateDoc } from "firebase/firestore";
|
|
60
|
+
|
|
61
|
+
const laRef = doc(db, "cities", "LA");
|
|
62
|
+
|
|
63
|
+
await updateDoc(laRef, {
|
|
64
|
+
capital: true
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Transactions
|
|
69
|
+
Perform an atomic read-modify-write operation.
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
import { runTransaction, doc } from "firebase/firestore";
|
|
73
|
+
|
|
74
|
+
const sfDocRef = doc(db, "cities", "SF");
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await runTransaction(db, async (transaction) => {
|
|
78
|
+
const sfDoc = await transaction.get(sfDocRef);
|
|
79
|
+
if (!sfDoc.exists()) {
|
|
80
|
+
throw "Document does not exist!";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const newPopulation = sfDoc.data().population + 1;
|
|
84
|
+
transaction.update(sfDocRef, { population: newPopulation });
|
|
85
|
+
});
|
|
86
|
+
console.log("Transaction successfully committed!");
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.log("Transaction failed: ", e);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Reading Data
|
|
93
|
+
|
|
94
|
+
### Get a Single Document (`getDoc`)
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
import { doc, getDoc } from "firebase/firestore";
|
|
98
|
+
|
|
99
|
+
const docRef = doc(db, "cities", "SF");
|
|
100
|
+
const docSnap = await getDoc(docRef);
|
|
101
|
+
|
|
102
|
+
if (docSnap.exists()) {
|
|
103
|
+
console.log("Document data:", docSnap.data());
|
|
104
|
+
} else {
|
|
105
|
+
console.log("No such document!");
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Get Multiple Documents (`getDocs`)
|
|
110
|
+
Fetches all documents in a query or collection once.
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
import { collection, getDocs } from "firebase/firestore";
|
|
114
|
+
|
|
115
|
+
const querySnapshot = await getDocs(collection(db, "cities"));
|
|
116
|
+
querySnapshot.forEach((doc) => {
|
|
117
|
+
// doc.data() is never undefined for query doc snapshots
|
|
118
|
+
console.log(doc.id, " => ", doc.data());
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Realtime Updates
|
|
123
|
+
|
|
124
|
+
### Listen to a Document/Query (`onSnapshot`)
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
import { doc, onSnapshot } from "firebase/firestore";
|
|
128
|
+
|
|
129
|
+
const unsub = onSnapshot(doc(db, "cities", "SF"), (doc) => {
|
|
130
|
+
console.log("Current data: ", doc.data());
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Stop listening
|
|
134
|
+
// unsub();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Handle Changes (Added/Modified/Removed)
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
import { collection, query, where, onSnapshot } from "firebase/firestore";
|
|
141
|
+
|
|
142
|
+
const q = query(collection(db, "cities"), where("state", "==", "CA"));
|
|
143
|
+
const unsubscribe = onSnapshot(q, (snapshot) => {
|
|
144
|
+
snapshot.docChanges().forEach((change) => {
|
|
145
|
+
if (change.type === "added") {
|
|
146
|
+
console.log("New city: ", change.doc.data());
|
|
147
|
+
}
|
|
148
|
+
if (change.type === "modified") {
|
|
149
|
+
console.log("Modified city: ", change.doc.data());
|
|
150
|
+
}
|
|
151
|
+
if (change.type === "removed") {
|
|
152
|
+
console.log("Removed city: ", change.doc.data());
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Queries
|
|
159
|
+
|
|
160
|
+
### Simple and Compound Queries
|
|
161
|
+
Use `query()` to combine filters.
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
import { collection, query, where, getDocs } from "firebase/firestore";
|
|
165
|
+
|
|
166
|
+
const citiesRef = collection(db, "cities");
|
|
167
|
+
|
|
168
|
+
// Simple equality
|
|
169
|
+
const q1 = query(citiesRef, where("state", "==", "CA"));
|
|
170
|
+
|
|
171
|
+
// Compound (AND)
|
|
172
|
+
// Note: Requires an index if filtering on different fields
|
|
173
|
+
const q2 = query(citiesRef, where("state", "==", "CA"), where("population", ">", 1000000));
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### OR Queries
|
|
177
|
+
```javascript
|
|
178
|
+
import { or } from "firebase/firestore";
|
|
179
|
+
|
|
180
|
+
const q = query(citiesRef, or(
|
|
181
|
+
where("capital", "==", true),
|
|
182
|
+
where("population", ">=", 1000000)
|
|
183
|
+
));
|
|
184
|
+
```
|
|
185
|
+
Max 30 disjunctions (DNF form). Cannot combine `not-in` with `in`/`array-contains-any`/`or`.
|
|
186
|
+
|
|
187
|
+
### Collection Group Queries
|
|
188
|
+
Query across ALL subcollections with the same name.
|
|
189
|
+
```javascript
|
|
190
|
+
import { collectionGroup } from "firebase/firestore";
|
|
191
|
+
const q = query(collectionGroup(db, "landmarks"), where("type", "==", "museum"));
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Order and Limit
|
|
195
|
+
Sort and limit results.
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
import { orderBy, limit } from "firebase/firestore";
|
|
199
|
+
|
|
200
|
+
const q = query(citiesRef, orderBy("name"), limit(3));
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Cursor Pagination
|
|
204
|
+
**Always use cursors** — Firestore has no offset-based pagination.
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
import { startAfter, limit, orderBy, getDocs } from "firebase/firestore";
|
|
208
|
+
|
|
209
|
+
// Page 1
|
|
210
|
+
const first = query(citiesRef, orderBy("name"), limit(25));
|
|
211
|
+
const snapshot = await getDocs(first);
|
|
212
|
+
const lastDoc = snapshot.docs[snapshot.docs.length - 1];
|
|
213
|
+
|
|
214
|
+
// Page 2
|
|
215
|
+
const next = query(citiesRef, orderBy("name"), startAfter(lastDoc), limit(25));
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Aggregation Queries
|
|
219
|
+
|
|
220
|
+
**NEVER fetch all documents and reduce client-side. Always use server-side aggregation.**
|
|
221
|
+
|
|
222
|
+
### Standard Edition
|
|
223
|
+
```javascript
|
|
224
|
+
import { count, sum, average, getAggregateFromServer } from "firebase/firestore";
|
|
225
|
+
|
|
226
|
+
const q = query(collection(db, "cities"), where("state", "==", "CA"));
|
|
227
|
+
const snapshot = await getAggregateFromServer(q, {
|
|
228
|
+
totalCount: count(),
|
|
229
|
+
totalPop: sum("population"),
|
|
230
|
+
avgPop: average("population")
|
|
231
|
+
});
|
|
232
|
+
console.log(snapshot.data()); // { totalCount: 42, totalPop: 12345678, avgPop: 294420 }
|
|
233
|
+
```
|
|
234
|
+
Supported: `count()`, `sum(field)`, `average(field)`. NOT supported: min, max, groupBy.
|
|
235
|
+
|
|
236
|
+
### Enterprise Edition (Pipeline)
|
|
237
|
+
```javascript
|
|
238
|
+
const result = await db.pipeline()
|
|
239
|
+
.collection("orders")
|
|
240
|
+
.where("status", "==", "shipped")
|
|
241
|
+
.aggregate({
|
|
242
|
+
accumulators: [
|
|
243
|
+
sum("totalAmount").as("revenue"),
|
|
244
|
+
count().as("orderCount"),
|
|
245
|
+
min("totalAmount").as("minOrder"),
|
|
246
|
+
countDistinct("customerId").as("uniqueCustomers")
|
|
247
|
+
],
|
|
248
|
+
groups: ["productId"] // GROUP BY
|
|
249
|
+
})
|
|
250
|
+
.sort("revenue", "desc")
|
|
251
|
+
.limit(10)
|
|
252
|
+
.execute();
|
|
253
|
+
```
|
|
254
|
+
Pipeline supports: min, max, countDistinct, groupBy, regex, map, filter-after-aggregate. **No real-time listeners on Pipeline queries.**
|
|
255
|
+
|
|
256
|
+
## Compound Query Constraints
|
|
257
|
+
|
|
258
|
+
| Rule | Detail |
|
|
259
|
+
|---|---|
|
|
260
|
+
| Inequality filters | Same query needs composite index (equality → inequality → orderBy) |
|
|
261
|
+
| `not-in` + `in`/`or` | Cannot combine |
|
|
262
|
+
| `array-contains` | Max 1 per query |
|
|
263
|
+
| Disjunction limit | 30 (DNF form) |
|
|
264
|
+
| `orderBy` required | For `startAt`/`startAfter` and inequality queries |
|
|
265
|
+
| Missing fields | `!=` and `not-in` exclude docs where field doesn't exist |
|