fireemu 0.8.0 → 0.10.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 CHANGED
@@ -1,122 +1,425 @@
1
- # fireemu
1
+ <!-- Generated from README.md by npm/scripts/prepare-launcher.mjs at pack time. Edit README.md in the repository, not this file. -->
2
2
 
3
- An experimental local runtime for testing Firebase SDK and Functions code.
3
+ # Fireemu
4
4
 
5
- Some Firebase failures are easy to miss locally. A query may need a composite index in production even though it passes against the official emulator. Production limits may reject a request that looked fine during development. Finding those problems only after running against a real Firebase project makes the feedback loop slow and can leave test data behind.
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
6
 
7
- fireemu was built for two jobs:
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.
8
8
 
9
- - catch selected production-facing problems earlier, including missing Firestore indexes and production limit violations;
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.
9
+ Fireemu is being developed to close these gaps. For example, it provides:
11
10
 
12
- fireemu is experimental. It is not a replacement for the official Firebase Emulator Suite or for testing against a real Firebase project. Use it as an additional test target, keep the official emulator in your test matrix, and verify important flows against production before shipping.
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.
13
14
 
14
- The real `firebase-admin`, `firebase` (Node and browser) and `firebase/firestore/lite` SDKs run against it unchanged. Run `npx fireemu capabilities` before depending on a specific API: each capability records whether it is implemented, partial, validation-only, or unsupported. The full feature summary, the known gaps from production Firebase and from the official Emulator Suite, and the project status are in the repository README linked below.
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.
15
16
 
