@autofleet/cli 2.27.0-alpha.1 → 2.27.0-alpha.2
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/dist/claude-marketplace/plugins/autofleet/commands/update-packages.md +393 -26
- package/dist/e2e.js +1 -1
- package/dist/git-autofleet.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{utils-BNpnqNAk.js → utils-Dnt11RmF.js} +2 -2
- package/dist/{utils-BNpnqNAk.js.map → utils-Dnt11RmF.js.map} +1 -1
- package/package.json +1 -1
|
@@ -10,6 +10,8 @@ You are migrating a microservice to use the latest versions of all `@autofleet/*
|
|
|
10
10
|
|
|
11
11
|
Follow these steps **in order**. For each step, first check if the migration applies to this service. If it does, add it to your TODO list, then execute it. Do NOT skip any applicable migration. Do NOT proceed to the next migration until the current one is complete.
|
|
12
12
|
|
|
13
|
+
**Critical rule: Never mix module systems.** When adding imports to a file, always match the module system already used in that file. If the file uses `import`/`export` (ESM), add `import` statements. If the file uses `require`/`module.exports` (CJS), add `require` calls. Never add a `require` in an ESM file or an `import` in a CJS file.
|
|
14
|
+
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
## Step 0: Scan and update package versions
|
|
@@ -343,8 +345,23 @@ const rabbit = new RabbitMq({
|
|
|
343
345
|
|
|
344
346
|
### 6.1 Replace express Router with node-common Router
|
|
345
347
|
|
|
346
|
-
Search ALL files for `require('express')` where `Router` is destructured, or `import { Router } from 'express'
|
|
348
|
+
Search ALL files for `require('express')` where `Router` is destructured, or `import { Router } from 'express'`.
|
|
349
|
+
|
|
350
|
+
**Critical: Match the module system of the file you are editing.** If the file uses `import`/`export` (ESM), use `import`. If it uses `require`/`module.exports` (CJS), use `require`. Never mix them.
|
|
351
|
+
|
|
352
|
+
**ESM file (uses `import`/`export`):**
|
|
353
|
+
```ts
|
|
354
|
+
// BEFORE
|
|
355
|
+
import { Router } from 'express';
|
|
356
|
+
const router = Router();
|
|
357
|
+
|
|
358
|
+
// AFTER
|
|
359
|
+
import { Router } from '@autofleet/node-common';
|
|
360
|
+
import logger from '../../logger'; // adjust relative path
|
|
361
|
+
const router = Router({ logger });
|
|
362
|
+
```
|
|
347
363
|
|
|
364
|
+
**CJS file (uses `require`/`module.exports`):**
|
|
348
365
|
```js
|
|
349
366
|
// BEFORE
|
|
350
367
|
const { Router } = require('express');
|
|
@@ -368,19 +385,164 @@ const router = Router();
|
|
|
368
385
|
const router = Router({ logger });
|
|
369
386
|
```
|
|
370
387
|
|
|
371
|
-
Each file that creates a router needs a logger import. Adjust the relative path based on the file's location relative to the logger module.
|
|
388
|
+
Each file that creates a router needs a logger import. Use `import` if the file is ESM, `require` if CJS. Adjust the relative path based on the file's location relative to the logger module.
|
|
372
389
|
|
|
373
390
|
**Important:** Files that already have `const { Router } = require('express')` just for types or other Express utilities — check if `Router` is actually used. Only update files that call `Router()`.
|
|
374
391
|
|
|
375
392
|
---
|
|
376
393
|
|
|
377
|
-
## Step 7:
|
|
394
|
+
## Step 7: Create a consolidated Redis client and migrate @autofleet/matmon (v3)
|
|
395
|
+
|
|
396
|
+
**Applies when:** The service has `@autofleet/matmon` in its dependencies OR uses Redis directly.
|
|
397
|
+
|
|
398
|
+
Matmon v3 uses `redis@4` (native promises, no more `*Async` methods). The service needs a shared Redis client instance that is passed to both matmon's `RedisCache` and used directly where needed.
|
|
399
|
+
|
|
400
|
+
### 7.1 Add `redis` as a production dependency
|
|
401
|
+
|
|
402
|
+
Move `redis` from devDependencies to dependencies (or add it if missing):
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
npm install redis@^4.7.1
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
If `redis` is currently in `devDependencies`, remove it from there.
|
|
409
|
+
|
|
410
|
+
### 7.2 Create a consolidated Redis client file
|
|
411
|
+
|
|
412
|
+
If the service doesn't already have a shared Redis client file, create `src/lib/redis.ts` (or `src/lib/redis.js`):
|
|
413
|
+
|
|
414
|
+
**TypeScript:**
|
|
415
|
+
```ts
|
|
416
|
+
import { createClient } from 'redis';
|
|
417
|
+
import logger from '../logger';
|
|
418
|
+
|
|
419
|
+
const client = createClient({
|
|
420
|
+
socket: {
|
|
421
|
+
connectTimeout: 60_000,
|
|
422
|
+
reconnectStrategy: (retries, cause) => {
|
|
423
|
+
logger.warn(`Redis reconnect attempt #${retries} due to:`, cause);
|
|
424
|
+
return Math.min(retries * 50, 2000); // Exponential backoff up to 2 seconds
|
|
425
|
+
},
|
|
426
|
+
host: process.env.REDIS_HOST_NAME,
|
|
427
|
+
port: Number.parseInt(process.env.REDIS_HOST_PORT || '6379', 10),
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
client.on('error', (err) => {
|
|
432
|
+
logger.error('Redis Client Error:', err);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
client.on('connect', () => {
|
|
436
|
+
logger.info('Redis client is connecting...');
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
client.on('ready', () => {
|
|
440
|
+
logger.info('Redis client is ready to use.');
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
(async () => {
|
|
444
|
+
try {
|
|
445
|
+
await client.connect();
|
|
446
|
+
logger.info('Redis client connected successfully.');
|
|
447
|
+
} catch (error) {
|
|
448
|
+
logger.error('Failed to connect to Redis:', error);
|
|
449
|
+
}
|
|
450
|
+
})();
|
|
451
|
+
|
|
452
|
+
export default client;
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**JavaScript (CJS):**
|
|
456
|
+
```js
|
|
457
|
+
const { createClient } = require('redis');
|
|
458
|
+
const logger = require('../logger');
|
|
459
|
+
|
|
460
|
+
const client = createClient({
|
|
461
|
+
socket: {
|
|
462
|
+
connectTimeout: 60_000,
|
|
463
|
+
reconnectStrategy: (retries, cause) => {
|
|
464
|
+
logger.warn(`Redis reconnect attempt #${retries} due to:`, cause);
|
|
465
|
+
return Math.min(retries * 50, 2000);
|
|
466
|
+
},
|
|
467
|
+
host: process.env.REDIS_HOST_NAME,
|
|
468
|
+
port: Number.parseInt(process.env.REDIS_HOST_PORT || '6379', 10),
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
client.on('error', (err) => {
|
|
473
|
+
logger.error('Redis Client Error:', err);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
client.on('connect', () => {
|
|
477
|
+
logger.info('Redis client is connecting...');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
client.on('ready', () => {
|
|
481
|
+
logger.info('Redis client is ready to use.');
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
(async () => {
|
|
485
|
+
try {
|
|
486
|
+
await client.connect();
|
|
487
|
+
logger.info('Redis client connected successfully.');
|
|
488
|
+
} catch (error) {
|
|
489
|
+
logger.error('Failed to connect to Redis:', error);
|
|
490
|
+
}
|
|
491
|
+
})();
|
|
492
|
+
|
|
493
|
+
module.exports = client;
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
If the service already has a Redis client file, ensure it uses `redis@4`'s `createClient` API and exports a connected client.
|
|
378
497
|
|
|
379
|
-
**
|
|
498
|
+
**Important:** If the service did NOT previously have a direct Redis client (i.e., you just created this file), the service now depends on `REDIS_HOST_NAME` and `REDIS_HOST_PORT` environment variables. Notify the user that these environment variables must be configured in the service's Helm chart / deployment config. Set `REDIS_HELM_CONFIG_NEEDED` to `true` so you remember to flag this in the PR (see Step 17).
|
|
380
499
|
|
|
381
|
-
### 7.
|
|
500
|
+
### 7.3 Add the redis client to healthManagerOptions
|
|
382
501
|
|
|
383
|
-
|
|
502
|
+
Import the redis client in the app file and add it to the health manager clients:
|
|
503
|
+
|
|
504
|
+
```js
|
|
505
|
+
import redis from './lib/redis';
|
|
506
|
+
|
|
507
|
+
// In SExpress or fastify-boilerplate healthManagerOptions:
|
|
508
|
+
healthManagerOptions: {
|
|
509
|
+
clients: {
|
|
510
|
+
// ... existing clients
|
|
511
|
+
redis: { connection: redis },
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Also add it to any alive/ping check functions that verify Redis connectivity.
|
|
517
|
+
|
|
518
|
+
### 7.4 Pass `client` and `logger` to RedisCache instances
|
|
519
|
+
|
|
520
|
+
`RedisCache` now requires the shared redis client and logger:
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
// BEFORE
|
|
524
|
+
const DriversCache = new RedisCache({
|
|
525
|
+
host: env.REDIS_HOST_NAME,
|
|
526
|
+
port: Number.parseInt(env.REDIS_HOST_PORT, 10) || undefined,
|
|
527
|
+
ttl: LIFETIME_IN_SEC,
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// AFTER
|
|
531
|
+
import redis from '../redis'; // the shared client from step 7.2
|
|
532
|
+
import logger from '../../logger';
|
|
533
|
+
|
|
534
|
+
const DriversCache = new RedisCache({
|
|
535
|
+
client: redis,
|
|
536
|
+
logger,
|
|
537
|
+
host: env.REDIS_HOST_NAME,
|
|
538
|
+
port: Number.parseInt(env.REDIS_HOST_PORT, 10) || undefined,
|
|
539
|
+
ttl: LIFETIME_IN_SEC,
|
|
540
|
+
});
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### 7.5 Remove all `*Async` Redis method calls
|
|
544
|
+
|
|
545
|
+
Redis v4 uses native promises. All `*Async` suffixed methods are removed:
|
|
384
546
|
|
|
385
547
|
```js
|
|
386
548
|
// BEFORE
|
|
@@ -396,11 +558,7 @@ await redis.del(key);
|
|
|
396
558
|
|
|
397
559
|
Search for all `Async` suffixed method calls on redis instances and remove the suffix.
|
|
398
560
|
|
|
399
|
-
### 7.
|
|
400
|
-
|
|
401
|
-
If the service creates `RedisCache` instances, ensure a logger is passed.
|
|
402
|
-
|
|
403
|
-
### 7.3 Update `getWithCacheSupport` calls
|
|
561
|
+
### 7.6 Update `getWithCacheSupport` calls
|
|
404
562
|
|
|
405
563
|
The API changed significantly. The key differences are:
|
|
406
564
|
- `logger` is now a required field
|
|
@@ -435,30 +593,52 @@ const result = await getWithCacheSupport({
|
|
|
435
593
|
});
|
|
436
594
|
```
|
|
437
595
|
|
|
438
|
-
### 7.
|
|
596
|
+
### 7.7 Update `getMultipleWithCache` calls
|
|
439
597
|
|
|
440
598
|
Same callback signature changes apply to `getMultipleWithCache` — update `cacheGet` and `cacheSet` callbacks to accept the key parameter.
|
|
441
599
|
|
|
600
|
+
### 7.8 Remove ORMCache imports
|
|
601
|
+
|
|
602
|
+
If the service imports `ORMCache` or `ORMTypes` from `@autofleet/matmon`, delete those imports — ORM Cache has been removed in matmon v3:
|
|
603
|
+
|
|
604
|
+
```ts
|
|
605
|
+
// DELETE:
|
|
606
|
+
import { ORMCache, ORMTypes } from '@autofleet/matmon';
|
|
607
|
+
```
|
|
608
|
+
|
|
442
609
|
---
|
|
443
610
|
|
|
444
611
|
## Step 8: Migrate @autofleet/sequelize-utils
|
|
445
612
|
|
|
446
|
-
**Applies when:** The service
|
|
613
|
+
**Applies when:** The service has `@autofleet/sequelize-utils` in its dependencies.
|
|
614
|
+
|
|
615
|
+
### 8.1 Pass logger to sequelizeUtilsInit
|
|
447
616
|
|
|
448
|
-
|
|
617
|
+
The init function now accepts a logger as the second argument:
|
|
449
618
|
|
|
450
619
|
```ts
|
|
451
620
|
// BEFORE
|
|
452
621
|
import sequelizeUtilsInit from '@autofleet/sequelize-utils';
|
|
453
|
-
const {
|
|
622
|
+
const { transactionWithRetry, registerModelEventHooks } = sequelizeUtilsInit(sequelize);
|
|
623
|
+
|
|
624
|
+
// AFTER
|
|
625
|
+
import sequelizeUtilsInit from '@autofleet/sequelize-utils';
|
|
626
|
+
import logger from '../../logger'; // adjust path
|
|
627
|
+
const { transactionWithRetry, registerModelEventHooks } = sequelizeUtilsInit(sequelize, logger);
|
|
628
|
+
```
|
|
454
629
|
|
|
630
|
+
Search for ALL files that call `sequelizeUtilsInit(sequelize)` — there may be more than one (e.g., `src/sequelize-utils.ts` and `src/models/utils/transactionManager.ts`). Update all of them.
|
|
631
|
+
|
|
632
|
+
### 8.2 Pass events instance to registerModelEventHooks
|
|
633
|
+
|
|
634
|
+
If the service uses `registerModelEventHooks`, it now requires the events instance as the second argument:
|
|
635
|
+
|
|
636
|
+
```ts
|
|
637
|
+
// BEFORE
|
|
455
638
|
registerModelEventHooks(modelEventsTableMapping);
|
|
456
639
|
|
|
457
640
|
// AFTER
|
|
458
|
-
import sequelizeUtilsInit from '@autofleet/sequelize-utils';
|
|
459
641
|
import events from '../../events'; // adjust path — import the events instance from Step 4
|
|
460
|
-
const { registerModelEventHooks, transactionWithRetry } = sequelizeUtilsInit(sequelize);
|
|
461
|
-
|
|
462
642
|
registerModelEventHooks(modelEventsTableMapping, events);
|
|
463
643
|
```
|
|
464
644
|
|
|
@@ -515,7 +695,107 @@ npm uninstall newrelic
|
|
|
515
695
|
|
|
516
696
|
---
|
|
517
697
|
|
|
518
|
-
## Step 11: Remove
|
|
698
|
+
## Step 11: Remove Bluebird and replace with native alternatives
|
|
699
|
+
|
|
700
|
+
**Applies when:** The service has `bluebird` in its dependencies or imports it anywhere.
|
|
701
|
+
|
|
702
|
+
Bluebird is no longer needed — use native alternatives.
|
|
703
|
+
|
|
704
|
+
### 11.1 Replace `Bluebird.map` / `Bluebird.mapSeries` with `promiseMap` from node-common or `for...of`
|
|
705
|
+
|
|
706
|
+
**For concurrent mapping** (`Bluebird.map` with `{ concurrency: N }`), use `promiseMap` from `@autofleet/node-common`:
|
|
707
|
+
|
|
708
|
+
```ts
|
|
709
|
+
// BEFORE
|
|
710
|
+
import Bluebird from 'bluebird';
|
|
711
|
+
await Bluebird.map(items, async (item) => {
|
|
712
|
+
await processItem(item);
|
|
713
|
+
}, { concurrency: 5 });
|
|
714
|
+
|
|
715
|
+
// AFTER
|
|
716
|
+
import { promiseMap } from '@autofleet/node-common';
|
|
717
|
+
await promiseMap(items, async (item) => {
|
|
718
|
+
await processItem(item);
|
|
719
|
+
}, { concurrency: 5 });
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
**For serial mapping** (`Bluebird.mapSeries`), use a simple `for...of` loop:
|
|
723
|
+
|
|
724
|
+
```ts
|
|
725
|
+
// BEFORE
|
|
726
|
+
import Bluebird from 'bluebird';
|
|
727
|
+
await Bluebird.mapSeries(items, async (item) => {
|
|
728
|
+
await processItem(item);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// AFTER
|
|
732
|
+
for (const item of items) {
|
|
733
|
+
await processItem(item);
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### 11.2 Remove `Promise.promisifyAll` usage
|
|
738
|
+
|
|
739
|
+
If any code uses Bluebird's `promisifyAll`:
|
|
740
|
+
|
|
741
|
+
```js
|
|
742
|
+
// BEFORE
|
|
743
|
+
const Promise = require('bluebird');
|
|
744
|
+
const Nexmo = Promise.promisifyAll(require('nexmo'), { suffix: 'Async' });
|
|
745
|
+
|
|
746
|
+
// AFTER
|
|
747
|
+
const Nexmo = require('nexmo');
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
Then update all `*Async` method calls on that module to use the module's native API (callbacks, promises, or wrap with `util.promisify`).
|
|
751
|
+
|
|
752
|
+
### 11.3 Uninstall bluebird
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
npm uninstall bluebird
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
Search the entire codebase for any remaining `require('bluebird')` or `import.*bluebird` to make sure nothing is missed.
|
|
759
|
+
|
|
760
|
+
---
|
|
761
|
+
|
|
762
|
+
## Step 12: Migrate @autofleet/zehut type and trace changes
|
|
763
|
+
|
|
764
|
+
**Applies when:** The service imports types or trace functions from `@autofleet/zehut`.
|
|
765
|
+
|
|
766
|
+
### 12.1 Replace old type imports
|
|
767
|
+
|
|
768
|
+
The `ApiUser` type has moved:
|
|
769
|
+
|
|
770
|
+
```ts
|
|
771
|
+
// BEFORE
|
|
772
|
+
import type ApiUser from '@autofleet/zehut/lib/user';
|
|
773
|
+
|
|
774
|
+
// AFTER
|
|
775
|
+
import { type User } from '@autofleet/zehut';
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
Update any interfaces that extend `ApiUser` to extend `User` instead.
|
|
779
|
+
|
|
780
|
+
### 12.2 Use `traceTypes` constants instead of string literals
|
|
781
|
+
|
|
782
|
+
If the service uses `newTrace` with string arguments:
|
|
783
|
+
|
|
784
|
+
```js
|
|
785
|
+
// BEFORE
|
|
786
|
+
const { newTrace } = require('@autofleet/zehut');
|
|
787
|
+
newTrace('Bull');
|
|
788
|
+
|
|
789
|
+
// AFTER
|
|
790
|
+
const { newTrace, traceTypes } = require('@autofleet/zehut');
|
|
791
|
+
newTrace(traceTypes.BULL_MQ);
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Search for all `newTrace(` calls and replace string literals with the appropriate `traceTypes` constant.
|
|
795
|
+
|
|
796
|
+
---
|
|
797
|
+
|
|
798
|
+
## Step 13: Remove obsolete dependencies
|
|
519
799
|
|
|
520
800
|
**Applies when:** The service has any of these in `package.json`.
|
|
521
801
|
|
|
@@ -529,13 +809,15 @@ Check for and remove:
|
|
|
529
809
|
|
|
530
810
|
2. **`newrelic`** — already handled in Step 10 above.
|
|
531
811
|
|
|
812
|
+
3. **`bluebird`** — already handled in Step 11 above.
|
|
813
|
+
|
|
532
814
|
---
|
|
533
815
|
|
|
534
|
-
## Step
|
|
816
|
+
## Step 14: Update Vitest/test configuration
|
|
535
817
|
|
|
536
818
|
**Applies when:** The service uses Vitest (`vitest.config.ts` exists).
|
|
537
819
|
|
|
538
|
-
###
|
|
820
|
+
### 14.1 Add resolve alias for joi
|
|
539
821
|
|
|
540
822
|
In `vitest.config.ts`, add a resolve alias to prevent Joi resolution issues:
|
|
541
823
|
|
|
@@ -551,7 +833,7 @@ export default defineConfig({
|
|
|
551
833
|
});
|
|
552
834
|
```
|
|
553
835
|
|
|
554
|
-
###
|
|
836
|
+
### 14.2 Add SSR noExternal for @autofleet packages
|
|
555
837
|
|
|
556
838
|
Ensure `@autofleet/*` packages are not externalized during testing:
|
|
557
839
|
|
|
@@ -565,17 +847,17 @@ export default defineConfig({
|
|
|
565
847
|
});
|
|
566
848
|
```
|
|
567
849
|
|
|
568
|
-
###
|
|
850
|
+
### 14.3 Remove `/alive` endpoint tests
|
|
569
851
|
|
|
570
852
|
If there are tests asserting on the `/alive` endpoint returning `{ status: 'ok' }`, remove them — the endpoint now returns a different response format from HealthManager.
|
|
571
853
|
|
|
572
854
|
---
|
|
573
855
|
|
|
574
|
-
## Step
|
|
856
|
+
## Step 15: Settings (non-async setLocal)
|
|
575
857
|
|
|
576
858
|
**Applies when:** The service uses `@autofleet/settings` and calls `await settings.setLocal(...)`.
|
|
577
859
|
|
|
578
|
-
###
|
|
860
|
+
### 15.1 Remove `await` from `settings.setLocal` calls
|
|
579
861
|
|
|
580
862
|
`setLocal` is no longer async:
|
|
581
863
|
|
|
@@ -591,7 +873,7 @@ Search for all `await settings.setLocal` and `await.*setLocal` patterns and remo
|
|
|
591
873
|
|
|
592
874
|
---
|
|
593
875
|
|
|
594
|
-
## Step
|
|
876
|
+
## Step 16: Verify the migration
|
|
595
877
|
|
|
596
878
|
After completing all applicable migrations:
|
|
597
879
|
|
|
@@ -602,3 +884,88 @@ After completing all applicable migrations:
|
|
|
602
884
|
5. **Start:** Verify the service starts locally without errors
|
|
603
885
|
|
|
604
886
|
If any step fails, read the error carefully — it likely points to a migration step that was missed or incompletely applied. Go back through the checklist and verify each applicable migration was fully completed.
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
## Step 17: Create or update the Pull Request
|
|
891
|
+
|
|
892
|
+
After all migrations pass verification, create a PR or update an existing one.
|
|
893
|
+
|
|
894
|
+
### 17.1 Check for an existing PR
|
|
895
|
+
|
|
896
|
+
Check if there is already an open PR for the current branch:
|
|
897
|
+
|
|
898
|
+
```bash
|
|
899
|
+
gh pr view --json number,title,url 2>/dev/null
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### 15.2 Build the PR description
|
|
903
|
+
|
|
904
|
+
Build the PR body using this template. Fill in only the sections that apply based on the migrations you performed:
|
|
905
|
+
|
|
906
|
+
```markdown
|
|
907
|
+
## Update @autofleet packages
|
|
908
|
+
|
|
909
|
+
### Packages updated
|
|
910
|
+
<!-- List each package with version change, e.g.: -->
|
|
911
|
+
<!-- - `@autofleet/super-express`: 1.1.17 → 8.1.14 (major) -->
|
|
912
|
+
<!-- - `@autofleet/logger`: 4.1.0 → 4.2.45 -->
|
|
913
|
+
|
|
914
|
+
### Breaking changes applied
|
|
915
|
+
<!-- Check off only the migrations that were performed: -->
|
|
916
|
+
- [ ] **super-express** — migrated to function call, healthManagerOptions, removed httpLogCb/nitur/enableTracing/zehut.middleware
|
|
917
|
+
- [ ] **fastify-boilerplate** — migrated to healthManagerOptions
|
|
918
|
+
- [ ] **logger** — added serviceName and version context, removed addContextMiddleware
|
|
919
|
+
- [ ] **events** — added getUserId, getTraceId, enableGracefulShutdown: false
|
|
920
|
+
- [ ] **rabbit** — added logger, dontGracefulShutdown: true
|
|
921
|
+
- [ ] **node-common Router** — replaced express Router, passing logger to all Router() calls
|
|
922
|
+
- [ ] **redis + matmon v3** — created consolidated redis client, passed client+logger to RedisCache, removed *Async methods, updated getWithCacheSupport API, removed ORMCache
|
|
923
|
+
- [ ] **sequelize-utils** — passing logger to init, passing events to registerModelEventHooks
|
|
924
|
+
- [ ] **database config** — password default null → ''
|
|
925
|
+
- [ ] **New Relic** — removed newrelic.js and initialization
|
|
926
|
+
- [ ] **bluebird** — replaced with promiseMap from node-common or native for...of, removed promisifyAll
|
|
927
|
+
- [ ] **zehut types/traces** — replaced ApiUser with User, replaced string trace literals with traceTypes constants
|
|
928
|
+
- [ ] **obsolete dependencies** — removed worker-threads-pool, newrelic, bluebird
|
|
929
|
+
- [ ] **vitest config** — added joi alias and SSR noExternal
|
|
930
|
+
- [ ] **settings** — removed await from setLocal calls
|
|
931
|
+
|
|
932
|
+
### Notes
|
|
933
|
+
<!-- Any additional context, things that need manual verification, or known issues -->
|
|
934
|
+
|
|
935
|
+
### Helm / Infra changes needed
|
|
936
|
+
<!-- List any infrastructure changes required before deploying this PR -->
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
**Important:** Fill in the "Helm / Infra changes needed" section with any applicable items:
|
|
940
|
+
- If the readiness probe was `/alive`, note it should be updated to `/ready`
|
|
941
|
+
- If a new Redis client was created in Step 7 (the service didn't previously have one), add: "Redis env vars `REDIS_HOST_NAME` and `REDIS_HOST_PORT` must be added to the Helm chart / deployment config"
|
|
942
|
+
|
|
943
|
+
### 17.2 Create or update the PR
|
|
944
|
+
|
|
945
|
+
**If no PR exists** — create one:
|
|
946
|
+
|
|
947
|
+
```bash
|
|
948
|
+
gh pr create --title "Update @autofleet packages" --body "<description>"
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
**If a PR already exists** — update its body:
|
|
952
|
+
|
|
953
|
+
```bash
|
|
954
|
+
gh pr edit --body "<description>"
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### 17.3 Add a PR comment if Redis env vars are needed
|
|
958
|
+
|
|
959
|
+
If a new Redis client was created in Step 7 (the service did not previously have a direct Redis connection), add a comment to the PR alerting the reviewer:
|
|
960
|
+
|
|
961
|
+
```bash
|
|
962
|
+
gh pr comment --body "⚠️ **Action required before deploy:** This PR adds a new direct Redis client to the service. The following environment variables must be configured in the Helm chart before deploying:
|
|
963
|
+
- \`REDIS_HOST_NAME\`
|
|
964
|
+
- \`REDIS_HOST_PORT\`
|
|
965
|
+
|
|
966
|
+
Without these, the service will fail to connect to Redis on startup."
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
### 17.4 Notify the user
|
|
970
|
+
|
|
971
|
+
Print the PR URL and remind the user about any infrastructure changes needed before merging.
|
package/dist/e2e.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{C as e,c as t,i as n,l as r,n as i,x as a}from"./utils-
|
|
2
|
+
import{C as e,c as t,i as n,l as r,n as i,x as a}from"./utils-Dnt11RmF.js";import o from"node:path";import s from"p-retry";const c=[`identity-ms`,`vehicle-ms`,`ride-ms`,`vehicle-live-data-ms`,`pricing-ms`,`payment-ms`,`route-plan-ms`,`task-ms`,`audit-ms`,`pgweb`,`placement-ms`,`demand-prediction-ms`,`control-center-ms`,`setting-ms`,`api-gateway-ms`],l={start:`--e2eBranch=<branchName> --branches="{\\"ride-ms\\": \\"AUT-7070\\"}"`,getIpsForEnv:`--variationId=<yourVariationUuid> --cluster=<dev1/e2eManager>`},u=e=>Object.keys(l).includes(e),{positionals:d,values:f,help:p}=e({strict:!0,binName:`autofleet-e2e`,allowPositionals:!0,options:{e2eBranch:{type:`string`,default:`master`,description:`Name of e2e branch`},branches:{type:`string`,default:``,description:`"ride-ms": "AUT-7070"`},cluster:{type:`string`,short:`c`,description:`The cluster name. One of dev1, e2eManager, expManager`},variationId:{type:`string`,short:`i`,description:`The variation id`}},commands:l});d.length||(console.log(p),process.exit(0));const[m]=d;if(u(m)||n(Error(`Command ${m} does not exist`),p),m===`start`){let e=(f.branches?.replace(`{`,` `)||``).replace(/[^a-z0-9]/gi,``),t=`${o.resolve()}/bin/utils/e2e.sh`,n=`${o.resolve()}/e2e.sh`;r({newFilePath:n,filePath:t,linesToReplace:[{find:`$E2E_BRANCH_PLACE_HOLDER$`,replace:f.e2eBranch},{find:`$BRANCHES_PLACE_HOLDER$`,replace:f.branches},{find:`$NAME_PLACE_HOLDER$`,replace:e}]}),await a(`chmod`,[`+x ${n} && ${n}`]),console.log(`finished e2e`)}const{cluster:h,variationId:g}=f;h||n(Error(`cluster required`),p),g||n(Error(`variationId required`),p);let _;try{_=await i({clusterName:h,variationId:g})}catch(e){n(e)}m===`getIpsForEnv`&&(await Promise.all(c.map(e=>a(`kubectl`,[`annotate`,_,`service`,e,`cloud.google.com/load-balancer-type-`],{print:!1}))),await Promise.all(c.map(e=>a(`kubectl`,[`patch`,`svc`,_,e,`-p`,`{"spec": {"type": "LoadBalancer"}}`],{print:!1}))),console.log(`exposed services successfully. the services are now creating public ips.`),await s(async()=>{let{stdout:e}=await a(`kubectl`,[_,`get`,`services`],{print:!1}),n=t(e);if(n.some(e=>e.includes(`<pending>`)))throw Error(`Some services are still pending`);n.forEach(e=>console.log(e))},{retries:10,factor:1,maxTimeout:5e3,randomize:!0}),process.exit(0));export{};
|
|
3
3
|
//# sourceMappingURL=e2e.js.map
|
package/dist/git-autofleet.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{C as e,_ as t,g as n,h as r,i,m as a,v as o,x as s,y as c}from"./utils-
|
|
2
|
+
import{C as e,_ as t,g as n,h as r,i,m as a,v as o,x as s,y as c}from"./utils-Dnt11RmF.js";import{basename as l,sep as u}from"node:path";import{readdir as d}from"node:fs/promises";import{randomUUID as f}from"node:crypto";const p=[`premajor`,`preminor`,`prepatch`,`prerelease`],m=[`major`,`minor`,`patch`,...p],h={bumpVersion:`[${m.join(`|`)}]`,openPR:`Opens github UI for creating a PR from the current branch.`,releaseBeta:`Bumps version if needed, and publishes a beta release to NPM registry.`},g=e=>Object.keys(h).includes(e),_=async({versionType:e,packageName:t,preReleaseId:n=``})=>{let{stdout:r}=await s(`npm`,[`version`,e,`--no-git-tag-version`,`--preid`,n],{print:!1,cwd:t?`.${u}packages${u}${t}`:void 0});return await s(`git`,[`add`,`package*`],{print:!1}),await s(`git`,[`commit`,`-m`,`${e} version: ${r}`],{print:!1}),console.log(`Created commit for version bump: ${e}. Version is now ${r}`),r},v=async e=>{let t=await c(),{default:{version:n}}=await import(`${e?`${t}${u}packages${u}${e}`:t}${u}package.json`,{assert:{type:`json`},with:{type:`json`}});return n.replace(/\d+\.\d+\.\d+(?:-beta-([\w]+)?\.\d+)?/,`$1`)},{positionals:y,help:b}=e({strict:!0,binName:`git autofleet`,allowPositionals:!0,options:{},commands:h});y.length||(console.log(b),process.exit(0));const[x,S,C]=y;if(g(x)||i(Error(`Command ${x} does not exist`),b),x===`openPR`){let[e,t]=await Promise.all([c(),o()]);await s(`open`,[`https://github.com/Autofleet/${l(e)}/compare/${t}?expand=1`],{print:!1}),process.exit(0)}async function w(){let e=await c();return e.split(u).pop()===`autorepo`?e:!1}async function T({message:e,pathIfAutorepo:t}){t??=await w();let r=C;if(t){let a=(await d(`${t}${u}packages`)).filter(e=>!e.startsWith(`.`));r?a.includes(r)||i(Error(`Package name ${r} does not exist in autorepo packages`),b):r=await n({message:e,choices:a.map(e=>({name:e,value:e}))}),r||i(Error(`No package name provided, exiting...`),b)}return r}if(x===`bumpVersion`){let e=await T({message:`Which package do you want to bump?`}),t=S||await n({message:`Which version type do you want to bump?`,choices:(x===`bumpVersion`?m:p).map(e=>({name:e,value:e}))});t||i(Error(`Must explicitly define version type.\n\tVersion type can be one of: ${m.join(`, `)}`),b);let r=await o();[`main`,`master`].includes(r)&&(await a(`Are you sure you want to bump version on main branch?`)||process.exit(0)),await _({versionType:t,packageName:e}),process.exit(0)}if(x===`releaseBeta`){let e=await w(),n=await T({message:`Which package do you want to release?`,pathIfAutorepo:e});S&&!p.includes(S)&&i(Error(`Invalid version type: ${S}\n\tVersion must be a prerelease type. (${p.join(`, `)})`),b);let c=await o();[`main`,`master`].includes(c)&&i(Error(`Cannot release beta from main branch`),b);let l=n?`.${u}packages${u}${n}`:void 0,d=!(await t(n?`.${u}packages${u}${n}${u}package.json`:`package.json`)).split(`
|
|
3
3
|
`).some(e=>/^\+\s*"version":\s*"\d+\.\d+\.\d+(?:-(?:[\w-]+\.)?\d+)?",/.test(e))||await a(`Version already bumped on branch. Should version be re-bumped?`),m=d&&await v(n),h=d&&await _({versionType:S||`prerelease`,preReleaseId:`beta-${m||f().split(`-`)[0]}`,packageName:n}),g=e?`pnpm`:`npm`;await s(g,[`run`,`build`],{cwd:l});let y=await r({message:`In case your token requires an OTP, please enter it:`});try{await s(g,[`publish`,`--tag`,`beta`,`--@autofleet:registry=https://registry.npmjs.org`,...y?[`--otp`,y]:[],...e?[`--no-git-checks`]:[]],{cwd:l})}catch(e){console.error(e),i(`Failed to publish beta version`)}if(h)console.log(`Published beta version with new version: ${h}`);else{let{stdout:e}=await s(g,[`info`,`.`,`version`],{print:!1,cwd:l});console.log(`Published beta version with existing version: ${e}`)}process.exit(0)}export{};
|
|
4
4
|
//# sourceMappingURL=git-autofleet.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{C as e,S as t,a as n,b as r,c as i,d as a,f as o,g as s,i as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g,v as ee,w as te,x as _,y as v}from"./utils-
|
|
2
|
+
import{C as e,S as t,a as n,b as r,c as i,d as a,f as o,g as s,i as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g,v as ee,w as te,x as _,y as v}from"./utils-Dnt11RmF.js";import{styleText as y}from"node:util";import*as ne from"node:fs";import b from"node:fs";import{execFile as re,execSync as x,spawn as ie}from"node:child_process";import{confirm as S}from"@inquirer/prompts";import*as C from"node:path";import w,{basename as T,join as E,resolve as D,sep as ae}from"node:path";import{mkdir as oe,readFile as O,writeFile as k}from"node:fs/promises";import*as se from"node:os";import ce,{EOL as A}from"node:os";import{randomUUID as le}from"node:crypto";import{setTimeout as ue}from"node:timers/promises";import{cwd as j}from"node:process";function M(e){return e.replace(/^[\^~]/,``)}function N(e){let t=M(e),n=/^(\d+)/.exec(t);return n?parseInt(n[1],10):0}function de(e,t){return N(t)>N(e)}function fe(e,t){if(e.includes(`-`)||t.includes(`-`))return!1;let n=e=>e.split(`.`).map(Number),[r,i,a]=n(e),[o,s,c]=n(t);return r===o?i===s?a>c:i>s:r>o}const P=w.join(ce.homedir(),`.autofleet`,`update-check.json`);function pe(){try{return JSON.parse(b.readFileSync(P,`utf-8`))}catch{return null}}function me(e){try{b.mkdirSync(w.dirname(P),{recursive:!0}),b.writeFileSync(P,JSON.stringify(e))}catch{}}function he(e,t){try{return x(`${e} ${t.join(` `)}`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()||null}catch{return null}}function ge(){let e=process.argv[1]??``;for(let{pm:t,args:n}of[{pm:`pnpm`,args:[`root`,`-g`]},{pm:`yarn`,args:[`global`,`dir`]},{pm:`npm`,args:[`root`,`-g`]}]){let r=he(t,n);if(r&&e.startsWith(r))return t}return`npm`}function _e(e){return e===`pnpm`?`pnpm add -g @autofleet/cli@latest`:e===`yarn`?`yarn global add @autofleet/cli@latest`:`${w.join(w.dirname(process.execPath),`npm`)} install -g @autofleet/cli@latest`}function ve(){re(`npm`,[`view`,`@autofleet/cli@latest`,`version`],{encoding:`utf-8`,timeout:15e3},(e,t)=>{if(e||!t.trim())return;let n=t.trim();n&&me({latestVersion:n,checkedAt:Date.now()})}).unref()}async function ye(e=!1){if(e)return;let t=pe();if(t&&fe(t.latestVersion,te)&&await l(`New @autofleet/cli version available (${te} → ${t.latestVersion}). Update now?`)){let e=_e(ge());console.log(`Updating @autofleet/cli...`),x(e,{stdio:`inherit`}),console.log(`✓ Updated! Please re-run your command.`),process.exit(0)}(t?Date.now()-t.checkedAt:1/0)>864e5&&ve()}const F=`.mirrord`;async function I({makeDir:e=!1}={}){let t=E(await v(),F);return e&&await oe(t,{recursive:!0}),E(t,`mirrord.json`)}async function be(e=le()){let t=await I({makeDir:!0}),n={feature:{network:{incoming:{mode:`steal`,http_filter:{ports:[8080],path_filter:`^(?!/alive|/ready)`}},outgoing:!0},fs:`read`,env:!0},target:{namespace:e},agent:{ephemeral:!0,startup_timeout:600,communication_timeout:120,namespace:e}};return await k(t,JSON.stringify(n,null,2)),{mirrordConfigData:n,mirrordPath:t}}async function xe(){try{let e=await O(await I(),{encoding:`utf-8`});return JSON.parse(e||`{}`)?.target?.namespace??``}catch{return``}}function Se(e){process.once(`exit`,e),process.once(`SIGTERM`,e),process.once(`SIGINT`,e)}function L(e){return e&&=e.trim(),e&&(e.startsWith(`'`)&&e.endsWith(`'`)&&(e=e.slice(1,-1)),e)}const R=`livenessProbe`,z=`readinessProbe`,B=e=>`jsonpath='{.spec.template.spec.containers[?(@.name=="worker")].${e}}'`,V=(...e)=>`{"spec": {"template": {"spec": {"containers": [{"name": "worker",${e.map(([e,t])=>`"${e}": ${t}`).join(`,`)}}]}}}}`;var Ce=async(e,t,n)=>{console.log(`Will now disable health check to allow debugging.`);let[{stdout:r},{stdout:i}]=await Promise.all([_(`kubectl`,[`get`,`deployment/${n}`,e,`-o`,B(R)],{print:!1}),_(`kubectl`,[`get`,`deployment/${n}`,e,`-o`,B(z)],{print:!1})]);r&&=L(r),i&&=L(i),(r||i)&&(await _(`kubectl`,[`patch`,`deployment/${n}`,e,`--patch`,V([R,`null`],[z,`null`])],{print:!1}),console.log(`Health check disabled, will now start mirrord and run "npm run dev". Make sure to attach a debugger!`));let a=new AbortController;Se(async()=>{globalThis.executedExitHooks||(globalThis.executedExitHooks=!0,a.abort(),(r||i)&&(console.log(`Re-enabling health check.`),await _(`kubectl`,[`patch`,`deployment/${n}`,e,`--patch`,`'${V([R,r],[z,i])}'`],{print:!1,shell:!0}),console.log(`health check enabled.`)),process.exit(0))});try{let e=await I();await _(`mirrord`,[`exec`,`-t`,`deployment/${n}`,`-n`,t,`-f`,e,`--steal`,`npm`,`run`,`dev`],{signal:a.signal})}catch(e){if(e&&typeof e==`object`&&`code`in e&&e.code===`ABORT_ERR`)return;c(e)}},we=async(e,t)=>{if(await p(e),e===`expManager`||e===`dev1`){let{variationId:e}=await g({variationId:t},[`variationId`]);e!==``&&(await n()===e&&(console.log(`Already connected to simulation with uuid: ${e}, skipping namespace switch...`),process.exit(0)),await _(`kubectl`,[`config`,`set-context`,`--current`,`--namespace=${e}`],{print:!1}),console.log(`Switched to simulation with uuid: ${e} successfully`))}process.exit(0)};async function Te(){try{await _(`telepresence`,[`version`],{print:!1})}catch{c(`Telepresence is not installed. Please install it from:
|
|
3
3
|
https://www.telepresence.io/docs/latest/quick-start/`)}}function Ee(){return s({message:`Do you want to leave the connection or create a new one?`,choices:[{name:`Leave the current connection`,value:`leave`},{name:`Create a new connection`,value:`connect`}],default:`connect`})}async function De(e){await Te();let{cluster:t=``,service:n=``}=e;await p(t);let r=await Ee();if([`leave`,`connect`].includes(r)||c(`Invalid action, how did you get here? 🤔`),r===`leave`){await _(`telepresence`,[`leave`,n]),console.log(`Disconnected! 👋`);return}let i=e.variationId||await f(),a=e[`local-port`]&&Number.parseInt(e[`local-port`],10)||Number.parseInt(await o()||``,10);await _(`telepresence`,[`connect`,`--namespace=${i}`]),await _(`telepresence`,[`intercept`,n,`--port`,a.toString(),`--env-file=./.env`]),console.log(`Everything is set up! 🚀`),console.log(`You can now access the service at localhost:${a}`),console.log(`You can only have one connection at a time. If you want to connect to a different service, you need to leave the current connection first.`),process.exit(0)}var Oe=async(e,t,n)=>{n||c(Error(`service is required for exposeService`),t),await _(`kubectl`,[`patch`,`svc`,e,n,`-p`,`{"spec": {"type": "LoadBalancer"}}`]),console.log(`exposed ${n} successfully. the service is now creating an internal load balancer ip to use with vpn`),process.exit(0)},ke=async(e,t,n,r,i)=>{let a=Number.parseInt(e[`local-port`]??`5432`,10);Number.isNaN(a)&&c(Error(`if defined, local-port must be a number`),r);let o=`afpass_${n}`,s=`postgres`,l=n.replaceAll(`-`,`_`),u=i?`${i.replaceAll(`-`,`_`)||``}_${l}`:`postgres`,d=`postgres://${s}:${o}@localhost:${a}/${u}?&nickname=var_${n}`;console.log(`Forwarding db to local port ${a}.\naccess the DB at ${d}\nPress ctrl+c to stop.`);let f=` DB_USERNAME=${s}
|
|
4
4
|
DB_PASSWORD=${o}
|
|
5
5
|
DB_NAME=${i?u:`<service_name>_${l}`}`;console.log(`Generated database environment for local development:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{parseArgs as e}from"node:util";import t from"node:fs";import{spawn as n}from"node:child_process";import{confirm as r,input as i,select as a}from"@inquirer/prompts";import{sep as o}from"node:path";import{readdir as s}from"node:fs/promises";var c=`2.27.0-alpha.
|
|
1
|
+
import{parseArgs as e}from"node:util";import t from"node:fs";import{spawn as n}from"node:child_process";import{confirm as r,input as i,select as a}from"@inquirer/prompts";import{sep as o}from"node:path";import{readdir as s}from"node:fs/promises";var c=`2.27.0-alpha.2`;const l={help:{type:`boolean`,short:`h`,default:!1,description:`Show help (prints this message)`},version:{type:`boolean`,short:`v`,default:!1,description:`Show version number`},quiet:{type:`boolean`,short:`q`,default:!1,description:`Skip interactive prompts (e.g. version update check)`}};function u(t){t.options={...l,...t.options};let n=e(t),{values:r}=n;r.version&&(console.log(c),process.exit(0));let i=t.commands?`
|
|
2
2
|
|
|
3
3
|
Commands:
|
|
4
4
|
${Object.entries(t.commands).map(([e,t])=>`${e.padEnd(30,` `)} ${t}`).join(`
|
|
@@ -10,4 +10,4 @@ Options:
|
|
|
10
10
|
`)}
|
|
11
11
|
`;return r.help&&(console.log(o),process.exit(0)),{...n,help:o}}const d=()=>{if(Promise.withResolvers)return Promise.withResolvers();let e,t;return{promise:new Promise((n,r)=>{e=r,t=n}),reject:e,resolve:t}};var f=async(e,t,{print:r=!0,env:i={},signal:a,shell:o,cwd:s}={})=>{typeof t==`boolean`&&(r=t,t=void 0);let c=Error(),l=c.stack?c.stack.replace(/^.*/,` ...`):null,{promise:u,reject:f,resolve:p}=d(),m=n(e,t,{env:Object.assign(i,process.env),signal:a,shell:o,cwd:s});r&&(m.stdout.pipe(process.stdout),m.stderr.pipe(process.stderr));let h=``,g=``;m.stdout?.on(`data`,e=>{h+=e}),m.stderr?.on(`data`,e=>{g+=e});let _=r?`exit`:`close`;function v(e){m.removeListener(_,y),Object.assign(e,{pid:m.pid,stdout:h,stderr:g,status:null,signal:null}),f(e)}function y(n,r){m.removeListener(`error`,v);let i={pid:m.pid,stdout:h,stderr:g,status:n,signal:r};if(n!==0){let a=typeof t!=`boolean`&&` ${t?.join(` `)}`||``,o=r?Error(`${e}${a} exited with signal: ${r}`):Error(`${e}${a} exited with non-zero code: ${n}`);o.stack&&l&&(o.stack+=`\n${l}`),Object.assign(o,i),f(o)}else p(i)}return m.once(_,y),m.once(`error`,v),u};const p=async()=>{let{stdout:e}=await f(`git`,[`branch`,`--show-current`],{print:!1});return e.trim()},m=async()=>{let{stdout:e}=await f(`git`,[`rev-parse`,`--show-toplevel`],{print:!1});return e.trim()},h=async e=>{let{stdout:t}=await f(`git`,[`diff`,`origin/master..`,e],{print:!1});return t},g=async e=>{try{return await f(`git`,[`check-ignore`,e],{print:!1}),!0}catch{return!1}};function _(){var e=typeof SuppressedError==`function`?SuppressedError:function(e,t){var n=Error();return n.name=`SuppressedError`,n.error=e,n.suppressed=t,n},t={},n=[];function r(e,t){if(t!=null){if(Object(t)!==t)throw TypeError(`using declarations can only be used with objects, functions, null, or undefined.`);if(e)var r=t[Symbol.asyncDispose||Symbol.for(`Symbol.asyncDispose`)];if(r===void 0&&(r=t[Symbol.dispose||Symbol.for(`Symbol.dispose`)],e))var i=r;if(typeof r!=`function`)throw TypeError(`Object is not disposable.`);i&&(r=function(){try{i.call(t)}catch(e){return Promise.reject(e)}}),n.push({v:t,d:r,a:e})}else e&&n.push({d:t,a:e});return t}return{e:t,u:r.bind(null,!1),a:r.bind(null,!0),d:function(){var r,i=this.e,a=0;function o(){for(;r=n.pop();)try{if(!r.a&&a===1)return a=0,n.push(r),Promise.resolve().then(o);if(r.d){var e=r.d.call(r.v);if(r.a)return a|=2,Promise.resolve(e).then(o,s)}else a|=1}catch(e){return s(e)}if(a===1)return i===t?Promise.resolve():Promise.reject(i);if(i!==t)throw i}function s(n){return i=i===t?n:new e(n,i),o()}return o()}}}Symbol.dispose??=Symbol.for(`nodejs.dispose`),Symbol.asyncDispose??=Symbol.for(`nodejs.asyncDispose`);let v;const y=()=>v?.();function b(){return process.once(`beforeExit`,y),{[Symbol.dispose](){process.off(`beforeExit`,y),v=void 0}}}async function x(e,...t){try{try{var n=_();n.u(b());let r=e(...t);return v=r.cancel,await r}catch(e){n.e=e}finally{n.d()}}catch(e){e instanceof Error&&[`CancelPromptError`,`ExitPromptError`].includes(e.constructor.name)&&J(`Cancelled! 👋`),J(e);return}}function S(...e){return x(a,...e)}function C(...e){return x(i,...e)}const w=e=>e.charAt(0).toUpperCase()+e.slice(1).split(/(?=[A-Z])/).join(` `);function T(){return S({message:`Which environment do you want to use?`,choices:B.map(e=>({name:w(e),value:e})),default:`expManager`})}async function E(e){return S({message:`Using from autorepo, Which app do you want to use?`,choices:(await s(`${e}${o}apps`)).filter(e=>!e.startsWith(`.`)&&!e.endsWith(`-e2e`)).map(e=>({name:e,value:e}))})}async function D(){let e=await m(),t=e.split(o).pop();return t===`autorepo`?E(e):C({message:`Enter the service name:`,default:t})}function O(e=`default`){return C({message:`Enter your simulator id (Press enter to pass):`,default:e})}async function k(e=`8080`,t=!1){return C({message:`Enter the ${t?`local `:``}port:`,default:e,validate(e){return/^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/.test(e)?!0:`Please enter a valid port number`}})}async function A(e=`5432`){return k(e,!0)}const j={cluster:T,variationId:O,service:D,port:(e=`8080`)=>k(e),"local-port":A};async function M(e,t){for(let n of t){let t=typeof n==`object`?n.option:n,r=typeof n==`object`?n.defaultVal:void 0;if(typeof t!=`string`)throw Error(`Invalid option ${String(t)}`);let i=j[t];if(!i)throw Error(`No value getter for ${String(t)}`);e[t]??=await i(r)}return e}async function N(e,t=!0){return x(r,{message:e,default:t})}const P={prodUs:[`us-autofleet-production`,`us-central1`,`autofleet-production`],prodEu:[`eu-prod-autofleet`,`europe-west1`,`autofleetprod`],prodJp:[`jp-autofleet-production`,`asia-northeast1`,`autofleet-japan`]},F={dev1:[`dev-cluster-2`,`europe-west1`,`dev1-experiment-manager`],e2eManager:[`e2e-cluster-2`,`europe-west1`,`e2e-project-1`],expManager:[`af-experiment-manager`,`europe-west1`]},I={staging:[`stg-autofleet`,`europe-west1`,`autofleet-staging`],loadTest:[`lt-autofleet-production`,`asia-northeast1`,`load-testing-428806`]},L={osrmProd:[`maps-prod`,`europe-west1`,`af-mapping`]},R={...P,...F,...I,...L},z={staging:`europe-west1`},B=Object.keys(R),V=async e=>{let t=R[e];if(!t)throw Error(`Cluster name ${e} does not exist!`);let[n,r,i]=t;return P[e]&&!await N(`❌❌ Are you sure you want to run this command on ${e}? ❌❌`,!1)?(console.log(`Aborting...`),process.exit(1)):{name:n,region:r,project:i}},H=({newFilePath:e,filePath:n,linesToReplace:r})=>{let i=t.readFileSync(n,`utf8`);for(let{find:e,replace:t}of r)i=i.replace(e,t);t.writeFileSync(e,i)};async function U(){try{let{stdout:e}=await f(`kubectl`,[`config`,`current-context`],{print:!1});return e.trim()}catch{return null}}const W=async e=>{let{name:t,region:n,project:r}=await V(e),i=`gke_${r||t}_${n}_${t}`;return await U()===i?(console.log(`Already connected to ${e}, skipping connection to cluster...`),{name:t,region:n,project:r}):(await f(`gcloud`,[`container`,`clusters`,`get-credentials`,t,`--region`,n,`--project`,r||t],{print:!1}),console.log(`Switched to ${e} successfully`),{name:t,region:n,project:r})};async function G(){try{let{stdout:e}=await f(`kubectl`,[`config`,`view`,`--minify`,`--output`,`jsonpath={..namespace}`],{print:!1});return e.trim()}catch{return`default`}}const K=async({clusterName:e,variationId:t})=>(await W(e),`--namespace=${t}`),q=e=>{let t=e.split(`
|
|
12
12
|
`).filter(Boolean);t.shift();let n=t.map(e=>{let[t,n,r,i,a,o]=e.split(/(\s+)/).map(e=>e.trim()).filter(Boolean);return`${t.replace(/-/g,`_`).toUpperCase()}_SERVICE_HOST=${i}`});return n=n.filter(e=>!e.includes(`none`)&&!e.includes(`undefined`)),n};function J(e,t){return e instanceof Error||(e=Error(e)),console.error(e),t&&console.log(t),process.exit(1)}const Y=e=>Object.keys(F).includes(e),X=e=>z[e]||R[e][1];export{u as C,d as S,h as _,G as a,g as b,q as c,E as d,A as f,S as g,C as h,J as i,H as l,N as m,K as n,X as o,O as p,W as r,Y as s,B as t,M as u,p as v,c as w,f as x,m as y};
|
|
13
|
-
//# sourceMappingURL=utils-
|
|
13
|
+
//# sourceMappingURL=utils-Dnt11RmF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils-BNpnqNAk.js","names":["parseArgs","nativeParseArgs","packageJson.version","reject!: PromiseWithResolvers<T>['reject']","resolve!: PromiseWithResolvers<T>['resolve']","result: SpawnResult","signal","asyncSpawn","path","cancelFn: (() => void) | undefined","asyncSpawn"],"sources":["../package.json","../bin/utils/parser.ts","../bin/utils/promise.ts","../bin/utils/asyncSpawn.ts","../bin/utils/git.ts","../bin/utils/inquirer.ts","../bin/utils/index.ts"],"sourcesContent":["","import { parseArgs as nativeParseArgs, type ParseArgsConfig } from 'node:util';\nimport packageJson from '../../package.json' with { type: 'json' };\n\ntype ParseArgsOptionConfig = NonNullable<ParseArgsConfig['options']>[string] & { description: string; };\ntype ParseArgsParams = Omit<ParseArgsConfig, 'options'> & { binName: string; options: Record<string, ParseArgsOptionConfig>; commands?: Record<string, string>; };\n\nconst GLOBAL_OPTIONS = {\n help: {\n type: 'boolean',\n short: 'h',\n default: false,\n description: 'Show help (prints this message)',\n },\n version: {\n type: 'boolean',\n short: 'v',\n default: false,\n description: 'Show version number',\n },\n quiet: {\n type: 'boolean',\n short: 'q',\n default: false,\n description: 'Skip interactive prompts (e.g. version update check)',\n },\n} satisfies ParseArgsParams['options'];\n\ntype Expanded<T extends ParseArgsParams> = T & { options: T['options'] & typeof GLOBAL_OPTIONS; };\ntype Parsed<T extends ParseArgsParams> = ReturnType<typeof nativeParseArgs<Expanded<T>>> & { help: string; };\n\nexport default function parseArgs<T extends ParseArgsParams = ParseArgsParams>(params: T): Parsed<T> {\n params.options = { ...GLOBAL_OPTIONS, ...params.options };\n const data = nativeParseArgs(params as Expanded<T>);\n const { values } = data;\n if ((values as { version: boolean; }).version) {\n console.log(packageJson.version);\n process.exit(0);\n }\n\n const commands = !params.commands ? '' : `\n\nCommands:\n ${Object.entries(params.commands).map(([command, example]) => `${command.padEnd(30, ' ')} ${example}`).join('\\n ')}\n`;\n\n const longestDescriptionLength = Math.max.apply(null, Object.values(params.options).map(({ description }) => description.length));\n\n const help = `\n${params.binName} ${commands ? '[command] ' : ''}<options>${commands}\nOptions:\n ${Object.entries(params.options).map(([option, { description, type, short }]) => {\n const key = short ? `--${option}, -${short}` : `--${option}`;\n return `${key.padEnd(30, ' ')} ${description.padEnd(longestDescriptionLength + 10, ' ')} [${type}]`;\n }).join('\\n ')}\n`;\n\n if ((values as { help: boolean; }).help) {\n console.log(help);\n process.exit(0);\n }\n\n return { ...data, help };\n}\n","declare global {\n interface PromiseWithResolvers<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n interface PromiseConstructor {\n /**\n * Creates a new Promise and returns it in an object, along with its resolve and reject functions.\n * @returns An object with the properties `promise`, `resolve`, and `reject`.\n *\n * ```ts\n * const { promise, resolve, reject } = Promise.withResolvers<T>();\n * ```\n */\n withResolvers<T>(): PromiseWithResolvers<T>;\n }\n}\n\nexport const createDeferredPromise = <T>() => {\n if (Promise.withResolvers) {\n return Promise.withResolvers<T>();\n }\n let reject!: PromiseWithResolvers<T>['reject'];\n let resolve!: PromiseWithResolvers<T>['resolve'];\n const promise = new Promise<T>((res, rej) => {\n reject = rej;\n resolve = res;\n });\n return { promise, reject, resolve };\n};\n","import { spawn } from 'node:child_process';\nimport { createDeferredPromise } from './promise.js';\n\ninterface SpawnResult {\n pid?: number;\n stdout: string;\n stderr: string;\n status: number | null;\n signal: string | null;\n}\n\nexport default async (\n command: string,\n args?: boolean | readonly string[],\n {\n print = true,\n env = {},\n signal,\n shell,\n cwd,\n }: { print?: boolean; env?: NodeJS.ProcessEnv; signal?: AbortSignal; shell?: string | boolean; cwd?: string; } = {},\n) => {\n if (typeof args === 'boolean') {\n print = args;\n args = undefined;\n }\n const stubError = new Error();\n const callerStack = stubError.stack ? stubError.stack.replace(/^.*/, ' ...') : null;\n const { promise, reject, resolve } = createDeferredPromise<SpawnResult>();\n const child = spawn(command, args, { env: Object.assign(env, process.env), signal, shell, cwd });\n if (print) {\n child.stdout.pipe(process.stdout);\n child.stderr.pipe(process.stderr);\n }\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (data) => {\n stdout += data;\n });\n child.stderr?.on('data', (data) => {\n stderr += data;\n });\n const completionEventName = print ? 'exit' : 'close';\n function errorListener(error: Error) {\n child.removeListener(completionEventName, completionListener);\n Object.assign(error, {\n pid: child.pid,\n stdout,\n stderr,\n status: null,\n signal: null,\n });\n reject(error);\n }\n function completionListener(code: number | null, signal: string | null) {\n child.removeListener('error', errorListener);\n const result: SpawnResult = {\n pid: child.pid,\n stdout,\n stderr,\n status: code,\n signal,\n };\n if (code !== 0) {\n const argumentString = (typeof args !== 'boolean' && ` ${args?.join(' ')}`) || '';\n const error = signal\n ? new Error(`${command}${argumentString} exited with signal: ${signal}`)\n : new Error(`${command}${argumentString} exited with non-zero code: ${code}`);\n if (error.stack && callerStack) {\n error.stack += `\\n${callerStack}`;\n }\n Object.assign(error, result);\n reject(error);\n } else {\n resolve(result);\n }\n }\n\n child.once(completionEventName, completionListener);\n child.once('error', errorListener);\n return promise;\n};\n","import asyncSpawn from './asyncSpawn.js';\n\nexport const getCurrentBranch = async () => {\n const { stdout: currentBranch } = await asyncSpawn('git', ['branch', '--show-current'], { print: false });\n return currentBranch.trim();\n};\n\nexport const getGitPath = async () => {\n const { stdout: gitPath } = await asyncSpawn('git', ['rev-parse', '--show-toplevel'], { print: false });\n return gitPath.trim();\n};\n\nexport const getChangesOfFile = async (filePath: string) => {\n const { stdout: changes } = await asyncSpawn('git', ['diff', 'origin/master..', filePath], { print: false });\n return changes;\n};\n\nexport const isPathIgnored = async (path: string) => {\n try {\n await asyncSpawn('git', ['check-ignore', path], { print: false });\n return true;\n } catch {\n return false;\n }\n};\n","import { confirm, input, select } from '@inquirer/prompts';\nimport type { Prompt } from '@inquirer/type';\nimport { getGitPath } from './git.js';\nimport { SUPPORTED_CLUSTERS, endWithError } from './index.js';\nimport { sep } from 'node:path';\nimport { readdir } from 'node:fs/promises';\n\ndeclare global {\n interface SymbolConstructor {\n /** A method that is used to release resources held by an object. Called by the semantics of the `using` statement. */\n readonly dispose: unique symbol;\n /** A method that is used to asynchronously release resources held by an object. Called by the semantics of the `await using` statement. */\n readonly asyncDispose: unique symbol;\n }\n}\n// @ts-expect-error polyfill for node < 18.18\nSymbol.dispose ??= Symbol.for('nodejs.dispose');\n// @ts-expect-error polyfill for node < 18.18\nSymbol.asyncDispose ??= Symbol.for('nodejs.asyncDispose');\nlet cancelFn: (() => void) | undefined;\nconst handler = () => cancelFn?.();\nfunction addExitListener() {\n process.once('beforeExit', handler);\n return {\n [Symbol.dispose]() {\n process.off('beforeExit', handler);\n cancelFn = undefined;\n },\n };\n}\n\nasync function inquirerWrapper<Value, Config>(fn: Prompt<Value, Config>, ...params: Parameters<Prompt<Value, Config>>) {\n try {\n using _ = addExitListener();\n const result = fn(...params);\n cancelFn = result.cancel;\n return await result;\n } catch (error) {\n if (error instanceof Error && ['CancelPromptError', 'ExitPromptError'].includes(error.constructor.name)) {\n endWithError('Cancelled! 👋');\n }\n endWithError(error as Error);\n return undefined;\n }\n}\n\nexport function safeSelect<Value = string>(...params: Parameters<typeof select<Value>>) {\n return inquirerWrapper(select, ...params);\n}\n\nexport function safeInput(...params: Parameters<typeof input>) {\n return inquirerWrapper(input, ...params);\n}\n\nconst splitWordAtCapital = (str: string) => str.charAt(0).toUpperCase() + str.slice(1).split(/(?=[A-Z])/).join(' ');\n\nfunction getEnvironment() {\n return safeSelect({\n message: 'Which environment do you want to use?',\n choices: SUPPORTED_CLUSTERS.map(cluster => ({ name: splitWordAtCapital(cluster), value: cluster })),\n default: 'expManager',\n });\n}\n\nexport async function getAutorepoAppName(currentServicePath: string) {\n const appNames = (await readdir(`${currentServicePath}${sep}apps`)).filter(name => !name.startsWith('.') && !name.endsWith('-e2e'));\n return safeSelect({\n message: 'Using from autorepo, Which app do you want to use?',\n choices: appNames.map(app => ({ name: app, value: app })),\n });\n}\n\nasync function getServiceName() {\n const gitPath = await getGitPath();\n const defaultValue = gitPath.split(sep).pop();\n if (defaultValue === 'autorepo') {\n return getAutorepoAppName(gitPath);\n }\n return safeInput({ message: 'Enter the service name:', default: defaultValue });\n}\n\nexport function getVariationId(defaultVal = 'default') {\n return safeInput({ message: 'Enter your simulator id (Press enter to pass):', default: defaultVal });\n}\n\nasync function getPort(defaultVal = '8080', isLocal = false) {\n return safeInput({\n message: `Enter the ${isLocal ? 'local ' : ''}port:`,\n default: defaultVal,\n validate(value) {\n // Regex source: https://stackoverflow.com/a/12968117\n if (!/^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/.test(value)) {\n return 'Please enter a valid port number';\n }\n return true;\n },\n });\n}\n\nexport async function getLocalPort(defaultVal = '5432') {\n return getPort(defaultVal, true);\n}\n\nconst VALUE_GETTER_MAP = {\n cluster: getEnvironment,\n variationId: getVariationId,\n service: getServiceName,\n port: (defaultVal = '8080') => getPort(defaultVal),\n 'local-port': getLocalPort,\n} as const;\n\nexport async function fillMissingRequiredValues<T extends { [key in keyof typeof VALUE_GETTER_MAP]?: string }>(values: T, requiredValues: (keyof T | { option: keyof T; defaultVal: string; })[]) {\n for (const value of requiredValues) {\n const option = typeof value === 'object' ? value.option : value;\n const defaultVal = typeof value === 'object' ? value.defaultVal : undefined;\n if (typeof option !== 'string') {\n throw new Error(`Invalid option ${String(option)}`);\n }\n const getter = VALUE_GETTER_MAP[option as keyof typeof VALUE_GETTER_MAP];\n if (!getter) {\n throw new Error(`No value getter for ${String(option)}`);\n }\n\n values[option] ??= await getter(defaultVal) as T[keyof T & string];\n }\n return values;\n}\n\nexport async function promptForConfirmation(message: string, defaultVal = true) {\n return inquirerWrapper(confirm, { message, default: defaultVal });\n}\n","#!/usr/bin/env node\nimport fs from 'node:fs';\nimport asyncSpawn from './asyncSpawn.js';\nimport { promptForConfirmation } from './inquirer.js';\n\nconst IP_LINE_SUFFIX = 'SERVICE_HOST';\nconst PROD_CLUSTER_TO_CMD_MAP = {\n prodUs: ['us-autofleet-production', 'us-central1', 'autofleet-production'],\n prodEu: ['eu-prod-autofleet', 'europe-west1', 'autofleetprod'],\n prodJp: ['jp-autofleet-production', 'asia-northeast1', 'autofleet-japan'],\n};\nconst SIMULATION_CLUSTER_TO_CMD_MAP = {\n dev1: ['dev-cluster-2', 'europe-west1', 'dev1-experiment-manager'],\n e2eManager: ['e2e-cluster-2', 'europe-west1', 'e2e-project-1'],\n expManager: ['af-experiment-manager', 'europe-west1'],\n};\nconst STAGING_CLUSTER_TO_CMD_MAP = {\n staging: ['stg-autofleet', 'europe-west1', 'autofleet-staging'],\n loadTest: ['lt-autofleet-production', 'asia-northeast1', 'load-testing-428806'],\n};\nconst MAPPING_CLUSTER_TO_CMD_MAP = {\n osrmProd: ['maps-prod', 'europe-west1', 'af-mapping'],\n};\nconst CLUSTER_TO_CMD_MAP = {\n ...PROD_CLUSTER_TO_CMD_MAP,\n ...SIMULATION_CLUSTER_TO_CMD_MAP,\n ...STAGING_CLUSTER_TO_CMD_MAP,\n ...MAPPING_CLUSTER_TO_CMD_MAP,\n};\n\nconst CLUSTER_TO_REDIS_REGION = {\n staging: 'europe-west1',\n};\n\nexport const SUPPORTED_CLUSTERS = Object.keys(CLUSTER_TO_CMD_MAP) as (keyof typeof CLUSTER_TO_CMD_MAP)[];\n\nconst getClusterDetails = async (clusterName: string) => {\n const command = CLUSTER_TO_CMD_MAP[clusterName as keyof typeof CLUSTER_TO_CMD_MAP];\n if (!command) {\n throw new Error(`Cluster name ${clusterName} does not exist!`);\n }\n const [name, region, project] = command;\n\n if (\n PROD_CLUSTER_TO_CMD_MAP[clusterName as keyof typeof PROD_CLUSTER_TO_CMD_MAP]\n && !(await promptForConfirmation(`❌❌ Are you sure you want to run this command on ${clusterName}? ❌❌`, false))) {\n console.log('Aborting...');\n return process.exit(1);\n }\n\n return { name, region, project };\n};\n\nexport const replaceInFile = ({ newFilePath, filePath, linesToReplace }: { newFilePath: string; filePath: string; linesToReplace: { find: string; replace: string; }[]; }) => {\n let fileAsString = fs.readFileSync(filePath, 'utf8');\n for (const { find, replace } of linesToReplace) {\n fileAsString = fileAsString.replace(find, replace);\n }\n fs.writeFileSync(newFilePath, fileAsString);\n};\n\nexport async function getCurrentCluster() {\n try {\n const { stdout } = await asyncSpawn('kubectl', ['config', 'current-context'], { print: false });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\nexport const connectToCluster = async (clusterName: string) => {\n const { name, region, project } = await getClusterDetails(clusterName);\n\n const expectedContext = `gke_${project || name}_${region}_${name}`;\n const currentContext = await getCurrentCluster();\n if (currentContext === expectedContext) {\n console.log(`Already connected to ${clusterName}, skipping connection to cluster...`);\n return { name, region, project };\n }\n\n await asyncSpawn('gcloud', ['container', 'clusters', 'get-credentials', name, '--region', region, '--project', project || name], { print: false });\n\n console.log(`Switched to ${clusterName} successfully`);\n\n return { name, region, project };\n};\n\nexport async function getCurrentNamespace() {\n try {\n const { stdout } = await asyncSpawn('kubectl', ['config', 'view', '--minify', '--output', 'jsonpath={..namespace}'], { print: false });\n return stdout.trim();\n } catch {\n return 'default'; // If no namespace is set, it defaults to 'default'\n }\n}\n\nexport const connectAndGetPrefix = async ({ clusterName, variationId }: { clusterName: string; variationId: string; }) => {\n await connectToCluster(clusterName);\n return `--namespace=${variationId}`;\n};\n\nexport const parseIps = (ips: string) => {\n const ipLines = ips.split('\\n').filter(Boolean);\n ipLines.shift();\n\n let formattedIps = ipLines.map((ipLine) => {\n const [name, _type, _clusterIp, externalIp, _port, _age] = ipLine.split(/(\\s+)/).map(s => s.trim()).filter(Boolean);\n const serviceName = name.replace(/-/g, '_').toUpperCase();\n const formattedLine = `${serviceName}_${IP_LINE_SUFFIX}=${externalIp}`;\n return formattedLine;\n });\n formattedIps = formattedIps.filter(formattedIp => !formattedIp.includes('none') && !formattedIp.includes('undefined'));\n return formattedIps;\n};\n\nexport function endWithError(error: string | Error, helpText?: string): never {\n if (!(error instanceof Error)) {\n error = new Error(error);\n }\n console.error(error);\n if (helpText) {\n console.log(helpText);\n }\n return process.exit(1);\n}\n\nexport const isSimulationCluster = (clusterName: string) => Object.keys(SIMULATION_CLUSTER_TO_CMD_MAP).includes(clusterName);\n\nexport const getRedisRegion = (clusterName: string) => CLUSTER_TO_REDIS_REGION[clusterName as keyof typeof CLUSTER_TO_REDIS_REGION]\n || CLUSTER_TO_CMD_MAP[clusterName as keyof typeof CLUSTER_TO_CMD_MAP][1];\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils-Dnt11RmF.js","names":["parseArgs","nativeParseArgs","packageJson.version","reject!: PromiseWithResolvers<T>['reject']","resolve!: PromiseWithResolvers<T>['resolve']","result: SpawnResult","signal","asyncSpawn","path","cancelFn: (() => void) | undefined","asyncSpawn"],"sources":["../package.json","../bin/utils/parser.ts","../bin/utils/promise.ts","../bin/utils/asyncSpawn.ts","../bin/utils/git.ts","../bin/utils/inquirer.ts","../bin/utils/index.ts"],"sourcesContent":["","import { parseArgs as nativeParseArgs, type ParseArgsConfig } from 'node:util';\nimport packageJson from '../../package.json' with { type: 'json' };\n\ntype ParseArgsOptionConfig = NonNullable<ParseArgsConfig['options']>[string] & { description: string; };\ntype ParseArgsParams = Omit<ParseArgsConfig, 'options'> & { binName: string; options: Record<string, ParseArgsOptionConfig>; commands?: Record<string, string>; };\n\nconst GLOBAL_OPTIONS = {\n help: {\n type: 'boolean',\n short: 'h',\n default: false,\n description: 'Show help (prints this message)',\n },\n version: {\n type: 'boolean',\n short: 'v',\n default: false,\n description: 'Show version number',\n },\n quiet: {\n type: 'boolean',\n short: 'q',\n default: false,\n description: 'Skip interactive prompts (e.g. version update check)',\n },\n} satisfies ParseArgsParams['options'];\n\ntype Expanded<T extends ParseArgsParams> = T & { options: T['options'] & typeof GLOBAL_OPTIONS; };\ntype Parsed<T extends ParseArgsParams> = ReturnType<typeof nativeParseArgs<Expanded<T>>> & { help: string; };\n\nexport default function parseArgs<T extends ParseArgsParams = ParseArgsParams>(params: T): Parsed<T> {\n params.options = { ...GLOBAL_OPTIONS, ...params.options };\n const data = nativeParseArgs(params as Expanded<T>);\n const { values } = data;\n if ((values as { version: boolean; }).version) {\n console.log(packageJson.version);\n process.exit(0);\n }\n\n const commands = !params.commands ? '' : `\n\nCommands:\n ${Object.entries(params.commands).map(([command, example]) => `${command.padEnd(30, ' ')} ${example}`).join('\\n ')}\n`;\n\n const longestDescriptionLength = Math.max.apply(null, Object.values(params.options).map(({ description }) => description.length));\n\n const help = `\n${params.binName} ${commands ? '[command] ' : ''}<options>${commands}\nOptions:\n ${Object.entries(params.options).map(([option, { description, type, short }]) => {\n const key = short ? `--${option}, -${short}` : `--${option}`;\n return `${key.padEnd(30, ' ')} ${description.padEnd(longestDescriptionLength + 10, ' ')} [${type}]`;\n }).join('\\n ')}\n`;\n\n if ((values as { help: boolean; }).help) {\n console.log(help);\n process.exit(0);\n }\n\n return { ...data, help };\n}\n","declare global {\n interface PromiseWithResolvers<T> {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n interface PromiseConstructor {\n /**\n * Creates a new Promise and returns it in an object, along with its resolve and reject functions.\n * @returns An object with the properties `promise`, `resolve`, and `reject`.\n *\n * ```ts\n * const { promise, resolve, reject } = Promise.withResolvers<T>();\n * ```\n */\n withResolvers<T>(): PromiseWithResolvers<T>;\n }\n}\n\nexport const createDeferredPromise = <T>() => {\n if (Promise.withResolvers) {\n return Promise.withResolvers<T>();\n }\n let reject!: PromiseWithResolvers<T>['reject'];\n let resolve!: PromiseWithResolvers<T>['resolve'];\n const promise = new Promise<T>((res, rej) => {\n reject = rej;\n resolve = res;\n });\n return { promise, reject, resolve };\n};\n","import { spawn } from 'node:child_process';\nimport { createDeferredPromise } from './promise.js';\n\ninterface SpawnResult {\n pid?: number;\n stdout: string;\n stderr: string;\n status: number | null;\n signal: string | null;\n}\n\nexport default async (\n command: string,\n args?: boolean | readonly string[],\n {\n print = true,\n env = {},\n signal,\n shell,\n cwd,\n }: { print?: boolean; env?: NodeJS.ProcessEnv; signal?: AbortSignal; shell?: string | boolean; cwd?: string; } = {},\n) => {\n if (typeof args === 'boolean') {\n print = args;\n args = undefined;\n }\n const stubError = new Error();\n const callerStack = stubError.stack ? stubError.stack.replace(/^.*/, ' ...') : null;\n const { promise, reject, resolve } = createDeferredPromise<SpawnResult>();\n const child = spawn(command, args, { env: Object.assign(env, process.env), signal, shell, cwd });\n if (print) {\n child.stdout.pipe(process.stdout);\n child.stderr.pipe(process.stderr);\n }\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (data) => {\n stdout += data;\n });\n child.stderr?.on('data', (data) => {\n stderr += data;\n });\n const completionEventName = print ? 'exit' : 'close';\n function errorListener(error: Error) {\n child.removeListener(completionEventName, completionListener);\n Object.assign(error, {\n pid: child.pid,\n stdout,\n stderr,\n status: null,\n signal: null,\n });\n reject(error);\n }\n function completionListener(code: number | null, signal: string | null) {\n child.removeListener('error', errorListener);\n const result: SpawnResult = {\n pid: child.pid,\n stdout,\n stderr,\n status: code,\n signal,\n };\n if (code !== 0) {\n const argumentString = (typeof args !== 'boolean' && ` ${args?.join(' ')}`) || '';\n const error = signal\n ? new Error(`${command}${argumentString} exited with signal: ${signal}`)\n : new Error(`${command}${argumentString} exited with non-zero code: ${code}`);\n if (error.stack && callerStack) {\n error.stack += `\\n${callerStack}`;\n }\n Object.assign(error, result);\n reject(error);\n } else {\n resolve(result);\n }\n }\n\n child.once(completionEventName, completionListener);\n child.once('error', errorListener);\n return promise;\n};\n","import asyncSpawn from './asyncSpawn.js';\n\nexport const getCurrentBranch = async () => {\n const { stdout: currentBranch } = await asyncSpawn('git', ['branch', '--show-current'], { print: false });\n return currentBranch.trim();\n};\n\nexport const getGitPath = async () => {\n const { stdout: gitPath } = await asyncSpawn('git', ['rev-parse', '--show-toplevel'], { print: false });\n return gitPath.trim();\n};\n\nexport const getChangesOfFile = async (filePath: string) => {\n const { stdout: changes } = await asyncSpawn('git', ['diff', 'origin/master..', filePath], { print: false });\n return changes;\n};\n\nexport const isPathIgnored = async (path: string) => {\n try {\n await asyncSpawn('git', ['check-ignore', path], { print: false });\n return true;\n } catch {\n return false;\n }\n};\n","import { confirm, input, select } from '@inquirer/prompts';\nimport type { Prompt } from '@inquirer/type';\nimport { getGitPath } from './git.js';\nimport { SUPPORTED_CLUSTERS, endWithError } from './index.js';\nimport { sep } from 'node:path';\nimport { readdir } from 'node:fs/promises';\n\ndeclare global {\n interface SymbolConstructor {\n /** A method that is used to release resources held by an object. Called by the semantics of the `using` statement. */\n readonly dispose: unique symbol;\n /** A method that is used to asynchronously release resources held by an object. Called by the semantics of the `await using` statement. */\n readonly asyncDispose: unique symbol;\n }\n}\n// @ts-expect-error polyfill for node < 18.18\nSymbol.dispose ??= Symbol.for('nodejs.dispose');\n// @ts-expect-error polyfill for node < 18.18\nSymbol.asyncDispose ??= Symbol.for('nodejs.asyncDispose');\nlet cancelFn: (() => void) | undefined;\nconst handler = () => cancelFn?.();\nfunction addExitListener() {\n process.once('beforeExit', handler);\n return {\n [Symbol.dispose]() {\n process.off('beforeExit', handler);\n cancelFn = undefined;\n },\n };\n}\n\nasync function inquirerWrapper<Value, Config>(fn: Prompt<Value, Config>, ...params: Parameters<Prompt<Value, Config>>) {\n try {\n using _ = addExitListener();\n const result = fn(...params);\n cancelFn = result.cancel;\n return await result;\n } catch (error) {\n if (error instanceof Error && ['CancelPromptError', 'ExitPromptError'].includes(error.constructor.name)) {\n endWithError('Cancelled! 👋');\n }\n endWithError(error as Error);\n return undefined;\n }\n}\n\nexport function safeSelect<Value = string>(...params: Parameters<typeof select<Value>>) {\n return inquirerWrapper(select, ...params);\n}\n\nexport function safeInput(...params: Parameters<typeof input>) {\n return inquirerWrapper(input, ...params);\n}\n\nconst splitWordAtCapital = (str: string) => str.charAt(0).toUpperCase() + str.slice(1).split(/(?=[A-Z])/).join(' ');\n\nfunction getEnvironment() {\n return safeSelect({\n message: 'Which environment do you want to use?',\n choices: SUPPORTED_CLUSTERS.map(cluster => ({ name: splitWordAtCapital(cluster), value: cluster })),\n default: 'expManager',\n });\n}\n\nexport async function getAutorepoAppName(currentServicePath: string) {\n const appNames = (await readdir(`${currentServicePath}${sep}apps`)).filter(name => !name.startsWith('.') && !name.endsWith('-e2e'));\n return safeSelect({\n message: 'Using from autorepo, Which app do you want to use?',\n choices: appNames.map(app => ({ name: app, value: app })),\n });\n}\n\nasync function getServiceName() {\n const gitPath = await getGitPath();\n const defaultValue = gitPath.split(sep).pop();\n if (defaultValue === 'autorepo') {\n return getAutorepoAppName(gitPath);\n }\n return safeInput({ message: 'Enter the service name:', default: defaultValue });\n}\n\nexport function getVariationId(defaultVal = 'default') {\n return safeInput({ message: 'Enter your simulator id (Press enter to pass):', default: defaultVal });\n}\n\nasync function getPort(defaultVal = '8080', isLocal = false) {\n return safeInput({\n message: `Enter the ${isLocal ? 'local ' : ''}port:`,\n default: defaultVal,\n validate(value) {\n // Regex source: https://stackoverflow.com/a/12968117\n if (!/^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/.test(value)) {\n return 'Please enter a valid port number';\n }\n return true;\n },\n });\n}\n\nexport async function getLocalPort(defaultVal = '5432') {\n return getPort(defaultVal, true);\n}\n\nconst VALUE_GETTER_MAP = {\n cluster: getEnvironment,\n variationId: getVariationId,\n service: getServiceName,\n port: (defaultVal = '8080') => getPort(defaultVal),\n 'local-port': getLocalPort,\n} as const;\n\nexport async function fillMissingRequiredValues<T extends { [key in keyof typeof VALUE_GETTER_MAP]?: string }>(values: T, requiredValues: (keyof T | { option: keyof T; defaultVal: string; })[]) {\n for (const value of requiredValues) {\n const option = typeof value === 'object' ? value.option : value;\n const defaultVal = typeof value === 'object' ? value.defaultVal : undefined;\n if (typeof option !== 'string') {\n throw new Error(`Invalid option ${String(option)}`);\n }\n const getter = VALUE_GETTER_MAP[option as keyof typeof VALUE_GETTER_MAP];\n if (!getter) {\n throw new Error(`No value getter for ${String(option)}`);\n }\n\n values[option] ??= await getter(defaultVal) as T[keyof T & string];\n }\n return values;\n}\n\nexport async function promptForConfirmation(message: string, defaultVal = true) {\n return inquirerWrapper(confirm, { message, default: defaultVal });\n}\n","#!/usr/bin/env node\nimport fs from 'node:fs';\nimport asyncSpawn from './asyncSpawn.js';\nimport { promptForConfirmation } from './inquirer.js';\n\nconst IP_LINE_SUFFIX = 'SERVICE_HOST';\nconst PROD_CLUSTER_TO_CMD_MAP = {\n prodUs: ['us-autofleet-production', 'us-central1', 'autofleet-production'],\n prodEu: ['eu-prod-autofleet', 'europe-west1', 'autofleetprod'],\n prodJp: ['jp-autofleet-production', 'asia-northeast1', 'autofleet-japan'],\n};\nconst SIMULATION_CLUSTER_TO_CMD_MAP = {\n dev1: ['dev-cluster-2', 'europe-west1', 'dev1-experiment-manager'],\n e2eManager: ['e2e-cluster-2', 'europe-west1', 'e2e-project-1'],\n expManager: ['af-experiment-manager', 'europe-west1'],\n};\nconst STAGING_CLUSTER_TO_CMD_MAP = {\n staging: ['stg-autofleet', 'europe-west1', 'autofleet-staging'],\n loadTest: ['lt-autofleet-production', 'asia-northeast1', 'load-testing-428806'],\n};\nconst MAPPING_CLUSTER_TO_CMD_MAP = {\n osrmProd: ['maps-prod', 'europe-west1', 'af-mapping'],\n};\nconst CLUSTER_TO_CMD_MAP = {\n ...PROD_CLUSTER_TO_CMD_MAP,\n ...SIMULATION_CLUSTER_TO_CMD_MAP,\n ...STAGING_CLUSTER_TO_CMD_MAP,\n ...MAPPING_CLUSTER_TO_CMD_MAP,\n};\n\nconst CLUSTER_TO_REDIS_REGION = {\n staging: 'europe-west1',\n};\n\nexport const SUPPORTED_CLUSTERS = Object.keys(CLUSTER_TO_CMD_MAP) as (keyof typeof CLUSTER_TO_CMD_MAP)[];\n\nconst getClusterDetails = async (clusterName: string) => {\n const command = CLUSTER_TO_CMD_MAP[clusterName as keyof typeof CLUSTER_TO_CMD_MAP];\n if (!command) {\n throw new Error(`Cluster name ${clusterName} does not exist!`);\n }\n const [name, region, project] = command;\n\n if (\n PROD_CLUSTER_TO_CMD_MAP[clusterName as keyof typeof PROD_CLUSTER_TO_CMD_MAP]\n && !(await promptForConfirmation(`❌❌ Are you sure you want to run this command on ${clusterName}? ❌❌`, false))) {\n console.log('Aborting...');\n return process.exit(1);\n }\n\n return { name, region, project };\n};\n\nexport const replaceInFile = ({ newFilePath, filePath, linesToReplace }: { newFilePath: string; filePath: string; linesToReplace: { find: string; replace: string; }[]; }) => {\n let fileAsString = fs.readFileSync(filePath, 'utf8');\n for (const { find, replace } of linesToReplace) {\n fileAsString = fileAsString.replace(find, replace);\n }\n fs.writeFileSync(newFilePath, fileAsString);\n};\n\nexport async function getCurrentCluster() {\n try {\n const { stdout } = await asyncSpawn('kubectl', ['config', 'current-context'], { print: false });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\nexport const connectToCluster = async (clusterName: string) => {\n const { name, region, project } = await getClusterDetails(clusterName);\n\n const expectedContext = `gke_${project || name}_${region}_${name}`;\n const currentContext = await getCurrentCluster();\n if (currentContext === expectedContext) {\n console.log(`Already connected to ${clusterName}, skipping connection to cluster...`);\n return { name, region, project };\n }\n\n await asyncSpawn('gcloud', ['container', 'clusters', 'get-credentials', name, '--region', region, '--project', project || name], { print: false });\n\n console.log(`Switched to ${clusterName} successfully`);\n\n return { name, region, project };\n};\n\nexport async function getCurrentNamespace() {\n try {\n const { stdout } = await asyncSpawn('kubectl', ['config', 'view', '--minify', '--output', 'jsonpath={..namespace}'], { print: false });\n return stdout.trim();\n } catch {\n return 'default'; // If no namespace is set, it defaults to 'default'\n }\n}\n\nexport const connectAndGetPrefix = async ({ clusterName, variationId }: { clusterName: string; variationId: string; }) => {\n await connectToCluster(clusterName);\n return `--namespace=${variationId}`;\n};\n\nexport const parseIps = (ips: string) => {\n const ipLines = ips.split('\\n').filter(Boolean);\n ipLines.shift();\n\n let formattedIps = ipLines.map((ipLine) => {\n const [name, _type, _clusterIp, externalIp, _port, _age] = ipLine.split(/(\\s+)/).map(s => s.trim()).filter(Boolean);\n const serviceName = name.replace(/-/g, '_').toUpperCase();\n const formattedLine = `${serviceName}_${IP_LINE_SUFFIX}=${externalIp}`;\n return formattedLine;\n });\n formattedIps = formattedIps.filter(formattedIp => !formattedIp.includes('none') && !formattedIp.includes('undefined'));\n return formattedIps;\n};\n\nexport function endWithError(error: string | Error, helpText?: string): never {\n if (!(error instanceof Error)) {\n error = new Error(error);\n }\n console.error(error);\n if (helpText) {\n console.log(helpText);\n }\n return process.exit(1);\n}\n\nexport const isSimulationCluster = (clusterName: string) => Object.keys(SIMULATION_CLUSTER_TO_CMD_MAP).includes(clusterName);\n\nexport const getRedisRegion = (clusterName: string) => CLUSTER_TO_REDIS_REGION[clusterName as keyof typeof CLUSTER_TO_REDIS_REGION]\n || CLUSTER_TO_CMD_MAP[clusterName as keyof typeof CLUSTER_TO_CMD_MAP][1];\n"],"mappings":"6QCMA,MAAM,EAAiB,CACrB,KAAM,CACJ,KAAM,UACN,MAAO,IACP,QAAS,GACT,YAAa,kCACd,CACD,QAAS,CACP,KAAM,UACN,MAAO,IACP,QAAS,GACT,YAAa,sBACd,CACD,MAAO,CACL,KAAM,UACN,MAAO,IACP,QAAS,GACT,YAAa,uDACd,CACF,CAKD,SAAwBA,EAAuD,EAAsB,CACnG,EAAO,QAAU,CAAE,GAAG,EAAgB,GAAG,EAAO,QAAS,CACzD,IAAM,EAAOC,EAAgB,EAAsB,CAC7C,CAAE,UAAW,EACd,EAAiC,UACpC,QAAQ,IAAIC,EAAoB,CAChC,QAAQ,KAAK,EAAE,EAGjB,IAAM,EAAY,EAAO,SAAgB;;;IAGvC,OAAO,QAAQ,EAAO,SAAS,CAAC,KAAK,CAAC,EAAS,KAAa,GAAG,EAAQ,OAAO,GAAI,IAAI,CAAC,GAAG,IAAU,CAAC,KAAK;IAAO,CAAC;EAHhF,GAM9B,EAA2B,KAAK,IAAI,MAAM,KAAM,OAAO,OAAO,EAAO,QAAQ,CAAC,KAAK,CAAE,iBAAkB,EAAY,OAAO,CAAC,CAE3H,EAAO;EACb,EAAO,QAAQ,GAAG,EAAW,aAAe,GAAG,WAAW,EAAS;;IAEjE,OAAO,QAAQ,EAAO,QAAQ,CAAC,KAAK,CAAC,EAAQ,CAAE,cAAa,OAAM,YAE3D,IADK,EAAQ,KAAK,EAAO,KAAK,IAAU,KAAK,KACtC,OAAO,GAAI,IAAI,CAAC,GAAG,EAAY,OAAO,EAA2B,GAAI,IAAI,CAAC,IAAI,EAAK,GACjG,CAAC,KAAK;IAAO,CAAC;EAQhB,OALK,EAA8B,OACjC,QAAQ,IAAI,EAAK,CACjB,QAAQ,KAAK,EAAE,EAGV,CAAE,GAAG,EAAM,OAAM,CC1C1B,MAAa,MAAiC,CAC5C,GAAI,QAAQ,cACV,OAAO,QAAQ,eAAkB,CAEnC,IAAIC,EACAC,EAKJ,MAAO,CAAE,QAJO,IAAI,SAAY,EAAK,IAAQ,CAC3C,EAAS,EACT,EAAU,GACV,CACgB,SAAQ,QAAA,EAAS,EClBrC,IAAA,EAAe,MACb,EACA,EACA,CACE,QAAQ,GACR,MAAM,EAAE,CACR,SACA,QACA,OAC+G,EAAE,GAChH,CACC,OAAO,GAAS,YAClB,EAAQ,EACR,EAAO,IAAA,IAET,IAAM,EAAgB,OAAO,CACvB,EAAc,EAAU,MAAQ,EAAU,MAAM,QAAQ,MAAO,UAAU,CAAG,KAC5E,CAAE,UAAS,SAAQ,QAAA,GAAY,GAAoC,CACnE,EAAQ,EAAM,EAAS,EAAM,CAAE,IAAK,OAAO,OAAO,EAAK,QAAQ,IAAI,CAAE,SAAQ,QAAO,MAAK,CAAC,CAC5F,IACF,EAAM,OAAO,KAAK,QAAQ,OAAO,CACjC,EAAM,OAAO,KAAK,QAAQ,OAAO,EAEnC,IAAI,EAAS,GACT,EAAS,GACb,EAAM,QAAQ,GAAG,OAAS,GAAS,CACjC,GAAU,GACV,CACF,EAAM,QAAQ,GAAG,OAAS,GAAS,CACjC,GAAU,GACV,CACF,IAAM,EAAsB,EAAQ,OAAS,QAC7C,SAAS,EAAc,EAAc,CACnC,EAAM,eAAe,EAAqB,EAAmB,CAC7D,OAAO,OAAO,EAAO,CACnB,IAAK,EAAM,IACX,SACA,SACA,OAAQ,KACR,OAAQ,KACT,CAAC,CACF,EAAO,EAAM,CAEf,SAAS,EAAmB,EAAqB,EAAuB,CACtE,EAAM,eAAe,QAAS,EAAc,CAC5C,IAAMC,EAAsB,CAC1B,IAAK,EAAM,IACX,SACA,SACA,OAAQ,EACR,OAAA,EACD,CACD,GAAI,IAAS,EAAG,CACd,IAAM,EAAkB,OAAO,GAAS,WAAa,IAAI,GAAM,KAAK,IAAI,IAAO,GACzE,EAAQC,EACN,MAAM,GAAG,IAAU,EAAe,uBAAuBA,IAAS,CAClE,MAAM,GAAG,IAAU,EAAe,8BAA8B,IAAO,CAC3E,EAAM,OAAS,IACjB,EAAM,OAAS,KAAK,KAEtB,OAAO,OAAO,EAAO,EAAO,CAC5B,EAAO,EAAM,MAEb,EAAQ,EAAO,CAMnB,OAFA,EAAM,KAAK,EAAqB,EAAmB,CACnD,EAAM,KAAK,QAAS,EAAc,CAC3B,GC9ET,MAAa,EAAmB,SAAY,CAC1C,GAAM,CAAE,OAAQ,GAAkB,MAAMC,EAAW,MAAO,CAAC,SAAU,iBAAiB,CAAE,CAAE,MAAO,GAAO,CAAC,CACzG,OAAO,EAAc,MAAM,EAGhB,EAAa,SAAY,CACpC,GAAM,CAAE,OAAQ,GAAY,MAAMA,EAAW,MAAO,CAAC,YAAa,kBAAkB,CAAE,CAAE,MAAO,GAAO,CAAC,CACvG,OAAO,EAAQ,MAAM,EAGV,EAAmB,KAAO,IAAqB,CAC1D,GAAM,CAAE,OAAQ,GAAY,MAAMA,EAAW,MAAO,CAAC,OAAQ,kBAAmB,EAAS,CAAE,CAAE,MAAO,GAAO,CAAC,CAC5G,OAAO,GAGI,EAAgB,KAAO,IAAiB,CACnD,GAAI,CAEF,OADA,MAAMA,EAAW,MAAO,CAAC,eAAgBC,EAAK,CAAE,CAAE,MAAO,GAAO,CAAC,CAC1D,QACD,CACN,MAAO,ijCCNX,OAAO,UAAY,OAAO,IAAI,iBAAiB,CAE/C,OAAO,eAAiB,OAAO,IAAI,sBAAsB,CACzD,IAAIC,EACJ,MAAM,MAAgB,KAAY,CAClC,SAAS,GAAkB,CAEzB,OADA,QAAQ,KAAK,aAAc,EAAQ,CAC5B,CACL,CAAC,OAAO,UAAW,CACjB,QAAQ,IAAI,aAAc,EAAQ,CAClC,EAAW,IAAA,IAEd,CAGH,eAAe,EAA+B,EAA2B,GAAG,EAA2C,CACrH,GAAI,eACI,EAAA,EAAI,GAAiB,CAAA,CAC3B,IAAM,EAAS,EAAG,GAAG,EAAO,CAE5B,MADA,GAAW,EAAO,OACX,MAAM,sCACN,EAAO,CACV,aAAiB,OAAS,CAAC,oBAAqB,kBAAkB,CAAC,SAAS,EAAM,YAAY,KAAK,EACrG,EAAa,gBAAgB,CAE/B,EAAa,EAAe,CAC5B,QAIJ,SAAgB,EAA2B,GAAG,EAA0C,CACtF,OAAO,EAAgB,EAAQ,GAAG,EAAO,CAG3C,SAAgB,EAAU,GAAG,EAAkC,CAC7D,OAAO,EAAgB,EAAO,GAAG,EAAO,CAG1C,MAAM,EAAsB,GAAgB,EAAI,OAAO,EAAE,CAAC,aAAa,CAAG,EAAI,MAAM,EAAE,CAAC,MAAM,YAAY,CAAC,KAAK,IAAI,CAEnH,SAAS,GAAiB,CACxB,OAAO,EAAW,CAChB,QAAS,wCACT,QAAS,EAAmB,IAAI,IAAY,CAAE,KAAM,EAAmB,EAAQ,CAAE,MAAO,EAAS,EAAE,CACnG,QAAS,aACV,CAAC,CAGJ,eAAsB,EAAmB,EAA4B,CAEnE,OAAO,EAAW,CAChB,QAAS,qDACT,SAHgB,MAAM,EAAQ,GAAG,IAAqB,EAAI,MAAM,EAAE,OAAO,GAAQ,CAAC,EAAK,WAAW,IAAI,EAAI,CAAC,EAAK,SAAS,OAAO,CAAC,CAG/G,IAAI,IAAQ,CAAE,KAAM,EAAK,MAAO,EAAK,EAAE,CAC1D,CAAC,CAGJ,eAAe,GAAiB,CAC9B,IAAM,EAAU,MAAM,GAAY,CAC5B,EAAe,EAAQ,MAAM,EAAI,CAAC,KAAK,CAI7C,OAHI,IAAiB,WACZ,EAAmB,EAAQ,CAE7B,EAAU,CAAE,QAAS,0BAA2B,QAAS,EAAc,CAAC,CAGjF,SAAgB,EAAe,EAAa,UAAW,CACrD,OAAO,EAAU,CAAE,QAAS,iDAAkD,QAAS,EAAY,CAAC,CAGtG,eAAe,EAAQ,EAAa,OAAQ,EAAU,GAAO,CAC3D,OAAO,EAAU,CACf,QAAS,aAAa,EAAU,SAAW,GAAG,OAC9C,QAAS,EACT,SAAS,EAAO,CAKd,MAHK,2FAA2F,KAAK,EAAM,CAGpG,GAFE,oCAIZ,CAAC,CAGJ,eAAsB,EAAa,EAAa,OAAQ,CACtD,OAAO,EAAQ,EAAY,GAAK,CAGlC,MAAM,EAAmB,CACvB,QAAS,EACT,YAAa,EACb,QAAS,EACT,MAAO,EAAa,SAAW,EAAQ,EAAW,CAClD,aAAc,EACf,CAED,eAAsB,EAAyF,EAAW,EAAwE,CAChM,IAAK,IAAM,KAAS,EAAgB,CAClC,IAAM,EAAS,OAAO,GAAU,SAAW,EAAM,OAAS,EACpD,EAAa,OAAO,GAAU,SAAW,EAAM,WAAa,IAAA,GAClE,GAAI,OAAO,GAAW,SACpB,MAAU,MAAM,kBAAkB,OAAO,EAAO,GAAG,CAErD,IAAM,EAAS,EAAiB,GAChC,GAAI,CAAC,EACH,MAAU,MAAM,uBAAuB,OAAO,EAAO,GAAG,CAG1D,EAAO,KAAY,MAAM,EAAO,EAAW,CAE7C,OAAO,EAGT,eAAsB,EAAsB,EAAiB,EAAa,GAAM,CAC9E,OAAO,EAAgB,EAAS,CAAE,UAAS,QAAS,EAAY,CAAC,CC5HnE,MACM,EAA0B,CAC9B,OAAQ,CAAC,0BAA2B,cAAe,uBAAuB,CAC1E,OAAQ,CAAC,oBAAqB,eAAgB,gBAAgB,CAC9D,OAAQ,CAAC,0BAA2B,kBAAmB,kBAAkB,CAC1E,CACK,EAAgC,CACpC,KAAM,CAAC,gBAAiB,eAAgB,0BAA0B,CAClE,WAAY,CAAC,gBAAiB,eAAgB,gBAAgB,CAC9D,WAAY,CAAC,wBAAyB,eAAe,CACtD,CACK,EAA6B,CACjC,QAAS,CAAC,gBAAiB,eAAgB,oBAAoB,CAC/D,SAAU,CAAC,0BAA2B,kBAAmB,sBAAsB,CAChF,CACK,EAA6B,CACjC,SAAU,CAAC,YAAa,eAAgB,aAAa,CACtD,CACK,EAAqB,CACzB,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACJ,CAEK,EAA0B,CAC9B,QAAS,eACV,CAEY,EAAqB,OAAO,KAAK,EAAmB,CAE3D,EAAoB,KAAO,IAAwB,CACvD,IAAM,EAAU,EAAmB,GACnC,GAAI,CAAC,EACH,MAAU,MAAM,gBAAgB,EAAY,kBAAkB,CAEhE,GAAM,CAAC,EAAM,EAAQ,GAAW,EAShC,OANE,EAAwB,IACrB,CAAE,MAAM,EAAsB,mDAAmD,EAAY,MAAO,GAAM,EAC7G,QAAQ,IAAI,cAAc,CACnB,QAAQ,KAAK,EAAE,EAGjB,CAAE,OAAM,SAAQ,UAAS,EAGrB,GAAiB,CAAE,cAAa,WAAU,oBAAuH,CAC5K,IAAI,EAAe,EAAG,aAAa,EAAU,OAAO,CACpD,IAAK,GAAM,CAAE,OAAM,aAAa,EAC9B,EAAe,EAAa,QAAQ,EAAM,EAAQ,CAEpD,EAAG,cAAc,EAAa,EAAa,EAG7C,eAAsB,GAAoB,CACxC,GAAI,CACF,GAAM,CAAE,UAAW,MAAMC,EAAW,UAAW,CAAC,SAAU,kBAAkB,CAAE,CAAE,MAAO,GAAO,CAAC,CAC/F,OAAO,EAAO,MAAM,MACd,CACN,OAAO,MAIX,MAAa,EAAmB,KAAO,IAAwB,CAC7D,GAAM,CAAE,OAAM,SAAQ,WAAY,MAAM,EAAkB,EAAY,CAEhE,EAAkB,OAAO,GAAW,EAAK,GAAG,EAAO,GAAG,IAW5D,OAVuB,MAAM,GAAmB,GACzB,GACrB,QAAQ,IAAI,wBAAwB,EAAY,qCAAqC,CAC9E,CAAE,OAAM,SAAQ,UAAS,GAGlC,MAAMA,EAAW,SAAU,CAAC,YAAa,WAAY,kBAAmB,EAAM,WAAY,EAAQ,YAAa,GAAW,EAAK,CAAE,CAAE,MAAO,GAAO,CAAC,CAElJ,QAAQ,IAAI,eAAe,EAAY,eAAe,CAE/C,CAAE,OAAM,SAAQ,UAAS,GAGlC,eAAsB,GAAsB,CAC1C,GAAI,CACF,GAAM,CAAE,UAAW,MAAMA,EAAW,UAAW,CAAC,SAAU,OAAQ,WAAY,WAAY,yBAAyB,CAAE,CAAE,MAAO,GAAO,CAAC,CACtI,OAAO,EAAO,MAAM,MACd,CACN,MAAO,WAIX,MAAa,EAAsB,MAAO,CAAE,cAAa,kBACvD,MAAM,EAAiB,EAAY,CAC5B,eAAe,KAGX,EAAY,GAAgB,CACvC,IAAM,EAAU,EAAI,MAAM;EAAK,CAAC,OAAO,QAAQ,CAC/C,EAAQ,OAAO,CAEf,IAAI,EAAe,EAAQ,IAAK,GAAW,CACzC,GAAM,CAAC,EAAM,EAAO,EAAY,EAAY,EAAO,GAAQ,EAAO,MAAM,QAAQ,CAAC,IAAI,GAAK,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ,CAGnH,MADsB,GADF,EAAK,QAAQ,KAAM,IAAI,CAAC,aAAa,CACpB,gBAAqB,KAE1D,CAEF,MADA,GAAe,EAAa,OAAO,GAAe,CAAC,EAAY,SAAS,OAAO,EAAI,CAAC,EAAY,SAAS,YAAY,CAAC,CAC/G,GAGT,SAAgB,EAAa,EAAuB,EAA0B,CAQ5E,OAPM,aAAiB,QACrB,EAAY,MAAM,EAAM,EAE1B,QAAQ,MAAM,EAAM,CAChB,GACF,QAAQ,IAAI,EAAS,CAEhB,QAAQ,KAAK,EAAE,CAGxB,MAAa,EAAuB,GAAwB,OAAO,KAAK,EAA8B,CAAC,SAAS,EAAY,CAE/G,EAAkB,GAAwB,EAAwB,IAC1E,EAAmB,GAAgD"}
|