fireemu 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +443 -72
- package/bin/fireemu.mjs +9 -4
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,122 +1,493 @@
|
|
|
1
|
+
<!-- Generated from README.md by npm/scripts/prepare-launcher.mjs at pack time. Edit README.md in the repository, not this file. -->
|
|
2
|
+
|
|
1
3
|
# Fireemu
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
Fireemu is a local emulator for testing applications built with Firebase. It aims to match production Firebase behavior as closely as possible, so you can test locally and in CI without using a real Firebase project.
|
|
6
|
+
|
|
7
|
+
Passing tests against the official emulator does not always mean your application will behave the same way in production. Some features are not supported by the official emulator, and some execution conditions are difficult to reproduce locally. These gaps can leave you relying on a real Firebase project for part of your testing.
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
Fireemu is being developed to close these gaps. For example, it provides:
|
|
6
10
|
|
|
7
|
-
Fireemu
|
|
11
|
+
- **Validation of configuration required in production:** Fireemu detects issues that the official emulator misses, such as missing Firestore index definitions.
|
|
12
|
+
- **Support for features not available in the official emulator:** Fireemu also supports features such as TOTP-based multi-factor authentication.
|
|
13
|
+
- **Control over test execution conditions:** You can set an arbitrary time and run scheduled functions to verify their behavior without waiting for the actual scheduled time.
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
- keep local test runs fast, with no JVM emulator to start and a single command that starts the services, runs the test command, and shuts everything down.
|
|
15
|
+
Fireemu is still in development and does not yet reproduce every feature and behavior of production Firebase. Before relying on Fireemu’s test results in CI or other automated workflows, we recommend differential testing against production Firebase services for the features and critical workflows your application uses. Run the same tests against Fireemu and a dedicated Firebase test project, and check for differences in results and behavior that could affect your application. Use a dedicated test project, not your live production project.
|
|
11
16
|
|
|
12
|
-
|
|
17
|
+
See the [capability manifest](https://github.com/t-k/fireemu/blob/v0.11.0/crates/fireemu/src/capabilities.json) and [compatibility documentation](https://github.com/t-k/fireemu/blob/v0.11.0/docs/compatibility-contract.md) for supported features, known differences, and the status of compatibility verification.
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
## Quick start
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
Node.js 20 or newer is required.
|
|
17
22
|
|
|
18
23
|
```sh
|
|
19
|
-
npm install -
|
|
24
|
+
npm install --save-dev fireemu
|
|
20
25
|
npx fireemu init
|
|
21
|
-
|
|
26
|
+
|
|
27
|
+
# Start the emulator
|
|
28
|
+
# UI (default): http://127.0.0.1:4000
|
|
29
|
+
npx fireemu up
|
|
22
30
|
```
|
|
23
31
|
|
|
32
|
+
Open the UI in your browser to inspect and manage data. Press `Ctrl+C` to stop the emulator.
|
|
33
|
+
|
|
34
|
+
To run the emulator only for the duration of a command, use `exec` instead of `up`.
|
|
35
|
+
|
|
24
36
|
```sh
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
# Start the emulator, run a command, and stop when the command exits
|
|
38
|
+
npx fireemu exec -- <command>
|
|
39
|
+
|
|
40
|
+
# Example: run tests
|
|
41
|
+
npx fireemu exec -- npm test
|
|
27
42
|
```
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
### Supported platforms
|
|
45
|
+
|
|
46
|
+
| OS | Architecture |
|
|
47
|
+
|---|---|
|
|
48
|
+
| macOS 13 or newer | Apple silicon, Intel |
|
|
49
|
+
| Linux | x86-64, arm64 |
|
|
50
|
+
| Windows 10 or newer | x86-64 |
|
|
33
51
|
|
|
34
|
-
|
|
52
|
+
Running Functions requires `firebase-functions` v6 or v7 in the codebase.
|
|
35
53
|
|
|
36
|
-
|
|
54
|
+
## Supported scope
|
|
37
55
|
|
|
38
|
-
|
|
56
|
+
Fireemu supports Cloud Firestore, Firebase Authentication (Identity Platform), Cloud Storage for Firebase, Cloud Functions for Firebase, Cloud Pub/Sub, and Eventarc. It also provides a local Firebase App Check implementation and a UI for managing data and viewing logs.
|
|
57
|
+
|
|
58
|
+
### Key unsupported features and limitations
|
|
59
|
+
|
|
60
|
+
| Product | Key unsupported features and limitations |
|
|
61
|
+
|---|---|
|
|
62
|
+
| Cloud Firestore | The Enterprise Pipeline API supports a limited subset of read operations. Full-text search supports index definition validation, but local search execution is not supported. |
|
|
63
|
+
| Firebase Authentication (Identity Platform) | Authentication flows that communicate with external identity providers, authorization-code exchange, and SAML XML signature verification are outside the supported scope. These are separate from local authentication behavior and provider configuration. |
|
|
64
|
+
| Cloud Storage for Firebase | Object versioning, signed URLs, ACL-based access control, object composition (`compose`), and notification configuration are not supported. |
|
|
65
|
+
| Cloud Functions for Firebase | Triggers that depend on unsupported products, such as Realtime Database (deferred), Remote Config, and Data Connect (deferred), cannot run. Storage archive events do not occur because object versioning is not supported. |
|
|
66
|
+
| Cloud Pub/Sub | Schemas, BigQuery and Cloud Storage subscription delivery, and some APIs such as `UpdateTopic` are not supported. |
|
|
67
|
+
| Firebase App Check | Local testing with debug tokens is supported. Real attestation through Play Integrity, App Attest, DeviceCheck, or reCAPTCHA, and replay protection using limited-use tokens, are not supported. |
|
|
68
|
+
|
|
69
|
+
This table highlights key limitations; it is not exhaustive. Check individual API support and conditions with the following command or the [capability manifest](https://github.com/t-k/fireemu/blob/v0.11.0/crates/fireemu/src/capabilities.json).
|
|
39
70
|
|
|
40
71
|
```sh
|
|
41
|
-
npx fireemu
|
|
42
|
-
|
|
72
|
+
npx fireemu capabilities
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
See the [Functions support inventory](https://github.com/t-k/fireemu/blob/v0.11.0/docs/functions-export-inventory.md) for support by trigger type.
|
|
76
|
+
|
|
77
|
+
### Unsupported products
|
|
78
|
+
|
|
79
|
+
Realtime Database, Firebase Hosting, App Hosting, and Data Connect are not currently supported. Firebase Extensions support is not planned.
|
|
80
|
+
|
|
81
|
+
## Usage
|
|
82
|
+
|
|
83
|
+
### Select services
|
|
84
|
+
|
|
85
|
+
Use `--only` to select the services to start.
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
# Start only Firebase Authentication (Identity Platform), Firestore, and Storage
|
|
89
|
+
npx fireemu up --only auth,firestore,storage
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
You can also select an individual Functions codebase.
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
npx fireemu up --only functions:<codebase>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Run a command
|
|
99
|
+
|
|
100
|
+
Service selection also works with `exec`.
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
npx fireemu exec --only auth,firestore,storage -- npm test
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`exec` sets the Firebase SDK emulator host environment variables for the child command and returns its exit status. SIGINT and SIGTERM are forwarded to the child command.
|
|
107
|
+
|
|
108
|
+
The official CLI command names are also available as aliases.
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
npx fireemu emulators:start
|
|
112
|
+
npx fireemu emulators:exec -- npm test
|
|
43
113
|
```
|
|
44
114
|
|
|
45
|
-
|
|
115
|
+
### UI
|
|
116
|
+
|
|
117
|
+
The default UI address is `http://127.0.0.1:4000`. Use `--ui-port` to specify a port.
|
|
46
118
|
|
|
47
119
|
```sh
|
|
48
|
-
|
|
120
|
+
# Start the UI on port 4001
|
|
121
|
+
npx fireemu up --ui-port 4001
|
|
49
122
|
```
|
|
50
123
|
|
|
51
|
-
`exec`
|
|
52
|
-
emulator host variables once every listener is bound, stops everything when the command exits and
|
|
53
|
-
exits with its status.
|
|
124
|
+
`exec` does not start the UI by default. Add `--ui` to use it while the command runs.
|
|
54
125
|
|
|
55
126
|
```sh
|
|
56
|
-
npx fireemu exec --
|
|
127
|
+
npx fireemu exec --ui -- npm test
|
|
57
128
|
```
|
|
58
129
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
130
|
+
Use `--ui-port 0` to disable the UI. When automatic UI startup cannot bind the default port, the emulator starts without the UI. Check the startup output if the UI is unavailable.
|
|
131
|
+
|
|
132
|
+
When a Functions codebase is loaded, the Functions page shows registered functions, triggers, invocation results, and logs. You can run scheduled functions immediately or advance the virtual clock to their next scheduled execution.
|
|
62
133
|
|
|
63
|
-
|
|
64
|
-
(`--ui-port 0` turns it off).
|
|
134
|
+
### Export data
|
|
65
135
|
|
|
66
|
-
|
|
136
|
+
To save data from a running emulator, run the following command in another terminal.
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
npx fireemu emulators:export ./emulator-data
|
|
140
|
+
```
|
|
67
141
|
|
|
68
|
-
|
|
69
|
-
| --- | --- | --- | --- |
|
|
70
|
-
| `@fireemu/darwin-arm64` | macOS 13+ | Apple silicon | `aarch64-apple-darwin` |
|
|
71
|
-
| `@fireemu/darwin-x64` | macOS 13+ | Intel | `x86_64-apple-darwin` |
|
|
72
|
-
| `@fireemu/linux-x64` | Linux (any libc) | x86-64 | `x86_64-unknown-linux-musl`, static |
|
|
73
|
-
| `@fireemu/linux-arm64` | Linux (any libc) | arm64 | `aarch64-unknown-linux-musl`, static |
|
|
74
|
-
| `@fireemu/win32-x64` | Windows 10+ | x86-64 | `x86_64-pc-windows-msvc` |
|
|
142
|
+
On Windows, `emulators:export` and `--export-on-exit` are not currently available. Import and other emulator functionality remain available.
|
|
75
143
|
|
|
76
|
-
|
|
77
|
-
distroless and Alpine containers.
|
|
144
|
+
On Unix-like systems, the destination and its ancestor directories are subject to ownership and permission restrictions. Use a dedicated, non-shared directory rather than `/tmp` or a shared directory.
|
|
78
145
|
|
|
79
|
-
|
|
146
|
+
On macOS, an ancestor directory with an ACL is refused when the ACL has an `allow` entry for anyone but the owner, root or the user running the export, whatever permissions the entry shows (the kernel grants rights that `ls -le` does not list). `deny` entries are accepted, so a standard home directory (which carries `group:everyone deny delete`) and the folders below it work. When an export is refused for an ACL, the message names the entry; remove it (`chmod -a`) or export to another directory, for example one under `$TMPDIR`. Check an ancestor with `/bin/ls -led <directory>`. A destination on a volume that ignores ownership (exFAT and FAT sticks and cards, or an external APFS volume with "Ignore ownership on this volume" turned on) is refused, because the owner and the mode of its directories do not show who can change them; export to a volume that enforces ownership. A volume without atomic directory rename (exFAT, FAT, some network filesystems) cannot publish an export either: use a directory on an APFS volume.
|
|
80
147
|
|
|
81
|
-
|
|
82
|
-
Node; Firestore, Auth, Storage and the UI are served by the binary itself.
|
|
83
|
-
- **No Java.** Unlike the Firebase Emulator Suite, `fireemu` runs no JVM emulator.
|
|
84
|
-
- For `--functions <dir>`: the codebase's own `node_modules`, with `firebase-functions` v6 or v7
|
|
85
|
-
and `firebase-admin`. `npx fireemu doctor` prints the range the bundled runner supports.
|
|
148
|
+
### Troubleshooting
|
|
86
149
|
|
|
87
|
-
|
|
150
|
+
For startup or installation problems, inspect the environment and bundled files with:
|
|
88
151
|
|
|
89
152
|
```sh
|
|
90
153
|
npx fireemu doctor
|
|
91
154
|
```
|
|
92
155
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
156
|
+
The command reports information about the Fireemu binary, UI, Functions runner, and Node.js, and suggests remedies when it finds missing components or problems.
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
### Initial setup and reusing `firebase.json`
|
|
161
|
+
|
|
162
|
+
`fireemu init` creates `fireemu.json` in the current directory. The interactive setup lets you choose a compatibility profile and whether to reuse an existing `firebase.json`.
|
|
163
|
+
|
|
164
|
+
When reusing `firebase.json`, Fireemu references the file rather than copying its settings. Rules, indexes, Functions codebases, and emulator ports are read from it each time Fireemu starts.
|
|
165
|
+
|
|
166
|
+
Use the non-interactive form for CI or scripted setup.
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
npx fireemu init --yes
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
An existing `fireemu.json` is not overwritten unless `--force` is supplied.
|
|
173
|
+
|
|
174
|
+
### Compatibility profiles
|
|
175
|
+
|
|
176
|
+
| Profile | Behavior |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `strict` | Matches production Firebase behavior and constraints as closely as possible within the supported scope. This is the default profile |
|
|
179
|
+
| `emulator` | Targets the behavior of a pinned version of the official emulator. Known differences are documented in the compatibility documentation |
|
|
180
|
+
|
|
181
|
+
Use `strict` when testing against production Firebase behavior. Add `emulator` to your test targets when you also need to check alignment with the official emulator.
|
|
182
|
+
|
|
183
|
+
Under the `emulator` profile, a request that names a tenant the project does not have creates that tenant, and the number of tenants is not limited, as in the official emulator. The `strict` profile refuses such a request, as production does. A request that Fireemu refuses (a wrong credential, a browser page without the control token on the emulator routes, App Check, an unknown API key) creates nothing. As in the official emulator, a plain `GET` without an `Origin` header to a tenant route of `127.0.0.1` creates the tenant, so any web page you visit while the emulator runs can add tenants to it (not read them); use the `strict` profile where that matters.
|
|
184
|
+
|
|
185
|
+
### Basic configuration
|
|
186
|
+
|
|
187
|
+
The initial configuration selects Standard edition Firestore with the Native API.
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"$schema": "https://fireemu.dev/spec/config/fireemu.schema.json",
|
|
192
|
+
"schemaVersion": 1,
|
|
193
|
+
"profile": "strict",
|
|
194
|
+
"firestore": {
|
|
195
|
+
"edition": "standard",
|
|
196
|
+
"apiMode": "native"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
You can specify the configuration file with `--config`.
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
npx fireemu up --config ./fireemu.json
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Keep Fireemu-specific settings in a Fireemu configuration file containing `"schemaVersion": 1`, rather than in `firebase.json`. See the [configuration schema](https://github.com/t-k/fireemu/blob/v0.11.0/spec/config/fireemu.schema.json) for all available settings.
|
|
208
|
+
|
|
209
|
+
### Firebase Authentication (Identity Platform) configuration
|
|
210
|
+
|
|
211
|
+
The `auth` section of `fireemu.json` lets you configure sign-in methods, password policies, account permissions, and other authentication settings. Match your application’s configuration to test registration, sign-in, and account management behavior.
|
|
212
|
+
|
|
213
|
+
| What you can configure | Main settings |
|
|
214
|
+
|---|---|
|
|
215
|
+
| Enable or disable email/password, anonymous, and phone sign-in | `auth.signIn` |
|
|
216
|
+
| Test phone numbers and verification codes | `auth.signIn.phoneNumber.testPhoneNumbers` |
|
|
217
|
+
| Initial authorized domains for action-link continue URLs | `auth.authorizedDomains` |
|
|
218
|
+
| Handling of accounts that share an email address | `auth.signIn.allowDuplicateEmails` |
|
|
219
|
+
| Password length and character requirements, and policy enforcement at sign-in | `auth.passwordPolicy` |
|
|
220
|
+
| TOTP-based multi-factor authentication | `auth.totp` |
|
|
221
|
+
| Initial multi-factor authentication (MFA) configuration of a project, such as enabling TOTP under the `strict` profile | `auth.mfa` |
|
|
222
|
+
| Tenants that exist when the daemon starts, and the multi-tenancy switch | `auth.tenants`, `auth.multiTenant` |
|
|
223
|
+
| Restrictions on end-user account creation and deletion | `auth.client.permissions` |
|
|
224
|
+
| Email enumeration protection | `auth.improvedEmailPrivacy` |
|
|
225
|
+
| Project- and tenant-specific password policies and account permissions | `auth.passwordPolicyOverrides`, `auth.configOverrides` |
|
|
226
|
+
| Blocking functions before account creation or sign-in, and credential forwarding to those functions | `auth.blockingFunctions` |
|
|
227
|
+
| Initial custom OIDC and SAML provider resources | `auth.providers.oidc`, `auth.providers.saml` |
|
|
228
|
+
| Public keys for verifying custom tokens and OIDC ID tokens | `auth.customTokenSigners`, `auth.idpSigners` |
|
|
229
|
+
| Sign-up quota configuration and local quota-exceeded simulation | `auth.quota`, `auth.quotaSimulation` |
|
|
230
|
+
|
|
231
|
+
See the [configuration schema](https://github.com/t-k/fireemu/blob/v0.11.0/spec/config/fireemu.schema.json) for the format and accepted values of each setting.
|
|
232
|
+
|
|
233
|
+
Declare `auth.authorizedDomains`, for example `["localhost", "app.test"]`, to set the initial project-wide list in either profile. The list replaces the defaults, so include `localhost` if your tests use it. An empty list authorizes no host under `strict`; an absent or `null` setting keeps the existing defaults (`localhost` and the project's `firebaseapp.com` and `web.app` domains). Validation uses the Admin API's rules for the selected profile, including its conversion of numeric and boolean entries to strings and omission of `null` entries; use strings when writing the list. Fireemu intentionally keeps `localhost` in its defaults for development. The [recorded production sandbox baseline](https://github.com/t-k/fireemu/blob/v0.11.0/conformance/auth-action-production.json) does not include it; new production projects may also omit it. This default is a Fireemu-only convenience, and declaring the list replaces it. Tenant-scoped strict action links currently check the tenant's default domains rather than this project declaration; support for the parent project's live list is pending.
|
|
234
|
+
|
|
235
|
+
An Admin update changes the live list. A session reset restores the declared list, when present, and preserves an Admin-written list when no initial list was declared. Projects created later receive the declaration word for word, independently of the default project's live list; listing a particular project's `firebaseapp.com` or `web.app` domain therefore gives later projects that same domain. Clearing accounts leaves the live list alone. Auth export/import carries accounts without the domain list or its declaration. The `emulator` profile accepts the setting and exposes the list, while continuing to allow action-link continue URLs outside it.
|
|
236
|
+
|
|
237
|
+
Declare project OIDC and SAML resources with `auth.providers.oidc` and `auth.providers.saml`, using the Admin v2 resource fields and an ID-only `name` such as `oidc.fixture` or `saml.fixture`. Full resource names are refused. For example, `"providers": {"oidc": [{"name": "oidc.fixture", "enabled": true, "clientId": "local-client", "issuer": "https://issuer.test", "responseType": {"idToken": true}}]}` initializes that provider in both profiles. Strict OIDC sign-in also needs matching public issuer keys in `auth.idpSigners`; no key is fetched. Strict SAML uses the declared provider certificates and retains its existing assertion checks.
|
|
238
|
+
|
|
239
|
+
Each declared array replaces that provider kind; `[]` explicitly starts it empty, while an absent or `null` kind declares no initial resources. Admin operations change live resources until a session reset restores the declared kinds. Later session and routed projects inherit the declaration independently of the default project's live changes. Tenant resources and built-in providers remain separate. Clearing accounts preserves live resources; snapshots and Auth export/import transfer no provider resources or declarations. The emulator profile continues to accept its unsigned fixture credentials without requiring configured or enabled provider resources. These declarations require a canonical document with `"schemaVersion": 1`; a Firebase deployment `auth.providers` section without that version remains deployment data and installs no provider seeds.
|
|
240
|
+
|
|
241
|
+
#### Example configuration
|
|
242
|
+
|
|
243
|
+
This example enables email/password and phone sign-in while disabling anonymous sign-in. It requires passwords of at least 12 characters containing uppercase letters, lowercase letters, and numbers, and prevents users from deleting their own accounts. Phone sign-in uses a configured test phone number and verification code.
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"$schema": "https://fireemu.dev/spec/config/fireemu.schema.json",
|
|
248
|
+
"schemaVersion": 1,
|
|
249
|
+
"profile": "strict",
|
|
250
|
+
"firestore": {
|
|
251
|
+
"edition": "standard",
|
|
252
|
+
"apiMode": "native"
|
|
253
|
+
},
|
|
254
|
+
"auth": {
|
|
255
|
+
"signIn": {
|
|
256
|
+
"allowDuplicateEmails": false,
|
|
257
|
+
"email": {
|
|
258
|
+
"enabled": true,
|
|
259
|
+
"passwordRequired": true
|
|
260
|
+
},
|
|
261
|
+
"anonymous": {
|
|
262
|
+
"enabled": false
|
|
263
|
+
},
|
|
264
|
+
"phoneNumber": {
|
|
265
|
+
"enabled": true,
|
|
266
|
+
"testPhoneNumbers": {
|
|
267
|
+
"+16505550101": "123456"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"passwordPolicy": {
|
|
272
|
+
"enforcementState": "ENFORCE",
|
|
273
|
+
"forceUpgradeOnSignin": true,
|
|
274
|
+
"constraints": {
|
|
275
|
+
"minLength": 12,
|
|
276
|
+
"requireUppercase": true,
|
|
277
|
+
"requireLowercase": true,
|
|
278
|
+
"requireNumeric": true
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
"client": {
|
|
282
|
+
"permissions": {
|
|
283
|
+
"disabledUserSignup": false,
|
|
284
|
+
"disabledUserDeletion": true
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
"improvedEmailPrivacy": true
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
For an existing `fireemu.json`, add the settings you need to its `auth` section. Adjust the values to match your application’s requirements.
|
|
293
|
+
|
|
294
|
+
#### Project- and tenant-specific settings
|
|
295
|
+
|
|
296
|
+
Use `auth.passwordPolicyOverrides` to specify different password policies for individual projects or tenants. Use `auth.configOverrides` to customize account creation and deletion permissions and email enumeration protection.
|
|
297
|
+
|
|
298
|
+
These settings are separate from creating the project or tenant. Naming a tenant in `auth.configOverrides` or `auth.passwordPolicyOverrides` does not create it (declare it in `auth.tenants` to have it created).
|
|
299
|
+
|
|
300
|
+
#### Enable multi-factor authentication at start
|
|
301
|
+
|
|
302
|
+
Under the `strict` profile, TOTP is enabled only through the project's MFA configuration, as in production, so `auth.totp` alone enables nothing there. Use `auth.mfa` to declare that configuration in the file instead of calling the Admin API in every session. It has the shape Identity Platform uses (`state`, `enabledProviders`, and `providerConfigs` with `totpProviderConfig.adjacentIntervals`):
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"schemaVersion": 1,
|
|
307
|
+
"profile": "strict",
|
|
308
|
+
"auth": {
|
|
309
|
+
"mfa": {
|
|
310
|
+
"state": "ENABLED",
|
|
311
|
+
"providerConfigs": [
|
|
312
|
+
{ "state": "ENABLED", "totpProviderConfig": { "adjacentIntervals": 1 } }
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
- The configuration applies when the daemon starts, in both profiles, and to every project created afterward (not the default project's current value). It is validated as the Admin API validates an update of `mfa`, and a value production would refuse stops the daemon at startup.
|
|
320
|
+
- Nothing else changes. An Admin API update replaces the current configuration, and disabling MFA refuses TOTP as production does. A tenant follows its own `mfaConfig`, not this setting.
|
|
321
|
+
- `POST /v1/sessions/{session}/reset` returns the project to the declared configuration. Without `auth.mfa`, a reset leaves a configuration set through the Admin API alone, as it does the rest of the project configuration. `DELETE /emulator/v1/projects/{project}/accounts` clears accounts only.
|
|
322
|
+
- An export does not carry the project's `mfa` configuration, so an imported project keeps its own `auth.mfa` or Admin-set configuration.
|
|
323
|
+
- The `emulator` profile keeps `auth.totp` working. Under `strict`, `auth.totp` without an `auth.mfa` that enables TOTP prints a startup warning naming `auth.mfa`.
|
|
324
|
+
|
|
325
|
+
#### Enable multi-tenancy
|
|
326
|
+
|
|
327
|
+
Under the `strict` profile, enable `multiTenant.allowTenants` before using multi-tenancy: through the Admin API, or with `auth.multiTenant` in the file (see "Start with tenants" below). See the [compatibility documentation](https://github.com/t-k/fireemu/blob/v0.11.0/docs/compatibility-contract.md) for the supported Admin API scope.
|
|
328
|
+
|
|
329
|
+
#### Start with tenants
|
|
330
|
+
|
|
331
|
+
Instead of creating tenants through the Admin API in every session, declare the default project's tenants and the multi-tenancy switch in the file. Each entry of `auth.tenants` is an Admin v2 `Tenant` document plus its `tenantId`, which has the form production generates, the display name, a hyphen, and five characters of `a-z0-9`:
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"schemaVersion": 1,
|
|
336
|
+
"profile": "strict",
|
|
337
|
+
"auth": {
|
|
338
|
+
"multiTenant": { "allowTenants": true },
|
|
339
|
+
"tenants": [
|
|
340
|
+
{
|
|
341
|
+
"tenantId": "acme-x7k2q",
|
|
342
|
+
"displayName": "acme",
|
|
343
|
+
"allowPasswordSignup": true,
|
|
344
|
+
"mfaConfig": { "state": "ENABLED", "enabledProviders": ["PHONE_SMS"] }
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
- A document is read by the code the Admin tenant create route reads one with, so a value production refuses stops the daemon at startup. The tenants are created empty, in file order, and settings such as `mfaConfig` and `passwordPolicyConfig` come from the document. The file is stricter than the route in the `emulator` profile: it also refuses an unknown member and a missing or invalid `displayName`, as production does.
|
|
352
|
+
- A declared tenant has what an Admin create with that document gives: an omitted `allowPasswordSignup`, `enableEmailLinkSignin` or `enableAnonymousUser` is off and multi-factor is off. A tenant the `emulator` profile creates on the way for a request that names an unknown id gets the official emulator's defaults instead (all three on, `PHONE_SMS` multi-factor), so declare what your test needs.
|
|
353
|
+
- Under the `strict` profile tenants are created only in a project that allows them, as in production, so `auth.tenants` needs `auth.multiTenant.allowTenants: true` (the daemon refuses to start otherwise). The `emulator` profile takes tenants without the switch.
|
|
354
|
+
- The default project and every session project (`POST /v1/sessions`) start with the declared switch and tenants. `POST /v1/sessions/{session}/reset` (the default session's included) creates them again, empty, and returns the switch to the declared value; without these keys a reset leaves an Admin-set switch alone. A namespace the `emulator` profile creates for an unknown project id (as the official emulator does) starts with the declared switch and tenants too: it is created by the requests that already created one (a user, a provider or a project setting written there) and by a request that writes a tenant there (creating one, or naming one that does not exist yet), not by a read alone. A session created for that project id (`POST /v1/sessions`) replaces the namespace, tenants included, and starts with the declared ones.
|
|
355
|
+
- With `--import`, an imported tenant is authoritative for its id, and a declared tenant the import did not carry is still created. If you keep re-importing your own export (`--import dir --export-on-exit dir`), an edit to a declared tenant is therefore ignored once its exported copy exists: the daemon prints a warning naming the tenant when the imported tenant's document differs from the declared one (every setting the file can declare is compared). The switch is the other way round: a declared `allowTenants` wins over the import. After a reset the declared version of a tenant comes back, not the imported one.
|
|
356
|
+
|
|
357
|
+
#### Test quota-exceeded behavior
|
|
358
|
+
|
|
359
|
+
Use `auth.quotaSimulation` to test sign-up quota-exceeded behavior locally. For example, set `auth.quotaSimulation` to:
|
|
360
|
+
|
|
361
|
+
```json
|
|
362
|
+
{
|
|
363
|
+
"mode": "enforce",
|
|
364
|
+
"defaultQuotaPerHour": 5
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
This is a local fixed-window simulation. It does not reproduce Firebase’s full quota accounting or abuse-prevention systems.
|
|
369
|
+
|
|
370
|
+
### Configure execution time
|
|
371
|
+
|
|
372
|
+
Set the emulator’s starting time with `daemon.clockStart`. Configure the clock, time zone, and other scheduling behavior in the `scheduler` section. This lets you test scheduled functions independently of the actual time. If the Functions event queue is full (4,096 active events or 64 MiB), a scheduled run that comes due stays due and runs once room frees, and a manual run answers `429 RESOURCE_EXHAUSTED` without being queued. The clock can also move backwards (`clock:set` with `"allowBackwards": true`, or **Allow moving backwards** on the Runtime page). In the `emulator` profile, ID tokens stay usable after a rewind, as with the official emulator, which does not check their issue time. In the `strict` profile, Authentication (`INVALID_ID_TOKEN`), Cloud Firestore and Cloud Storage refuse an ID token issued after the new time until the clock reaches its issue time again. In both profiles an expired token is still refused. A rewind to before an account was created, or before its tokens were revoked, makes even a new sign-in's token count as revoked; move the clock forward again or reset the session instead. Conversely, a revocation or password change made after a rewind does not revoke tokens issued at later times on the timeline: the `emulator` profile accepts them at once, and `strict` accepts them once the clock reaches their issue time. Move the clock forward past their issue time before revoking, or reset the session.
|
|
373
|
+
|
|
374
|
+
A schedule is driven by the virtual clock alone: real time passing runs nothing. Each clock move (`clock:advance`, `clock:advanceTo`, `clock:set` or the Runtime page) makes every occurrence in `(previous time, new time]` due, so an occurrence exactly at the new time is included and one exactly at the previous time is not. `scheduler.catchUp` decides what runs: `all` runs each one (at most `scheduler.maxCatchUpRuns` per job and clock move, the rest stays due and runs as room frees), `latest` runs the newest and `none` runs none, and both record one `skipped: catch-up` summary per job and clock move that drops runs. A rewind never replays: occurrences the schedule already passed are not run again when the clock moves forward over them, and the schedule resumes once the clock passes the last occurrence it ran or still owes (its cursor). After a rewind below that point, runs still owed from a backlog or held for room stay owed but do not come due again until the clock passes it. A session reset or a snapshot restore starts every schedule from the clock's current time instead, so after a rewind the occurrences between that time and a later one are due again. `scheduler.overlap` (`allow`, `skip`, `queue`, `reject`) applies to scheduler runs and manual runs alike. A function counts as busy while any of its runs is queued or running, including a run that is waiting to be retried, so under `skip` and `reject` a retry still pending can suppress the next occurrence; fireemu does not distinguish a retry attempt from an overlapping run here. With `catchUp: all`, one clock move over several occurrences of the same function overlaps itself, so `skip` keeps the oldest of them and `reject` counts the rest as rejections.
|
|
375
|
+
|
|
376
|
+
A manual run (`POST /v1/sessions/{session}/functions/{name}:run`) enqueues one run of a scheduled function stamped with the current virtual time. It does not consume the next occurrence, which still comes due and is subject to `scheduler.overlap` like any run. It answers `400 INVALID_ARGUMENT` for an unknown function, a function that is not scheduled, an overlap policy refusal, or a runtime that is shutting down, and `429 RESOURCE_EXHAUSTED` when the event queue is full. This is fireemu's own control, not a claim about production's run-now.
|
|
377
|
+
|
|
378
|
+
See the [configuration schema](https://github.com/t-k/fireemu/blob/v0.11.0/spec/config/fireemu.schema.json) for available settings and values.
|
|
379
|
+
|
|
380
|
+
### Firestore history limits
|
|
381
|
+
|
|
382
|
+
Like production, Fireemu keeps every version of a document written in the last hour, so a `read_time` read, a read-only transaction or a resumed listener can see it. A test that updates the same documents many times therefore holds many versions in memory. These limits are local; production has none of them:
|
|
383
|
+
|
|
384
|
+
- Each database retains at most 1 GiB of logical history bytes and 1,000,000 versions, each session 1 GiB and 1,000,000 versions, and the daemon 4 GiB and 4,000,000 versions. Logical bytes follow Firestore's storage size model and are not the process's memory, which can be several times larger.
|
|
385
|
+
- On a pinned clock (`daemon.clockStart`), time does not pass on its own, so a database keeps at most 1,024 versions of any one document. This limit works like `maxVersionsPerPath` below: once a document goes past it, older snapshots are released for the whole database.
|
|
386
|
+
- A commit that would cross a limit is refused whole with `RESOURCE_EXHAUSTED` and the message `Firestore retained history capacity is exhausted`.
|
|
387
|
+
|
|
388
|
+
Versions older than an hour are released by the next commit after the clock passes them. To use less memory, lower the limits in the `firestore.history` section:
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"schemaVersion": 1,
|
|
393
|
+
"firestore": {
|
|
394
|
+
"edition": "standard",
|
|
395
|
+
"apiMode": "native",
|
|
396
|
+
"history": {
|
|
397
|
+
"maxVersionsPerPath": 16,
|
|
398
|
+
"maxBytes": 268435456
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
- `maxVersionsPerPath` (1 to 1,000,000) sets the most versions of one document a database keeps, on either clock, in place of the defaults above.
|
|
405
|
+
- `maxBytes` (1 MiB to 1 GiB) lowers the logical history bytes one database retains. An import (`--import`) is not checked against it: a larger import starts with a `warning:` line naming the database, its bytes and the limit (a snapshot restore above it logs the same line), and every later write that would grow the history is refused with `RESOURCE_EXHAUSTED`.
|
|
406
|
+
|
|
407
|
+
Without these keys the defaults apply.
|
|
408
|
+
|
|
409
|
+
The version cap applies to each document separately, but its effect reaches the whole database. Snapshots stay exact for every document, so once any document has more versions than the cap, the database keeps no snapshot older than that document's oldest kept version. After that:
|
|
410
|
+
|
|
411
|
+
- A `read_time` read older than that point is refused with `FAILED_PRECONDITION` (`The requested 'read_time' is no longer retained by this database.`). So is a new read-only transaction at such a `read_time` (`read_time is no longer retained by this database`).
|
|
412
|
+
- A transaction already open on an older snapshot fails its next read or commit with `ABORTED` (`The referenced transaction has expired or is no longer valid.`), even if it only touches other documents.
|
|
413
|
+
- A listener resuming from an older point is reset and sent the full result again.
|
|
414
|
+
|
|
415
|
+
Client SDKs retry `ABORTED` transactions. One frequently updated document under a small cap can therefore keep aborting unrelated transactions. Avoid very small caps: choose a cap above the number of times any document is updated while your longest transaction is open.
|
|
416
|
+
|
|
417
|
+
To see how much history a running daemon holds, run `fireemu doctor --connect <control URL>`. Its `history.*` lines report the retained versions and logical bytes against the session and daemon limits, and how many bytes the next compaction would release.
|
|
418
|
+
|
|
419
|
+
### Storage stored-byte limit
|
|
420
|
+
|
|
421
|
+
The Storage emulator keeps every object in memory, and by default nothing bounds how much. To cap it, set `storage.maxStoredBytes` (bytes, at least 1). Production has no such limit; it is local to Fireemu:
|
|
422
|
+
|
|
423
|
+
```json
|
|
424
|
+
{
|
|
425
|
+
"schemaVersion": 1,
|
|
426
|
+
"storage": { "maxStoredBytes": 1073741824 }
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
- Each object counts its bytes once, and a copy counts again. Replacing an object counts only the difference in size.
|
|
431
|
+
- An upload, a resumable upload's final request or a copy that would grow the total past the limit is refused with HTTP 402 and the message `storage.maxStoredBytes limit exceeded` (in the JSON API, with the reason `storageCapacityExceeded`). There is no `Retry-After`. Delete objects to make room. The Firebase client SDKs report a 402 at once as `storage/quota-exceeded` without retrying; the Cloud Storage client libraries do not retry it either.
|
|
432
|
+
- A write that does not grow the total, such as an equal or smaller replacement, always passes, even while the store holds more than the limit (after an import, a restore or a lowered limit).
|
|
433
|
+
- A refused write changes nothing. A resumable upload stays open: its offset does not advance, so you can send the same final chunk again after making room, or cancel the upload.
|
|
434
|
+
- An import (`--import`) and a snapshot restore are not checked against the limit. An import above it starts with a `warning:` line naming the stored bytes and the limit, and a snapshot restore above it logs the same line.
|
|
435
|
+
|
|
436
|
+
`fireemu doctor --connect <control URL>` reports the stored bytes as `objects.bytes` against this limit.
|
|
437
|
+
|
|
438
|
+
## Compatibility and limitations
|
|
439
|
+
|
|
440
|
+
### Differences from production Firebase
|
|
441
|
+
|
|
442
|
+
Fireemu aims to match production Firebase behavior, but a successful local operation does not guarantee the same result in production.
|
|
443
|
+
|
|
444
|
+
The local runtime does not reproduce real quota accounting, billing, IAM, organization policies, regional or network differences, or service rollouts. Local performance measurements also do not predict production latency or throughput.
|
|
445
|
+
|
|
446
|
+
Security Rules and API behavior are implemented using documentation and observations of Firebase’s actual behavior. Changes in Firebase or untested conditions may introduce differences that have not yet been recorded.
|
|
447
|
+
|
|
448
|
+
### Comparison with the official emulator
|
|
449
|
+
|
|
450
|
+
The official emulator comparison baseline is `firebase-tools 15.28.2`.
|
|
451
|
+
|
|
452
|
+
Fireemu is compatible with the listed Local Emulator Suite products as shipped by firebase-tools 15.28.2 -- Cloud Firestore, Firebase Authentication, Cloud Storage for Firebase, Cloud Functions, Cloud Pub/Sub and Eventarc, with Security Rules on the Firestore and Storage surfaces -- under the `emulator` compatibility profile and the evidence recorded in `spec/compatibility/contract.json`; it makes no complete-suite and no unqualified superset claim while Realtime Database, Firebase Hosting, App Hosting and Data Connect are deferred and Firebase Extensions is not planned.
|
|
453
|
+
|
|
454
|
+
The `strict` and `emulator` profiles differ in some behaviors to serve their respective purposes. Where the official emulator and production Firebase disagree, Fireemu also adopts production behavior in some cases and records the difference.
|
|
455
|
+
|
|
456
|
+
Check support for the specific APIs and conditions your application uses, rather than relying on product names alone. The [compatibility contract](https://github.com/t-k/fireemu/blob/v0.11.0/spec/compatibility/contract.json) records scope and known differences. The [compatibility documentation](https://github.com/t-k/fireemu/blob/v0.11.0/docs/compatibility-contract.md) explains the verification process and how to interpret its records.
|
|
457
|
+
|
|
458
|
+
### Network exposure of the Storage port
|
|
459
|
+
|
|
460
|
+
Like the official emulator, the Storage port reflects the requested object name in the HTML body of its 404 for a missing media read (`text/html`, as production answers). A web page opened on the developer's machine could therefore run script on that origin. Keep the emulator bound to localhost, which is the default, and do not expose the Storage port to other hosts or networks. The strict profile sends no `X-Content-Type-Options: nosniff` on the JSON API answers where production sends none, so it does not add that defence in depth; the emulator profile keeps the official emulator's header.
|
|
461
|
+
|
|
462
|
+
### Changes between versions
|
|
463
|
+
|
|
464
|
+
Configuration and behavior may change between releases. See the [CHANGELOG](https://github.com/t-k/fireemu/blob/v0.11.0/CHANGELOG.md) for release changes.
|
|
465
|
+
|
|
466
|
+
## Performance
|
|
467
|
+
|
|
468
|
+
Fireemu is compared with the official Firestore emulator using the same SDK workloads. Both run sequentially on the same GitHub Actions Linux runner, with Fireemu using the `emulator` profile. The benchmark also validates operation results.
|
|
469
|
+
|
|
470
|
+
The following results come from one `standard` run at commit `f3b942c`. Ratios are calculated from five measured pairs; absolute values depend on the execution environment.
|
|
471
|
+
|
|
472
|
+
| Metric | Official emulator | Fireemu | Comparison |
|
|
473
|
+
|---|---:|---:|---:|
|
|
474
|
+
| Startup until the SDK can use Firestore | 3,043 ms | 140 ms | 21.6× faster |
|
|
475
|
+
| Idle memory with an empty database (PSS) | 390 MiB | 11 MiB | About 1/34 the memory |
|
|
476
|
+
| Peak memory during a trial (cgroup) | 849 MiB | 79 MiB | About 1/10.7 the memory |
|
|
477
|
+
| Firestore throughput across 22 workloads | — | — | 1.1× to 5.2× higher |
|
|
478
|
+
|
|
479
|
+
The 95% confidence interval for the startup-time ratio is `[21.4, 21.8]`. For the peak-memory ratio, calculated as official emulator divided by Fireemu, it is `[10.2, 11.1]`.
|
|
480
|
+
|
|
481
|
+
See [benchmark.yml](https://github.com/t-k/fireemu/blob/v0.11.0/.github/workflows/benchmark.yml) for the workflow and [`tools/bench/`](https://github.com/t-k/fireemu/tree/v0.11.0/tools/bench/) for the benchmark implementation.
|
|
98
482
|
|
|
99
|
-
##
|
|
483
|
+
## Reporting issues and contributing
|
|
100
484
|
|
|
101
|
-
|
|
102
|
-
own exact version, so `fireemu@1.2.3` can only resolve `@fireemu/linux-x64@1.2.3` -- an upgrade
|
|
103
|
-
moves the binary and the launcher together, never one without the other.
|
|
104
|
-
- **Uninstall**: `npm uninstall fireemu`. Nothing is installed outside `node_modules`: no cache
|
|
105
|
-
directory, no global binary, no downloaded component.
|
|
106
|
-
- **Offline**: `npm install --offline` works once the tarballs are in the npm cache, and so does
|
|
107
|
-
installing from a private registry that mirrors the `fireemu` and `@fireemu` packages. Warm a
|
|
108
|
-
cache with `npm install` on a machine of the same platform, or vendor the tarballs with
|
|
109
|
-
`npm pack`.
|
|
110
|
-
- **A vendored or self-built binary**: set `FIREEMU_BINARY_PATH` to it and the launcher runs that
|
|
111
|
-
instead of resolving a platform package.
|
|
112
|
-
- **If the binary is missing**: the launcher says which package it looked for and what is
|
|
113
|
-
published. The usual cause is an install that skipped optional dependencies
|
|
114
|
-
(`--omit=optional`, `--no-optional`, or a lockfile built on another platform).
|
|
485
|
+
When reporting an issue, include the Fireemu and Firebase SDK versions, the profile used, the Firebase product involved, and a minimal reproduction.
|
|
115
486
|
|
|
116
|
-
|
|
487
|
+
For compatibility issues, also state whether you are comparing against the actual Firebase service or the official emulator, along with the expected and actual results. Remove credentials and personal information before sharing configuration files or logs.
|
|
117
488
|
|
|
118
|
-
|
|
489
|
+
See the [development guide](https://github.com/t-k/fireemu/blob/v0.11.0/docs/development.md) for build and test instructions.
|
|
119
490
|
|
|
120
491
|
## License
|
|
121
492
|
|
|
122
|
-
Apache
|
|
493
|
+
Fireemu is licensed under the [Apache License 2.0](https://github.com/t-k/fireemu/blob/v0.11.0/LICENSE). Third-party notices are listed in [THIRD_PARTY_LICENSES.txt](https://github.com/t-k/fireemu/blob/v0.11.0/THIRD_PARTY_LICENSES.txt).
|
package/bin/fireemu.mjs
CHANGED
|
@@ -116,10 +116,11 @@ function main() {
|
|
|
116
116
|
ensureExecutable(binary.path);
|
|
117
117
|
// Keep terminal I/O and the foreground process group intact. PID-directed signals only
|
|
118
118
|
// reach this launcher, so forward them and wait for the daemon's own shutdown to finish.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
//
|
|
120
|
+
// The handlers are registered before the daemon is spawned: a signal that reached the launcher
|
|
121
|
+
// between spawn() and process.on() would kill it with the default action and orphan the daemon.
|
|
122
|
+
// Node runs a handler on the event loop, after main() has returned, so `child` exists by then.
|
|
123
|
+
let child;
|
|
123
124
|
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
124
125
|
process.on(signal, () => {
|
|
125
126
|
// Windows delivers console Ctrl-C to the child directly; child.kill would forcefully
|
|
@@ -129,6 +130,10 @@ function main() {
|
|
|
129
130
|
if (child.exitCode === null && child.signalCode === null) child.kill(signal);
|
|
130
131
|
});
|
|
131
132
|
}
|
|
133
|
+
child = spawn(binary.path, process.argv.slice(2), {
|
|
134
|
+
stdio: "inherit",
|
|
135
|
+
windowsHide: false,
|
|
136
|
+
});
|
|
132
137
|
child.on("error", (error) => {
|
|
133
138
|
if (child.pid) {
|
|
134
139
|
// A failed signal delivery must not orphan a daemon that is still running.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fireemu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "A local runtime for testing Firebase SDK and Functions code, checked against production Firebase behavior",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"emulator",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
],
|
|
28
28
|
"type": "module",
|
|
29
29
|
"optionalDependencies": {
|
|
30
|
-
"@fireemu/darwin-arm64": "0.
|
|
31
|
-
"@fireemu/darwin-x64": "0.
|
|
32
|
-
"@fireemu/linux-x64": "0.
|
|
33
|
-
"@fireemu/linux-arm64": "0.
|
|
34
|
-
"@fireemu/win32-x64": "0.
|
|
30
|
+
"@fireemu/darwin-arm64": "0.11.0",
|
|
31
|
+
"@fireemu/darwin-x64": "0.11.0",
|
|
32
|
+
"@fireemu/linux-x64": "0.11.0",
|
|
33
|
+
"@fireemu/linux-arm64": "0.11.0",
|
|
34
|
+
"@fireemu/win32-x64": "0.11.0"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=20"
|