16
- ## Install
17
+ See the [capability manifest](https://github.com/t-k/fireemu/blob/v0.10.0/crates/fireemu/src/capabilities.json) and [compatibility documentation](https://github.com/t-k/fireemu/blob/v0.10.0/docs/compatibility-contract.md) for supported features, known differences, and the status of compatibility verification.
18
+
19
+ ## Quick start
20
+
21
+ Node.js 20 or newer is required.
17
22
 
18
23
  ```sh
19
- npm install -D fireemu
24
+ npm install --save-dev fireemu
20
25
  npx fireemu init
21
- npx fireemu doctor
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
- pnpm add -D fireemu # or: pnpm dlx fireemu doctor
26
- yarn add -D fireemu
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
- Nothing is downloaded at install time and there is no install script. `fireemu` is a small Node
30
- launcher; the daemon for your platform arrives as an optional dependency
31
- (`@fireemu/darwin-arm64` and friends), so npm installs exactly one binary and skips the rest.
32
- An install that resolved from a cache or a private registry is a complete, offline installation.
44
+ ### Supported platforms
33
45
 
34
- ## Initialize
46
+ | OS | Architecture |
47
+ |---|---|
48
+ | macOS 13 or newer | Apple silicon, Intel |
49
+ | Linux | x86-64, arm64 |
50
+ | Windows 10 or newer | x86-64 |
35
51
 
36
- `npx fireemu init` creates `fireemu.json` with the recommended `strict` profile. In a terminal, its wizard explains the production behavior supplied by `strict`, the pinned official emulator behavior selected by `emulator`, and how a referenced Firebase project configuration stays live. In CI or with redirected input it uses the strict defaults without prompting; use `--interactive` to force the wizard or `--yes`/`--no-interactive` to suppress it.
52
+ Running Functions requires `firebase-functions` v6 or v7 in the codebase.
37
53
 
38
- If the project already has `firebase.json`, init records it as a live `firebaseJson` reference so rules, indexes, Functions codebases and emulator ports are loaded again on every start. `--profile emulator` and `--firebase-json <file>` choose alternatives explicitly. Existing `fireemu.json` files are preserved unless `--force` is supplied, and a symbolic link is never overwritten.
54
+ ## Supported scope
55
+
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.10.0/crates/fireemu/src/capabilities.json).
39
70
 
40
71
  ```sh
41
- npx fireemu init --yes
42
- npx fireemu up --config fireemu.json
72
+ npx fireemu capabilities
73
+ ```
74
+
75
+ See the [Functions support inventory](https://github.com/t-k/fireemu/blob/v0.10.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>
43
96
  ```
44
97
 
45
- ## Use
98
+ ### Run a command
99
+
100
+ Service selection also works with `exec`.
46
101
 
47
102
  ```sh
48
- npx fireemu up --config fireemu.json
103
+ npx fireemu exec --only auth,firestore,storage -- npm test
49
104
  ```
50
105
 
51
- `exec` is the `firebase emulators:exec` equivalent: it serves the same, runs a command with the
52
- emulator host variables once every listener is bound, stops everything when the command exits and
53
- exits with its status.
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.
54
109
 
55
110
  ```sh
56
- npx fireemu exec --firebase-json firebase.json --project my-app --only auth,firestore,storage -- vitest run
111
+ npx fireemu emulators:start
112
+ npx fireemu emulators:exec -- npm test
57
113
  ```
58
114
 
59
- The command receives `FIRESTORE_EMULATOR_HOST`, `FIREBASE_AUTH_EMULATOR_HOST`,
60
- `FIREBASE_STORAGE_EMULATOR_HOST` / `STORAGE_EMULATOR_HOST`, `GOOGLE_CLOUD_PROJECT` /
61
- `GCLOUD_PROJECT`, and `FIREEMU_CONTROL_TOKEN` / `FIREEMU_CONTROL_URL` for the control API.
115
+ ### UI
62
116
 
63
- An Emulator UI is compiled into the binary and served at `http://127.0.0.1:4000/ui`
64
- (`--ui-port 0` turns it off).
117
+ The default UI address is `http://127.0.0.1:4000`. Use `--ui-port` to specify a port.
65
118
 
66
- ## Supported platforms
119
+ ```sh
120
+ # Start the UI on port 4001
121
+ npx fireemu up --ui-port 4001
122
+ ```
67
123
 
68
- | Package | OS | Arch | Built for |
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` |
124
+ `exec` does not start the UI by default. Add `--ui` to use it while the command runs.
125
+
126
+ ```sh
127
+ npx fireemu exec --ui -- npm test
128
+ ```
75
129
 
76
- The Linux builds are statically linked against musl, so they run on any distribution and inside
77
- distroless and Alpine containers.
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.
78
131
 
79
- ## Prerequisites
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.
80
133
 
81
- - **Node 20 or newer** to run the launcher, and to run a Functions codebase. Nothing else needs
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.
134
+ ### Export data
86
135
 
87
- ## Check an installation
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
+ ```
141
+
142
+ On Windows, `emulators:export` and `--export-on-exit` are not currently available. Import and other emulator functionality remain available.
143
+
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.
145
+
146
+ ### Troubleshooting
147
+
148
+ For startup or installation problems, inspect the environment and bundled files with:
88
149
 
89
150
  ```sh
90
151
  npx fireemu doctor
91
152
  ```
92
153
 
93
- It reports the version and platform of the installed binary, whether the Emulator UI is compiled
94
- in, where the Node runner was found and which `firebase-functions` majors it instruments, the Node
95
- version, and that no JVM is required. Anything missing comes with a remediation line, and a broken
96
- installation exits non-zero so a setup script can gate on it. The report carries versions and paths
97
- only -- never tokens, keys, or the contents of a configuration file.
154
+ The command reports information about the Fireemu binary, UI, Functions runner, and Node.js, and suggests remedies when it finds missing components or problems.
155
+
156
+ ## Configuration
157
+
158
+ ### Initial setup and reusing `firebase.json`
159
+
160
+ `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`.
161
+
162
+ 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.
163
+
164
+ Use the non-interactive form for CI or scripted setup.
165
+
166
+ ```sh
167
+ npx fireemu init --yes
168
+ ```
169
+
170
+ An existing `fireemu.json` is not overwritten unless `--force` is supplied.
171
+
172
+ ### Compatibility profiles
173
+
174
+ | Profile | Behavior |
175
+ |---|---|
176
+ | `strict` | Matches production Firebase behavior and constraints as closely as possible within the supported scope. This is the default profile |
177
+ | `emulator` | Targets the behavior of a pinned version of the official emulator. Known differences are documented in the compatibility documentation |
178
+
179
+ 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.
180
+
181
+ 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.
182
+
183
+ ### Basic configuration
184
+
185
+ The initial configuration selects Standard edition Firestore with the Native API.
186
+
187
+ ```json
188
+ {
189
+ "$schema": "https://fireemu.dev/spec/config/fireemu.schema.json",
190
+ "schemaVersion": 1,
191
+ "profile": "strict",
192
+ "firestore": {
193
+ "edition": "standard",
194
+ "apiMode": "native"
195
+ }
196
+ }
197
+ ```
198
+
199
+ You can specify the configuration file with `--config`.
200
+
201
+ ```sh
202
+ npx fireemu up --config ./fireemu.json
203
+ ```
204
+
205
+ 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.10.0/spec/config/fireemu.schema.json) for all available settings.
206
+
207
+ ### Firebase Authentication (Identity Platform) configuration
208
+
209
+ 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.
210
+
211
+ | What you can configure | Main settings |
212
+ |---|---|
213
+ | Enable or disable email/password, anonymous, and phone sign-in | `auth.signIn` |
214
+ | Test phone numbers and verification codes | `auth.signIn.phoneNumber.testPhoneNumbers` |
215
+ | Initial authorized domains for action-link continue URLs | `auth.authorizedDomains` |
216
+ | Handling of accounts that share an email address | `auth.signIn.allowDuplicateEmails` |
217
+ | Password length and character requirements, and policy enforcement at sign-in | `auth.passwordPolicy` |
218
+ | TOTP-based multi-factor authentication | `auth.totp` |
219
+ | Initial multi-factor authentication (MFA) configuration of a project, such as enabling TOTP under the `strict` profile | `auth.mfa` |
220
+ | Tenants that exist when the daemon starts, and the multi-tenancy switch | `auth.tenants`, `auth.multiTenant` |
221
+ | Restrictions on end-user account creation and deletion | `auth.client.permissions` |
222
+ | Email enumeration protection | `auth.improvedEmailPrivacy` |
223
+ | Project- and tenant-specific password policies and account permissions | `auth.passwordPolicyOverrides`, `auth.configOverrides` |
224
+ | Blocking functions before account creation or sign-in, and credential forwarding to those functions | `auth.blockingFunctions` |
225
+ | Initial custom OIDC and SAML provider resources | `auth.providers.oidc`, `auth.providers.saml` |
226
+ | Public keys for verifying custom tokens and OIDC ID tokens | `auth.customTokenSigners`, `auth.idpSigners` |
227
+ | Sign-up quota configuration and local quota-exceeded simulation | `auth.quota`, `auth.quotaSimulation` |
228
+
229
+ See the [configuration schema](https://github.com/t-k/fireemu/blob/v0.10.0/spec/config/fireemu.schema.json) for the format and accepted values of each setting.
230
+
231
+ 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.10.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.
232
+
233
+ 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.
234
+
235
+ 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.
236
+
237
+ 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.
238
+
239
+ #### Example configuration
240
+
241
+ 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.
242
+
243
+ ```json
244
+ {
245
+ "$schema": "https://fireemu.dev/spec/config/fireemu.schema.json",
246
+ "schemaVersion": 1,
247
+ "profile": "strict",
248
+ "firestore": {
249
+ "edition": "standard",
250
+ "apiMode": "native"
251
+ },
252
+ "auth": {
253
+ "signIn": {
254
+ "allowDuplicateEmails": false,
255
+ "email": {
256
+ "enabled": true,
257
+ "passwordRequired": true
258
+ },
259
+ "anonymous": {
260
+ "enabled": false
261
+ },
262
+ "phoneNumber": {
263
+ "enabled": true,
264
+ "testPhoneNumbers": {
265
+ "+16505550101": "123456"
266
+ }
267
+ }
268
+ },
269
+ "passwordPolicy": {
270
+ "enforcementState": "ENFORCE",
271
+ "forceUpgradeOnSignin": true,
272
+ "constraints": {
273
+ "minLength": 12,
274
+ "requireUppercase": true,
275
+ "requireLowercase": true,
276
+ "requireNumeric": true
277
+ }
278
+ },
279
+ "client": {
280
+ "permissions": {
281
+ "disabledUserSignup": false,
282
+ "disabledUserDeletion": true
283
+ }
284
+ },
285
+ "improvedEmailPrivacy": true
286
+ }
287
+ }
288
+ ```
289
+
290
+ For an existing `fireemu.json`, add the settings you need to its `auth` section. Adjust the values to match your application’s requirements.
291
+
292
+ #### Project- and tenant-specific settings
293
+
294
+ 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.
295
+
296
+ 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).
297
+
298
+ #### Enable multi-factor authentication at start
299
+
300
+ 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`):
301
+
302
+ ```json
303
+ {
304
+ "schemaVersion": 1,
305
+ "profile": "strict",
306
+ "auth": {
307
+ "mfa": {
308
+ "state": "ENABLED",
309
+ "providerConfigs": [
310
+ { "state": "ENABLED", "totpProviderConfig": { "adjacentIntervals": 1 } }
311
+ ]
312
+ }
313
+ }
314
+ }
315
+ ```
316
+
317
+ - 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.
318
+ - 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.
319
+ - `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.
320
+ - An export does not carry the project's `mfa` configuration, so an imported project keeps its own `auth.mfa` or Admin-set configuration.
321
+ - 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`.
322
+
323
+ #### Enable multi-tenancy
324
+
325
+ 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.10.0/docs/compatibility-contract.md) for the supported Admin API scope.
326
+
327
+ #### Start with tenants
328
+
329
+ 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`:
330
+
331
+ ```json
332
+ {
333
+ "schemaVersion": 1,
334
+ "profile": "strict",
335
+ "auth": {
336
+ "multiTenant": { "allowTenants": true },
337
+ "tenants": [
338
+ {
339
+ "tenantId": "acme-x7k2q",
340
+ "displayName": "acme",
341
+ "allowPasswordSignup": true,
342
+ "mfaConfig": { "state": "ENABLED", "enabledProviders": ["PHONE_SMS"] }
343
+ }
344
+ ]
345
+ }
346
+ }
347
+ ```
348
+
349
+ - 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.
350
+ - 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.
351
+ - 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.
352
+ - 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.
353
+ - 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.
354
+
355
+ #### Test quota-exceeded behavior
356
+
357
+ Use `auth.quotaSimulation` to test sign-up quota-exceeded behavior locally. For example, set `auth.quotaSimulation` to:
358
+
359
+ ```json
360
+ {
361
+ "mode": "enforce",
362
+ "defaultQuotaPerHour": 5
363
+ }
364
+ ```
365
+
366
+ This is a local fixed-window simulation. It does not reproduce Firebase’s full quota accounting or abuse-prevention systems.
367
+
368
+ ### Configure execution time
369
+
370
+ 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.
371
+
372
+ See the [configuration schema](https://github.com/t-k/fireemu/blob/v0.10.0/spec/config/fireemu.schema.json) for available settings and values.
373
+
374
+ ## Compatibility and limitations
375
+
376
+ ### Differences from production Firebase
377
+
378
+ Fireemu aims to match production Firebase behavior, but a successful local operation does not guarantee the same result in production.
379
+
380
+ 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.
381
+
382
+ 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.
383
+
384
+ ### Comparison with the official emulator
385
+
386
+ The official emulator comparison baseline is `firebase-tools 15.28.2`.
387
+
388
+ 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.
389
+
390
+ 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.
391
+
392
+ 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.10.0/spec/compatibility/contract.json) records scope and known differences. The [compatibility documentation](https://github.com/t-k/fireemu/blob/v0.10.0/docs/compatibility-contract.md) explains the verification process and how to interpret its records.
393
+
394
+ ### Changes between versions
395
+
396
+ Configuration and behavior may change between releases. See the [CHANGELOG](https://github.com/t-k/fireemu/blob/v0.10.0/CHANGELOG.md) for release changes.
397
+
398
+ ## Performance
399
+
400
+ 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.
401
+
402
+ 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.
403
+
404
+ | Metric | Official emulator | Fireemu | Comparison |
405
+ |---|---:|---:|---:|
406
+ | Startup until the SDK can use Firestore | 3,043 ms | 140 ms | 21.6× faster |
407
+ | Idle memory with an empty database (PSS) | 390 MiB | 11 MiB | About 1/34 the memory |
408
+ | Peak memory during a trial (cgroup) | 849 MiB | 79 MiB | About 1/10.7 the memory |
409
+ | Firestore throughput across 22 workloads | — | — | 1.1× to 5.2× higher |
410
+
411
+ 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]`.
412
+
413
+ See [benchmark.yml](https://github.com/t-k/fireemu/blob/v0.10.0/.github/workflows/benchmark.yml) for the workflow and [`tools/bench/`](https://github.com/t-k/fireemu/tree/v0.10.0/tools/bench/) for the benchmark implementation.
98
414
 
99
- ## Upgrade, uninstall, offline
415
+ ## Reporting issues and contributing
100
416
 
101
- - **Upgrade**: `npm install -D fireemu@<version>`. The launcher pins its platform packages to its
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).
417
+ When reporting an issue, include the Fireemu and Firebase SDK versions, the profile used, the Firebase product involved, and a minimal reproduction.
115
418
 
116
- ## Documentation and source
419
+ 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
420
 
118
- <https://github.com/t-k/fireemu>
421
+ See the [development guide](https://github.com/t-k/fireemu/blob/v0.10.0/docs/development.md) for build and test instructions.
119
422
 
120
423
  ## License
121
424
 
122
- Apache-2.0
425
+ Fireemu is licensed under the [Apache License 2.0](https://github.com/t-k/fireemu/blob/v0.10.0/LICENSE). Third-party notices are listed in [THIRD_PARTY_LICENSES.txt](https://github.com/t-k/fireemu/blob/v0.10.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
- const child = spawn(binary.path, process.argv.slice(2), {
120
- stdio: "inherit",
121
- windowsHide: false,
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,7 +1,7 @@
1
1
  {
2
2
  "name": "fireemu",
3
- "version": "0.8.0",
4
- "description": "An experimental local runtime for testing Firebase SDK and Functions code",
3
+ "version": "0.10.0",
4
+ "description": "A local runtime for testing Firebase SDK and Functions code, checked against production Firebase behavior",
5
5
  "keywords": [
6
6
  "emulator",
7
7
  "firebase",
@@ -27,11 +27,11 @@
27
27
  ],
28
28
  "type": "module",
29
29
  "optionalDependencies": {
30
- "@fireemu/darwin-arm64": "0.8.0",
31
- "@fireemu/darwin-x64": "0.8.0",
32
- "@fireemu/linux-x64": "0.8.0",
33
- "@fireemu/linux-arm64": "0.8.0",
34
- "@fireemu/win32-x64": "0.8.0"
30
+ "@fireemu/darwin-arm64": "0.10.0",
31
+ "@fireemu/darwin-x64": "0.10.0",
32
+ "@fireemu/linux-x64": "0.10.0",
33
+ "@fireemu/linux-arm64": "0.10.0",
34
+ "@fireemu/win32-x64": "0.10.0"
35
35
  },
36
36
  "engines": {
37
37
  "node": ">=20"