@fazetitans/fscopy 1.1.2 → 1.2.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 +86 -33
- package/package.json +3 -3
- package/src/cli.ts +82 -620
- package/src/config/defaults.ts +4 -0
- package/src/config/parser.ts +4 -0
- package/src/config/validator.ts +52 -0
- package/src/firebase/index.ts +82 -0
- package/src/interactive.ts +59 -56
- package/src/orchestrator.ts +407 -0
- package/src/output/display.ts +221 -0
- package/src/state/index.ts +188 -1
- package/src/transfer/clear.ts +162 -104
- package/src/transfer/count.ts +83 -44
- package/src/transfer/transfer.ts +487 -156
- package/src/transform/loader.ts +31 -0
- package/src/types.ts +18 -0
- package/src/utils/credentials.ts +9 -4
- package/src/utils/doc-size.ts +41 -70
- package/src/utils/errors.ts +1 -1
- package/src/utils/index.ts +2 -1
- package/src/utils/integrity.ts +122 -0
- package/src/utils/logger.ts +59 -3
- package/src/utils/output.ts +265 -0
- package/src/utils/patterns.ts +3 -2
- package/src/utils/progress.ts +102 -0
- package/src/utils/rate-limiter.ts +4 -2
- package/src/webhook/index.ts +24 -6
package/README.md
CHANGED
|
@@ -237,6 +237,8 @@ The transform function receives:
|
|
|
237
237
|
|
|
238
238
|
Return the transformed document, or `null` to skip it.
|
|
239
239
|
|
|
240
|
+
> **Security Warning**: The `--transform` option executes arbitrary code from the specified file. Only use transform files from trusted sources. Never run transforms from untrusted or unverified files as they have full access to your system.
|
|
241
|
+
|
|
240
242
|
### Webhook Notifications
|
|
241
243
|
|
|
242
244
|
Get notified when transfers complete (success or failure):
|
|
@@ -351,39 +353,42 @@ fscopy --init config.json
|
|
|
351
353
|
|
|
352
354
|
## CLI Reference
|
|
353
355
|
|
|
354
|
-
| Option | Alias | Type | Default
|
|
355
|
-
| -------------------------- | ----- | ------- |
|
|
356
|
-
| `--init` | | string |
|
|
357
|
-
| `--config` | `-f` | string |
|
|
358
|
-
| `--source-project` | | string |
|
|
359
|
-
| `--dest-project` | | string |
|
|
360
|
-
| `--collections` | `-c` | array |
|
|
361
|
-
| `--include-subcollections` | `-s` | boolean | `false`
|
|
362
|
-
| `--where` | `-w` | array |
|
|
363
|
-
| `--exclude` | `-x` | array |
|
|
364
|
-
| `--merge` | `-m` | boolean | `false`
|
|
365
|
-
| `--parallel` | `-p` | number | `1`
|
|
366
|
-
| `--dry-run` | `-d` | boolean | `true`
|
|
367
|
-
| `--batch-size` | `-b` | number | `500`
|
|
368
|
-
| `--limit` | `-l` | number | `0`
|
|
369
|
-
| `--retries` | | number | `3`
|
|
370
|
-
| `--log` | | string |
|
|
371
|
-
| `--quiet` | `-q` | boolean | `false`
|
|
372
|
-
| `--yes` | `-y` | boolean | `false`
|
|
373
|
-
| `--clear` | | boolean | `false`
|
|
374
|
-
| `--delete-missing` | | boolean | `false`
|
|
375
|
-
| `--interactive` | `-i` | boolean | `false`
|
|
376
|
-
| `--transform` | `-t` | string |
|
|
377
|
-
| `--rename-collection` | `-r` | array |
|
|
378
|
-
| `--id-prefix` | | string |
|
|
379
|
-
| `--id-suffix` | | string |
|
|
380
|
-
| `--webhook` | | string |
|
|
381
|
-
| `--resume` | | boolean | `false`
|
|
382
|
-
| `--state-file` | | string | `.fscopy-state.json` | State file path
|
|
383
|
-
| `--verify` | | boolean | `false`
|
|
384
|
-
| `--rate-limit` | | number | `0`
|
|
385
|
-
| `--skip-oversized` | | boolean | `false`
|
|
386
|
-
| `--json` | | boolean | `false`
|
|
356
|
+
| Option | Alias | Type | Default | Description |
|
|
357
|
+
| -------------------------- | ----- | ------- | -------------------- | --------------------------------------- |
|
|
358
|
+
| `--init` | | string | | Generate config template |
|
|
359
|
+
| `--config` | `-f` | string | | Path to config file |
|
|
360
|
+
| `--source-project` | | string | | Source Firebase project |
|
|
361
|
+
| `--dest-project` | | string | | Destination project |
|
|
362
|
+
| `--collections` | `-c` | array | | Collections to transfer |
|
|
363
|
+
| `--include-subcollections` | `-s` | boolean | `false` | Include subcollections |
|
|
364
|
+
| `--where` | `-w` | array | | Filter documents |
|
|
365
|
+
| `--exclude` | `-x` | array | | Exclude subcollections |
|
|
366
|
+
| `--merge` | `-m` | boolean | `false` | Merge instead of overwrite |
|
|
367
|
+
| `--parallel` | `-p` | number | `1` | Parallel transfers |
|
|
368
|
+
| `--dry-run` | `-d` | boolean | `true` | Preview without writing |
|
|
369
|
+
| `--batch-size` | `-b` | number | `500` | Documents per batch |
|
|
370
|
+
| `--limit` | `-l` | number | `0` | Limit docs (0 = no limit) |
|
|
371
|
+
| `--retries` | | number | `3` | Retries on error |
|
|
372
|
+
| `--log` | | string | | Log file path |
|
|
373
|
+
| `--quiet` | `-q` | boolean | `false` | No progress bar |
|
|
374
|
+
| `--yes` | `-y` | boolean | `false` | Skip confirmation |
|
|
375
|
+
| `--clear` | | boolean | `false` | Clear destination before transfer |
|
|
376
|
+
| `--delete-missing` | | boolean | `false` | Delete dest docs not in source |
|
|
377
|
+
| `--interactive` | `-i` | boolean | `false` | Interactive mode with prompts |
|
|
378
|
+
| `--transform` | `-t` | string | | Path to JS/TS transform file |
|
|
379
|
+
| `--rename-collection` | `-r` | array | | Rename collection (source:dest) |
|
|
380
|
+
| `--id-prefix` | | string | | Add prefix to document IDs |
|
|
381
|
+
| `--id-suffix` | | string | | Add suffix to document IDs |
|
|
382
|
+
| `--webhook` | | string | | Webhook URL for notifications |
|
|
383
|
+
| `--resume` | | boolean | `false` | Resume from saved state |
|
|
384
|
+
| `--state-file` | | string | `.fscopy-state.json` | State file path |
|
|
385
|
+
| `--verify` | | boolean | `false` | Verify counts after transfer |
|
|
386
|
+
| `--rate-limit` | | number | `0` | Limit docs/second (0 = unlimited) |
|
|
387
|
+
| `--skip-oversized` | | boolean | `false` | Skip documents > 1MB |
|
|
388
|
+
| `--json` | | boolean | `false` | JSON output for CI/CD |
|
|
389
|
+
| `--max-depth` | | number | `0` | Max subcollection depth (0 = unlimited) |
|
|
390
|
+
| `--detect-conflicts` | | boolean | `false` | Detect concurrent modifications |
|
|
391
|
+
| `--verify-integrity` | | boolean | `false` | Verify document integrity with hash |
|
|
387
392
|
|
|
388
393
|
## How It Works
|
|
389
394
|
|
|
@@ -393,6 +398,14 @@ fscopy --init config.json
|
|
|
393
398
|
4. **Retry logic** - Automatic retry with exponential backoff on failures
|
|
394
399
|
5. **Subcollection discovery** - Uses `listCollections()` to find nested data
|
|
395
400
|
|
|
401
|
+
## Security
|
|
402
|
+
|
|
403
|
+
- **Transform files execute arbitrary code** - The `--transform` option uses dynamic imports to load and execute JavaScript/TypeScript files. Only use transform files you have written or thoroughly reviewed. Malicious transform files could access your filesystem, network, or credentials.
|
|
404
|
+
|
|
405
|
+
- **Webhook URLs should use HTTPS** - fscopy warns if you use HTTP webhooks (except localhost). Webhook payloads contain project names and transfer statistics that could be sensitive.
|
|
406
|
+
|
|
407
|
+
- **Credentials via ADC** - fscopy uses Google Application Default Credentials. Ensure you're authenticated with the correct account before running transfers.
|
|
408
|
+
|
|
396
409
|
## Notes
|
|
397
410
|
|
|
398
411
|
- **Dry run is ON by default** - Use `-d false` for actual transfer
|
|
@@ -405,6 +418,46 @@ fscopy --init config.json
|
|
|
405
418
|
- **Transform applies to all** - Transform function is applied to both root and subcollection docs
|
|
406
419
|
- **Same project allowed** - Source and destination can be the same project when using `--rename-collection` or `--id-prefix`/`--id-suffix`
|
|
407
420
|
|
|
421
|
+
## Limitations
|
|
422
|
+
|
|
423
|
+
### Firestore Special Types
|
|
424
|
+
|
|
425
|
+
When reading documents, Firestore sentinel values are resolved to their actual values:
|
|
426
|
+
|
|
427
|
+
| Sentinel | Behavior |
|
|
428
|
+
| -------- | -------- |
|
|
429
|
+
| `serverTimestamp()` | Resolved to actual `Timestamp` value |
|
|
430
|
+
| `increment()` | Resolved to current numeric value |
|
|
431
|
+
| `arrayUnion()` / `arrayRemove()` | Resolved to current array value |
|
|
432
|
+
|
|
433
|
+
These sentinels are **write-time operations**, not persistent values. fscopy transfers the resolved data, which is the expected behavior for data migration.
|
|
434
|
+
|
|
435
|
+
### Document References
|
|
436
|
+
|
|
437
|
+
`DocumentReference` fields are transferred as-is. If the reference points to a document in the source project, it will still point to the source after transfer. Consider using `--transform` to update references if needed.
|
|
438
|
+
|
|
439
|
+
### Collection and Document IDs
|
|
440
|
+
|
|
441
|
+
fscopy validates IDs according to Firestore rules:
|
|
442
|
+
|
|
443
|
+
- Cannot be empty
|
|
444
|
+
- Cannot be `.` or `..`
|
|
445
|
+
- Cannot match `__*__` pattern (reserved by Firestore)
|
|
446
|
+
|
|
447
|
+
Unicode characters, special characters (`#`, `$`, `[`, `]`), and forward slashes in nested paths are all supported.
|
|
448
|
+
|
|
449
|
+
### Subcollection Depth
|
|
450
|
+
|
|
451
|
+
Use `--max-depth` to limit recursion when copying deeply nested subcollections:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
# Copy only first level of subcollections
|
|
455
|
+
fscopy -f config.ini -s --max-depth 1
|
|
456
|
+
|
|
457
|
+
# Copy up to 3 levels deep
|
|
458
|
+
fscopy -f config.ini -s --max-depth 3
|
|
459
|
+
```
|
|
460
|
+
|
|
408
461
|
## Development
|
|
409
462
|
|
|
410
463
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fazetitans/fscopy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Fast CLI tool to copy Firestore collections between Firebase projects with filtering, parallel transfers, and subcollection support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"test": "bun test",
|
|
13
13
|
"test:watch": "bun test --watch",
|
|
14
14
|
"type-check": "tsc --noEmit",
|
|
15
|
-
"lint": "eslint src/**/*.ts",
|
|
16
|
-
"lint:fix": "eslint src/**/*.ts --fix",
|
|
15
|
+
"lint": "eslint src/**/*.ts --ignore-pattern 'src/__tests__/**' --no-warn-ignored",
|
|
16
|
+
"lint:fix": "eslint src/**/*.ts --ignore-pattern 'src/__tests__/**' --no-warn-ignored --fix",
|
|
17
17
|
"format": "prettier --write src/**/*.ts",
|
|
18
18
|
"format:check": "prettier --check src/**/*.ts",
|
|
19
19
|
"prepublishOnly": "bun run type-check && bun run lint && bun test"
|