@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 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 | Description |
355
- | -------------------------- | ----- | ------- | ------- | --------------------------------- |
356
- | `--init` | | string | | Generate config template |
357
- | `--config` | `-f` | string | | Path to config file |
358
- | `--source-project` | | string | | Source Firebase project |
359
- | `--dest-project` | | string | | Destination project |
360
- | `--collections` | `-c` | array | | Collections to transfer |
361
- | `--include-subcollections` | `-s` | boolean | `false` | Include subcollections |
362
- | `--where` | `-w` | array | | Filter documents |
363
- | `--exclude` | `-x` | array | | Exclude subcollections |
364
- | `--merge` | `-m` | boolean | `false` | Merge instead of overwrite |
365
- | `--parallel` | `-p` | number | `1` | Parallel transfers |
366
- | `--dry-run` | `-d` | boolean | `true` | Preview without writing |
367
- | `--batch-size` | `-b` | number | `500` | Documents per batch |
368
- | `--limit` | `-l` | number | `0` | Limit docs (0 = no limit) |
369
- | `--retries` | | number | `3` | Retries on error |
370
- | `--log` | | string | | Log file path |
371
- | `--quiet` | `-q` | boolean | `false` | No progress bar |
372
- | `--yes` | `-y` | boolean | `false` | Skip confirmation |
373
- | `--clear` | | boolean | `false` | Clear destination before transfer |
374
- | `--delete-missing` | | boolean | `false` | Delete dest docs not in source |
375
- | `--interactive` | `-i` | boolean | `false` | Interactive mode with prompts |
376
- | `--transform` | `-t` | string | | Path to JS/TS transform file |
377
- | `--rename-collection` | `-r` | array | | Rename collection (source:dest) |
378
- | `--id-prefix` | | string | | Add prefix to document IDs |
379
- | `--id-suffix` | | string | | Add suffix to document IDs |
380
- | `--webhook` | | string | | Webhook URL for notifications |
381
- | `--resume` | | boolean | `false` | Resume from saved state |
382
- | `--state-file` | | string | `.fscopy-state.json` | State file path |
383
- | `--verify` | | boolean | `false` | Verify counts after transfer |
384
- | `--rate-limit` | | number | `0` | Limit docs/second (0 = unlimited) |
385
- | `--skip-oversized` | | boolean | `false` | Skip documents > 1MB |
386
- | `--json` | | boolean | `false` | JSON output for CI/CD |
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.1.2",
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"