├── .all-contributorsrc
├── .eslintignore
├── .eslintrc.js
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc
├── .vscode
└── settings.json
├── FirestoreEmulator
├── .firebaserc
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
├── package-lock.json
├── package.json
└── sample_data
│ ├── firebase-export-metadata.json
│ └── firestore_export
│ ├── all_namespaces
│ └── all_kinds
│ │ ├── all_namespaces_all_kinds.export_metadata
│ │ └── output-0
│ └── firestore_export.overall_export_metadata
├── LICENSE
├── README.md
├── jest.config.js
├── package-lock.json
├── package.json
├── src
├── Connection
│ ├── class.ts
│ ├── index.ts
│ └── types
│ │ └── ModelPOJO.ts
├── Document
│ ├── class.ts
│ ├── index.ts
│ └── types
│ │ ├── error.ts
│ │ └── updateOptions.ts
├── Firefly
│ ├── class.ts
│ ├── index.ts
│ └── types
│ │ └── error.ts
├── Model
│ ├── class.ts
│ ├── index.ts
│ └── types
│ │ └── error.ts
├── Query
│ ├── Base
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── buildExtQuery.ts
│ │ │ ├── config.ts
│ │ │ ├── errors.ts
│ │ │ └── operators.ts
│ ├── DeleteQuery
│ │ ├── class.ts
│ │ └── index.ts
│ ├── MultipleQuery
│ │ ├── class.ts
│ │ ├── index.ts
│ │ ├── types
│ │ │ └── extConfig.ts
│ │ └── utils
│ │ │ └── buildExtQuery.ts
│ ├── SingleQuery
│ │ ├── class.ts
│ │ ├── index.ts
│ │ ├── types
│ │ │ └── extConfig.ts
│ │ └── utils
│ │ │ └── buildExtQuery.ts
│ ├── UpdateQuery
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── updateConfig.ts
│ │ │ └── updateOptions.ts
│ ├── index.ts
│ └── utils
│ │ ├── buildQuery.ts
│ │ ├── convertDate.ts
│ │ ├── operatorsTypeCheck.ts
│ │ └── parseOperators.ts
├── SchemaTypes
│ ├── Base
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── BaseError.ts
│ │ │ ├── BaseSchema.ts
│ │ │ └── ValidateFn.ts
│ ├── Boolean
│ │ ├── checks
│ │ │ ├── allChecks.ts
│ │ │ ├── index.ts
│ │ │ └── typeCheck.ts
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── BooleanError.ts
│ │ │ └── BooleanSchema.ts
│ ├── Date
│ │ ├── checks
│ │ │ ├── allChecks.ts
│ │ │ ├── index.ts
│ │ │ ├── typeCheck.ts
│ │ │ └── valueCheck.ts
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── DateError.ts
│ │ │ └── DateSchema.ts
│ ├── Number
│ │ ├── checks
│ │ │ ├── allChecks.ts
│ │ │ ├── enumCheck.ts
│ │ │ ├── index.ts
│ │ │ ├── integerCheck.ts
│ │ │ ├── typeCheck.ts
│ │ │ └── valueCheck.ts
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── NumberError.ts
│ │ │ └── NumberSchema.ts
│ ├── Object
│ │ ├── checks
│ │ │ ├── allChecks.ts
│ │ │ ├── index.ts
│ │ │ ├── keyCheck.ts
│ │ │ ├── patternCheck.ts
│ │ │ ├── type.ts
│ │ │ └── typeCheck.ts
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── KeyValue.ts
│ │ │ ├── ObjectError.ts
│ │ │ └── ObjectSchema.ts
│ ├── String
│ │ ├── checks
│ │ │ ├── allChecks.ts
│ │ │ ├── caseCheck.ts
│ │ │ ├── enumCheck.ts
│ │ │ ├── formatCheck.ts
│ │ │ ├── index.ts
│ │ │ ├── lengthCheck.ts
│ │ │ └── typeCheck.ts
│ │ ├── class.ts
│ │ ├── index.ts
│ │ └── types
│ │ │ ├── StringError.ts
│ │ │ └── StringSchema.ts
│ └── index.ts
├── __tests__
│ ├── Connection
│ │ └── class.test.ts
│ ├── Firefly
│ │ └── class.test.ts
│ ├── Query
│ │ ├── class.test.ts
│ │ └── operators.test.ts
│ └── SchemaTypes
│ │ ├── Boolean
│ │ └── class.test.ts
│ │ ├── Date
│ │ └── class.test.ts
│ │ ├── Object
│ │ └── class.test.ts
│ │ └── String
│ │ └── class.test.ts
├── index.ts
└── utils
│ └── makeError.ts
└── tsconfig.json
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "aniketbiswas21",
10 | "name": "Aniket Biswas",
11 | "avatar_url": "https://avatars.githubusercontent.com/u/51146347?v=4",
12 | "profile": "https://aniket.codes/",
13 | "contributions": [
14 | "code",
15 | "doc",
16 | "maintenance"
17 | ]
18 | },
19 | {
20 | "login": "Aryaman1706",
21 | "name": "Aryaman Grover",
22 | "avatar_url": "https://avatars.githubusercontent.com/u/56519273?v=4",
23 | "profile": "https://github.com/Aryaman1706",
24 | "contributions": [
25 | "code",
26 | "doc",
27 | "maintenance"
28 | ]
29 | }
30 | ],
31 | "contributorsPerLine": 7,
32 | "projectName": "fireflyjs-core",
33 | "projectOwner": "FireflyJS",
34 | "repoType": "github",
35 | "repoHost": "https://github.com",
36 | "skipCi": true
37 | }
38 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | tsconfig.json
3 | jest.config.js
4 | src/__tests__/
5 | lib/
6 | node_modules/
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["airbnb-typescript/base", "plugin:prettier/recommended"],
3 | parserOptions: {
4 | project: "./tsconfig.json",
5 | },
6 | plugins: ["prettier"],
7 | rules: {
8 | quotes: "off",
9 | "@typescript-eslint/quotes": [
10 | "error",
11 | "double",
12 | { allowTemplateLiterals: true },
13 | ],
14 | "no-console": "off",
15 | "no-underscore-dangle": "off",
16 | "import/prefer-default-export": "off",
17 | "no-useless-escape": "off",
18 | "no-control-regex": "off",
19 | "import/no-cycle": "off",
20 | "max-classes-per-file": "off",
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: run-tests
2 | on:
3 | push:
4 | branches:
5 | - "develop"
6 |
7 | pull_request:
8 | branches:
9 | - "develop"
10 |
11 | jobs:
12 | prepare:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v2
17 |
18 | - name: Setup Node
19 | uses: actions/setup-node@v2
20 | with:
21 | node-version: "14"
22 |
23 | - name: Install typescript
24 | run: npm install -g typescript
25 |
26 | - name: Install dependencies
27 | run: npm clean-install
28 |
29 | - name: Compile typescript
30 | run: npm run compile
31 |
32 | - name: Format code
33 | run: npm run format
34 |
35 | - name: Lint and fix code
36 | run: npm run lint
37 |
38 | test:
39 | runs-on: ubuntu-latest
40 | needs: prepare
41 | steps:
42 | - name: Checkout code
43 | uses: actions/checkout@v2
44 |
45 | - name: Setup Node
46 | uses: actions/setup-node@v2
47 | with:
48 | node-version: "14"
49 |
50 | - name: Install dependencies
51 | run: npm clean-install
52 |
53 | - name: Install firebase
54 | run: sudo npm i -g firebase-tools
55 |
56 | - name: Setup Java
57 | uses: actions/setup-java@v2
58 | with:
59 | java-version: "11"
60 | distribution: "temurin"
61 |
62 | - name: Cache JAR files
63 | uses: actions/cache@v2
64 | with:
65 | path: ~/.cache/firebase/emulators/
66 | key: emulator-jar-files
67 |
68 | - name: Run tests
69 | run: npm run test:ci
70 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | **/*.log
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm run compile
5 | npm run format
6 | npm run lint
7 | npm run test
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | tsconfig.json
2 | package-lock.json
3 | node_modules/
4 | lib/
5 | docs/
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.enable": true,
3 | "eslint.format.enable": true
4 | }
5 |
--------------------------------------------------------------------------------
/FirestoreEmulator/.firebaserc:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/FirestoreEmulator/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "firestore": {
3 | "rules": "firestore.rules",
4 | "indexes": "firestore.indexes.json"
5 | },
6 | "emulators": {
7 | "firestore": {
8 | "port": 8080
9 | },
10 | "ui": {
11 | "enabled": true,
12 | "port": 8000
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/FirestoreEmulator/firestore.indexes.json:
--------------------------------------------------------------------------------
1 | {
2 | "indexes": [],
3 | "fieldOverrides": []
4 | }
5 |
--------------------------------------------------------------------------------
/FirestoreEmulator/firestore.rules:
--------------------------------------------------------------------------------
1 | service cloud.firestore {
2 | match /databases/{database}/documents {
3 | match /{document=**} {
4 | // This rule allows anyone with your database reference to view, edit,
5 | // and delete all data in your database. It is useful for getting
6 | // started, but it is configured to expire after 30 days because it
7 | // leaves your app open to attackers. At that time, all client
8 | // requests to your database will be denied.
9 | //
10 | // Make sure to write security rules for your app before that time, or
11 | // else all client requests to your database will be denied until you
12 | // update your rules.
13 | allow read, write: if request.time < timestamp.date(2021, 8, 22);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/FirestoreEmulator/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "firestore-emulator",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@firebase/analytics": {
8 | "version": "0.7.1",
9 | "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.7.1.tgz",
10 | "integrity": "sha512-fTUN47UK4obzIJwmgLMJU46dWZ7pzitCEO+80pQZC7mdLlVs/NW0+tMf6rETwMpKjGSgb25cKidpgEuioQtT7w==",
11 | "requires": {
12 | "@firebase/component": "0.5.7",
13 | "@firebase/installations": "0.5.1",
14 | "@firebase/logger": "0.3.0",
15 | "@firebase/util": "1.4.0",
16 | "tslib": "^2.1.0"
17 | }
18 | },
19 | "@firebase/analytics-compat": {
20 | "version": "0.1.2",
21 | "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.1.2.tgz",
22 | "integrity": "sha512-TpWpz0s8EgVt9aqyOCFktONqVkjyrNRR4esn3cEYrueH+XXSMDLWY9oFHuUJzntcoEOlOBWMvpsJCPG/1kthkg==",
23 | "requires": {
24 | "@firebase/analytics": "0.7.1",
25 | "@firebase/analytics-types": "0.7.0",
26 | "@firebase/component": "0.5.7",
27 | "@firebase/util": "1.4.0",
28 | "tslib": "^2.1.0"
29 | }
30 | },
31 | "@firebase/analytics-types": {
32 | "version": "0.7.0",
33 | "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.7.0.tgz",
34 | "integrity": "sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ=="
35 | },
36 | "@firebase/app": {
37 | "version": "0.7.2",
38 | "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.2.tgz",
39 | "integrity": "sha512-xKO3KWxVqCLijJToaBGvBnXCaVGvIw+rT2Dtd9B2iyOFJieQQ+xx8/zRWgoSqbMBIZ2crQVr0KdsoyP9D2nQfg==",
40 | "requires": {
41 | "@firebase/component": "0.5.7",
42 | "@firebase/logger": "0.3.0",
43 | "@firebase/util": "1.4.0",
44 | "tslib": "^2.1.0"
45 | }
46 | },
47 | "@firebase/app-check": {
48 | "version": "0.4.1",
49 | "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.4.1.tgz",
50 | "integrity": "sha512-Kpqh0Y2zpx+acTL7eOVYIWBOmAwoconJpqOAlByGNXuxm/ccP00XREo+HsqaC7wapZRXh+h8BK0jZjvdV36qow==",
51 | "requires": {
52 | "@firebase/component": "0.5.7",
53 | "@firebase/logger": "0.3.0",
54 | "@firebase/util": "1.4.0",
55 | "tslib": "^2.1.0"
56 | }
57 | },
58 | "@firebase/app-check-compat": {
59 | "version": "0.1.2",
60 | "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.1.2.tgz",
61 | "integrity": "sha512-JB+OHk4Cp9ZgT+UfX0A+lwH1AoM5Y2X1rDhmhCsEXcKKwz1w9DpL9PjStciP8UYg1dpqbp5p9OMxmty+21EBsA==",
62 | "requires": {
63 | "@firebase/app-check": "0.4.1",
64 | "@firebase/component": "0.5.7",
65 | "@firebase/logger": "0.3.0",
66 | "@firebase/util": "1.4.0",
67 | "tslib": "^2.1.0"
68 | }
69 | },
70 | "@firebase/app-check-interop-types": {
71 | "version": "0.1.0",
72 | "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.1.0.tgz",
73 | "integrity": "sha512-uZfn9s4uuRsaX5Lwx+gFP3B6YsyOKUE+Rqa6z9ojT4VSRAsZFko9FRn6OxQUA1z5t5d08fY4pf+/+Dkd5wbdbA=="
74 | },
75 | "@firebase/app-compat": {
76 | "version": "0.1.3",
77 | "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.3.tgz",
78 | "integrity": "sha512-+/U2RgRLfLznPuluIMW3bsAehTBTVWKxA6l6jjk9noozPuP99xOulReMqf5kCrXVdW1aMHdRuKfntjbTAR8+aw==",
79 | "requires": {
80 | "@firebase/app": "0.7.2",
81 | "@firebase/component": "0.5.7",
82 | "@firebase/logger": "0.3.0",
83 | "@firebase/util": "1.4.0",
84 | "tslib": "^2.1.0"
85 | }
86 | },
87 | "@firebase/app-types": {
88 | "version": "0.7.0",
89 | "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz",
90 | "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg=="
91 | },
92 | "@firebase/auth": {
93 | "version": "0.18.1",
94 | "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.18.1.tgz",
95 | "integrity": "sha512-q455ls7Hjug3yGp7htLL/LABqySoXGXL/ADLJPyiSnVl22a5oQWuTKUL6N5PAXHc5LwygFfHYiHrNhpQDaGm3w==",
96 | "requires": {
97 | "@firebase/component": "0.5.7",
98 | "@firebase/logger": "0.3.0",
99 | "@firebase/util": "1.4.0",
100 | "node-fetch": "2.6.5",
101 | "selenium-webdriver": "4.0.0-rc-1",
102 | "tslib": "^2.1.0"
103 | }
104 | },
105 | "@firebase/auth-compat": {
106 | "version": "0.1.4",
107 | "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.1.4.tgz",
108 | "integrity": "sha512-Vn7Dsxa7B50ihgDAMQAVb/IxU9tcQyR1JDbWjZzf2b1212hBuuwEs1V1u01xoKunMXMSg+P8ztbG7IRxOj2FdQ==",
109 | "requires": {
110 | "@firebase/auth": "0.18.1",
111 | "@firebase/auth-types": "0.11.0",
112 | "@firebase/component": "0.5.7",
113 | "@firebase/util": "1.4.0",
114 | "node-fetch": "2.6.5",
115 | "selenium-webdriver": "^4.0.0-beta.2",
116 | "tslib": "^2.1.0"
117 | }
118 | },
119 | "@firebase/auth-interop-types": {
120 | "version": "0.1.6",
121 | "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz",
122 | "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g=="
123 | },
124 | "@firebase/auth-types": {
125 | "version": "0.11.0",
126 | "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.11.0.tgz",
127 | "integrity": "sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw=="
128 | },
129 | "@firebase/component": {
130 | "version": "0.5.7",
131 | "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.7.tgz",
132 | "integrity": "sha512-CiAHUPXh2hn/lpzMShNmfAxHNQhKQwmQUJSYMPCjf2bCCt4Z2vLGpS+UWEuNFm9Zf8LNmkS+Z+U/s4Obi5carg==",
133 | "requires": {
134 | "@firebase/util": "1.4.0",
135 | "tslib": "^2.1.0"
136 | }
137 | },
138 | "@firebase/database": {
139 | "version": "0.12.1",
140 | "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.1.tgz",
141 | "integrity": "sha512-Ethk0hc476qnkSKNBa+8Yc7iM8AO69HYWsaD+QUC983FZtnuMyNLHtEeSUbLQYvyHo7cOjcc52slop14WmfZeQ==",
142 | "requires": {
143 | "@firebase/auth-interop-types": "0.1.6",
144 | "@firebase/component": "0.5.7",
145 | "@firebase/logger": "0.3.0",
146 | "@firebase/util": "1.4.0",
147 | "faye-websocket": "0.11.4",
148 | "tslib": "^2.1.0"
149 | }
150 | },
151 | "@firebase/database-compat": {
152 | "version": "0.1.1",
153 | "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.1.tgz",
154 | "integrity": "sha512-K3DFWiw0YkLZtlfA9TOGPw6zVXKu5dQ1XqIGztUufFVRYW8IizReXVxzSSmJNR4Adr2LiU9j66Wenc6e5UfwaQ==",
155 | "requires": {
156 | "@firebase/component": "0.5.7",
157 | "@firebase/database": "0.12.1",
158 | "@firebase/database-types": "0.9.1",
159 | "@firebase/logger": "0.3.0",
160 | "@firebase/util": "1.4.0",
161 | "tslib": "^2.1.0"
162 | }
163 | },
164 | "@firebase/database-types": {
165 | "version": "0.9.1",
166 | "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.1.tgz",
167 | "integrity": "sha512-RUixK/YrbpxbfdE+nYP0wMcEsz1xPTnafP0q3UlSS/+fW744OITKtR1J0cMRaXbvY7EH0wUVTNVkrtgxYY8IgQ==",
168 | "requires": {
169 | "@firebase/app-types": "0.7.0",
170 | "@firebase/util": "1.4.0"
171 | }
172 | },
173 | "@firebase/firestore": {
174 | "version": "3.1.0",
175 | "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-3.1.0.tgz",
176 | "integrity": "sha512-vOXueHNRjlgBlKVCWuUDhr3dQW2hJwbcqcJaFiIV9V+PamfyhOHzX8pEQkrPort4YQQvoRmY9uiFhfOGj2hbeA==",
177 | "requires": {
178 | "@firebase/component": "0.5.7",
179 | "@firebase/logger": "0.3.0",
180 | "@firebase/util": "1.4.0",
181 | "@firebase/webchannel-wrapper": "0.6.0",
182 | "@grpc/grpc-js": "^1.3.2",
183 | "@grpc/proto-loader": "^0.6.0",
184 | "node-fetch": "2.6.2",
185 | "tslib": "^2.1.0"
186 | },
187 | "dependencies": {
188 | "node-fetch": {
189 | "version": "2.6.2",
190 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.2.tgz",
191 | "integrity": "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA=="
192 | }
193 | }
194 | },
195 | "@firebase/firestore-compat": {
196 | "version": "0.1.3",
197 | "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.1.3.tgz",
198 | "integrity": "sha512-tO3uAkIguKeFeKPu99GR7F7v1/Hc8nV1h7B1kdpkVRRBe+NfVYA3qAUictQ3OAA0oy7Ae9z4SfEURO/R1b6YlQ==",
199 | "requires": {
200 | "@firebase/component": "0.5.7",
201 | "@firebase/firestore": "3.1.0",
202 | "@firebase/firestore-types": "2.5.0",
203 | "@firebase/util": "1.4.0",
204 | "tslib": "^2.1.0"
205 | }
206 | },
207 | "@firebase/firestore-types": {
208 | "version": "2.5.0",
209 | "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.5.0.tgz",
210 | "integrity": "sha512-I6c2m1zUhZ5SH0cWPmINabDyH5w0PPFHk2UHsjBpKdZllzJZ2TwTkXbDtpHUZNmnc/zAa0WNMNMvcvbb/xJLKA=="
211 | },
212 | "@firebase/functions": {
213 | "version": "0.7.2",
214 | "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.7.2.tgz",
215 | "integrity": "sha512-B+b57xXtpsRYD3UgVtteeyavXjXfBTtuv+sG8LA0vgJs6bhORswVlKZQqpfW9SDxCMBwzzytzn1m3ZZGfUw2Lg==",
216 | "requires": {
217 | "@firebase/app-check-interop-types": "0.1.0",
218 | "@firebase/auth-interop-types": "0.1.6",
219 | "@firebase/component": "0.5.7",
220 | "@firebase/messaging-interop-types": "0.1.0",
221 | "@firebase/util": "1.4.0",
222 | "node-fetch": "2.6.2",
223 | "tslib": "^2.1.0"
224 | },
225 | "dependencies": {
226 | "node-fetch": {
227 | "version": "2.6.2",
228 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.2.tgz",
229 | "integrity": "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA=="
230 | }
231 | }
232 | },
233 | "@firebase/functions-compat": {
234 | "version": "0.1.3",
235 | "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.1.3.tgz",
236 | "integrity": "sha512-NdobePNq5LUHCI1dJHUGlOTw+Qmq/FJre981/ELEMBdEs95kmKwnXB2UaLjAQYWkgkr4YS3lEnNpsiSTaEHFCg==",
237 | "requires": {
238 | "@firebase/component": "0.5.7",
239 | "@firebase/functions": "0.7.2",
240 | "@firebase/functions-types": "0.5.0",
241 | "@firebase/util": "1.4.0",
242 | "tslib": "^2.1.0"
243 | }
244 | },
245 | "@firebase/functions-types": {
246 | "version": "0.5.0",
247 | "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.5.0.tgz",
248 | "integrity": "sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA=="
249 | },
250 | "@firebase/installations": {
251 | "version": "0.5.1",
252 | "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.5.1.tgz",
253 | "integrity": "sha512-KZ1XHrEPmCx3Z70P9d8mHmYEZXA/uiLIkV0D8R45Q65c0DUxBDm5tSQs56QWofxB/wx16xmO3xAZw4BdJUBnlQ==",
254 | "requires": {
255 | "@firebase/component": "0.5.7",
256 | "@firebase/util": "1.4.0",
257 | "idb": "3.0.2",
258 | "tslib": "^2.1.0"
259 | }
260 | },
261 | "@firebase/logger": {
262 | "version": "0.3.0",
263 | "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.0.tgz",
264 | "integrity": "sha512-7oQ+TctqekfgZImWkKuda50JZfkmAKMgh5qY4aR4pwRyqZXuJXN1H/BKkHvN1y0S4XWtF0f/wiCLKHhyi1ppPA==",
265 | "requires": {
266 | "tslib": "^2.1.0"
267 | }
268 | },
269 | "@firebase/messaging": {
270 | "version": "0.9.1",
271 | "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.9.1.tgz",
272 | "integrity": "sha512-0g3JWTfkv0WHnu4xgx1zcChJXU2dLjWT0e2MI13Q7NbP3TgLu5CgQ/H/lad16j4Zb4RNqZbAUJurEAB6v2BJ/w==",
273 | "requires": {
274 | "@firebase/component": "0.5.7",
275 | "@firebase/installations": "0.5.1",
276 | "@firebase/messaging-interop-types": "0.1.0",
277 | "@firebase/util": "1.4.0",
278 | "idb": "3.0.2",
279 | "tslib": "^2.1.0"
280 | }
281 | },
282 | "@firebase/messaging-compat": {
283 | "version": "0.1.1",
284 | "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.1.1.tgz",
285 | "integrity": "sha512-8FxrQjJCOfP9HibFsymT3qB18rBBmMPxOV+k0n6B7L6KW6Idswq01hMW12d93ZnvlNNKdikCKwUtBNbITBd8FA==",
286 | "requires": {
287 | "@firebase/component": "0.5.7",
288 | "@firebase/messaging": "0.9.1",
289 | "@firebase/util": "1.4.0",
290 | "tslib": "^2.1.0"
291 | }
292 | },
293 | "@firebase/messaging-interop-types": {
294 | "version": "0.1.0",
295 | "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.1.0.tgz",
296 | "integrity": "sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ=="
297 | },
298 | "@firebase/performance": {
299 | "version": "0.5.1",
300 | "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.5.1.tgz",
301 | "integrity": "sha512-O93Yry8KhAaFrhnmBaMkM0lpgVmpd7CRX0eq1S0IKLdE3MdF+oAtbQiZG/NuRl3Vz8vjoz96R6bPbCWaDuiy8Q==",
302 | "requires": {
303 | "@firebase/component": "0.5.7",
304 | "@firebase/installations": "0.5.1",
305 | "@firebase/logger": "0.3.0",
306 | "@firebase/util": "1.4.0",
307 | "tslib": "^2.1.0"
308 | }
309 | },
310 | "@firebase/performance-compat": {
311 | "version": "0.1.1",
312 | "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.1.1.tgz",
313 | "integrity": "sha512-xN/TjU0hVNiJshZzrUvPYB+3sPS9SgaWrfxh3p0eGFVdwHp/3Z8HlT772bkHAEKXVc64v19ktpUVd+sF5aoJNQ==",
314 | "requires": {
315 | "@firebase/component": "0.5.7",
316 | "@firebase/logger": "0.3.0",
317 | "@firebase/performance": "0.5.1",
318 | "@firebase/performance-types": "0.1.0",
319 | "@firebase/util": "1.4.0",
320 | "tslib": "^2.1.0"
321 | }
322 | },
323 | "@firebase/performance-types": {
324 | "version": "0.1.0",
325 | "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.1.0.tgz",
326 | "integrity": "sha512-6p1HxrH0mpx+622Ql6fcxFxfkYSBpE3LSuwM7iTtYU2nw91Hj6THC8Bc8z4nboIq7WvgsT/kOTYVVZzCSlXl8w=="
327 | },
328 | "@firebase/polyfill": {
329 | "version": "0.3.36",
330 | "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.36.tgz",
331 | "integrity": "sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg==",
332 | "requires": {
333 | "core-js": "3.6.5",
334 | "promise-polyfill": "8.1.3",
335 | "whatwg-fetch": "2.0.4"
336 | }
337 | },
338 | "@firebase/remote-config": {
339 | "version": "0.3.0",
340 | "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.3.0.tgz",
341 | "integrity": "sha512-Yf9/iXToC6Kbec1yOQ9mdTc1MP0mR2VCCR/n3Q+Ol3U+PML+ePXfqWiL2VHrUA86BeB2hpXF1BcTxvD4uOiDnA==",
342 | "requires": {
343 | "@firebase/component": "0.5.7",
344 | "@firebase/installations": "0.5.1",
345 | "@firebase/logger": "0.3.0",
346 | "@firebase/util": "1.4.0",
347 | "tslib": "^2.1.0"
348 | }
349 | },
350 | "@firebase/remote-config-compat": {
351 | "version": "0.1.1",
352 | "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.1.1.tgz",
353 | "integrity": "sha512-ZHRHYTdDztXHxgYXzuuD6Goa6ScmAqtctXl2eC6D8vxA8fIGRQKHN+9AMwxm8b3JHzdVY/5XhAOmKCcFvPOgtw==",
354 | "requires": {
355 | "@firebase/component": "0.5.7",
356 | "@firebase/logger": "0.3.0",
357 | "@firebase/remote-config": "0.3.0",
358 | "@firebase/remote-config-types": "0.2.0",
359 | "@firebase/util": "1.4.0",
360 | "tslib": "^2.1.0"
361 | }
362 | },
363 | "@firebase/remote-config-types": {
364 | "version": "0.2.0",
365 | "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.2.0.tgz",
366 | "integrity": "sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw=="
367 | },
368 | "@firebase/storage": {
369 | "version": "0.8.3",
370 | "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.8.3.tgz",
371 | "integrity": "sha512-oraycQ787tEr6xu2Qc4nngLz1YEoEjZ+lrjThx0CJZB7VwdlkIJ24TkzJ9xoeWc+cpo34deg/If4w8AU5/WupQ==",
372 | "requires": {
373 | "@firebase/component": "0.5.7",
374 | "@firebase/util": "1.4.0",
375 | "node-fetch": "2.6.2",
376 | "tslib": "^2.1.0"
377 | },
378 | "dependencies": {
379 | "node-fetch": {
380 | "version": "2.6.2",
381 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.2.tgz",
382 | "integrity": "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA=="
383 | }
384 | }
385 | },
386 | "@firebase/storage-compat": {
387 | "version": "0.1.3",
388 | "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.1.3.tgz",
389 | "integrity": "sha512-m2htGJjCFlTONsqYRKXTfzkux3nbhpIpd72RK2iPkRPE69nQ0wiVplIE7bCaq3CSFMbkI3ETOtTTfW1wrOpF2g==",
390 | "requires": {
391 | "@firebase/component": "0.5.7",
392 | "@firebase/storage": "0.8.3",
393 | "@firebase/storage-types": "0.6.0",
394 | "@firebase/util": "1.4.0",
395 | "tslib": "^2.1.0"
396 | }
397 | },
398 | "@firebase/storage-types": {
399 | "version": "0.6.0",
400 | "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.6.0.tgz",
401 | "integrity": "sha512-1LpWhcCb1ftpkP/akhzjzeFxgVefs6eMD2QeKiJJUGH1qOiows2w5o0sKCUSQrvrRQS1lz3SFGvNR1Ck/gqxeA=="
402 | },
403 | "@firebase/util": {
404 | "version": "1.4.0",
405 | "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.0.tgz",
406 | "integrity": "sha512-Qn58d+DVi1nGn0bA9RV89zkz0zcbt6aUcRdyiuub/SuEvjKYstWmHcHwh1C0qmE1wPf9a3a+AuaRtduaGaRT7A==",
407 | "requires": {
408 | "tslib": "^2.1.0"
409 | }
410 | },
411 | "@firebase/webchannel-wrapper": {
412 | "version": "0.6.0",
413 | "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.6.0.tgz",
414 | "integrity": "sha512-Pz4+7HPzKvOFI1ICQ6pyUv/VgStEWq9IGiVaaV1cQLi66NIA1mD5INnY4CDNoVAxlkuZvDEUZ+cVHLQ8iwA2hQ=="
415 | },
416 | "@grpc/grpc-js": {
417 | "version": "1.3.7",
418 | "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.7.tgz",
419 | "integrity": "sha512-CKQVuwuSPh40tgOkR7c0ZisxYRiN05PcKPW72mQL5y++qd7CwBRoaJZvU5xfXnCJDFBmS3qZGQ71Frx6Ofo2XA==",
420 | "requires": {
421 | "@types/node": ">=12.12.47"
422 | }
423 | },
424 | "@grpc/proto-loader": {
425 | "version": "0.6.5",
426 | "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.5.tgz",
427 | "integrity": "sha512-GZdzyVQI1Bln/kCzIYgTKu+rQJ5dno0gVrfmLe4jqQu7T2e7svSwJzpCBqVU5hhBSJP3peuPjOMWsj5GR61YmQ==",
428 | "requires": {
429 | "@types/long": "^4.0.1",
430 | "lodash.camelcase": "^4.3.0",
431 | "long": "^4.0.0",
432 | "protobufjs": "^6.10.0",
433 | "yargs": "^16.1.1"
434 | }
435 | },
436 | "@protobufjs/aspromise": {
437 | "version": "1.1.2",
438 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
439 | "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
440 | },
441 | "@protobufjs/base64": {
442 | "version": "1.1.2",
443 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
444 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
445 | },
446 | "@protobufjs/codegen": {
447 | "version": "2.0.4",
448 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
449 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
450 | },
451 | "@protobufjs/eventemitter": {
452 | "version": "1.1.0",
453 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
454 | "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
455 | },
456 | "@protobufjs/fetch": {
457 | "version": "1.1.0",
458 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
459 | "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
460 | "requires": {
461 | "@protobufjs/aspromise": "^1.1.1",
462 | "@protobufjs/inquire": "^1.1.0"
463 | }
464 | },
465 | "@protobufjs/float": {
466 | "version": "1.0.2",
467 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
468 | "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
469 | },
470 | "@protobufjs/inquire": {
471 | "version": "1.1.0",
472 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
473 | "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
474 | },
475 | "@protobufjs/path": {
476 | "version": "1.1.2",
477 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
478 | "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
479 | },
480 | "@protobufjs/pool": {
481 | "version": "1.1.0",
482 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
483 | "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
484 | },
485 | "@protobufjs/utf8": {
486 | "version": "1.1.0",
487 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
488 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
489 | },
490 | "@types/long": {
491 | "version": "4.0.1",
492 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
493 | "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
494 | },
495 | "@types/node": {
496 | "version": "16.10.2",
497 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz",
498 | "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ=="
499 | },
500 | "ansi-regex": {
501 | "version": "5.0.1",
502 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
503 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
504 | },
505 | "ansi-styles": {
506 | "version": "4.3.0",
507 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
508 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
509 | "requires": {
510 | "color-convert": "^2.0.1"
511 | }
512 | },
513 | "balanced-match": {
514 | "version": "1.0.2",
515 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
516 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
517 | },
518 | "brace-expansion": {
519 | "version": "1.1.11",
520 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
521 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
522 | "requires": {
523 | "balanced-match": "^1.0.0",
524 | "concat-map": "0.0.1"
525 | }
526 | },
527 | "cliui": {
528 | "version": "7.0.4",
529 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
530 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
531 | "requires": {
532 | "string-width": "^4.2.0",
533 | "strip-ansi": "^6.0.0",
534 | "wrap-ansi": "^7.0.0"
535 | }
536 | },
537 | "color-convert": {
538 | "version": "2.0.1",
539 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
540 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
541 | "requires": {
542 | "color-name": "~1.1.4"
543 | }
544 | },
545 | "color-name": {
546 | "version": "1.1.4",
547 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
548 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
549 | },
550 | "concat-map": {
551 | "version": "0.0.1",
552 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
553 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
554 | },
555 | "core-js": {
556 | "version": "3.6.5",
557 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
558 | "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
559 | },
560 | "core-util-is": {
561 | "version": "1.0.3",
562 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
563 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
564 | },
565 | "emoji-regex": {
566 | "version": "8.0.0",
567 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
568 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
569 | },
570 | "escalade": {
571 | "version": "3.1.1",
572 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
573 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
574 | },
575 | "faye-websocket": {
576 | "version": "0.11.4",
577 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
578 | "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
579 | "requires": {
580 | "websocket-driver": ">=0.5.1"
581 | }
582 | },
583 | "firebase": {
584 | "version": "9.1.1",
585 | "resolved": "https://registry.npmjs.org/firebase/-/firebase-9.1.1.tgz",
586 | "integrity": "sha512-106PqKLwWo4vINQUwEbk2aU/nAFhRbCBE2IdnQmf7UDaW4wqJGZcgRvy3jSyuZr/dkqnT7ymKX0GGrDSzNLU6g==",
587 | "requires": {
588 | "@firebase/analytics": "0.7.1",
589 | "@firebase/analytics-compat": "0.1.2",
590 | "@firebase/app": "0.7.2",
591 | "@firebase/app-check": "0.4.1",
592 | "@firebase/app-check-compat": "0.1.2",
593 | "@firebase/app-compat": "0.1.3",
594 | "@firebase/app-types": "0.7.0",
595 | "@firebase/auth": "0.18.1",
596 | "@firebase/auth-compat": "0.1.4",
597 | "@firebase/database": "0.12.1",
598 | "@firebase/database-compat": "0.1.1",
599 | "@firebase/firestore": "3.1.0",
600 | "@firebase/firestore-compat": "0.1.3",
601 | "@firebase/functions": "0.7.2",
602 | "@firebase/functions-compat": "0.1.3",
603 | "@firebase/installations": "0.5.1",
604 | "@firebase/messaging": "0.9.1",
605 | "@firebase/messaging-compat": "0.1.1",
606 | "@firebase/performance": "0.5.1",
607 | "@firebase/performance-compat": "0.1.1",
608 | "@firebase/polyfill": "0.3.36",
609 | "@firebase/remote-config": "0.3.0",
610 | "@firebase/remote-config-compat": "0.1.1",
611 | "@firebase/storage": "0.8.3",
612 | "@firebase/storage-compat": "0.1.3",
613 | "@firebase/util": "1.4.0"
614 | }
615 | },
616 | "fs.realpath": {
617 | "version": "1.0.0",
618 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
619 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
620 | },
621 | "get-caller-file": {
622 | "version": "2.0.5",
623 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
624 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
625 | },
626 | "glob": {
627 | "version": "7.2.0",
628 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
629 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
630 | "requires": {
631 | "fs.realpath": "^1.0.0",
632 | "inflight": "^1.0.4",
633 | "inherits": "2",
634 | "minimatch": "^3.0.4",
635 | "once": "^1.3.0",
636 | "path-is-absolute": "^1.0.0"
637 | }
638 | },
639 | "http-parser-js": {
640 | "version": "0.5.3",
641 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz",
642 | "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg=="
643 | },
644 | "idb": {
645 | "version": "3.0.2",
646 | "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz",
647 | "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw=="
648 | },
649 | "immediate": {
650 | "version": "3.0.6",
651 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
652 | "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
653 | },
654 | "inflight": {
655 | "version": "1.0.6",
656 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
657 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
658 | "requires": {
659 | "once": "^1.3.0",
660 | "wrappy": "1"
661 | }
662 | },
663 | "inherits": {
664 | "version": "2.0.4",
665 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
666 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
667 | },
668 | "is-fullwidth-code-point": {
669 | "version": "3.0.0",
670 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
671 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
672 | },
673 | "isarray": {
674 | "version": "1.0.0",
675 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
676 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
677 | },
678 | "jszip": {
679 | "version": "3.7.1",
680 | "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
681 | "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
682 | "requires": {
683 | "lie": "~3.3.0",
684 | "pako": "~1.0.2",
685 | "readable-stream": "~2.3.6",
686 | "set-immediate-shim": "~1.0.1"
687 | }
688 | },
689 | "lie": {
690 | "version": "3.3.0",
691 | "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
692 | "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
693 | "requires": {
694 | "immediate": "~3.0.5"
695 | }
696 | },
697 | "lodash.camelcase": {
698 | "version": "4.3.0",
699 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
700 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
701 | },
702 | "long": {
703 | "version": "4.0.0",
704 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
705 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
706 | },
707 | "minimatch": {
708 | "version": "3.0.4",
709 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
710 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
711 | "requires": {
712 | "brace-expansion": "^1.1.7"
713 | }
714 | },
715 | "node-fetch": {
716 | "version": "2.6.5",
717 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
718 | "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
719 | "requires": {
720 | "whatwg-url": "^5.0.0"
721 | }
722 | },
723 | "once": {
724 | "version": "1.4.0",
725 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
726 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
727 | "requires": {
728 | "wrappy": "1"
729 | }
730 | },
731 | "pako": {
732 | "version": "1.0.11",
733 | "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
734 | "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
735 | },
736 | "path-is-absolute": {
737 | "version": "1.0.1",
738 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
739 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
740 | },
741 | "process-nextick-args": {
742 | "version": "2.0.1",
743 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
744 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
745 | },
746 | "promise-polyfill": {
747 | "version": "8.1.3",
748 | "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz",
749 | "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g=="
750 | },
751 | "protobufjs": {
752 | "version": "6.11.2",
753 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
754 | "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
755 | "requires": {
756 | "@protobufjs/aspromise": "^1.1.2",
757 | "@protobufjs/base64": "^1.1.2",
758 | "@protobufjs/codegen": "^2.0.4",
759 | "@protobufjs/eventemitter": "^1.1.0",
760 | "@protobufjs/fetch": "^1.1.0",
761 | "@protobufjs/float": "^1.0.2",
762 | "@protobufjs/inquire": "^1.1.0",
763 | "@protobufjs/path": "^1.1.2",
764 | "@protobufjs/pool": "^1.1.0",
765 | "@protobufjs/utf8": "^1.1.0",
766 | "@types/long": "^4.0.1",
767 | "@types/node": ">=13.7.0",
768 | "long": "^4.0.0"
769 | }
770 | },
771 | "readable-stream": {
772 | "version": "2.3.7",
773 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
774 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
775 | "requires": {
776 | "core-util-is": "~1.0.0",
777 | "inherits": "~2.0.3",
778 | "isarray": "~1.0.0",
779 | "process-nextick-args": "~2.0.0",
780 | "safe-buffer": "~5.1.1",
781 | "string_decoder": "~1.1.1",
782 | "util-deprecate": "~1.0.1"
783 | }
784 | },
785 | "require-directory": {
786 | "version": "2.1.1",
787 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
788 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
789 | },
790 | "rimraf": {
791 | "version": "3.0.2",
792 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
793 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
794 | "requires": {
795 | "glob": "^7.1.3"
796 | }
797 | },
798 | "safe-buffer": {
799 | "version": "5.1.2",
800 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
801 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
802 | },
803 | "selenium-webdriver": {
804 | "version": "4.0.0-rc-1",
805 | "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-rc-1.tgz",
806 | "integrity": "sha512-bcrwFPRax8fifRP60p7xkWDGSJJoMkPAzufMlk5K2NyLPht/YZzR2WcIk1+3gR8VOCLlst1P2PI+MXACaFzpIw==",
807 | "requires": {
808 | "jszip": "^3.6.0",
809 | "rimraf": "^3.0.2",
810 | "tmp": "^0.2.1",
811 | "ws": ">=7.4.6"
812 | }
813 | },
814 | "set-immediate-shim": {
815 | "version": "1.0.1",
816 | "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
817 | "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
818 | },
819 | "string-width": {
820 | "version": "4.2.3",
821 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
822 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
823 | "requires": {
824 | "emoji-regex": "^8.0.0",
825 | "is-fullwidth-code-point": "^3.0.0",
826 | "strip-ansi": "^6.0.1"
827 | }
828 | },
829 | "string_decoder": {
830 | "version": "1.1.1",
831 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
832 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
833 | "requires": {
834 | "safe-buffer": "~5.1.0"
835 | }
836 | },
837 | "strip-ansi": {
838 | "version": "6.0.1",
839 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
840 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
841 | "requires": {
842 | "ansi-regex": "^5.0.1"
843 | }
844 | },
845 | "tmp": {
846 | "version": "0.2.1",
847 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
848 | "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
849 | "requires": {
850 | "rimraf": "^3.0.0"
851 | }
852 | },
853 | "tr46": {
854 | "version": "0.0.3",
855 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
856 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
857 | },
858 | "tslib": {
859 | "version": "2.3.1",
860 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
861 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
862 | },
863 | "util-deprecate": {
864 | "version": "1.0.2",
865 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
866 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
867 | },
868 | "webidl-conversions": {
869 | "version": "3.0.1",
870 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
871 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
872 | },
873 | "websocket-driver": {
874 | "version": "0.7.4",
875 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
876 | "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
877 | "requires": {
878 | "http-parser-js": ">=0.5.1",
879 | "safe-buffer": ">=5.1.0",
880 | "websocket-extensions": ">=0.1.1"
881 | }
882 | },
883 | "websocket-extensions": {
884 | "version": "0.1.4",
885 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
886 | "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
887 | },
888 | "whatwg-fetch": {
889 | "version": "2.0.4",
890 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
891 | "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
892 | },
893 | "whatwg-url": {
894 | "version": "5.0.0",
895 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
896 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
897 | "requires": {
898 | "tr46": "~0.0.3",
899 | "webidl-conversions": "^3.0.0"
900 | }
901 | },
902 | "wrap-ansi": {
903 | "version": "7.0.0",
904 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
905 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
906 | "requires": {
907 | "ansi-styles": "^4.0.0",
908 | "string-width": "^4.1.0",
909 | "strip-ansi": "^6.0.0"
910 | }
911 | },
912 | "wrappy": {
913 | "version": "1.0.2",
914 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
915 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
916 | },
917 | "ws": {
918 | "version": "8.2.2",
919 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.2.tgz",
920 | "integrity": "sha512-Q6B6H2oc8QY3llc3cB8kVmQ6pnJWVQbP7Q5algTcIxx7YEpc0oU4NBVHlztA7Ekzfhw2r0rPducMUiCGWKQRzw=="
921 | },
922 | "y18n": {
923 | "version": "5.0.8",
924 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
925 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
926 | },
927 | "yargs": {
928 | "version": "16.2.0",
929 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
930 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
931 | "requires": {
932 | "cliui": "^7.0.2",
933 | "escalade": "^3.1.1",
934 | "get-caller-file": "^2.0.5",
935 | "require-directory": "^2.1.1",
936 | "string-width": "^4.2.0",
937 | "y18n": "^5.0.5",
938 | "yargs-parser": "^20.2.2"
939 | }
940 | },
941 | "yargs-parser": {
942 | "version": "20.2.9",
943 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
944 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
945 | }
946 | }
947 | }
948 |
--------------------------------------------------------------------------------
/FirestoreEmulator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "firestore-emulator",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "firebase emulators:start --project demo-firefly --import=./sample_data",
8 | "add-data": "firebase emulators:start --project demo-firefly --export-on-exit=./sample_data"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC"
13 | }
14 |
--------------------------------------------------------------------------------
/FirestoreEmulator/sample_data/firebase-export-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "9.11.0",
3 | "firestore": {
4 | "version": "1.11.15",
5 | "path": "firestore_export",
6 | "metadata_file": "firestore_export/firestore_export.overall_export_metadata"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/FirestoreEmulator/sample_data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FireflyJS/fireflyjs-core/98e273baf42c3196409575e771f48d5e5568a3b8/FirestoreEmulator/sample_data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata
--------------------------------------------------------------------------------
/FirestoreEmulator/sample_data/firestore_export/all_namespaces/all_kinds/output-0:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FireflyJS/fireflyjs-core/98e273baf42c3196409575e771f48d5e5568a3b8/FirestoreEmulator/sample_data/firestore_export/all_namespaces/all_kinds/output-0
--------------------------------------------------------------------------------
/FirestoreEmulator/sample_data/firestore_export/firestore_export.overall_export_metadata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FireflyJS/fireflyjs-core/98e273baf42c3196409575e771f48d5e5568a3b8/FirestoreEmulator/sample_data/firestore_export/firestore_export.overall_export_metadata
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | MIT License
3 |
4 | Copyright (c) 2021 FireflyJS
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
FireflyJS
13 |
14 |
15 | The only ODM you will ever need for Firestore.
16 |
17 |
18 |
19 |
20 |
21 |
22 | Report Bug
23 | ·
24 | Request Feature
25 |
26 |
27 |
28 | [](https://github.com/Saurav-Shrivastav/covaccinate/issues)
29 | 
30 |
31 |
32 |
33 | [](https://github.com/Saurav-Shrivastav/covaccinate/blob/master/.pre-commit-config.yaml)
34 | [](https://github.com/FireflyJS/fireflyjs-core/blob/main/LICENSE)
35 |
36 |
37 |
38 | [](#contributors-)
39 |
40 |
41 |
42 |
43 |
44 | Table of Contents
45 |
46 | -
47 | About The Project
48 |
49 | -
50 | Features
51 |
52 | -
53 | Getting Started
54 |
58 |
59 | - Documentation
60 |
61 | - Contributing
62 | - Contributors
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | ## About The Project
72 |
73 | [![Product Name Screen Shot][product-screenshot]](https://example.com)
74 |
75 | FireflyJS is an object document model(ODM) library for Firestore. It aims to ease the process of developing applications using Firestore by providing robust validation of data, exposing a multitude of ways to query, add, etc.
76 |
77 | ### Built With
78 |
79 | - [TypeScript](https://www.typescriptlang.org/)
80 | - [Jest](https://jestjs.io/)
81 | - [Firebase](https://firebase.google.com/)
82 |
83 |
84 |
85 | ## Features
86 |
87 | - Define a schema for your data and add a multitude of operations before adding data to the database.
88 | - Written completely in TypeScript. Get intelligent intellisense and speed up your development workflow.
89 | - Easy to pick up, with a syntax inspired from popular libraries like [Mongoose](https://github.com/Automattic/mongoose) and [Yup](https://github.com/jquense/yup).
90 | - Query data like a pro! With methods like `findById`, `findOne`, etc.
91 | - Add and update data made easy and safe with proper validation, data transformation according to your defined schema and much more!
92 |
93 |
94 |
95 | ## Getting Started
96 |
97 | Coming Soon!
98 |
99 | For now, you can refer [**tests**](https://github.com/FireflyJS/fireflyjs-core/tree/main/src/__tests__) for usage and examples as we are still developing the npm package.
100 |
101 |
102 |
103 | ## Documentation
104 |
105 | Inline documentation for all methods is available in the codebase. Documentation website is in the works. Stay Tuned :)
106 |
107 |
108 |
109 | ## Contributing
110 |
111 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
112 |
113 | 1. Fork the Project
114 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
115 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
116 | 4. Push to the Branch (`git push origin feature/AmazingFeature`)
117 | 5. Open a Pull Request
118 |
119 | [contributors-shield]: https://img.shields.io/github/contributors/othneildrew/Best-README-Template.svg?style=for-the-badge
120 | [contributors-url]: https://github.com/FireflyJS/fireflyjs-core/graphs/contributors
121 | [forks-shield]: https://img.shields.io/github/forks/othneildrew/Best-README-Template.svg?style=for-the-badge
122 | [forks-url]: https://github.com/FireflyJS/fireflyjs-core/network/members
123 | [stars-shield]: https://img.shields.io/github/stars/othneildrew/Best-README-Template.svg?style=for-the-badge
124 | [stars-url]: https://github.com/FireflyJS/fireflyjs-core/stargazers
125 | [issues-shield]: https://img.shields.io/github/issues/othneildrew/Best-README-Template.svg?style=for-the-badge
126 | [issues-url]: https://github.com/FireflyJS/fireflyjs-core/issues
127 | [license-shield]: https://img.shields.io/github/license/othneildrew/Best-README-Template.svg?style=for-the-badge
128 | [license-url]: https://github.com/othneildrew/Best-README-Template/blob/master/LICENSE.txt
129 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
130 |
131 |
132 |
133 | [product-screenshot]: https://user-images.githubusercontent.com/51146347/130316345-b68999ff-8eb5-4620-bf04-bc9eef91e2c6.png
134 |
135 | ## Contributors
136 |
137 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
138 |
139 |
140 |
141 |
142 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
155 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: "ts-jest",
3 | testEnvironment: "node",
4 | testMatch: ["**/__tests__/**/*.test.[jt]s?(x)"],
5 | };
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fireflyjs/core",
3 | "version": "0.0.5",
4 | "description": "ODM for FireStore",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "files": [
8 | "lib/**/*"
9 | ],
10 | "scripts": {
11 | "prepare": "husky install",
12 | "compile": "tsc",
13 | "format": "npx prettier --write .",
14 | "lint": "npx eslint --fix .",
15 | "test": "jest",
16 | "test:watch": "jest --watch",
17 | "test:verbose": "jest --verbose"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/FireflyJS/fireflyjs-core.git"
22 | },
23 | "keywords": [],
24 | "author": "",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/FireflyJS/fireflyjs-core/issues"
28 | },
29 | "homepage": "https://github.com/FireflyJS/fireflyjs-core#readme",
30 | "devDependencies": {
31 | "@types/jest": "^26.0.24",
32 | "@types/node": "^16.3.1",
33 | "@typescript-eslint/eslint-plugin": "^4.28.3",
34 | "@typescript-eslint/parser": "^4.28.3",
35 | "eslint": "^7.30.0",
36 | "eslint-config-airbnb-typescript": "^12.3.1",
37 | "eslint-config-prettier": "^8.3.0",
38 | "eslint-plugin-import": "^2.23.4",
39 | "eslint-plugin-prettier": "^3.4.0",
40 | "husky": "^7.0.0",
41 | "jest": "^27.0.6",
42 | "prettier": "^2.3.2",
43 | "ts-jest": "^27.0.3",
44 | "typescript": "^4.3.5"
45 | },
46 | "dependencies": {
47 | "firebase-admin": "^9.11.0"
48 | },
49 | "directories": {
50 | "lib": "lib"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Connection/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import Model from "../Model";
3 | import { ObjectSchema, KeyValueStore } from "../SchemaTypes";
4 | import { ModelPOJO } from ".";
5 |
6 | /**
7 | * Class for a Firefly connection.
8 | */
9 | class Connection {
10 | private __db: __firestore.Firestore;
11 |
12 | private __modelMap: Map = new Map();
13 |
14 | /**
15 | * Initializes a new connection instance.
16 | * @constructor
17 | * @param {__firestore.Firestore} firestore - The [Firestore](https://firebase.google.com/docs/reference/node/firebase.firestore.Firestore) instance to use.
18 | */
19 | constructor(firestore: __firestore.Firestore) {
20 | this.__db = firestore;
21 | }
22 |
23 | /**
24 | * A getter function that returns all the models registered with this connection.
25 | * @returns {ModelPOJO} A javascript POJO(Plain Old Javascript object) model names and their corresponding model instances.
26 | */
27 | get models(): ModelPOJO {
28 | const pojo: ModelPOJO = {};
29 | this.__modelMap.forEach((val, key) => {
30 | pojo[key] = val;
31 | });
32 |
33 | return pojo;
34 | }
35 |
36 | /**
37 | * Returns all the models registered with this connection.
38 | * @returns {string[]} An array containing all the names of models.
39 | */
40 | public modelNames = (): string[] => {
41 | const keys: string[] = [...this.__modelMap.keys()];
42 |
43 | return keys;
44 | };
45 |
46 | /**
47 | * Returns an instance of the model corresponding to the provided name
48 | * @param {string} name - The name of the model.
49 | * @returns {Model | undefined} Model instance corresponding to `name`. Returns `undefined` if not present.
50 | */
51 | public getModel = (name: string): Model | undefined => {
52 | const model = this.__modelMap.get(name);
53 |
54 | return model;
55 | };
56 |
57 | /**
58 | * Registers a new model with the underlying connection.
59 | * @typeParam T - Type defination of the provided schema, must extend {@link KeyValueStore | KeyValueStore}
60 | * @param {string} name - Name for the new model. Overwrites the existing model instance under the same name if present.
61 | * @param {ObjectSchema.Class} schema - The schema for the new model. This schema would be used to validate data before writing to the database.
62 | * @returns {Model} The new model instance.
63 | */
64 | public model = (
65 | name: string,
66 | schema: ObjectSchema
67 | ): Model => {
68 | const model = new Model(name, schema, this.__db);
69 | this.__modelMap.set(name, model);
70 |
71 | return model;
72 | };
73 | }
74 |
75 | export default Connection;
76 |
--------------------------------------------------------------------------------
/src/Connection/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { ModelPOJO } from "./types/ModelPOJO";
3 |
--------------------------------------------------------------------------------
/src/Connection/types/ModelPOJO.ts:
--------------------------------------------------------------------------------
1 | import Model from "../../Model/class";
2 |
3 | /**
4 | * [POJO](https://masteringjs.io/tutorials/fundamentals/pojo#:~:text=The%20intuition%20behind%20POJOs%20is,create%20POJOs%20by%20calling%20Object.) containing model name as keys and Model instance as values
5 | */
6 | type ModelPOJO = { [k: string]: Model };
7 |
8 | export { ModelPOJO };
9 |
--------------------------------------------------------------------------------
/src/Document/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import { ObjectSchema, KeyValueStore } from "../SchemaTypes";
3 | import { Errors, UpdateOptions } from ".";
4 | import makeError from "../utils/makeError";
5 |
6 | /**
7 | * Class for Firefly document
8 | */
9 | class Document {
10 | private __docRef: __firestore.DocumentReference;
11 |
12 | private __schema: ObjectSchema;
13 |
14 | /**
15 | * Initialize a new Document instance.
16 | * @constructor
17 | * @param {__firestore.DocumentReference} docRef - The Firestore document reference.
18 | * @param {ObjectSchema} schema - The schema for the document.
19 | */
20 | constructor(
21 | docRef: __firestore.DocumentReference,
22 | schema: ObjectSchema
23 | ) {
24 | this.__docRef = docRef;
25 | this.__schema = schema;
26 | }
27 |
28 | /**
29 | * returns the document schema
30 | * @returns {ObjectSchema} Document Schema
31 | */
32 | get schema(): ObjectSchema {
33 | return this.__schema;
34 | }
35 |
36 | /**
37 | * returns the document id
38 | * @returns {string} Document id
39 | */
40 | get id(): string {
41 | return this.__docRef.id;
42 | }
43 |
44 | /**
45 | * Fetches the document data from Firestore.
46 | * @returns {Promise} Promise that resolves with the document data.
47 | */
48 | public data = async (): Promise => {
49 | const docSnap = await this.__docRef.get().catch((err: Error) => {
50 | throw makeError(Errors.Firestore, err.message);
51 | });
52 |
53 | const docData = docSnap.data();
54 | if (typeof docData === "undefined" || !docSnap.exists)
55 | throw makeError(Errors.Invalid, "Document is empty or non existant");
56 |
57 | return docData;
58 | };
59 |
60 | /**
61 | * Updates the document value in Firestore with the supplied data.
62 | * @param {Partial} data - The new data to update the document with.
63 | * @param {UpdateOptions} [options = {merge: true}] - The update options to be used while updating the document in Firestore. See reference @link https://googleapis.dev/nodejs/firestore/latest/global.html#SetOptions
64 | */
65 | public update = async (
66 | data: Partial,
67 | options: UpdateOptions = { merge: true }
68 | ) => {
69 | const { valid, value, errors } = this.__schema.validate(data, undefined, {
70 | onlySupplied: true,
71 | });
72 | if (!valid) {
73 | throw makeError(Errors.Validation, errors);
74 | }
75 |
76 | await this.__docRef.set(value, options);
77 | };
78 |
79 | /**
80 | * Deletes the document from Firestore.
81 | */
82 | public delete = async () => {
83 | await this.__docRef.delete();
84 | };
85 | }
86 |
87 | export default Document;
88 |
--------------------------------------------------------------------------------
/src/Document/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { DocErrorTypes as Errors } from "./types/error";
3 | export { UpdateOptions } from "./types/updateOptions";
4 |
--------------------------------------------------------------------------------
/src/Document/types/error.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * All possible error types in Firefly Document.
3 | */
4 | enum DocErrorTypes {
5 | Invalid = "Document/Invalid",
6 | Firestore = "Document/Firestore",
7 | Validation = "Document/Validation",
8 | }
9 |
10 | export { DocErrorTypes };
11 |
--------------------------------------------------------------------------------
/src/Document/types/updateOptions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The update options to be used while updating the document in Firestore. See reference
3 | * @link https://googleapis.dev/nodejs/firestore/latest/global.html#SetOptions
4 | */
5 | type UpdateOptions = {
6 | merge: boolean;
7 | mergeFields?: string[];
8 | };
9 |
10 | export { UpdateOptions };
11 |
--------------------------------------------------------------------------------
/src/Firefly/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import Connection from "../Connection";
3 | import { Errors } from ".";
4 | import makeError from "../utils/makeError";
5 |
6 | /**
7 | * The main entrypoint for establishing a Firefly instance.
8 | * */
9 | class Firefly {
10 | private __dbMap: Map = new Map();
11 |
12 | /**
13 | * Establishes a connection to the Firestore database. Can be used to create multiple set of connections.
14 | * @param {__firestore.Firestore} firestore - The [Firestore](https://firebase.google.com/docs/reference/node/firebase.firestore.Firestore) instance to use.
15 | * @param {dbName} dbName = default - The name of the connection to use.
16 | * @return {Connection} An instance of Firefly Connection.
17 | */
18 | public createConnection = (
19 | firestore: __firestore.Firestore,
20 | dbName: string = "default"
21 | ): Connection => {
22 | const connection: Connection = new Connection(firestore);
23 | this.__dbMap.set(dbName, connection);
24 | return connection;
25 | };
26 |
27 | /**
28 | * Retrieves an instance of Firefly connection to the Firestore database.
29 | * @param {dbName} dbName - The name of the connection to retrieve.
30 | * @return {Connection} The instance of Firefly Connection corresponding to `dbName`.
31 | */
32 | public getConnection = (dbName: string): Connection => {
33 | const connection: Connection | undefined = this.__dbMap.get(dbName);
34 |
35 | if (typeof connection === "undefined") {
36 | throw makeError(
37 | Errors.Invalid,
38 | `Firefly Connection with key ${dbName} does not exist`
39 | );
40 | }
41 |
42 | return connection;
43 | };
44 | }
45 |
46 | export default Firefly;
47 |
--------------------------------------------------------------------------------
/src/Firefly/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { FireflyErrorTypes as Errors } from "./types/error";
3 |
--------------------------------------------------------------------------------
/src/Firefly/types/error.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * All possible error types in Firefly class.
3 | */
4 | enum FireflyErrorTypes {
5 | Invalid = "Firefly/Invalid",
6 | }
7 |
8 | export { FireflyErrorTypes };
9 |
--------------------------------------------------------------------------------
/src/Model/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import Document from "../Document";
3 | import {
4 | DeleteQuery,
5 | MultipleQuery,
6 | SingleQuery,
7 | UpdateQuery,
8 | BaseQueryNs,
9 | UpdateQueryNs,
10 | } from "../Query";
11 | import { KeyValueStore, ObjectSchema } from "../SchemaTypes";
12 | import { Errors } from ".";
13 | import makeError from "../utils/makeError";
14 |
15 | class Model {
16 | private __name: string;
17 |
18 | private __schema: ObjectSchema;
19 |
20 | private __db: __firestore.Firestore;
21 |
22 | constructor(
23 | name: string,
24 | schema: ObjectSchema,
25 | firestore: __firestore.Firestore
26 | ) {
27 | this.__name = name;
28 | this.__schema = schema;
29 | this.__db = firestore;
30 | }
31 |
32 | get _name() {
33 | return this.__name;
34 | }
35 |
36 | get _schema() {
37 | return this.__schema;
38 | }
39 |
40 | get _db() {
41 | return this.__db;
42 | }
43 |
44 | public create = async (data: Partial) => {
45 | const { valid, value, errors } = this.__schema.validate(data, undefined, {
46 | onlyKeys: true,
47 | });
48 | if (!valid) {
49 | throw makeError(Errors.Validation, errors);
50 | }
51 |
52 | const docRef = await this.__db
53 | .collection(this.__name)
54 | .add(value)
55 | .catch((err: Error) => {
56 | throw makeError(Errors.Firestore, err.message);
57 | });
58 |
59 | return new Document(
60 | docRef as __firestore.DocumentReference,
61 | this.__schema
62 | );
63 | };
64 |
65 | public findById = (id: string) => {
66 | const collectionRef = this.__db.collection(this.__name);
67 |
68 | const config: BaseQueryNs.ConfigPOJOWithId = {
69 | _id: id,
70 | };
71 |
72 | return new SingleQuery(config, collectionRef, this.__schema, true);
73 | };
74 |
75 | public find = (query: BaseQueryNs.ConfigPOJO) => {
76 | const collectionRef = this.__db.collection(this.__name);
77 |
78 | return new MultipleQuery(query, collectionRef, this.__schema);
79 | };
80 |
81 | public findOne = (query: BaseQueryNs.ConfigPOJO) => {
82 | const collectionRef = this.__db.collection(this.__name);
83 |
84 | return new SingleQuery(query, collectionRef, this.__schema, false);
85 | };
86 |
87 | public findOneAndUpdate = (
88 | query: BaseQueryNs.ConfigPOJO,
89 | updateQuery: UpdateQueryNs.UpdateConfigPOJO,
90 | updateOptions: UpdateQueryNs.UpdateOptions
91 | ) => {
92 | const collectionRef = this.__db.collection(this.__name);
93 |
94 | return new UpdateQuery(
95 | query,
96 | collectionRef,
97 | this.__schema,
98 | updateQuery,
99 | updateOptions,
100 | false
101 | );
102 | };
103 |
104 | public findByIdAndUpdate = (
105 | id: string,
106 | updateQuery: UpdateQueryNs.UpdateConfigPOJO,
107 | updateOptions: UpdateQueryNs.UpdateOptions
108 | ) => {
109 | const collectionRef = this.__db.collection(this.__name);
110 |
111 | const config: BaseQueryNs.ConfigPOJOWithId = {
112 | _id: id,
113 | };
114 |
115 | return new UpdateQuery(
116 | config,
117 | collectionRef,
118 | this.__schema,
119 | updateQuery,
120 | updateOptions,
121 | true
122 | );
123 | };
124 |
125 | public findByIdAndDelete = (id: string) => {
126 | const collectionRef = this.__db.collection(this.__name);
127 |
128 | const config: BaseQueryNs.ConfigPOJOWithId = {
129 | _id: id,
130 | };
131 |
132 | return new DeleteQuery(config, collectionRef, this.__schema, true);
133 | };
134 |
135 | public findOneAndDelete = (query: BaseQueryNs.ConfigPOJO) => {
136 | const collectionRef = this.__db.collection(this.__name);
137 |
138 | return new DeleteQuery(query, collectionRef, this.__schema, false);
139 | };
140 | }
141 |
142 | export default Model;
143 |
--------------------------------------------------------------------------------
/src/Model/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { ModelErrorTypes as Errors } from "./types/error";
3 |
--------------------------------------------------------------------------------
/src/Model/types/error.ts:
--------------------------------------------------------------------------------
1 | enum ModelErrorTypes {
2 | Firestore = "Model/Firestore",
3 | Validation = "Model/SchemaValidation",
4 | Misc = "Model/Misc",
5 | }
6 |
7 | export { ModelErrorTypes };
8 |
--------------------------------------------------------------------------------
/src/Query/Base/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import { ObjectSchema, KeyValueStore } from "../../SchemaTypes";
3 | import Document from "../../Document";
4 |
5 | /**
6 | * Base Query Class to define the structure of other derived Query classes
7 | */
8 | abstract class BaseQuery {
9 | protected abstract __config: K;
10 |
11 | protected abstract __collectionRef: __firestore.CollectionReference;
12 |
13 | protected abstract __schema: ObjectSchema;
14 |
15 | /**
16 | * Executes the query and returns the result
17 | * @returns {Promise | Document[]> | undefined | void} The result of the query
18 | */
19 | abstract exec: () => Promise | Document[] | undefined | void>;
20 | }
21 |
22 | export default BaseQuery;
23 |
--------------------------------------------------------------------------------
/src/Query/Base/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { BuildExtQueryFn } from "./types/buildExtQuery";
3 | export { ConfigPOJO, ConfigPOJOWithId } from "./types/config";
4 | export { QueryErrorTypes as Errors } from "./types/errors";
5 | export { Operators } from "./types/operators";
6 |
--------------------------------------------------------------------------------
/src/Query/Base/types/buildExtQuery.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 |
3 | type BuildExtQueryFn = (
4 | query: __firestore.Query | __firestore.CollectionReference,
5 | extConfig: T
6 | ) => __firestore.Query;
7 |
8 | export { BuildExtQueryFn };
9 |
--------------------------------------------------------------------------------
/src/Query/Base/types/config.ts:
--------------------------------------------------------------------------------
1 | import { Operators } from "..";
2 | import { KeyValueStore } from "../../../SchemaTypes";
3 |
4 | type ConfigPOJO = {
5 | [k in keyof T]?: Partial | Operators;
6 | };
7 |
8 | type ConfigPOJOWithId =
9 | | ConfigPOJO
10 | | {
11 | _id: string;
12 | };
13 |
14 | export { ConfigPOJO, ConfigPOJOWithId };
15 |
--------------------------------------------------------------------------------
/src/Query/Base/types/errors.ts:
--------------------------------------------------------------------------------
1 | enum QueryErrorTypes {
2 | Invalid = "Query/Invalid",
3 | MissingConfig = "Query/MissingConfiguration",
4 | NotFound = "Query/NoDocumentFound",
5 | MissingUpdateConfig = "Query/UpdateConfigMissing",
6 | }
7 |
8 | export { QueryErrorTypes };
9 |
--------------------------------------------------------------------------------
/src/Query/Base/types/operators.ts:
--------------------------------------------------------------------------------
1 | type OperatorValues = string | number | boolean | null | Date;
2 |
3 | type Operators = {
4 | $gt?: OperatorValues;
5 | $gte?: OperatorValues;
6 | $lt?: OperatorValues;
7 | $lte?: OperatorValues;
8 | $eq?: OperatorValues;
9 | $neq?: OperatorValues;
10 | $ctn?: OperatorValues | OperatorValues[];
11 | $in?: OperatorValues[];
12 | $nin?: OperatorValues[];
13 | };
14 |
15 | export { Operators };
16 |
--------------------------------------------------------------------------------
/src/Query/DeleteQuery/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import BaseQuery, { ConfigPOJOWithId, Errors } from "../Base";
3 | import Document from "../../Document";
4 | import { ObjectSchema, KeyValueStore } from "../../SchemaTypes";
5 | import buildQuery from "../utils/buildQuery";
6 | import makeError from "../../utils/makeError";
7 |
8 | class DeleteQuery extends BaseQuery<
9 | T,
10 | ConfigPOJOWithId
11 | > {
12 | protected __config: ConfigPOJOWithId;
13 |
14 | protected __collectionRef: __firestore.CollectionReference;
15 |
16 | protected __schema: ObjectSchema;
17 |
18 | private __queryById: boolean = false;
19 |
20 | constructor(
21 | input: ConfigPOJOWithId,
22 | collectionRef: __firestore.CollectionReference,
23 | schema: ObjectSchema,
24 | queryById: boolean = false
25 | ) {
26 | super();
27 | this.__config = input;
28 | this.__collectionRef = collectionRef;
29 | this.__schema = schema;
30 | this.__queryById = queryById;
31 | }
32 |
33 | /**
34 | * @returns {Promise} void
35 | */
36 | public exec = async (): Promise => {
37 | if (!this.__config) {
38 | throw makeError(Errors.MissingConfig, "Query is not configured.");
39 | }
40 |
41 | let query: __firestore.CollectionReference | __firestore.Query;
42 | let documentRef: __firestore.DocumentReference | undefined;
43 |
44 | if (
45 | this.__queryById &&
46 | this.__config._id &&
47 | typeof this.__config._id === "string"
48 | ) {
49 | documentRef = this.__collectionRef.doc(
50 | this.__config._id
51 | ) as __firestore.DocumentReference;
52 | } else {
53 | query = this.__collectionRef;
54 |
55 | Object.keys(this.__config).forEach((k) => {
56 | const key = k as keyof ConfigPOJOWithId;
57 |
58 | if (key !== "_id") {
59 | query = buildQuery(key, this.__config[key], query);
60 | }
61 | });
62 |
63 | const querySnapshot = await query.get();
64 |
65 | documentRef = querySnapshot.docs[0]
66 | ?.ref as __firestore.DocumentReference;
67 | }
68 |
69 | if (typeof documentRef === "undefined") {
70 | throw makeError(Errors.NotFound, "No document found matching the query.");
71 | }
72 |
73 | const document = new Document(documentRef, this.__schema);
74 |
75 | await document.delete();
76 | };
77 | }
78 |
79 | export default DeleteQuery;
80 |
--------------------------------------------------------------------------------
/src/Query/DeleteQuery/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 |
--------------------------------------------------------------------------------
/src/Query/MultipleQuery/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import BaseQuery, { ConfigPOJO, Errors } from "../Base";
3 | import Document from "../../Document";
4 | import { ObjectSchema, KeyValueStore } from "../../SchemaTypes";
5 | import { ExtConfigPOJO } from ".";
6 | import { buildExtQuery } from "./utils/buildExtQuery";
7 | import buildQuery from "../utils/buildQuery";
8 | import makeError from "../../utils/makeError";
9 |
10 | class MultipleQuery extends BaseQuery<
11 | T,
12 | ConfigPOJO
13 | > {
14 | protected __config: ConfigPOJO;
15 |
16 | protected __collectionRef: __firestore.CollectionReference;
17 |
18 | protected __schema: ObjectSchema;
19 |
20 | private __extConfig: ExtConfigPOJO;
21 |
22 | constructor(
23 | input: ConfigPOJO,
24 | collectionRef: __firestore.CollectionReference,
25 | schema: ObjectSchema
26 | ) {
27 | super();
28 | this.__config = input;
29 | this.__collectionRef = collectionRef;
30 | this.__schema = schema;
31 | this.__extConfig = {};
32 | }
33 |
34 | /**
35 | * limits the number of documents to be returned
36 | * @param {number} limit The no of first matching documents to return
37 | * @returns {this} returns Multiple Query instance
38 | */
39 | public limit = (limit: number) => {
40 | this.__extConfig.limit = limit;
41 |
42 | return this;
43 | };
44 |
45 | /**
46 | * Specifies the offset of the returned results.
47 | * @param {number} offset Offset Number
48 | * @returns {this} returns Multiple Query instance
49 | */
50 | public offset = (offset: number) => {
51 | this.__extConfig.offset = offset;
52 |
53 | return this;
54 | };
55 |
56 | /**
57 | * Starts at the provided set of field values relative to the order of the query.
58 | * @param {number} value No to startAt relative to the order of the query
59 | * @returns {this} returns Multiple Query instance
60 | */
61 | public startAt = (value: number) => {
62 | this.__extConfig.startAt = value;
63 |
64 | return this;
65 | };
66 |
67 | /**
68 | * sorts by the specified field, optionally in descending order instead of ascending.
69 | * @param {string[]} fields Comma seperated list of fields to sort by preceded by either '+' or '-' denoting ascending or descending order respectively. If passed none, defaults to ascending order.
70 | * @returns {this} returns Multiple Query instance
71 | */
72 | public orderBy = (...fields: string[]) => {
73 | this.__extConfig.orderBy = fields;
74 |
75 | return this;
76 | };
77 |
78 | /**
79 | * Selects the fields to be included in the final result.
80 | * @param fields Comma separated list of fields to include in the final result.
81 | * @returns {this} returns Multiple Query instance
82 | */
83 | public select = (...fields: string[]) => {
84 | this.__extConfig.select = fields;
85 |
86 | return this;
87 | };
88 |
89 | /**
90 | * @returns {Promise[]>} returns a promise that resolves to an array of documents
91 | */
92 | public exec = async (): Promise[]> => {
93 | if (!this.__config) {
94 | throw makeError(Errors.MissingConfig, "Query is not configured.");
95 | }
96 |
97 | let query: __firestore.CollectionReference | __firestore.Query =
98 | this.__collectionRef;
99 |
100 | Object.keys(this.__config).forEach((key: keyof ConfigPOJO) => {
101 | query = buildQuery(key, this.__config[key], query);
102 | });
103 |
104 | query = buildExtQuery(query, this.__extConfig);
105 |
106 | const querySnapshot = await query.get();
107 |
108 | const documents: Document[] = [];
109 |
110 | querySnapshot.forEach((docSnap) => {
111 | documents.push(
112 | new Document(
113 | docSnap.ref as __firestore.DocumentReference,
114 | this.__schema
115 | )
116 | );
117 | });
118 |
119 | return documents;
120 | };
121 | }
122 |
123 | export default MultipleQuery;
124 |
--------------------------------------------------------------------------------
/src/Query/MultipleQuery/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { ExtConfigPOJO } from "./types/extConfig";
3 | export { buildExtQuery } from "./utils/buildExtQuery";
4 |
--------------------------------------------------------------------------------
/src/Query/MultipleQuery/types/extConfig.ts:
--------------------------------------------------------------------------------
1 | type ExtConfigPOJO = {
2 | limit?: number;
3 | offset?: number;
4 | startAt?: number;
5 | orderBy?: string[];
6 | select?: string[];
7 | };
8 |
9 | export { ExtConfigPOJO };
10 |
--------------------------------------------------------------------------------
/src/Query/MultipleQuery/utils/buildExtQuery.ts:
--------------------------------------------------------------------------------
1 | import { ExtConfigPOJO } from "..";
2 | import { BuildExtQueryFn } from "../../Base";
3 |
4 | const buildExtQuery: BuildExtQueryFn = (query, extConfig) => {
5 | let extendedQuery = query;
6 |
7 | if (extConfig.startAt) {
8 | extendedQuery = extendedQuery.startAt(extConfig.startAt);
9 | }
10 |
11 | if (extConfig.offset) {
12 | extendedQuery = extendedQuery.offset(extConfig.offset);
13 | }
14 |
15 | if (extConfig.orderBy) {
16 | extConfig.orderBy.forEach((field: string) => {
17 | if (field[0] === "-") {
18 | const key = field.replace("-", "");
19 | extendedQuery = extendedQuery.orderBy(key, "desc");
20 | } else if (field[0] === "+") {
21 | const key = field.replace("+", "");
22 | extendedQuery = extendedQuery.orderBy(key, "asc");
23 | } else {
24 | extendedQuery = extendedQuery.orderBy(field, "asc");
25 | }
26 | });
27 | }
28 |
29 | if (extConfig.select) {
30 | extendedQuery = extendedQuery.select(...extConfig.select);
31 | }
32 |
33 | if (extConfig.limit) {
34 | extendedQuery = extendedQuery.limit(extConfig.limit);
35 | }
36 |
37 | return extendedQuery;
38 | };
39 |
40 | export { buildExtQuery };
41 |
--------------------------------------------------------------------------------
/src/Query/SingleQuery/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import Document from "../../Document";
3 | import BaseQuery, { ConfigPOJOWithId, Errors } from "../Base";
4 | import { ExtConfigPOJO } from ".";
5 | import { ObjectSchema, KeyValueStore } from "../../SchemaTypes";
6 | import { buildExtQuery } from "./utils/buildExtQuery";
7 | import makeError from "../../utils/makeError";
8 | import buildQuery from "../utils/buildQuery";
9 |
10 | class SingleQuery extends BaseQuery<
11 | T,
12 | ConfigPOJOWithId
13 | > {
14 | protected __config: ConfigPOJOWithId;
15 |
16 | protected __collectionRef: __firestore.CollectionReference;
17 |
18 | protected __schema: ObjectSchema;
19 |
20 | private __extConfig: ExtConfigPOJO;
21 |
22 | private __queryById: boolean = false;
23 |
24 | constructor(
25 | input: ConfigPOJOWithId,
26 | collectionRef: __firestore.CollectionReference,
27 | schema: ObjectSchema,
28 | queryById: boolean = false
29 | ) {
30 | super();
31 | this.__config = input;
32 | this.__collectionRef = collectionRef;
33 | this.__schema = schema;
34 | this.__extConfig = {};
35 | this.__queryById = queryById;
36 | }
37 |
38 | /**
39 | * Selects the fields to be included in the final result.
40 | * @param fields Comma separated list of fields to include in the final result.
41 | * @returns {this} returns Single Query instance
42 | */
43 | public select = (...fields: string[]) => {
44 | this.__extConfig.select = fields;
45 |
46 | return this;
47 | };
48 |
49 | /**
50 | * @returns {Promise>} returns a promise that resolves to a single document
51 | */
52 | exec = async (): Promise | undefined> => {
53 | if (!this.__config) {
54 | throw makeError(Errors.MissingConfig, "Query is not configured.");
55 | }
56 |
57 | if (
58 | this.__queryById &&
59 | this.__config._id &&
60 | typeof this.__config._id === "string"
61 | ) {
62 | const docRef = this.__collectionRef.doc(this.__config._id);
63 |
64 | return new Document(
65 | docRef as __firestore.DocumentReference,
66 | this.__schema
67 | );
68 | }
69 |
70 | let query: __firestore.CollectionReference | __firestore.Query =
71 | this.__collectionRef;
72 |
73 | Object.keys(this.__config).forEach((k) => {
74 | const key = k as keyof ConfigPOJOWithId;
75 | if (key !== "_id") {
76 | query = buildQuery(key, this.__config[key], query);
77 | }
78 | });
79 |
80 | query = buildExtQuery(query, this.__extConfig);
81 |
82 | const querySnapshot = await query.get();
83 |
84 | if (querySnapshot.empty) {
85 | return undefined;
86 | }
87 | const documentRef = querySnapshot.docs[0];
88 |
89 | if (typeof documentRef === "undefined") {
90 | return undefined;
91 | }
92 |
93 | return new Document(
94 | documentRef.ref as __firestore.DocumentReference,
95 | this.__schema
96 | );
97 | };
98 | }
99 |
100 | export default SingleQuery;
101 |
--------------------------------------------------------------------------------
/src/Query/SingleQuery/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { ExtConfigPOJO } from "./types/extConfig";
3 | export { buildExtQuery } from "./utils/buildExtQuery";
4 |
--------------------------------------------------------------------------------
/src/Query/SingleQuery/types/extConfig.ts:
--------------------------------------------------------------------------------
1 | type ExtConfigPOJO = {
2 | select?: string[];
3 | };
4 |
5 | export { ExtConfigPOJO };
6 |
--------------------------------------------------------------------------------
/src/Query/SingleQuery/utils/buildExtQuery.ts:
--------------------------------------------------------------------------------
1 | import { ExtConfigPOJO } from "..";
2 | import { BuildExtQueryFn } from "../../Base";
3 |
4 | const buildExtQuery: BuildExtQueryFn = (query, extConfig) => {
5 | let extendedQuery = query;
6 |
7 | if (extConfig.select) {
8 | extendedQuery = extendedQuery.select(...extConfig.select);
9 | }
10 |
11 | return extendedQuery;
12 | };
13 |
14 | export { buildExtQuery };
15 |
--------------------------------------------------------------------------------
/src/Query/UpdateQuery/class.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import BaseQuery, { ConfigPOJOWithId, Errors } from "../Base";
3 | import Document from "../../Document";
4 | import { ObjectSchema, KeyValueStore } from "../../SchemaTypes";
5 | import { UpdateOptions, UpdateConfigPOJO } from ".";
6 | import buildQuery from "../utils/buildQuery";
7 | import makeError from "../../utils/makeError";
8 |
9 | class UpdateQuery extends BaseQuery<
10 | T,
11 | ConfigPOJOWithId
12 | > {
13 | protected __config: ConfigPOJOWithId;
14 |
15 | protected __collectionRef: __firestore.CollectionReference;
16 |
17 | protected __schema: ObjectSchema;
18 |
19 | private __updateConfig: UpdateConfigPOJO;
20 |
21 | private __updateOptions: UpdateOptions;
22 |
23 | private __queryById: boolean = false;
24 |
25 | constructor(
26 | input: ConfigPOJOWithId,
27 | collectionRef: __firestore.CollectionReference,
28 | schema: ObjectSchema,
29 | updateConfig: UpdateConfigPOJO,
30 | updateOptions: UpdateOptions,
31 | queryById: boolean = false
32 | ) {
33 | super();
34 | this.__config = input;
35 | this.__collectionRef = collectionRef;
36 | this.__schema = schema;
37 | this.__updateConfig = updateConfig;
38 | this.__updateOptions = updateOptions;
39 | this.__queryById = queryById;
40 | }
41 |
42 | public exec = async (): Promise> => {
43 | if (!this.__config) {
44 | throw makeError(Errors.MissingConfig, "Query is not configured.");
45 | }
46 |
47 | if (!this.__updateConfig) {
48 | throw makeError(
49 | Errors.MissingUpdateConfig,
50 | "No data is provided to update."
51 | );
52 | }
53 |
54 | let query: __firestore.CollectionReference | __firestore.Query;
55 | let documentRef: __firestore.DocumentReference;
56 |
57 | if (
58 | this.__queryById &&
59 | this.__config._id &&
60 | typeof this.__config._id === "string"
61 | ) {
62 | documentRef = this.__collectionRef.doc(
63 | this.__config._id
64 | ) as __firestore.DocumentReference;
65 | } else {
66 | query = this.__collectionRef;
67 |
68 | Object.keys(this.__config).forEach((k: string) => {
69 | const key = k as keyof ConfigPOJOWithId;
70 |
71 | if (key !== "_id") {
72 | query = buildQuery(key, this.__config[key], query);
73 | }
74 | });
75 |
76 | const querySnapshot = await query.get();
77 |
78 | documentRef = querySnapshot.docs[0]
79 | ?.ref as __firestore.DocumentReference;
80 | }
81 |
82 | if (typeof documentRef === "undefined") {
83 | throw makeError(Errors.NotFound, "No document found matching the query.");
84 | }
85 |
86 | const document = new Document(documentRef, this.__schema);
87 |
88 | await document.update(this.__updateConfig, this.__updateOptions);
89 |
90 | return document;
91 | };
92 | }
93 |
94 | export default UpdateQuery;
95 |
--------------------------------------------------------------------------------
/src/Query/UpdateQuery/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { UpdateConfigPOJO } from "./types/updateConfig";
3 | export { UpdateOptions } from "./types/updateOptions";
4 |
--------------------------------------------------------------------------------
/src/Query/UpdateQuery/types/updateConfig.ts:
--------------------------------------------------------------------------------
1 | import { KeyValueStore } from "../../../SchemaTypes";
2 |
3 | type UpdateConfigPOJO = {
4 | [k in keyof T]?: T[k];
5 | };
6 |
7 | export { UpdateConfigPOJO };
8 |
--------------------------------------------------------------------------------
/src/Query/UpdateQuery/types/updateOptions.ts:
--------------------------------------------------------------------------------
1 | type UpdateOptions = {
2 | merge: boolean;
3 | mergeFields?: string[];
4 | };
5 |
6 | export { UpdateOptions };
7 |
--------------------------------------------------------------------------------
/src/Query/index.ts:
--------------------------------------------------------------------------------
1 | import * as BaseQueryNs from "./Base";
2 | import * as SingleQueryNs from "./SingleQuery";
3 | import * as MultipleQueryNs from "./MultipleQuery";
4 | import * as DeleteQueryNs from "./DeleteQuery";
5 | import * as UpdateQueryNs from "./UpdateQuery";
6 | import { KeyValueStore } from "../SchemaTypes";
7 |
8 | export class SingleQuery<
9 | T extends KeyValueStore
10 | > extends SingleQueryNs.default {}
11 | export class MultipleQuery<
12 | T extends KeyValueStore
13 | > extends MultipleQueryNs.default {}
14 | export class DeleteQuery<
15 | T extends KeyValueStore
16 | > extends DeleteQueryNs.default {}
17 | export class UpdateQuery<
18 | T extends KeyValueStore
19 | > extends UpdateQueryNs.default {}
20 |
21 | export {
22 | BaseQueryNs,
23 | SingleQueryNs,
24 | MultipleQueryNs,
25 | DeleteQueryNs,
26 | UpdateQueryNs,
27 | };
28 |
--------------------------------------------------------------------------------
/src/Query/utils/buildQuery.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import { KeyValueStore } from "../../SchemaTypes";
3 | import { ConfigPOJO, Errors } from "../Base";
4 | import operatorsTypeCheck from "./operatorsTypeCheck";
5 | import parseOperators from "./parseOperators";
6 | import makeError from "../../utils/makeError";
7 |
8 | const buildQuery = (
9 | key: keyof ConfigPOJO,
10 | value: ConfigPOJO[keyof T],
11 | query: __firestore.Query | __firestore.CollectionReference
12 | ): __firestore.Query => {
13 | if (typeof value === "object" && value !== null) {
14 | if (Array.isArray(value)) {
15 | throw makeError(Errors.Invalid, "Arrays are not allowed in query.");
16 | }
17 |
18 | if (value instanceof Date) {
19 | return query.where(
20 | key.toString(),
21 | "==",
22 | __firestore.Timestamp.fromDate(value)
23 | );
24 | }
25 |
26 | if (operatorsTypeCheck(value)) {
27 | return parseOperators(key, value, query);
28 | }
29 |
30 | // Flatten KeyValueStore and buildQuery recursively
31 | }
32 |
33 | return query.where(key.toString(), "==", value);
34 | };
35 |
36 | export default buildQuery;
37 |
--------------------------------------------------------------------------------
/src/Query/utils/convertDate.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import { Operators } from "../Base";
3 |
4 | const convertDate = (value: Operators[keyof Operators]) => {
5 | if (Array.isArray(value)) {
6 | return value.map((val) =>
7 | val instanceof Date ? __firestore.Timestamp.fromDate(val) : val
8 | );
9 | }
10 |
11 | return value instanceof Date ? __firestore.Timestamp.fromDate(value) : value;
12 | };
13 |
14 | export default convertDate;
15 |
--------------------------------------------------------------------------------
/src/Query/utils/operatorsTypeCheck.ts:
--------------------------------------------------------------------------------
1 | import { Operators } from "../Base";
2 |
3 | const operatorsTypeCheck = (value: object): value is Operators => {
4 | if (value === null || Array.isArray(value) || value instanceof Date)
5 | return false;
6 |
7 | // eslint-disable-next-line no-restricted-syntax
8 | for (const key in value) {
9 | if (key.trim().charAt(0) !== "$") return false;
10 | }
11 |
12 | return true;
13 | };
14 |
15 | export default operatorsTypeCheck;
16 |
--------------------------------------------------------------------------------
/src/Query/utils/parseOperators.ts:
--------------------------------------------------------------------------------
1 | import { firestore as __firestore } from "firebase-admin";
2 | import { ConfigPOJO, Operators } from "../Base";
3 | import { KeyValueStore } from "../../SchemaTypes";
4 | import convertDate from "./convertDate";
5 |
6 | const parseOperators = (
7 | key: keyof ConfigPOJO,
8 | value: Operators,
9 | query: __firestore.Query
10 | ): __firestore.Query => {
11 | let constructedQuery: __firestore.Query = query;
12 |
13 | if (value.$gt) {
14 | constructedQuery = constructedQuery.where(
15 | key.toString(),
16 | ">",
17 | convertDate(value.$gt)
18 | );
19 | }
20 |
21 | if (value.$lt) {
22 | constructedQuery = constructedQuery.where(
23 | key.toString(),
24 | "<",
25 | convertDate(value.$lt)
26 | );
27 | }
28 |
29 | if (value.$gte) {
30 | constructedQuery = constructedQuery.where(
31 | key.toString(),
32 | ">=",
33 | convertDate(value.$gte)
34 | );
35 | }
36 |
37 | if (value.$lte) {
38 | constructedQuery = constructedQuery.where(
39 | key.toString(),
40 | "<=",
41 | convertDate(value.$lte)
42 | );
43 | }
44 |
45 | if (value.$eq) {
46 | constructedQuery = constructedQuery.where(
47 | key.toString(),
48 | "==",
49 | convertDate(value.$eq)
50 | );
51 | }
52 |
53 | if (value.$neq) {
54 | constructedQuery = constructedQuery.where(
55 | key.toString(),
56 | "!=",
57 | convertDate(value.$neq)
58 | );
59 | }
60 |
61 | if (value.$ctn) {
62 | constructedQuery = constructedQuery.where(
63 | key.toString(),
64 | Array.isArray(value.$ctn) ? "array-contains-any" : "array-contains",
65 | convertDate(value.$ctn)
66 | );
67 | }
68 |
69 | if (value.$in) {
70 | constructedQuery = constructedQuery.where(
71 | key.toString(),
72 | "in",
73 | convertDate(value.$in)
74 | );
75 | }
76 |
77 | if (value.$nin) {
78 | constructedQuery = constructedQuery.where(
79 | key.toString(),
80 | "not-in",
81 | convertDate(value.$nin)
82 | );
83 | }
84 |
85 | return constructedQuery;
86 | };
87 |
88 | export default parseOperators;
89 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Base/class.ts:
--------------------------------------------------------------------------------
1 | import { BaseSchemaConfig as Config } from "./types/BaseSchema";
2 | import { ValidateFn } from "./types/ValidateFn";
3 |
4 | /**
5 | * Base SchemaType Class to define the structure of other SchemaType Classes
6 | */
7 | abstract class SchemaType {
8 | protected __config: Config = {};
9 |
10 | /**
11 | * marks the value as a required value.
12 | * @returns {this} same instance of class with property set as required.
13 | */
14 | public required = (): this => {
15 | this.__config.required = true;
16 | return this;
17 | };
18 |
19 | /**
20 | * sets a default value for given property.
21 | * @param {T} x value to be set as default
22 | * @returns {this} same instance of class with property having a default value.
23 | */
24 | public default = (x: T): this => {
25 | this.__config.default = x;
26 | return this;
27 | };
28 |
29 | public get __required(): boolean {
30 | return Boolean(this.__config.required);
31 | }
32 |
33 | public get __default(): T | undefined {
34 | return this.__config.default;
35 | }
36 |
37 | abstract validate: ValidateFn;
38 | }
39 |
40 | export default SchemaType;
41 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Base/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { BaseError as Errors } from "./types/BaseError";
3 | export { BaseSchemaConfig as Config } from "./types/BaseSchema";
4 | export { ValidateFn, Options as ValidationOptions } from "./types/ValidateFn";
5 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Base/types/BaseError.ts:
--------------------------------------------------------------------------------
1 | interface BaseError {
2 | error: string | null;
3 | errorType: T;
4 | }
5 |
6 | export { BaseError };
7 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Base/types/BaseSchema.ts:
--------------------------------------------------------------------------------
1 | interface BaseSchemaConfig {
2 | required?: true;
3 | default?: T;
4 | }
5 |
6 | export { BaseSchemaConfig };
7 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Base/types/ValidateFn.ts:
--------------------------------------------------------------------------------
1 | import { firestore } from "firebase-admin";
2 | import { BaseError } from "./BaseError";
3 |
4 | type Options = {
5 | onlySupplied?: boolean;
6 | onlyKeys?: boolean;
7 | };
8 |
9 | type ValidateFn = (
10 | x: any,
11 | key: string,
12 | options?: Options
13 | ) => {
14 | value: T | Partial | firestore.Timestamp;
15 | valid: boolean;
16 | errors: BaseError[];
17 | };
18 |
19 | export { ValidateFn, Options };
20 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Boolean/checks/allChecks.ts:
--------------------------------------------------------------------------------
1 | export { typeCheck as type } from "./typeCheck";
2 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Boolean/checks/index.ts:
--------------------------------------------------------------------------------
1 | import { Errors as BaseErrors } from "../../Base";
2 | import { Errors, Config } from "..";
3 | import * as check from "./allChecks";
4 |
5 | const checkRunner = (
6 | x: any,
7 | config: Config,
8 | key: string
9 | ): {
10 | value: boolean;
11 | errors: BaseErrors[];
12 | } => {
13 | const { strict } = config;
14 | const errors: BaseErrors[] = [];
15 |
16 | let val = x;
17 | if (!strict) val = Boolean(val);
18 |
19 | // type check
20 | if (!check.type(val)) {
21 | errors.push({
22 | error: `${key} must be a boolean`,
23 | errorType: Errors.Type,
24 | });
25 | return { value: val, errors };
26 | }
27 |
28 | return { value: val, errors };
29 | };
30 |
31 | export default checkRunner;
32 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Boolean/checks/typeCheck.ts:
--------------------------------------------------------------------------------
1 | const typeCheck = (x: any): x is boolean => {
2 | if (typeof x === "boolean") {
3 | return true;
4 | }
5 | return false;
6 | };
7 |
8 | export { typeCheck };
9 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Boolean/class.ts:
--------------------------------------------------------------------------------
1 | import BaseSchema, { Errors as BaseErrors } from "../Base";
2 | import { Config, Errors } from ".";
3 | import checkRunner from "./checks";
4 |
5 | class BooleanSchema extends BaseSchema {
6 | protected override __config: Config = {};
7 |
8 | public strict(): this {
9 | this.__config.strict = true;
10 | return this;
11 | }
12 |
13 | validate = (
14 | x: any,
15 | key: string = "value"
16 | ): {
17 | value: boolean;
18 | valid: boolean;
19 | errors: BaseErrors[];
20 | } => {
21 | const { value, errors } = checkRunner(x, this.__config, key);
22 |
23 | return {
24 | valid: Boolean(errors.length === 0),
25 | value,
26 | errors,
27 | };
28 | };
29 | }
30 |
31 | export default BooleanSchema;
32 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Boolean/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { BooleanSchemaErrors as Errors } from "./types/BooleanError";
3 | export { BooleanSchemaConfig as Config } from "./types/BooleanSchema";
4 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Boolean/types/BooleanError.ts:
--------------------------------------------------------------------------------
1 | enum BooleanSchemaErrors {
2 | None = "None",
3 | Type = "Boolean/Type",
4 | }
5 |
6 | export { BooleanSchemaErrors };
7 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Boolean/types/BooleanSchema.ts:
--------------------------------------------------------------------------------
1 | import { Config as BaseConfig } from "../../Base";
2 |
3 | interface BooleanSchemaConfig extends BaseConfig {
4 | strict?: true;
5 | }
6 |
7 | export { BooleanSchemaConfig };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/checks/allChecks.ts:
--------------------------------------------------------------------------------
1 | export { typeCheck as type } from "./typeCheck";
2 | export {
3 | greaterThanCheck as greaterThan,
4 | greaterThanOrEqualToCheck as greaterThanOrEqual,
5 | lessThanCheck as lessThan,
6 | lessThanOrEqualToCheck as lessThanOrEqual,
7 | equalToCheck as equalTo,
8 | } from "./valueCheck";
9 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/checks/index.ts:
--------------------------------------------------------------------------------
1 | import { Errors as BaseErrors } from "../../Base";
2 | import { Errors, Config } from "..";
3 | import * as check from "./allChecks";
4 |
5 | const checkRunner = (
6 | x: any,
7 | config: Config,
8 | key: string
9 | ): { value: Date; errors: BaseErrors[] } => {
10 | const {
11 | greaterThan,
12 | greaterThanOrEqualTo,
13 | lessThan,
14 | lessThanOrEqualTo,
15 | equalTo,
16 | } = config;
17 | const errors: BaseErrors[] = [];
18 |
19 | // type check
20 | if (!check.type(x)) {
21 | errors.push({
22 | error: `${key} must be a Date object`,
23 | errorType: Errors.Type,
24 | });
25 | return { value: x, errors };
26 | }
27 |
28 | // value check
29 | if (greaterThan && !check.greaterThan(x, greaterThan)) {
30 | errors.push({
31 | error: `${key} must be greater than ${greaterThan}`,
32 | errorType: Errors.Value,
33 | });
34 | }
35 | if (
36 | greaterThanOrEqualTo &&
37 | !check.greaterThanOrEqual(x, greaterThanOrEqualTo)
38 | ) {
39 | errors.push({
40 | error: `${key} must be greater than or equal to ${greaterThanOrEqualTo}`,
41 | errorType: Errors.Value,
42 | });
43 | }
44 | if (lessThan && !check.lessThan(x, lessThan)) {
45 | errors.push({
46 | error: `${key} must be less than ${lessThan}`,
47 | errorType: Errors.Value,
48 | });
49 | }
50 | if (lessThanOrEqualTo && !check.lessThanOrEqual(x, lessThanOrEqualTo)) {
51 | errors.push({
52 | error: `${key} must be less than or equal to ${lessThanOrEqualTo}`,
53 | errorType: Errors.Value,
54 | });
55 | }
56 | if (equalTo && !check.equalTo(x, equalTo)) {
57 | errors.push({
58 | error: `${key} must be equal to ${equalTo}`,
59 | errorType: Errors.Value,
60 | });
61 | }
62 |
63 | return { value: x, errors };
64 | };
65 |
66 | export default checkRunner;
67 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/checks/typeCheck.ts:
--------------------------------------------------------------------------------
1 | const typeCheck = (x: any): x is Date => {
2 | if (x instanceof Date) return true;
3 |
4 | return false;
5 | };
6 |
7 | export { typeCheck };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/checks/valueCheck.ts:
--------------------------------------------------------------------------------
1 | const greaterThanCheck = (x: Date, threshold: Date): boolean => {
2 | if (x.getTime() > threshold.getTime()) return true;
3 |
4 | return false;
5 | };
6 |
7 | const greaterThanOrEqualToCheck = (x: Date, threshold: Date): boolean => {
8 | if (x.getTime() >= threshold.getTime()) return true;
9 |
10 | return false;
11 | };
12 |
13 | const lessThanCheck = (x: Date, threshold: Date): boolean => {
14 | if (x.getTime() < threshold.getTime()) return true;
15 |
16 | return false;
17 | };
18 |
19 | const lessThanOrEqualToCheck = (x: Date, threshold: Date): boolean => {
20 | if (x.getTime() <= threshold.getTime()) return true;
21 |
22 | return false;
23 | };
24 |
25 | const equalToCheck = (x: Date, threshold: Date): boolean => {
26 | if (x.getTime() === threshold.getTime()) return true;
27 |
28 | return false;
29 | };
30 |
31 | export {
32 | greaterThanCheck,
33 | greaterThanOrEqualToCheck,
34 | lessThanCheck,
35 | lessThanOrEqualToCheck,
36 | equalToCheck,
37 | };
38 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/class.ts:
--------------------------------------------------------------------------------
1 | import BaseSchema, { Errors as BaseErrors } from "../Base";
2 | import { Errors, Config } from ".";
3 | import checkRunner from "./checks";
4 |
5 | class DateSchema extends BaseSchema {
6 | protected override __config: Config = {};
7 |
8 | public greaterThan = (x: Date): this => {
9 | this.__config.greaterThan = x;
10 | return this;
11 | };
12 |
13 | public greaterThanOrEqualTo = (x: Date): this => {
14 | this.__config.greaterThanOrEqualTo = x;
15 | return this;
16 | };
17 |
18 | public lessThan = (x: Date): this => {
19 | this.__config.lessThan = x;
20 | return this;
21 | };
22 |
23 | public lessThanOrEqualTo = (x: Date): this => {
24 | this.__config.lessThanOrEqualTo = x;
25 | return this;
26 | };
27 |
28 | public equalTo = (x: Date): this => {
29 | this.__config.equalTo = x;
30 | return this;
31 | };
32 |
33 | validate = (
34 | x: any,
35 | key: string = "value"
36 | ): {
37 | value: Date;
38 | valid: boolean;
39 | errors: BaseErrors[];
40 | } => {
41 | const { value, errors } = checkRunner(x, this.__config, key);
42 |
43 | return {
44 | valid: Boolean(errors.length === 0),
45 | value,
46 | errors,
47 | };
48 | };
49 | }
50 |
51 | export default DateSchema;
52 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { DateSchemaErrors as Errors } from "./types/DateError";
3 | export { DateSchemaConfig as Config } from "./types/DateSchema";
4 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/types/DateError.ts:
--------------------------------------------------------------------------------
1 | enum DateSchemaErrors {
2 | None = "None",
3 | Type = "Date/Type",
4 | Value = "Date/InvalidValue",
5 | }
6 |
7 | export { DateSchemaErrors };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Date/types/DateSchema.ts:
--------------------------------------------------------------------------------
1 | import { Config as BaseConfig } from "../../Base";
2 |
3 | interface DateSchemaConfig extends BaseConfig {
4 | greaterThan?: Date;
5 | greaterThanOrEqualTo?: Date;
6 | lessThan?: Date;
7 | lessThanOrEqualTo?: Date;
8 | equalTo?: Date;
9 | }
10 |
11 | export { DateSchemaConfig };
12 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/checks/allChecks.ts:
--------------------------------------------------------------------------------
1 | export { typeCheck as type } from "./typeCheck";
2 | export { integerCheck as integer } from "./integerCheck";
3 | export { minCheck as min, maxCheck as max } from "./valueCheck";
4 | export { enumCheck as enum } from "./enumCheck";
5 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/checks/enumCheck.ts:
--------------------------------------------------------------------------------
1 | const enumCheck = (x: number, allowedVals: number[]): boolean => {
2 | if (allowedVals.includes(x)) return true;
3 |
4 | return false;
5 | };
6 |
7 | export { enumCheck };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/checks/index.ts:
--------------------------------------------------------------------------------
1 | import { Errors as BaseErrors } from "../../Base";
2 | import { Errors, Config } from "..";
3 | import * as check from "./allChecks";
4 |
5 | const checkRunner = (
6 | x: any,
7 | config: Config,
8 | key: string
9 | ): { value: number; errors: BaseErrors[] } => {
10 | const { integer, min, max, enum: cEnum } = config;
11 | const errors: BaseErrors[] = [];
12 |
13 | // type check
14 | if (!check.type(x)) {
15 | errors.push({
16 | error: `${key} must be a number`,
17 | errorType: Errors.Type,
18 | });
19 | return { value: x, errors };
20 | }
21 |
22 | // integer check
23 | if (integer && !check.integer(x)) {
24 | errors.push({
25 | error: `${key} must be an integer`,
26 | errorType: Errors.Integer,
27 | });
28 | }
29 |
30 | // value check
31 | if (min && !check.min(x, min)) {
32 | errors.push({
33 | error: `minimum allowed value for ${key} is ${min}`,
34 | errorType: Errors.Value,
35 | });
36 | }
37 | if (max && !check.max(x, max)) {
38 | errors.push({
39 | error: `maximum allowed value for ${key} is ${max}`,
40 | errorType: Errors.Value,
41 | });
42 | }
43 |
44 | // enum check
45 | if (cEnum && cEnum.length > 0 && !check.enum(x, cEnum)) {
46 | errors.push({
47 | error: `${key} is not assignable to specified enum`,
48 | errorType: Errors.Enum,
49 | });
50 | }
51 |
52 | return { value: x, errors };
53 | };
54 |
55 | export default checkRunner;
56 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/checks/integerCheck.ts:
--------------------------------------------------------------------------------
1 | const integerCheck = (x: number) => {
2 | if (Number.isInteger(x)) return true;
3 |
4 | return false;
5 | };
6 |
7 | export { integerCheck };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/checks/typeCheck.ts:
--------------------------------------------------------------------------------
1 | const typeCheck = (x: any): x is number => {
2 | if (typeof x === "number") return true;
3 |
4 | return false;
5 | };
6 |
7 | export { typeCheck };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/checks/valueCheck.ts:
--------------------------------------------------------------------------------
1 | const minCheck = (x: number, val: number): boolean => {
2 | if (x >= val) return true;
3 |
4 | return false;
5 | };
6 |
7 | const maxCheck = (x: number, val: number): boolean => {
8 | if (x <= val) return true;
9 |
10 | return false;
11 | };
12 |
13 | export { minCheck, maxCheck };
14 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/class.ts:
--------------------------------------------------------------------------------
1 | import BaseSchema, { Errors as BaseErrors } from "../Base";
2 | import { Errors, Config } from ".";
3 | import checkRunner from "./checks";
4 |
5 | class NumberSchema extends BaseSchema {
6 | protected override __config: Config = {};
7 |
8 | public integer = (): this => {
9 | this.__config.integer = true;
10 | return this;
11 | };
12 |
13 | public min = (x: number): this => {
14 | this.__config.min = x;
15 | return this;
16 | };
17 |
18 | public max = (x: number): this => {
19 | this.__config.max = x;
20 | return this;
21 | };
22 |
23 | public enum = (x: number[]): this => {
24 | this.__config.enum = x;
25 | return this;
26 | };
27 |
28 | validate = (
29 | x: any,
30 | key: string = "value"
31 | ): {
32 | value: number;
33 | valid: boolean;
34 | errors: BaseErrors[];
35 | } => {
36 | const { value, errors } = checkRunner(x, this.__config, key);
37 |
38 | return {
39 | valid: Boolean(errors.length === 0),
40 | value,
41 | errors,
42 | };
43 | };
44 | }
45 |
46 | export default NumberSchema;
47 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { NumberSchemaErrors as Errors } from "./types/NumberError";
3 | export { NumberSchemaConfig as Config } from "./types/NumberSchema";
4 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/types/NumberError.ts:
--------------------------------------------------------------------------------
1 | enum NumberSchemaErrors {
2 | None = "None",
3 | Type = "Number/Type",
4 | Integer = "Number/Integer",
5 | Value = "Number/Value",
6 | Enum = "Number/InvalidValue",
7 | }
8 |
9 | export { NumberSchemaErrors };
10 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Number/types/NumberSchema.ts:
--------------------------------------------------------------------------------
1 | import { Config as BaseConfig } from "../../Base";
2 |
3 | interface NumberSchemaConfig extends BaseConfig {
4 | integer?: true;
5 | min?: number;
6 | max?: number;
7 | enum?: number[];
8 | }
9 |
10 | export { NumberSchemaConfig };
11 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/checks/allChecks.ts:
--------------------------------------------------------------------------------
1 | export { typeCheck as type } from "./typeCheck";
2 | export { keyCheck as key } from "./keyCheck";
3 | export { patternCheck as pattern } from "./patternCheck";
4 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/checks/index.ts:
--------------------------------------------------------------------------------
1 | import SchemaType, {
2 | Errors as BaseErrors,
3 | ValidationOptions,
4 | } from "../../Base";
5 | import { Errors, Config, KeyValueStore } from "..";
6 | import { CheckReturn } from "./type";
7 | import * as check from "./allChecks";
8 |
9 | const checksRunner = (
10 | x: any,
11 | config: Config,
12 | key: string,
13 | options: NonNullable
14 | ): {
15 | value: Partial;
16 | errors: BaseErrors[];
17 | } => {
18 | const { keys: configKeys, pattern: configPattern } = config;
19 |
20 | if (!check.type(x))
21 | return {
22 | value: x,
23 | errors: [
24 | {
25 | error: `${key} must contain key value pairs`,
26 | errorType: Errors.Type,
27 | },
28 | ],
29 | };
30 |
31 | const transformed: Partial = {};
32 | const errors: BaseErrors[] = [];
33 |
34 | const requiredKeys = new Map();
35 | const defaultKeys = new Map();
36 |
37 | if (
38 | configKeys &&
39 | Object.keys(configKeys).length > 0 &&
40 | !options.onlySupplied
41 | ) {
42 | Object.keys(configKeys).forEach((k: keyof T) => {
43 | const schema = configKeys[k];
44 |
45 | if (
46 | schema &&
47 | schema.__required &&
48 | typeof schema.__default === "undefined"
49 | )
50 | requiredKeys.set(k, schema);
51 | if (schema && typeof schema.__default !== "undefined")
52 | defaultKeys.set(k, schema.__default);
53 | });
54 | }
55 |
56 | Object.keys(x).forEach((k: string) => {
57 | const keyResolution: CheckReturn = {
58 | resolved: false,
59 | valid: false,
60 | value: null,
61 | errors: [],
62 | };
63 |
64 | if (configKeys && Object.keys(configKeys).length > 0) {
65 | const keyCheckResolution = check.key(k, x[k]!, configKeys, options);
66 | Object.assign(keyResolution, keyCheckResolution);
67 | }
68 |
69 | if (
70 | !options.onlyKeys &&
71 | !keyResolution.resolved &&
72 | configPattern &&
73 | configPattern.length === 2
74 | ) {
75 | const patternCheckResolution = check.pattern(k, x[k]!, configPattern);
76 | Object.assign(keyResolution, patternCheckResolution);
77 | }
78 |
79 | if (keyResolution.resolved) {
80 | if (!options.onlySupplied) {
81 | if (requiredKeys.has(k)) requiredKeys.delete(k);
82 | if (defaultKeys.has(k)) defaultKeys.delete(k);
83 | }
84 | if (!keyResolution.valid) errors.push(...keyResolution.errors);
85 |
86 | // @ts-ignore
87 | transformed[k] = keyResolution.value;
88 | } else {
89 | errors.push({
90 | error: `${k} is not allowed`,
91 | errorType: Errors.Keys,
92 | });
93 | }
94 | });
95 |
96 | if (!options.onlySupplied) {
97 | defaultKeys.forEach((v, k) => {
98 | transformed[k] = v;
99 | });
100 |
101 | requiredKeys.forEach((_, k) => {
102 | errors.push({
103 | error: `${k} is required`,
104 | errorType: Errors.Keys,
105 | });
106 | });
107 | }
108 |
109 | return {
110 | value: transformed,
111 | errors,
112 | };
113 | };
114 |
115 | export default checksRunner;
116 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/checks/keyCheck.ts:
--------------------------------------------------------------------------------
1 | import { Keys } from "../types/ObjectSchema";
2 | import { KeyValueStore } from "../types/KeyValue";
3 | import { CheckReturn } from "./type";
4 | import { ValidationOptions } from "../../Base";
5 |
6 | const keyCheck = (
7 | key: string,
8 | value: KeyValueStore[string],
9 | configKeys: Keys,
10 | options: ValidationOptions
11 | ): CheckReturn => {
12 | const schema = configKeys[key];
13 | if (!schema) {
14 | return {
15 | resolved: false,
16 | valid: false,
17 | value,
18 | errors: [],
19 | };
20 | }
21 |
22 | const {
23 | valid,
24 | value: modValue,
25 | errors,
26 | } = schema.validate(value, key.toString(), options);
27 |
28 | return {
29 | resolved: true,
30 | valid,
31 | value: modValue,
32 | errors,
33 | };
34 | };
35 |
36 | export { keyCheck };
37 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/checks/patternCheck.ts:
--------------------------------------------------------------------------------
1 | import { KeyValueStore } from "../types/KeyValue";
2 | import { Pattern } from "../types/ObjectSchema";
3 | import { CheckReturn } from "./type";
4 |
5 | const patternCheck = (
6 | key: string,
7 | value: KeyValueStore[string],
8 | pattern: Pattern
9 | ): CheckReturn => {
10 | const [keyPattern, valuePattern] = pattern;
11 |
12 | const { valid: keyValid } = keyPattern.validate(key, key.toString());
13 | if (keyValid) {
14 | const valueValidation = valuePattern.validate(value, key.toString());
15 |
16 | return {
17 | resolved: true,
18 | ...valueValidation,
19 | };
20 | }
21 |
22 | return {
23 | resolved: false,
24 | valid: false,
25 | value: null,
26 | errors: [],
27 | };
28 | };
29 |
30 | export { patternCheck };
31 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/checks/type.ts:
--------------------------------------------------------------------------------
1 | import { Errors as BaseErrors } from "../../Base";
2 | import { KeyValueStore } from "../types/KeyValue";
3 |
4 | type CheckReturn = {
5 | resolved: boolean;
6 | valid: boolean;
7 | value: KeyValueStore[string];
8 | errors: BaseErrors[];
9 | };
10 |
11 | export { CheckReturn };
12 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/checks/typeCheck.ts:
--------------------------------------------------------------------------------
1 | import { KeyValueStore } from "../types/KeyValue";
2 |
3 | const typeCheck = (x: any): x is KeyValueStore => {
4 | if (x !== null && typeof x === "object" && !Array.isArray(x)) return true;
5 |
6 | return false;
7 | };
8 |
9 | export { typeCheck };
10 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/class.ts:
--------------------------------------------------------------------------------
1 | import SchemaType, { Errors as BaseErrors, ValidationOptions } from "../Base";
2 | import { KeyValueStore, Config, Keys, Pattern } from ".";
3 | import checksRunner from "./checks";
4 |
5 | class ObjectSchema extends SchemaType {
6 | protected override __config: Config = {};
7 |
8 | get __keys() {
9 | const configKeys = this.__config.keys;
10 | if (configKeys) return Object.keys(configKeys);
11 | return [];
12 | }
13 |
14 | public keys = (x: Keys): this => {
15 | this.__config.keys = x;
16 | return this;
17 | };
18 |
19 | public pattern = (x: Pattern): this => {
20 | if (x.length === 2) {
21 | this.__config.pattern = x;
22 | }
23 | return this;
24 | };
25 |
26 | validate = (
27 | x: any,
28 | key: string = "value",
29 | options: ValidationOptions = {
30 | onlySupplied: false,
31 | onlyKeys: false,
32 | }
33 | ): {
34 | value: Partial;
35 | valid: boolean;
36 | errors: BaseErrors[];
37 | } => {
38 | const { value, errors } = checksRunner(x, this.__config, key, options);
39 |
40 | return {
41 | value,
42 | valid: Boolean(errors.length === 0),
43 | errors,
44 | };
45 | };
46 | }
47 |
48 | export default ObjectSchema;
49 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { ObjectSchemaErrors as Errors } from "./types/ObjectError";
3 | export {
4 | ObjectSchemaConfig as Config,
5 | Keys,
6 | Pattern,
7 | } from "./types/ObjectSchema";
8 | export { KeyValueStore } from "./types/KeyValue";
9 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/types/KeyValue.ts:
--------------------------------------------------------------------------------
1 | type ValidValueTypes =
2 | | string
3 | | number
4 | | boolean
5 | | Date
6 | | null
7 | | KeyValueStore
8 | | ValidValueTypes[];
9 |
10 | type KeyValueStore = {
11 | [k in keyof T]: ValidValueTypes;
12 | };
13 |
14 | export { KeyValueStore };
15 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/types/ObjectError.ts:
--------------------------------------------------------------------------------
1 | enum ObjectSchemaErrors {
2 | None = "None",
3 | Type = "Object/Type",
4 | Keys = "Object/InvalidKeys",
5 | Pattern = "Object/InvalidPattern",
6 | }
7 |
8 | export { ObjectSchemaErrors };
9 |
--------------------------------------------------------------------------------
/src/SchemaTypes/Object/types/ObjectSchema.ts:
--------------------------------------------------------------------------------
1 | import BaseSchema, { Config } from "../../Base";
2 | import StringSchema from "../../String";
3 |
4 | // TODO:- convert basic type to custom type
5 | type Keys = {
6 | [k in keyof T]: BaseSchema;
7 | };
8 |
9 | type Pattern = [StringSchema, BaseSchema];
10 |
11 | interface ObjectSchemaConfig extends Config {
12 | keys?: Keys;
13 | pattern?: Pattern;
14 | }
15 |
16 | export { ObjectSchemaConfig, Keys, Pattern };
17 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/checks/allChecks.ts:
--------------------------------------------------------------------------------
1 | export { typeCheck as type } from "./typeCheck";
2 | export {
3 | minlengthCheck as minLength,
4 | maxlengthCheck as maxLength,
5 | } from "./lengthCheck";
6 | export {
7 | lowercaseCheck as lowercase,
8 | uppercaseCheck as uppercase,
9 | } from "./caseCheck";
10 | export { formatCheck as format } from "./formatCheck";
11 | export { enumCheck as enum } from "./enumCheck";
12 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/checks/caseCheck.ts:
--------------------------------------------------------------------------------
1 | const uppercaseCheck = (x: string): boolean => {
2 | if (x === x.toUpperCase()) return true;
3 |
4 | return false;
5 | };
6 |
7 | const lowercaseCheck = (x: string): boolean => {
8 | if (x === x.toLowerCase()) return true;
9 |
10 | return false;
11 | };
12 |
13 | export { lowercaseCheck, uppercaseCheck };
14 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/checks/enumCheck.ts:
--------------------------------------------------------------------------------
1 | const enumCheck = (x: string, allowedValues: string[]): boolean => {
2 | if (allowedValues.includes(x)) return true;
3 |
4 | return false;
5 | };
6 |
7 | export { enumCheck };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/checks/formatCheck.ts:
--------------------------------------------------------------------------------
1 | const rEmail =
2 | /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
3 |
4 | const formatCheck = (x: string, regex: RegExp | "email"): boolean => {
5 | const exp = regex === "email" ? rEmail : regex;
6 |
7 | if (exp.test(x) === true) {
8 | return true;
9 | }
10 |
11 | return false;
12 | };
13 |
14 | export { formatCheck };
15 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/checks/index.ts:
--------------------------------------------------------------------------------
1 | import { Errors as BaseErrors } from "../../Base";
2 | import { Errors, Config } from "..";
3 | import * as check from "./allChecks";
4 |
5 | const checkRunner = (
6 | x: any,
7 | config: Config,
8 | key: string
9 | ): { value: string; errors: BaseErrors[] } => {
10 | const {
11 | trim,
12 | minLength,
13 | maxLength,
14 | uppercase,
15 | lowercase,
16 | regex,
17 | enum: cEnum,
18 | } = config;
19 | const errors: BaseErrors[] = [];
20 |
21 | // type check
22 | if (!check.type(x)) {
23 | errors.push({
24 | error: `${key} must be a string`,
25 | errorType: Errors.Type,
26 | });
27 | return { value: x, errors };
28 | }
29 |
30 | // modifications
31 | let str = x;
32 | if (trim) str = str.trim();
33 | if (uppercase) str = str.toUpperCase();
34 | if (lowercase) str = str.toLowerCase();
35 |
36 | // length checks
37 | if (minLength && !check.minLength(str, minLength)) {
38 | errors.push({
39 | error: `minimum allowed length for ${key} is ${minLength}`,
40 | errorType: Errors.Length,
41 | });
42 | }
43 | if (maxLength && !check.maxLength(str, maxLength)) {
44 | errors.push({
45 | error: `maximum allowed length for ${key} is ${maxLength}`,
46 | errorType: Errors.Length,
47 | });
48 | }
49 |
50 | // case checks
51 | if (uppercase && !check.uppercase(str)) {
52 | errors.push({
53 | error: `${key} must be all uppercase`,
54 | errorType: Errors.Case,
55 | });
56 | }
57 | if (lowercase && !check.lowercase(str)) {
58 | errors.push({
59 | error: `${key} must be all lowercase`,
60 | errorType: Errors.Case,
61 | });
62 | }
63 |
64 | // format check
65 | if (regex && !check.format(str, regex)) {
66 | const errorErrors = `${key} must be ${
67 | regex === "email" ? "a valid email" : "of valid format"
68 | }`;
69 | errors.push({
70 | error: errorErrors,
71 | errorType: Errors.Format,
72 | });
73 | }
74 |
75 | // enum check
76 | if (cEnum && !check.enum(str, cEnum)) {
77 | errors.push({
78 | error: `${key} is not assignable to specified enum`,
79 | errorType: Errors.Enum,
80 | });
81 | }
82 |
83 | return { value: str, errors };
84 | };
85 |
86 | export default checkRunner;
87 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/checks/lengthCheck.ts:
--------------------------------------------------------------------------------
1 | const minlengthCheck = (x: string, len: number): boolean => {
2 | if (x.length >= len) return true;
3 |
4 | return false;
5 | };
6 |
7 | const maxlengthCheck = (x: string, len: number): boolean => {
8 | if (x.length <= len) return true;
9 |
10 | return false;
11 | };
12 |
13 | export { minlengthCheck, maxlengthCheck };
14 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/checks/typeCheck.ts:
--------------------------------------------------------------------------------
1 | const typeCheck = (x: any): x is string => {
2 | if (typeof x === "string") return true;
3 |
4 | return false;
5 | };
6 |
7 | export { typeCheck };
8 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/class.ts:
--------------------------------------------------------------------------------
1 | import BaseSchema, { Errors as BaseErrors } from "../Base";
2 | import { Config, Errors } from ".";
3 | import checkRunner from "./checks";
4 |
5 | class StringSchema extends BaseSchema {
6 | protected override __config: Config = {};
7 |
8 | public trim = (): this => {
9 | this.__config.trim = true;
10 | return this;
11 | };
12 |
13 | public minLength = (x: number): this => {
14 | this.__config.minLength = x;
15 | return this;
16 | };
17 |
18 | public maxLength = (x: number): this => {
19 | this.__config.maxLength = x;
20 | return this;
21 | };
22 |
23 | public lowercase = (): this => {
24 | this.__config.lowercase = true;
25 | return this;
26 | };
27 |
28 | public uppercase = (): this => {
29 | this.__config.uppercase = true;
30 | return this;
31 | };
32 |
33 | public email = (): this => {
34 | this.__config.regex = "email";
35 | return this;
36 | };
37 |
38 | public regex = (x: RegExp): this => {
39 | this.__config.regex = x;
40 | return this;
41 | };
42 |
43 | public enum = (x: string[]): this => {
44 | if (x.length > 0) {
45 | this.__config.enum = x;
46 | }
47 | return this;
48 | };
49 |
50 | validate = (
51 | x: any,
52 | key: string = "value"
53 | ): { value: string; valid: boolean; errors: BaseErrors[] } => {
54 | const { value, errors } = checkRunner(x, this.__config, key);
55 |
56 | return {
57 | valid: Boolean(errors.length === 0),
58 | value,
59 | errors,
60 | };
61 | };
62 | }
63 |
64 | export default StringSchema;
65 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./class";
2 | export { StringSchemaErrors as Errors } from "./types/StringError";
3 | export { StringSchemaConfig as Config } from "./types/StringSchema";
4 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/types/StringError.ts:
--------------------------------------------------------------------------------
1 | enum StringSchemaErrors {
2 | None = "None",
3 | Type = "String/Type",
4 | Length = "String/Length",
5 | Case = "String/Case",
6 | Format = "String/Format",
7 | Enum = "String/InvalidValue",
8 | }
9 |
10 | export { StringSchemaErrors };
11 |
--------------------------------------------------------------------------------
/src/SchemaTypes/String/types/StringSchema.ts:
--------------------------------------------------------------------------------
1 | import * as BaseSchema from "../../Base";
2 |
3 | interface StringSchemaConfig extends BaseSchema.Config {
4 | trim?: true;
5 | minLength?: number;
6 | maxLength?: number;
7 | lowercase?: true;
8 | uppercase?: true;
9 | regex?: RegExp | "email";
10 | enum?: string[];
11 | }
12 |
13 | export { StringSchemaConfig };
14 |
--------------------------------------------------------------------------------
/src/SchemaTypes/index.ts:
--------------------------------------------------------------------------------
1 | import * as BaseSchemaNs from "./Base";
2 | import * as StringSchemaNs from "./String";
3 | import * as NumberSchemaNs from "./Number";
4 | import * as BooleanSchemaNs from "./Boolean";
5 | import * as DateSchemaNs from "./Date";
6 | import * as ObjectSchemaNs from "./Object";
7 |
8 | export type KeyValueStore = ObjectSchemaNs.KeyValueStore;
9 |
10 | export class StringSchema extends StringSchemaNs.default {}
11 | export class NumberSchema extends NumberSchemaNs.default {}
12 | export class BooleanSchema extends BooleanSchemaNs.default {}
13 | export class DateSchema extends DateSchemaNs.default {}
14 | export class ObjectSchema<
15 | T extends KeyValueStore = any
16 | > extends ObjectSchemaNs.default {}
17 |
18 | export const string = () => new StringSchema();
19 | export const number = () => new NumberSchema();
20 | export const boolean = () => new BooleanSchema();
21 | export const date = () => new DateSchema();
22 | export const object = () =>
23 | new ObjectSchema();
24 |
25 | export {
26 | BaseSchemaNs,
27 | StringSchemaNs,
28 | NumberSchemaNs,
29 | BooleanSchemaNs,
30 | DateSchemaNs,
31 | ObjectSchemaNs,
32 | };
33 |
--------------------------------------------------------------------------------
/src/__tests__/Connection/class.test.ts:
--------------------------------------------------------------------------------
1 | import admin from "firebase-admin";
2 | import Connection from "../../Connection/class";
3 | import * as SchemaType from "../../SchemaTypes";
4 |
5 | let firestore: FirebaseFirestore.Firestore;
6 |
7 | beforeAll(() => {
8 | process.env["FIRESTORE_EMULATOR_HOST"] = "localhost:8080";
9 |
10 | admin.initializeApp({
11 | projectId: "demo-firefly",
12 | });
13 |
14 | firestore = admin.firestore();
15 | });
16 |
17 | describe("Connects to local emulators", () => {
18 | it("connects to firestore emulator", async () => {
19 | expect.assertions(1);
20 |
21 | try {
22 | const connection = new Connection(firestore);
23 | const model = connection.model(
24 | "test",
25 | SchemaType.object<{
26 | hello: string;
27 | }>().keys({
28 | hello: SchemaType.string().required(),
29 | })
30 | );
31 |
32 | const doc = await model.create({});
33 |
34 | const data = await doc.data();
35 |
36 | expect(data).toEqual({
37 | hello: "world",
38 | });
39 | } catch (error) {
40 | const errorType = JSON.parse(error.message).type;
41 | expect(errorType).toBe("Model/SchemaValidation");
42 | }
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/src/__tests__/Firefly/class.test.ts:
--------------------------------------------------------------------------------
1 | import admin from "firebase-admin";
2 | import Connection from "../../Connection/class";
3 | import Firefly from "../../Firefly/class";
4 |
5 | let firestore: FirebaseFirestore.Firestore;
6 |
7 | beforeAll(() => {
8 | process.env["FIRESTORE_EMULATOR_HOST"] = "localhost:8080";
9 |
10 | admin.initializeApp({
11 | projectId: "demo-firefly",
12 | });
13 |
14 | firestore = admin.firestore();
15 | });
16 |
17 | describe("Test Firefly main class", () => {
18 | it("should create a Connection instance", () => {
19 | const firefly = new Firefly();
20 | const fireflyConn = firefly.createConnection(firestore);
21 | expect(fireflyConn).toBeDefined();
22 | expect(fireflyConn instanceof Connection).toBe(true);
23 | });
24 |
25 | it("should create a Connection instance with custom names", () => {
26 | const firefly = new Firefly();
27 | const fireflyConn = firefly.createConnection(firestore, "my-db");
28 | expect(fireflyConn).toBeDefined();
29 | expect(fireflyConn instanceof Connection).toBe(true);
30 | const dbConn = firefly.getConnection("my-db");
31 | expect(dbConn).toBeDefined();
32 | expect(dbConn instanceof Connection).toBe(true);
33 | });
34 |
35 | it("should throw an error, if passed a non-existent connection key", () => {
36 | try {
37 | const firefly = new Firefly();
38 | const fireflyConn = firefly.createConnection(firestore);
39 | expect(fireflyConn).toBeDefined();
40 | const dbConn = firefly.getConnection("bad-key");
41 | expect(dbConn).toBeUndefined();
42 | } catch (err) {
43 | expect(err.message).toBe(
44 | '{"type":"Firefly/Invalid","data":"Firefly Connection with key bad-key does not exist"}'
45 | );
46 | }
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/src/__tests__/Query/class.test.ts:
--------------------------------------------------------------------------------
1 | import admin from "firebase-admin";
2 | import Firefly from "../../Firefly/class";
3 | import Model from "../../Model/class";
4 | import * as SchemaType from "../../SchemaTypes";
5 |
6 | let firestore: FirebaseFirestore.Firestore;
7 |
8 | let firefly = new Firefly();
9 |
10 | let user: Model<{
11 | userName: string;
12 | isAdmin: boolean;
13 | sNO: number;
14 | }>;
15 |
16 | let userId: string;
17 |
18 | beforeAll(async () => {
19 | process.env["FIRESTORE_EMULATOR_HOST"] = "localhost:8080";
20 |
21 | admin.initializeApp({
22 | projectId: "demo-firefly",
23 | });
24 |
25 | firestore = admin.firestore();
26 |
27 | const fireflyConn = firefly.createConnection(firestore);
28 |
29 | const userSchema = SchemaType.object<{
30 | userName: string;
31 | isAdmin: boolean;
32 | sNO: number;
33 | }>().keys({
34 | userName: SchemaType.string().required(),
35 | isAdmin: SchemaType.boolean().default(false),
36 | sNO: SchemaType.number().required(),
37 | });
38 |
39 | user = fireflyConn.model("User", userSchema);
40 |
41 | const [userDocument] = await Promise.all([
42 | user.create({
43 | userName: "lorem",
44 | isAdmin: true,
45 | sNO: 1,
46 | }),
47 | user.create({
48 | userName: "ipsum",
49 | isAdmin: false,
50 | sNO: 2,
51 | }),
52 | user.create({
53 | userName: "dolor",
54 | sNO: 3,
55 | }),
56 | user.create({
57 | userName: "sit",
58 | isAdmin: true,
59 | sNO: 4,
60 | }),
61 | user.create({
62 | userName: "amet",
63 | isAdmin: true,
64 | sNO: 5,
65 | }),
66 | user.create({
67 | userName: "consectetur",
68 | isAdmin: false,
69 | sNO: 6,
70 | }),
71 | ]);
72 |
73 | userId = userDocument.id;
74 | }, 10 * 60 * 1000);
75 |
76 | describe("Tests for query", () => {
77 | it("find returns documents", async () => {
78 | const query = await user
79 | .find({
80 | userName: "lorem",
81 | })
82 | .exec();
83 |
84 | if (!query) {
85 | throw new Error("Document returned from query is empty");
86 | }
87 |
88 | expect(query).toBeDefined();
89 | expect(query.length).toBeGreaterThan(0);
90 |
91 | const doc = query[0];
92 |
93 | if (!doc) {
94 | throw new Error("Document returned from query is empty");
95 | }
96 |
97 | expect(doc).toBeDefined();
98 |
99 | expect(await doc.data()).toEqual({
100 | userName: "lorem",
101 | isAdmin: true,
102 | sNO: 1,
103 | });
104 | });
105 |
106 | it("findById returns a single document if passed a valid id", async () => {
107 | const doc = await user.findById(userId).exec();
108 |
109 | if (!doc) {
110 | throw new Error("Document returned from query is empty");
111 | }
112 |
113 | expect(doc).toBeDefined();
114 |
115 | expect(await doc.data()).toEqual({
116 | userName: "lorem",
117 | isAdmin: true,
118 | sNO: 1,
119 | });
120 |
121 | expect(doc.id).toEqual(userId);
122 | });
123 |
124 | it("findById returns null if passed an invalid id", async () => {
125 | try {
126 | const doc = await user.findById("invalidId").exec();
127 |
128 | const docData = await doc.data();
129 |
130 | expect(docData).toBeNull();
131 | } catch (err) {
132 | const errObj = JSON.parse(err.message);
133 | expect(errObj.type).toBe("Document/Invalid");
134 | }
135 | });
136 |
137 | it("findOne returns a single document if passed a valid condition", async () => {
138 | const doc = await user
139 | .findOne({
140 | userName: "lorem",
141 | })
142 | .exec();
143 |
144 | if (!doc) {
145 | throw new Error("Document returned from query is empty");
146 | }
147 |
148 | expect(doc).toBeDefined();
149 |
150 | expect(await doc.data()).toEqual({
151 | userName: "lorem",
152 | isAdmin: true,
153 | sNO: 1,
154 | });
155 | });
156 |
157 | it("findOne returns error if passed invalid condtions", async () => {
158 | try {
159 | const doc = await user
160 | .findOne({
161 | userName: "invalid",
162 | })
163 | .exec();
164 |
165 | const docData = await doc.data();
166 |
167 | expect(docData).toBeNull();
168 | } catch (err) {
169 | const errObj = JSON.parse(err.message);
170 | expect(errObj.type).toBe("SingleQuery/NotFound");
171 | }
172 | });
173 |
174 | it("limit returns only the specific no of documents", async () => {
175 | const query = await user
176 | .find({
177 | isAdmin: true,
178 | })
179 | .limit(1)
180 | .exec();
181 |
182 | if (!query) {
183 | throw new Error("Document returned from query is empty");
184 | }
185 |
186 | expect(query).toBeDefined();
187 | expect(query.length).toBe(1);
188 |
189 | const doc = query[0];
190 |
191 | if (!doc) {
192 | throw new Error("Document returned from query is empty");
193 | }
194 |
195 | expect(doc).toBeDefined();
196 |
197 | const docData = await doc.data();
198 |
199 | expect(docData.isAdmin).toBe(true);
200 | });
201 |
202 | it("orderBy +string arranges in ascending order", async () => {
203 | const query = await user
204 | .find({
205 | isAdmin: true,
206 | })
207 | .orderBy("+sNO")
208 | .exec();
209 |
210 | if (!query) {
211 | throw new Error("Document returned from query is empty");
212 | }
213 |
214 | expect(query).toBeDefined();
215 |
216 | const doc = query[0];
217 |
218 | if (!doc) {
219 | throw new Error("Document returned from query is empty");
220 | }
221 |
222 | expect(doc).toBeDefined();
223 |
224 | const docData = await doc.data();
225 |
226 | expect(docData.sNO).toBe(1);
227 |
228 | const lastDoc = query[query.length - 1];
229 |
230 | if (!lastDoc) {
231 | throw new Error("Document returned from query is empty");
232 | }
233 |
234 | const lastDocData = await lastDoc.data();
235 |
236 | expect(lastDocData.sNO).toBe(5);
237 | });
238 |
239 | it("orderBy -string arranges in descending order", async () => {
240 | const query = await user
241 | .find({
242 | isAdmin: true,
243 | })
244 | .orderBy("-sNO")
245 | .exec();
246 |
247 | if (!query) {
248 | throw new Error("Document returned from query is empty");
249 | }
250 |
251 | expect(query).toBeDefined();
252 |
253 | const doc = query[0];
254 |
255 | if (!doc) {
256 | throw new Error("Document returned from query is empty");
257 | }
258 |
259 | expect(doc).toBeDefined();
260 |
261 | const docData = await doc.data();
262 |
263 | expect(docData.sNO).toBe(5);
264 |
265 | const lastDoc = query[query.length - 1];
266 |
267 | if (!lastDoc) {
268 | throw new Error("Document returned from query is empty");
269 | }
270 |
271 | const lastDocData = await lastDoc.data();
272 |
273 | expect(lastDocData.sNO).toBe(1);
274 | });
275 |
276 | // ! Needs to be fixed
277 | it("select returns only the selected fields", async () => {
278 | const query = await user
279 | .find({
280 | userName: "lorem",
281 | })
282 | .select("userName")
283 | .exec();
284 |
285 | if (!query) {
286 | throw new Error("Document returned from query is empty");
287 | }
288 |
289 | expect(query).toBeDefined();
290 |
291 | const doc = query[0];
292 |
293 | if (!doc) {
294 | throw new Error("Document returned from query is empty");
295 | }
296 |
297 | expect(doc).toBeDefined();
298 |
299 | const docData = await doc.data();
300 |
301 | expect(docData).toEqual({
302 | userName: "lorem",
303 | sNO: 1,
304 | isAdmin: true,
305 | });
306 | });
307 |
308 | it("findByIdAndUpdate updates the data", async () => {
309 | const doc = await user
310 | .findByIdAndUpdate(
311 | userId,
312 | {
313 | userName: "updated lorem",
314 | },
315 | {
316 | merge: true,
317 | }
318 | )
319 | .exec();
320 |
321 | if (!doc) {
322 | throw new Error("Document returned from query is empty");
323 | }
324 |
325 | expect(doc).toBeDefined();
326 |
327 | const docData = await doc.data();
328 |
329 | expect(docData).toEqual({
330 | userName: "updated lorem",
331 | sNO: 1,
332 | isAdmin: true,
333 | });
334 | });
335 |
336 | it("findByIdAndDelete removes the document", async () => {
337 | try {
338 | await user.findByIdAndDelete(userId).exec();
339 |
340 | const doc = await user.findById(userId).exec();
341 |
342 | if (!doc) {
343 | throw new Error("Document returned from query is empty");
344 | }
345 |
346 | expect(doc).toBeDefined();
347 |
348 | const docData = await doc.data();
349 |
350 | expect(docData).toBeUndefined();
351 | } catch (err) {
352 | const errObj = JSON.parse(err.message);
353 | expect(errObj.type).toBe("Document/Invalid");
354 | }
355 | });
356 |
357 | it("findOneAndDelete removes the document", async () => {
358 | try {
359 | await user
360 | .findOneAndDelete({
361 | userName: "ipsum",
362 | })
363 | .exec();
364 |
365 | const doc = await user
366 | .findOne({
367 | userName: "ipsum",
368 | })
369 | .exec();
370 |
371 | if (!doc) {
372 | throw new Error("Document returned from query is empty");
373 | }
374 |
375 | expect(doc).toBeDefined();
376 |
377 | const docData = await doc.data();
378 |
379 | expect(docData).toBeUndefined();
380 | } catch (err) {
381 | const errObj = JSON.parse(err.message);
382 | expect(errObj.type).toBe("SingleQuery/NotFound");
383 | }
384 | });
385 | });
386 |
--------------------------------------------------------------------------------
/src/__tests__/Query/operators.test.ts:
--------------------------------------------------------------------------------
1 | import admin from "firebase-admin";
2 | import Firefly from "../../Firefly/class";
3 | import Model from "../../Model/class";
4 | import * as SchemaTypes from "../../SchemaTypes";
5 |
6 | type User = {
7 | index: number;
8 | name: string;
9 | address: {
10 | houseNo: number;
11 | city: string;
12 | };
13 | };
14 |
15 | let User: Model;
16 |
17 | beforeAll(() => {
18 | process.env["FIRESTORE_EMULATOR_HOST"] = "localhost:8080";
19 |
20 | admin.initializeApp({
21 | projectId: "demo-firefly",
22 | });
23 |
24 | const firestore = admin.firestore();
25 |
26 | const connection = new Firefly().createConnection(firestore);
27 |
28 | const userSchema = SchemaTypes.object().keys({
29 | index: SchemaTypes.number().required(),
30 | name: SchemaTypes.string().required(),
31 | address: SchemaTypes.object()
32 | .keys({
33 | houseNo: SchemaTypes.number().required(),
34 | city: SchemaTypes.string().required(),
35 | })
36 | .required(),
37 | });
38 |
39 | User = connection.model("users", userSchema);
40 | });
41 |
42 | describe("Tests for operators in Query", () => {
43 | it("$gt operator", async () => {
44 | expect.assertions(2);
45 |
46 | const documents = await User.find({
47 | index: { $gt: 40 },
48 | }).exec();
49 |
50 | expect(documents.length).toBe(9);
51 |
52 | const docData = await documents[0]?.data();
53 |
54 | expect(docData!.index).toBe(41);
55 | });
56 |
57 | it("$gt + $lt operator", async () => {
58 | expect.assertions(2);
59 |
60 | const documents = await User.find({
61 | index: { $gt: 40, $lt: 45 },
62 | }).exec();
63 |
64 | expect(documents.length).toBe(4);
65 |
66 | const docData = await documents[0]?.data();
67 |
68 | expect(docData!.index).toBe(41);
69 | });
70 |
71 | it("$gte + $lte operator", async () => {
72 | expect.assertions(2);
73 |
74 | const documents = await User.find({
75 | index: { $gte: 40, $lte: 45 },
76 | }).exec();
77 |
78 | expect(documents.length).toBe(6);
79 |
80 | const docData = await documents[0]?.data();
81 |
82 | expect(docData!.index).toBe(40);
83 | });
84 |
85 | it("$eq operator", async () => {
86 | expect.assertions(2);
87 |
88 | const documents = await User.find({
89 | index: { $eq: 1 },
90 | }).exec();
91 |
92 | expect(documents.length).toBe(1);
93 |
94 | const docData = await documents[0]?.data();
95 |
96 | expect(docData!.index).toBe(1);
97 | });
98 |
99 | it("$neq operator", async () => {
100 | expect.assertions(1);
101 |
102 | const documents = await User.find({
103 | index: { $neq: 0 },
104 | }).exec();
105 |
106 | expect(documents.length).toBe(50);
107 | });
108 | });
109 |
--------------------------------------------------------------------------------
/src/__tests__/SchemaTypes/Boolean/class.test.ts:
--------------------------------------------------------------------------------
1 | import * as SchemaType from "../../../SchemaTypes/index";
2 |
3 | describe("test Boolean Schema Parser", () => {
4 | it("returns no error if passed a boolean", () => {
5 | type IObjectSchema = {
6 | isBoolean: boolean;
7 | };
8 |
9 | const objectSchema = SchemaType.object().keys({
10 | isBoolean: SchemaType.boolean(),
11 | });
12 |
13 | const testObj = {
14 | isBoolean: true,
15 | };
16 |
17 | const { valid, value, errors } = objectSchema.validate(testObj);
18 |
19 | expect(valid).toBe(true);
20 | expect(value).toEqual(testObj);
21 | expect(errors).toEqual([]);
22 | });
23 |
24 | it("returns an error if passed a non-boolean value and is in strict mode", () => {
25 | type IObjectSchema = {
26 | isBoolean: boolean;
27 | };
28 |
29 | const objectSchema = SchemaType.object().keys({
30 | isBoolean: SchemaType.boolean().strict(),
31 | });
32 |
33 | const testObj = {
34 | isBoolean: "true",
35 | };
36 |
37 | const { valid, value, errors } = objectSchema.validate(testObj);
38 |
39 | expect(valid).toBe(false);
40 | expect(value).toEqual(testObj);
41 | expect(errors).toEqual([
42 | {
43 | error: "isBoolean must be a boolean",
44 | errorType: "Boolean/Type",
45 | },
46 | ]);
47 | });
48 |
49 | it("converts the value to boolean if passed a non-boolean value and is not in strict mode", () => {
50 | type IObjectSchema = {
51 | isBoolean: boolean;
52 | };
53 |
54 | const objectSchema = SchemaType.object().keys({
55 | isBoolean: SchemaType.boolean(),
56 | });
57 |
58 | const testObj = {
59 | isBoolean: "true",
60 | };
61 |
62 | const { valid, value, errors } = objectSchema.validate(testObj);
63 |
64 | expect(valid).toBe(true);
65 | expect(value).toEqual({
66 | isBoolean: true,
67 | });
68 | expect(errors).toEqual([]);
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/src/__tests__/SchemaTypes/Date/class.test.ts:
--------------------------------------------------------------------------------
1 | import * as SchemaTypes from "../../../SchemaTypes/index";
2 |
3 | describe("Test DateSchema type", () => {
4 | it("returns an error if passed a value that is not an instance of Date", () => {
5 | type IObjectSchema = {
6 | date: Date;
7 | };
8 |
9 | const objectSchema = SchemaTypes.object().keys({
10 | date: SchemaTypes.date(),
11 | });
12 |
13 | const testObj = {
14 | date: "test",
15 | };
16 |
17 | const { valid, value, errors } = objectSchema.validate(testObj);
18 |
19 | expect(valid).toBe(false);
20 | expect(value).toEqual(testObj);
21 | expect(errors).toEqual([
22 | {
23 | error: "date must be a Date object",
24 | errorType: "Date/Type",
25 | },
26 | ]);
27 | });
28 |
29 | it("returns no error if value passed is an instance of Date and also formats the given value to firestore timestamp", () => {
30 | type IObjectSchema = {
31 | date: Date;
32 | };
33 |
34 | const objectSchema = SchemaTypes.object().keys({
35 | date: SchemaTypes.date(),
36 | });
37 |
38 | const testObj = {
39 | date: new Date("1995-12-17T03:24:00"),
40 | };
41 |
42 | const { valid, value, errors } = objectSchema.validate(testObj);
43 |
44 | expect(valid).toBe(true);
45 | expect(value).toEqual({
46 | date: new Date("1995-12-17T03:24:00"),
47 | });
48 | expect(errors).toEqual([]);
49 | });
50 |
51 | it("returns an error if date is less than the value specified in greaterThan threshold", () => {
52 | type IObjectSchema = {
53 | date: Date;
54 | };
55 |
56 | const objectSchema = SchemaTypes.object().keys({
57 | date: SchemaTypes.date().greaterThan(new Date("1995-12-17T03:24:00")),
58 | });
59 |
60 | const testObj = {
61 | date: new Date("1995-12-11T03:24:00"),
62 | };
63 |
64 | const { valid, value, errors } = objectSchema.validate(testObj);
65 |
66 | expect(valid).toBe(false);
67 | expect(value).toEqual(testObj);
68 | expect(errors).toEqual([
69 | {
70 | error:
71 | "date must be greater than Sun Dec 17 1995 03:24:00 GMT+0530 (India Standard Time)",
72 | errorType: "Date/InvalidValue",
73 | },
74 | ]);
75 | });
76 |
77 | it("returns an error if date is greater than the value specified in lessThan threshold", () => {
78 | type IObjectSchema = {
79 | date: Date;
80 | };
81 |
82 | const objectSchema = SchemaTypes.object().keys({
83 | date: SchemaTypes.date().lessThan(new Date("1995-12-17T03:24:00")),
84 | });
85 |
86 | const testObj = {
87 | date: new Date("1995-12-18T03:24:00"),
88 | };
89 |
90 | const { valid, value, errors } = objectSchema.validate(testObj);
91 |
92 | expect(valid).toBe(false);
93 | expect(value).toEqual(testObj);
94 | expect(errors).toEqual([
95 | {
96 | error:
97 | "date must be less than Sun Dec 17 1995 03:24:00 GMT+0530 (India Standard Time)",
98 | errorType: "Date/InvalidValue",
99 | },
100 | ]);
101 | });
102 |
103 | it("returns no error if date is equal to the threshold value provided in equalTo method", () => {
104 | type IObjectSchema = {
105 | date: Date;
106 | };
107 |
108 | const objectSchema = SchemaTypes.object().keys({
109 | date: SchemaTypes.date().equalTo(new Date("1995-12-17T03:24:00")),
110 | });
111 |
112 | const testObj = {
113 | date: new Date("1995-12-17T03:24:00"),
114 | };
115 |
116 | const { valid, value, errors } = objectSchema.validate(testObj);
117 |
118 | expect(valid).toBe(true);
119 | expect(value).toEqual(testObj);
120 | expect(errors).toEqual([]);
121 | });
122 |
123 | it("returns no error if the date is equal to or less than the threshold provided in lessThanOrEqualTo method", () => {
124 | type IObjectSchema = {
125 | date: Date;
126 | };
127 |
128 | const objectSchema = SchemaTypes.object().keys({
129 | date: SchemaTypes.date().lessThanOrEqualTo(
130 | new Date("1995-12-17T03:24:00")
131 | ),
132 | });
133 |
134 | const testObj = {
135 | date: new Date("1995-12-11T03:24:00"),
136 | };
137 |
138 | const { valid, value, errors } = objectSchema.validate(testObj);
139 |
140 | expect(valid).toBe(true);
141 | expect(value).toEqual(testObj);
142 | expect(errors).toEqual([]);
143 | });
144 |
145 | it("returns no error if the date is equal to or greater than the threshold provided in greaterThanOrEqualTo method", () => {
146 | type IObjectSchema = {
147 | date: Date;
148 | };
149 |
150 | const objectSchema = SchemaTypes.object().keys({
151 | date: SchemaTypes.date().greaterThanOrEqualTo(
152 | new Date("1995-12-17T03:24:00")
153 | ),
154 | });
155 |
156 | const testObj = {
157 | date: new Date("1995-12-18T03:24:00"),
158 | };
159 |
160 | const { valid, value, errors } = objectSchema.validate(testObj);
161 |
162 | expect(valid).toBe(true);
163 | expect(value).toEqual(testObj);
164 | expect(errors).toEqual([]);
165 | });
166 | });
167 |
--------------------------------------------------------------------------------
/src/__tests__/SchemaTypes/Object/class.test.ts:
--------------------------------------------------------------------------------
1 | import * as SchemaTypes from "../../../SchemaTypes/index";
2 |
3 | describe("Test ObjectSchemaType", () => {
4 | it("returns no error if testObj has defined keys", () => {
5 | type IObjectSchema = {
6 | name: string;
7 | age: number;
8 | };
9 |
10 | const objectSchema = SchemaTypes.object().keys({
11 | name: SchemaTypes.string().required(),
12 | age: SchemaTypes.number().required(),
13 | });
14 |
15 | const testObj = {
16 | name: "firefly",
17 | age: 5,
18 | };
19 |
20 | const { valid, value, errors } = objectSchema.validate(testObj);
21 |
22 | expect(valid).toBe(true);
23 | expect(value).toEqual(testObj);
24 | expect(errors).toEqual([]);
25 | });
26 |
27 | it("returns error if testObj has missing keys", () => {
28 | type IObjectSchema = {
29 | name: string;
30 | age: number;
31 | };
32 |
33 | const objectSchema = SchemaTypes.object().keys({
34 | name: SchemaTypes.string().uppercase().required(),
35 | age: SchemaTypes.number().required(),
36 | });
37 |
38 | const testObj = {
39 | name: "firefly",
40 | };
41 |
42 | const { valid, value, errors } = objectSchema.validate(testObj);
43 |
44 | expect(valid).toBe(false);
45 | expect(value).toEqual({
46 | name: "FIREFLY",
47 | });
48 | expect(errors[0]).toBeDefined();
49 | expect(errors[0]!.errorType).toBe("Object/InvalidKeys");
50 | });
51 |
52 | it("returns no error if testObj has missing keys that match the specified pattern", () => {
53 | type IObjectSchema = {
54 | name: string;
55 | };
56 |
57 | const objectSchema = SchemaTypes.object()
58 | .keys({
59 | name: SchemaTypes.string().required(),
60 | })
61 | .pattern([SchemaTypes.string(), SchemaTypes.number()]);
62 |
63 | const testObj = {
64 | name: "firefly",
65 | age: 10,
66 | };
67 |
68 | const { valid, value, errors } = objectSchema.validate(testObj);
69 |
70 | expect(valid).toBe(true);
71 | expect(value).toEqual(testObj);
72 | expect(errors).toEqual([]);
73 | });
74 |
75 | it("returns error if testObj has missing keys that do not match the specified pattern", () => {
76 | type IObjectSchema = {
77 | name: string;
78 | };
79 |
80 | const objectSchema = SchemaTypes.object()
81 | .keys({
82 | name: SchemaTypes.string().required(),
83 | })
84 | .pattern([SchemaTypes.string().regex(/^age$/), SchemaTypes.number()]);
85 |
86 | const testObj = {
87 | name: "firefly",
88 | username: "firefly",
89 | };
90 |
91 | const { valid, value, errors } = objectSchema.validate(testObj);
92 |
93 | expect(valid).toBe(false);
94 | expect(value).toEqual({
95 | name: "firefly",
96 | });
97 | expect(errors[0]).toBeDefined();
98 | expect(errors[0]!.errorType).toBe("Object/InvalidKeys");
99 | });
100 |
101 | it("returns error if testObj has missing keys whose values do not match the specified pattern", () => {
102 | type IObjectSchema = {
103 | name: string;
104 | };
105 |
106 | const objectSchema = SchemaTypes.object()
107 | .keys({
108 | name: SchemaTypes.string().required(),
109 | })
110 | .pattern([SchemaTypes.string().regex(/^age$/), SchemaTypes.number()]);
111 |
112 | const testObj = {
113 | name: "firefly",
114 | age: "firefly",
115 | };
116 |
117 | const { valid, value, errors } = objectSchema.validate(testObj);
118 |
119 | expect(valid).toBe(false);
120 | expect(value).toEqual(testObj);
121 | expect(errors[0]).toBeDefined();
122 | expect(errors[0]!.errorType).toBe("Number/Type");
123 | });
124 |
125 | it("returns error if testObj is missing required keys", () => {
126 | type IObjectSchema = {
127 | name: string;
128 | age: string;
129 | };
130 |
131 | const objectSchema = SchemaTypes.object().keys({
132 | name: SchemaTypes.string().required(),
133 | age: SchemaTypes.number().required(),
134 | });
135 |
136 | const testObj = {
137 | age: 10,
138 | };
139 |
140 | const { valid, value, errors } = objectSchema.validate(testObj);
141 |
142 | expect(valid).toBe(false);
143 | expect(value).toEqual(testObj);
144 | expect(errors[0]).toBeDefined();
145 | expect(errors[0]!.errorType).toBe("Object/InvalidKeys");
146 | });
147 |
148 | it("returns no error if testObj has missing default keys", () => {
149 | type IObjectSchema = {
150 | name: string;
151 | age: string;
152 | };
153 |
154 | const objectSchema = SchemaTypes.object().keys({
155 | name: SchemaTypes.string().default("username"),
156 | age: SchemaTypes.number().required(),
157 | });
158 |
159 | const testObj = {
160 | age: 10,
161 | };
162 |
163 | const { valid, value, errors } = objectSchema.validate(testObj);
164 |
165 | expect(valid).toBe(true);
166 | expect(value).toEqual({ ...testObj, name: "username" });
167 | expect(errors).toEqual([]);
168 | });
169 |
170 | it("returns no error if testObj is a nested object and default value is provided", () => {
171 | type IObjectSchema = {
172 | name: {
173 | firstName: string;
174 | lastName: string;
175 | };
176 | age: number;
177 | };
178 |
179 | const objectSchema = SchemaTypes.object().keys({
180 | name: SchemaTypes.object()
181 | .keys({
182 | firstName: SchemaTypes.string().required(),
183 | lastName: SchemaTypes.string().default("."),
184 | })
185 | .default("username"),
186 | age: SchemaTypes.number().required(),
187 | });
188 |
189 | const testObj = {
190 | age: 10,
191 | };
192 |
193 | const { valid, value, errors } = objectSchema.validate(testObj);
194 |
195 | expect(valid).toBe(true);
196 | expect(value).toEqual({ ...testObj, name: "username" });
197 | expect(errors).toEqual([]);
198 | });
199 |
200 | it("returns error if testObj is a nested object and keys are missing in the nested object", () => {
201 | type IObjectSchema = {
202 | name: {
203 | firstName: string;
204 | lastName: string;
205 | };
206 | age: number;
207 | };
208 |
209 | const objectSchema = SchemaTypes.object().keys({
210 | name: SchemaTypes.object()
211 | .keys({
212 | firstName: SchemaTypes.string().required(),
213 | lastName: SchemaTypes.string().default("."),
214 | })
215 | .required(),
216 | age: SchemaTypes.number().required(),
217 | });
218 |
219 | const testObj = {
220 | name: {},
221 | age: 10,
222 | };
223 |
224 | const { valid, value, errors } = objectSchema.validate(testObj);
225 |
226 | expect(valid).toBe(false);
227 | expect(value).toEqual({
228 | age: 10,
229 | name: {
230 | lastName: ".",
231 | },
232 | });
233 | expect(errors[0]).toBeDefined();
234 | expect(errors[0]!.error).toEqual("firstName is required");
235 | });
236 |
237 | it("adds default value for default keys", () => {
238 | type Address = {
239 | city: string;
240 | state: string;
241 | };
242 |
243 | type User = {
244 | name: string;
245 | address: Address;
246 | };
247 |
248 | const schema = SchemaTypes.object().keys({
249 | name: SchemaTypes.string().default("Username"),
250 | address: SchemaTypes.object().keys({
251 | city: SchemaTypes.string().required(),
252 | state: SchemaTypes.string().default("Not Mentioned"),
253 | }),
254 | });
255 |
256 | const testObj = {
257 | address: {
258 | city: "Delhi",
259 | },
260 | };
261 |
262 | const { valid, value, errors } = schema.validate(testObj);
263 |
264 | expect(valid).toBe(true);
265 | expect(value).toEqual({
266 | name: "Username",
267 | address: {
268 | city: "Delhi",
269 | state: "Not Mentioned",
270 | },
271 | });
272 | expect(errors).toEqual([]);
273 | });
274 |
275 | it("checks only the supplied keys", () => {
276 | type Address = {
277 | city: string;
278 | state: string;
279 | };
280 |
281 | type User = {
282 | name: string;
283 | address: Address;
284 | };
285 |
286 | const schema = SchemaTypes.object().keys({
287 | name: SchemaTypes.string().default("Username"),
288 | address: SchemaTypes.object().keys({
289 | city: SchemaTypes.string().minLength(10).required(),
290 | state: SchemaTypes.string().required(),
291 | }),
292 | });
293 |
294 | const testObj = {
295 | address: {
296 | city: "Delhi",
297 | },
298 | };
299 |
300 | const { valid, value, errors } = schema.validate(testObj, undefined, {
301 | onlySupplied: true,
302 | });
303 |
304 | expect(valid).toBe(false);
305 | expect(value).toEqual({
306 | address: {
307 | city: "Delhi",
308 | },
309 | });
310 | expect(errors.length).toBe(1);
311 | expect(errors[0]).toEqual({
312 | error: "minimum allowed length for city is 10",
313 | errorType: "String/Length",
314 | });
315 | });
316 |
317 | it("only checks keys and ignores the pattern check", () => {
318 | type IObjectSchema = {
319 | name: string;
320 | };
321 |
322 | const objectSchema = SchemaTypes.object()
323 | .keys({
324 | name: SchemaTypes.string().required(),
325 | })
326 | .pattern([SchemaTypes.string(), SchemaTypes.number()]);
327 |
328 | const testObj = {
329 | name: "firefly",
330 | age: 10,
331 | };
332 |
333 | const { valid, value, errors } = objectSchema.validate(testObj, undefined, {
334 | onlyKeys: true,
335 | });
336 |
337 | expect(valid).toBe(false);
338 | expect(value).toEqual({
339 | name: "firefly",
340 | });
341 | expect(errors.length).toBe(1);
342 | expect(errors[0]).toEqual({
343 | error: "age is not allowed",
344 | errorType: "Object/InvalidKeys",
345 | });
346 | });
347 | });
348 |
--------------------------------------------------------------------------------
/src/__tests__/SchemaTypes/String/class.test.ts:
--------------------------------------------------------------------------------
1 | import * as SchemaType from "../../../SchemaTypes/index";
2 |
3 | describe("test String Schema Parser", () => {
4 | it("returns no errors if passed a string", () => {
5 | type IObjectSchema = {
6 | name: string;
7 | };
8 |
9 | const objectSchema = SchemaType.object().keys({
10 | name: SchemaType.string(),
11 | });
12 |
13 | const testObj = {
14 | name: "Lorem",
15 | };
16 |
17 | const { valid, value, errors } = objectSchema.validate(testObj);
18 |
19 | expect(valid).toBe(true);
20 | expect(value).toEqual(testObj);
21 | expect(errors).toEqual([]);
22 | });
23 |
24 | it("returns error if passed a value other than string", () => {
25 | type IObjectSchema = {
26 | name: string;
27 | };
28 |
29 | const objectSchema = SchemaType.object().keys({
30 | name: SchemaType.string(),
31 | });
32 |
33 | const testObj = {
34 | name: 1,
35 | };
36 |
37 | const { valid, value, errors } = objectSchema.validate(testObj);
38 |
39 | expect(valid).toBe(false);
40 | expect(value).toEqual(testObj);
41 | expect(errors).toEqual([
42 | {
43 | error: "name must be a string",
44 | errorType: "String/Type",
45 | },
46 | ]);
47 | });
48 |
49 | it("returns error if passed required option and no value is passed", () => {
50 | type IObjectSchema = {
51 | name: string;
52 | };
53 |
54 | const objectSchema = SchemaType.object().keys({
55 | name: SchemaType.string().required(),
56 | });
57 |
58 | const { valid, value, errors } = objectSchema.validate({});
59 |
60 | expect(valid).toBe(false);
61 | expect(value).toEqual({});
62 | expect(errors).toEqual([
63 | {
64 | error: "name is required",
65 | errorType: "Object/InvalidKeys",
66 | },
67 | ]);
68 | });
69 |
70 | it("trim removes whitespaces", () => {
71 | type IObjectSchema = {
72 | name: string;
73 | };
74 |
75 | const objectSchema = SchemaType.object().keys({
76 | name: SchemaType.string().trim(),
77 | });
78 |
79 | const testObj = {
80 | name: " Lorem ",
81 | };
82 |
83 | const { valid, value, errors } = objectSchema.validate(testObj);
84 |
85 | expect(valid).toBe(true);
86 | expect(value).toEqual({
87 | name: "Lorem",
88 | });
89 | expect(errors).toEqual([]);
90 | });
91 |
92 | it("minLength throws an error if string is lesser than specified value", () => {
93 | type IObjectSchema = {
94 | name: string;
95 | };
96 |
97 | const objectSchema = SchemaType.object().keys({
98 | name: SchemaType.string().minLength(5),
99 | });
100 |
101 | const testObj = {
102 | name: "test",
103 | };
104 |
105 | const { valid, value, errors } = objectSchema.validate(testObj);
106 |
107 | expect(valid).toBe(false);
108 | expect(value).toEqual(testObj);
109 | expect(errors).toEqual([
110 | {
111 | error: "minimum allowed length for name is 5",
112 | errorType: "String/Length",
113 | },
114 | ]);
115 | });
116 |
117 | it("maxLength throws an error if string is greater than specified value", () => {
118 | type IObjectSchema = {
119 | name: string;
120 | };
121 |
122 | const objectSchema = SchemaType.object().keys({
123 | name: SchemaType.string().maxLength(5),
124 | });
125 |
126 | const testObj = {
127 | name: "123456",
128 | };
129 |
130 | const { valid, value, errors } = objectSchema.validate(testObj);
131 |
132 | expect(valid).toBe(false);
133 | expect(value).toEqual(testObj);
134 | expect(errors).toEqual([
135 | {
136 | error: "maximum allowed length for name is 5",
137 | errorType: "String/Length",
138 | },
139 | ]);
140 | });
141 |
142 | it("lowercase formats the string to lowercase value", () => {
143 | type IObjectSchema = {
144 | name: string;
145 | };
146 |
147 | const objectSchema = SchemaType.object().keys({
148 | name: SchemaType.string().lowercase(),
149 | });
150 |
151 | const testObj = {
152 | name: "Lorem",
153 | };
154 |
155 | const { valid, value, errors } = objectSchema.validate(testObj);
156 |
157 | expect(valid).toBe(true);
158 | expect(value).toEqual({
159 | name: "lorem",
160 | });
161 | expect(errors).toEqual([]);
162 | });
163 |
164 | it("uppercase formats the string to uppercase value", () => {
165 | type IObjectSchema = {
166 | name: string;
167 | };
168 |
169 | const objectSchema = SchemaType.object().keys({
170 | name: SchemaType.string().uppercase(),
171 | });
172 |
173 | const testObj = {
174 | name: "lorem",
175 | };
176 |
177 | const { valid, value, errors } = objectSchema.validate(testObj);
178 |
179 | expect(valid).toBe(true);
180 | expect(value).toEqual({
181 | name: "LOREM",
182 | });
183 | expect(errors).toEqual([]);
184 | });
185 |
186 | it("email validates successfully if passed a proper email", () => {
187 | type IObjectSchema = {
188 | name: string;
189 | };
190 |
191 | const objectSchema = SchemaType.object().keys({
192 | name: SchemaType.string().email(),
193 | });
194 |
195 | const testObj = {
196 | name: "lorem@gmail.com",
197 | };
198 |
199 | const { valid, value, errors } = objectSchema.validate(testObj);
200 |
201 | expect(valid).toBe(true);
202 | expect(value).toEqual({
203 | name: "lorem@gmail.com",
204 | });
205 | expect(errors).toEqual([]);
206 | });
207 |
208 | it("email throws an error if passed an invalid email", () => {
209 | type IObjectSchema = {
210 | name: string;
211 | };
212 |
213 | const objectSchema = SchemaType.object().keys({
214 | name: SchemaType.string().email(),
215 | });
216 |
217 | const testObj = {
218 | name: "lorem.com",
219 | };
220 |
221 | const { valid, value, errors } = objectSchema.validate(testObj);
222 |
223 | expect(valid).toBe(false);
224 | expect(value).toEqual(testObj);
225 | expect(errors).toEqual([
226 | {
227 | error: "name must be a valid email",
228 | errorType: "String/Format",
229 | },
230 | ]);
231 | });
232 |
233 | it("regex validates incoming string", () => {
234 | type IObjectSchema = {
235 | name: string;
236 | };
237 |
238 | const objectSchema = SchemaType.object().keys({
239 | name: SchemaType.string().regex(/^[a-zA-Z]+$/),
240 | });
241 |
242 | const testObj = {
243 | name: "Lorem",
244 | };
245 |
246 | const { valid, value, errors } = objectSchema.validate(testObj);
247 |
248 | expect(valid).toBe(true);
249 | expect(value).toEqual({
250 | name: "Lorem",
251 | });
252 | expect(errors).toEqual([]);
253 | });
254 |
255 | it("regex throws an error if regex does not match", () => {
256 | type IObjectSchema = {
257 | name: string;
258 | };
259 |
260 | const objectSchema = SchemaType.object().keys({
261 | name: SchemaType.string().regex(/^[a-zA-Z]+$/),
262 | });
263 |
264 | const testObj = {
265 | name: "123",
266 | };
267 |
268 | const { valid, value, errors } = objectSchema.validate(testObj);
269 |
270 | expect(valid).toBe(false);
271 | expect(value).toEqual(testObj);
272 | expect(errors).toEqual([
273 | {
274 | error: "name must be of valid format",
275 | errorType: "String/Format",
276 | },
277 | ]);
278 | });
279 |
280 | it("enum only allows the specified values", () => {
281 | type IObjectSchema = {
282 | name: string;
283 | };
284 |
285 | const objectSchema = SchemaType.object().keys({
286 | name: SchemaType.string().enum(["Lorem", "Ipsum"]),
287 | });
288 |
289 | const testObj = {
290 | name: "Lorem",
291 | };
292 |
293 | const { valid, value, errors } = objectSchema.validate(testObj);
294 |
295 | expect(valid).toBe(true);
296 | expect(value).toEqual({
297 | name: "Lorem",
298 | });
299 | expect(errors).toEqual([]);
300 | });
301 |
302 | it("enum throws an error if value is not in the enum", () => {
303 | type IObjectSchema = {
304 | name: string;
305 | };
306 |
307 | const objectSchema = SchemaType.object().keys({
308 | name: SchemaType.string().enum(["Lorem", "Ipsum"]),
309 | });
310 |
311 | const testObj = {
312 | name: "123",
313 | };
314 |
315 | const { valid, value, errors } = objectSchema.validate(testObj);
316 |
317 | expect(valid).toBe(false);
318 | expect(value).toEqual(testObj);
319 | expect(errors).toEqual([
320 | {
321 | error: "name is not assignable to specified enum",
322 | errorType: "String/InvalidValue",
323 | },
324 | ]);
325 | });
326 | });
327 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as FireflyNs from "./Firefly";
2 | import * as ConnectionNs from "./Connection";
3 | import * as SchemaTypes from "./SchemaTypes";
4 | import * as ModelNs from "./Model";
5 | import * as QueryNs from "./Query";
6 |
7 | class Firefly extends FireflyNs.default {}
8 | const firefly = new Firefly();
9 |
10 | class Connection extends ConnectionNs.default {}
11 |
12 | class Model extends ModelNs.default {}
13 |
14 | export default firefly;
15 | export {
16 | Firefly,
17 | FireflyNs,
18 | Connection,
19 | ConnectionNs,
20 | SchemaTypes,
21 | Model,
22 | ModelNs,
23 | QueryNs,
24 | };
25 |
--------------------------------------------------------------------------------
/src/utils/makeError.ts:
--------------------------------------------------------------------------------
1 | const makeError = (type: T, data: string | object) => {
2 | const error = {
3 | type,
4 | data,
5 | };
6 |
7 | return new Error(JSON.stringify(error));
8 | };
9 |
10 | export default makeError;
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */,
8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
13 | "declaration": true /* Generates corresponding '.d.ts' file. */,
14 | "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */,
15 | "sourceMap": true /* Generates corresponding '.map' file. */,
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | "outDir": "./lib" /* Redirect output structure to the directory. */,
18 | "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */,
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true /* Enable all strict type-checking options. */,
29 | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
30 | "strictNullChecks": true /* Enable strict null checks. */,
31 | "strictFunctionTypes": true /* Enable strict checking of function types. */,
32 | "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */,
33 | "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
34 | "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
35 | "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
36 |
37 | /* Additional Checks */
38 | "noUnusedLocals": true /* Report errors on unused locals. */,
39 | "noUnusedParameters": true /* Report errors on unused parameters. */,
40 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
41 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
42 | "noUncheckedIndexedAccess": true /* Include 'undefined' in index signature results */,
43 | "noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an 'override' modifier. */,
44 | "noPropertyAccessFromIndexSignature": true /* Require undeclared properties from index signatures to use element accesses. */,
45 |
46 | /* Module Resolution Options */
47 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
48 | "baseUrl": "./" /* Base directory to resolve non-absolute module names. */,
49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
51 | // "typeRoots": [], /* List of folders to include type definitions from. */
52 | // "types": [], /* Type declaration files to be included in compilation. */
53 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
54 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
55 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
56 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
57 |
58 | /* Source Map Options */
59 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
60 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
61 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
62 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
63 |
64 | /* Experimental Options */
65 | "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
66 | "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
67 |
68 | /* Advanced Options */
69 | "skipLibCheck": true /* Skip type checking of declaration files. */,
70 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
71 | },
72 | "include": ["./src/**/*.ts", "./.eslintrc.js"],
73 | "exclude": ["node_modules/", "lib/**/*", "./src/__tests__"],
74 | "compileOnSave": true
75 | }
76 |
--------------------------------------------------------------------------------