@alacard-project/config-sdk 1.0.0 → 1.0.4
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 +14 -68
- package/dist/config-client.d.ts +0 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/nest-helpers.d.ts +16 -0
- package/dist/nest-helpers.js +57 -0
- package/eslint.config.mjs +27 -0
- package/package.json +41 -9
- package/src/config-client.ts +0 -2
- package/src/index.ts +2 -0
- package/src/nest-helpers.ts +63 -0
- package/test/config.client.spec.ts +34 -0
- package/.idea/workspace.xml +0 -45
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @alacard/config-sdk
|
|
1
|
+
# @alacard-project/config-sdk
|
|
2
2
|
|
|
3
3
|
**Alacard Config SDK** — это клиентская библиотека для взаимодействия с централизованным микросервисом конфигураций (CCS) через gRPC. SDK обеспечивает строго типизированный доступ к настройкам, поддерживает горячую перезагрузку через Kafka и имеет встроенный fallback на `.env` файлы.
|
|
4
4
|
|
|
@@ -8,14 +8,12 @@
|
|
|
8
8
|
- 🛡️ **Строгая типизация**: Автоматическая генерация TypeScript-интерфейсов из Protocol Buffers (`ts-proto`).
|
|
9
9
|
- ⚡ **Hot-Reloading**: Мгновенное обновление параметров в памяти приложения при изменении в базе (через Kafka).
|
|
10
10
|
- 📂 **.env Fallback**: Поддержка локальных `.env.<environment>` файлов для разработки и как резервный вариант.
|
|
11
|
-
- 🏗️ **Instance-based**: Поддержка нескольких экземпляров клиента в одном
|
|
11
|
+
- 🏗️ **Instance-based**: Поддержка нескольких экземпляров клиента в одном процессе.
|
|
12
12
|
|
|
13
13
|
## Установка
|
|
14
14
|
|
|
15
|
-
Так как пакет является приватным, убедитесь, что ваш `.npmrc` настроен для доступа к соответствующему реестру.
|
|
16
|
-
|
|
17
15
|
```bash
|
|
18
|
-
npm install @alacard/config-sdk
|
|
16
|
+
npm install @alacard-project/config-sdk
|
|
19
17
|
```
|
|
20
18
|
|
|
21
19
|
## Быстрый старт
|
|
@@ -25,83 +23,31 @@ npm install @alacard/config-sdk
|
|
|
25
23
|
Инициализируйте клиент при запуске вашего приложения (например, в `main.ts` или `bootstrap.ts`):
|
|
26
24
|
|
|
27
25
|
```typescript
|
|
28
|
-
import { ConfigClient } from '@alacard/config-sdk';
|
|
26
|
+
import { ConfigClient } from '@alacard-project/config-sdk';
|
|
29
27
|
|
|
30
28
|
async function bootstrap() {
|
|
31
29
|
const config = await ConfigClient.initialize({
|
|
32
30
|
serviceName: 'auth-service',
|
|
33
31
|
environment: process.env.NODE_ENV || 'development',
|
|
34
|
-
grpcUrl: process.env.GRPC_CONFIG_URL || '
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Опционально: Интеграция с Vault (KV + PKI)
|
|
38
|
-
vault: {
|
|
39
|
-
address: 'https://vault.prod.alacard.local:8200',
|
|
40
|
-
roleId: process.env.VAULT_ROLE_ID!,
|
|
41
|
-
secretId: process.env.VAULT_SECRET_ID!,
|
|
42
|
-
pkiPath: 'pki/issue/config-service', // Путь для динамических сертификатов
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
// Опционально: Ручная настройка mTLS (если не используется Vault PKI)
|
|
46
|
-
/*
|
|
47
|
-
tls: {
|
|
48
|
-
rootCert: fs.readFileSync('./certs/ca.crt'),
|
|
49
|
-
clientCert: fs.readFileSync('./certs/client.crt'),
|
|
50
|
-
clientKey: fs.readFileSync('./certs/client.key'),
|
|
51
|
-
},
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
|
-
kafkaBrokers: ['localhost:9092'],
|
|
55
|
-
useDotenvFallback: true,
|
|
32
|
+
grpcUrl: process.env.GRPC_CONFIG_URL || 'config-microservice:50055',
|
|
33
|
+
kafkaBrokers: ['kafka:9092'],
|
|
34
|
+
useDotenvFallback: true, // Использовать .env если gRPC недоступен
|
|
56
35
|
});
|
|
57
36
|
|
|
58
37
|
const dbUrl = config.get('DATABASE_URL');
|
|
38
|
+
console.log('Config loaded!');
|
|
59
39
|
}
|
|
60
40
|
```
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
1. **.env Fallback**: SDK пробует прочитать локальный `.env.<env>` файл (если `useDotenvFallback: true`).
|
|
65
|
-
2. **Vault KV**: Если настроен `vault`, SDK логинится через AppRole и забирает секреты из `secret/data/config-service/<serviceName>`.
|
|
66
|
-
3. **Vault PKI (mTLS)**: Если в `vault` указан `pkiPath`, SDK получает динамические сертификаты для защиты gRPC соединения.
|
|
67
|
-
4. **gRPC Config Service**: SDK запрашивает финальный конфиг через зашифрованное соединение с микросервиса конфигураций.
|
|
68
|
-
5. **Kafka Watch**: Запускается подписка на изменения для обновления параметров "на лету".
|
|
42
|
+
### Использование в NestJS
|
|
69
43
|
|
|
70
|
-
|
|
71
|
-
const port = config.getInt('PORT', 3000);
|
|
72
|
-
const dbUrl = config.get('DATABASE_URL');
|
|
44
|
+
Рекомендуется создать `ConfigService` обертку или использовать `ConfigModule` (если он реализован в вашем проекте), который внутри вызывает `ConfigClient.initialize`.
|
|
73
45
|
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Использование в других частях кода
|
|
79
|
-
|
|
80
|
-
После инициализации вы можете получить доступ к инстансу в любом месте приложения:
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { ConfigClient } from '@alacard/config-sdk';
|
|
84
|
-
|
|
85
|
-
const config = ConfigClient.getInstance();
|
|
86
|
-
const apiKey = config.get('EXTERNAL_API_KEY');
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Доступные методы
|
|
90
|
-
|
|
91
|
-
- `get(key: string, defaultValue?: string): string` — получить строковое значение.
|
|
92
|
-
- `getInt(key: string, defaultValue?: number): number` — получить целое число.
|
|
93
|
-
- `getAll(): Record<string, string>` — получить все загруженные настройки.
|
|
94
|
-
|
|
95
|
-
## Команды для разработки
|
|
96
|
-
|
|
97
|
-
Если вы вносите правки в сам SDK:
|
|
98
|
-
|
|
99
|
-
- `npm run gen:proto` — перегенерировать TypeScript-код из `.proto` файлов.
|
|
100
|
-
- `npm run build` — скомпилировать TypeScript в JavaScript для публикации.
|
|
101
|
-
|
|
102
|
-
## Структура конфигурации (gRPC)
|
|
46
|
+
## Как это работает (Порядок загрузки)
|
|
103
47
|
|
|
104
|
-
|
|
48
|
+
1. **.env Fallback**: SDK пробует прочитать локальный `.env.<env>` файл (если `useDotenvFallback: true`).
|
|
49
|
+
2. **gRPC Config Service**: SDK запрашивает финальный конфиг через gRPC.
|
|
50
|
+
3. **Kafka Watch**: Запускается подписка на изменения для обновления параметров "на лету".
|
|
105
51
|
|
|
106
52
|
---
|
|
107
53
|
© 2026 Alacard Team
|
package/dist/config-client.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -15,3 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./config-client"), exports);
|
|
18
|
+
__exportStar(require("./vault-client"), exports);
|
|
19
|
+
__exportStar(require("./nest-helpers"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Environment } from '@alacard-project/shared';
|
|
2
|
+
export declare abstract class BaseConfigService {
|
|
3
|
+
protected readonly configService: any;
|
|
4
|
+
private remoteConfig;
|
|
5
|
+
constructor(configService: any);
|
|
6
|
+
setRemoteConfig(config: Record<string, string>): void;
|
|
7
|
+
protected get<T = string>(key: string, defaultValue?: T): T;
|
|
8
|
+
protected getRequiredString(key: string): string;
|
|
9
|
+
protected getRequiredNumber(key: string): number;
|
|
10
|
+
protected getOptionalString(key: string, defaultValue: string): string;
|
|
11
|
+
protected getOptionalNumber(key: string, defaultValue: number): number;
|
|
12
|
+
get environment(): Environment;
|
|
13
|
+
get databaseUrl(): string;
|
|
14
|
+
get kafkaBrokers(): string[];
|
|
15
|
+
get isProduction(): boolean;
|
|
16
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseConfigService = void 0;
|
|
4
|
+
const shared_1 = require("@alacard-project/shared");
|
|
5
|
+
// Note: This requires @nestjs/config to be installed in the consumer microservice
|
|
6
|
+
// We keep it in the SDK as a helper for NestJS-based microservices.
|
|
7
|
+
class BaseConfigService {
|
|
8
|
+
configService;
|
|
9
|
+
remoteConfig = {};
|
|
10
|
+
constructor(configService) {
|
|
11
|
+
this.configService = configService;
|
|
12
|
+
}
|
|
13
|
+
setRemoteConfig(config) {
|
|
14
|
+
this.remoteConfig = { ...this.remoteConfig, ...config };
|
|
15
|
+
}
|
|
16
|
+
get(key, defaultValue) {
|
|
17
|
+
if (this.remoteConfig[key] !== undefined) {
|
|
18
|
+
return this.remoteConfig[key];
|
|
19
|
+
}
|
|
20
|
+
return this.configService.get(key, defaultValue);
|
|
21
|
+
}
|
|
22
|
+
getRequiredString(key) {
|
|
23
|
+
const value = this.get(key);
|
|
24
|
+
if (!value && process.env.NODE_ENV !== shared_1.Environment.TEST) {
|
|
25
|
+
throw new Error(`Configuration Error: ${key} is required`);
|
|
26
|
+
}
|
|
27
|
+
return value || '';
|
|
28
|
+
}
|
|
29
|
+
getRequiredNumber(key) {
|
|
30
|
+
const value = this.get(key);
|
|
31
|
+
if (!value && process.env.NODE_ENV !== shared_1.Environment.TEST) {
|
|
32
|
+
throw new Error(`Configuration Error: ${key} is required`);
|
|
33
|
+
}
|
|
34
|
+
return parseInt(value || '0', 10);
|
|
35
|
+
}
|
|
36
|
+
getOptionalString(key, defaultValue) {
|
|
37
|
+
return this.get(key, defaultValue) || defaultValue;
|
|
38
|
+
}
|
|
39
|
+
getOptionalNumber(key, defaultValue) {
|
|
40
|
+
const value = this.get(key);
|
|
41
|
+
return value ? parseInt(value, 10) : defaultValue;
|
|
42
|
+
}
|
|
43
|
+
get environment() {
|
|
44
|
+
return this.get('NODE_ENV', shared_1.Environment.DEVELOPMENT) || shared_1.Environment.DEVELOPMENT;
|
|
45
|
+
}
|
|
46
|
+
get databaseUrl() {
|
|
47
|
+
return this.getRequiredString('DATABASE_URL');
|
|
48
|
+
}
|
|
49
|
+
get kafkaBrokers() {
|
|
50
|
+
const brokers = this.get('KAFKA_BROKERS', 'localhost:9092');
|
|
51
|
+
return (brokers || 'localhost:9092').split(',');
|
|
52
|
+
}
|
|
53
|
+
get isProduction() {
|
|
54
|
+
return this.environment === shared_1.Environment.PRODUCTION;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.BaseConfigService = BaseConfigService;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import eslint from '@eslint/js';
|
|
3
|
+
import tseslint from 'typescript-eslint';
|
|
4
|
+
import globals from 'globals';
|
|
5
|
+
|
|
6
|
+
export default tseslint.config(
|
|
7
|
+
{
|
|
8
|
+
ignores: ['dist/**'],
|
|
9
|
+
},
|
|
10
|
+
eslint.configs.recommended,
|
|
11
|
+
...tseslint.configs.recommended,
|
|
12
|
+
{
|
|
13
|
+
languageOptions: {
|
|
14
|
+
globals: {
|
|
15
|
+
...globals.node,
|
|
16
|
+
...globals.jest,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
rules: {
|
|
20
|
+
'@typescript-eslint/interface-name-prefix': 'off',
|
|
21
|
+
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
22
|
+
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
23
|
+
'@typescript-eslint/no-explicit-any': 'off', // Relaxed for SDK flexibility
|
|
24
|
+
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alacard-project/config-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=24.0.0"
|
|
6
|
+
},
|
|
4
7
|
"private": false,
|
|
5
8
|
"description": "Standalone gRPC-based Configuration SDK for Alacard microservices",
|
|
6
9
|
"main": "dist/index.js",
|
|
@@ -8,18 +11,47 @@
|
|
|
8
11
|
"scripts": {
|
|
9
12
|
"build": "tsc",
|
|
10
13
|
"gen:proto": "protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=src/proto --proto_path=proto proto/config.proto --ts_proto_opt=outputServices=grpc-js,env=node,useOptionals=messages,exportCommonSymbols=false,esModuleInterop=true",
|
|
14
|
+
"lint": "eslint \"src/**/*.ts\" --fix",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"test:cov": "jest --coverage",
|
|
11
17
|
"prepublishOnly": "npm run build"
|
|
12
18
|
},
|
|
13
19
|
"dependencies": {
|
|
14
|
-
"@
|
|
15
|
-
"@
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"axios": "^1.7.9"
|
|
20
|
+
"@alacard-project/shared": "^1.0.4",
|
|
21
|
+
"@bufbuild/protobuf": "^2.2.3",
|
|
22
|
+
"@grpc/grpc-js": "^1.12.5",
|
|
23
|
+
"@grpc/proto-loader": "^0.7.13",
|
|
24
|
+
"axios": "^1.7.9",
|
|
25
|
+
"dotenv": "^16.4.7",
|
|
26
|
+
"kafkajs": "^2.2.4"
|
|
19
27
|
},
|
|
20
28
|
"devDependencies": {
|
|
21
|
-
"
|
|
22
|
-
"@types/
|
|
23
|
-
"
|
|
29
|
+
"@eslint/js": "^9.18.0",
|
|
30
|
+
"@types/jest": "^29.5.14",
|
|
31
|
+
"@types/node": "^22.10.7",
|
|
32
|
+
"eslint": "^9.18.0",
|
|
33
|
+
"globals": "^15.14.0",
|
|
34
|
+
"jest": "^29.7.0",
|
|
35
|
+
"ts-jest": "^29.2.5",
|
|
36
|
+
"ts-proto": "^2.6.1",
|
|
37
|
+
"typescript": "^5.7.3",
|
|
38
|
+
"typescript-eslint": "^8.20.0"
|
|
39
|
+
},
|
|
40
|
+
"jest": {
|
|
41
|
+
"moduleFileExtensions": [
|
|
42
|
+
"js",
|
|
43
|
+
"json",
|
|
44
|
+
"ts"
|
|
45
|
+
],
|
|
46
|
+
"rootDir": "src",
|
|
47
|
+
"testRegex": ".*\\.spec\\.ts$",
|
|
48
|
+
"transform": {
|
|
49
|
+
"^.+\\.(t|j)s$": "ts-jest"
|
|
50
|
+
},
|
|
51
|
+
"collectCoverageFrom": [
|
|
52
|
+
"**/*.(t|j)s"
|
|
53
|
+
],
|
|
54
|
+
"coverageDirectory": "../coverage",
|
|
55
|
+
"testEnvironment": "node"
|
|
24
56
|
}
|
|
25
57
|
}
|
package/src/config-client.ts
CHANGED
|
@@ -6,8 +6,6 @@ import { ConfigServiceClient } from './proto/config';
|
|
|
6
6
|
import { GetConfigRequest } from './proto/config';
|
|
7
7
|
import { VaultClient, VaultOptions } from './vault-client';
|
|
8
8
|
|
|
9
|
-
export { VaultOptions };
|
|
10
|
-
|
|
11
9
|
export interface ConfigOptions {
|
|
12
10
|
serviceName: string;
|
|
13
11
|
environment: string;
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Environment } from '@alacard-project/shared';
|
|
2
|
+
|
|
3
|
+
// Note: This requires @nestjs/config to be installed in the consumer microservice
|
|
4
|
+
// We keep it in the SDK as a helper for NestJS-based microservices.
|
|
5
|
+
|
|
6
|
+
export abstract class BaseConfigService {
|
|
7
|
+
private remoteConfig: Record<string, string> = {};
|
|
8
|
+
|
|
9
|
+
constructor(protected readonly configService: any) { }
|
|
10
|
+
|
|
11
|
+
setRemoteConfig(config: Record<string, string>) {
|
|
12
|
+
this.remoteConfig = { ...this.remoteConfig, ...config };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
protected get<T = string>(key: string, defaultValue?: T): T {
|
|
16
|
+
if (this.remoteConfig[key] !== undefined) {
|
|
17
|
+
return this.remoteConfig[key] as unknown as T;
|
|
18
|
+
}
|
|
19
|
+
return this.configService.get(key, defaultValue);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
protected getRequiredString(key: string): string {
|
|
23
|
+
const value = this.get<string>(key);
|
|
24
|
+
if (!value && process.env.NODE_ENV !== Environment.TEST) {
|
|
25
|
+
throw new Error(`Configuration Error: ${key} is required`);
|
|
26
|
+
}
|
|
27
|
+
return value || '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected getRequiredNumber(key: string): number {
|
|
31
|
+
const value = this.get<string>(key);
|
|
32
|
+
if (!value && process.env.NODE_ENV !== Environment.TEST) {
|
|
33
|
+
throw new Error(`Configuration Error: ${key} is required`);
|
|
34
|
+
}
|
|
35
|
+
return parseInt(value || '0', 10);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected getOptionalString(key: string, defaultValue: string): string {
|
|
39
|
+
return this.get<string>(key, defaultValue) || defaultValue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected getOptionalNumber(key: string, defaultValue: number): number {
|
|
43
|
+
const value = this.get<string>(key);
|
|
44
|
+
return value ? parseInt(value, 10) : defaultValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get environment(): Environment {
|
|
48
|
+
return this.get<Environment>('NODE_ENV', Environment.DEVELOPMENT) || Environment.DEVELOPMENT;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get databaseUrl(): string {
|
|
52
|
+
return this.getRequiredString('DATABASE_URL');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get kafkaBrokers(): string[] {
|
|
56
|
+
const brokers = this.get<string>('KAFKA_BROKERS', 'localhost:9092');
|
|
57
|
+
return (brokers || 'localhost:9092').split(',');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get isProduction(): boolean {
|
|
61
|
+
return this.environment === Environment.PRODUCTION;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ConfigClient } from '../src/config-client';
|
|
2
|
+
|
|
3
|
+
// Mock gRPC and Kafka dependencies
|
|
4
|
+
jest.mock('@grpc/grpc-js', () => ({
|
|
5
|
+
credentials: { createInsecure: jest.fn() },
|
|
6
|
+
ServiceClient: jest.fn(),
|
|
7
|
+
loadPackageDefinition: jest.fn(),
|
|
8
|
+
}));
|
|
9
|
+
jest.mock('@grpc/proto-loader', () => ({
|
|
10
|
+
loadSync: jest.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe('ConfigClient', () => {
|
|
14
|
+
let client: ConfigClient;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
// Reset mocks if needed
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should be defined', () => {
|
|
22
|
+
expect(ConfigClient).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Add more tests for initialization and fetching
|
|
26
|
+
it('should handle environment variables gracefully if remote fails', async () => {
|
|
27
|
+
// Simulation of fallback
|
|
28
|
+
process.env.TEST_VAR = 'fallback_value';
|
|
29
|
+
|
|
30
|
+
// This test would ideally mock the static initialize logic
|
|
31
|
+
// But for now we just verify structural integrity
|
|
32
|
+
expect(process.env.TEST_VAR).toBe('fallback_value');
|
|
33
|
+
});
|
|
34
|
+
});
|
package/.idea/workspace.xml
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="ChangeListManager">
|
|
4
|
-
<list default="true" id="d6ddd62d-b258-48c7-8ac2-ae28e47081ad" name="Changes" comment="" />
|
|
5
|
-
<option name="SHOW_DIALOG" value="false" />
|
|
6
|
-
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
7
|
-
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
8
|
-
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
9
|
-
</component>
|
|
10
|
-
<component name="MarkdownSettingsMigration">
|
|
11
|
-
<option name="stateVersion" value="1" />
|
|
12
|
-
</component>
|
|
13
|
-
<component name="ProjectId" id="38cLw1hfbaQQ7IG2qhtRfg4zBzC" />
|
|
14
|
-
<component name="ProjectViewState">
|
|
15
|
-
<option name="hideEmptyMiddlePackages" value="true" />
|
|
16
|
-
<option name="showLibraryContents" value="true" />
|
|
17
|
-
</component>
|
|
18
|
-
<component name="PropertiesComponent"><![CDATA[{
|
|
19
|
-
"keyToString": {
|
|
20
|
-
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
21
|
-
"WebServerToolWindowFactoryState": "false",
|
|
22
|
-
"node.js.detected.package.eslint": "true",
|
|
23
|
-
"node.js.detected.package.tslint": "true",
|
|
24
|
-
"node.js.selected.package.eslint": "(autodetect)",
|
|
25
|
-
"node.js.selected.package.tslint": "(autodetect)",
|
|
26
|
-
"nodejs_package_manager_path": "npm",
|
|
27
|
-
"vue.rearranger.settings.migration": "true"
|
|
28
|
-
}
|
|
29
|
-
}]]></component>
|
|
30
|
-
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
|
31
|
-
<component name="TaskManager">
|
|
32
|
-
<task active="true" id="Default" summary="Default task">
|
|
33
|
-
<changelist id="d6ddd62d-b258-48c7-8ac2-ae28e47081ad" name="Changes" comment="" />
|
|
34
|
-
<created>1769094147893</created>
|
|
35
|
-
<option name="number" value="Default" />
|
|
36
|
-
<option name="presentableId" value="Default" />
|
|
37
|
-
<updated>1769094147893</updated>
|
|
38
|
-
<workItem from="1769094150802" duration="320000" />
|
|
39
|
-
</task>
|
|
40
|
-
<servers />
|
|
41
|
-
</component>
|
|
42
|
-
<component name="TypeScriptGeneratedFilesManager">
|
|
43
|
-
<option name="version" value="3" />
|
|
44
|
-
</component>
|
|
45
|
-
</project>
|