Skip to content

Commit 2b63c97

Browse files
committed
SYNC: main branch
2 parents e24488e + 820cdb6 commit 2b63c97

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3141
-1402
lines changed

lib/cache/service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import { Injectable, OnModuleInit } from '@nestjs/common';
2-
import { IntentConfig } from '../config/service';
32
import { logTime } from '../utils/helpers';
43
import { InternalLogger } from '../utils/logger';
54
import { InMemoryDriver } from './drivers/inMemory';
65
import { RedisDriver } from './drivers/redis';
76
import { CacheDriver, CacheOptions } from './interfaces';
7+
import { ConfigService } from '../config';
88

99
@Injectable()
1010
export class CacheService implements OnModuleInit {
1111
static driverMap = { redis: RedisDriver, memory: InMemoryDriver };
1212
public static data: CacheOptions;
1313
static stores: Record<string, CacheDriver>;
1414

15-
constructor(config: IntentConfig) {
16-
CacheService.data = config.get('cache');
15+
constructor(config: ConfigService) {
16+
CacheService.data = config.get('cache') as CacheOptions;
1717
}
1818

1919
onModuleInit() {

lib/codegen/command.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ export class CodegenCommand {
2828
_cli.success(`Successfully created ${filePath}`);
2929
} catch (e) {
3030
_cli.error(e['message']);
31-
return;
3231
}
32+
33+
process.exit();
3334
}
3435

3536
@Command('make:controller {name}', { desc: 'Command to create a controller' })
@@ -50,8 +51,9 @@ export class CodegenCommand {
5051
_cli.success(`Successfully created ${filePath}`);
5152
} catch (e) {
5253
_cli.error(e['message']);
53-
return;
5454
}
55+
56+
process.exit();
5557
}
5658

5759
@Command('make:service {name}', { desc: 'Command to create a service' })
@@ -67,8 +69,9 @@ export class CodegenCommand {
6769
_cli.success(`Successfully created ${filePath}`);
6870
} catch (e) {
6971
_cli.error(e['message']);
70-
return;
7172
}
73+
74+
process.exit();
7275
}
7376

7477
@Command('make:job {name}', { desc: 'Command to create a job' })
@@ -90,8 +93,8 @@ export class CodegenCommand {
9093
} catch (e) {
9194
console.log(e);
9295
_cli.error(e['message']);
93-
return;
9496
}
97+
process.exit();
9598
}
9699

97100
@Command('make:model {name}', { desc: 'Command to create a model' })
@@ -111,8 +114,8 @@ export class CodegenCommand {
111114
_cli.success(`Successfully created ${filePath}`);
112115
} catch (e) {
113116
_cli.error(e['message']);
114-
return;
115117
}
118+
process.exit();
116119
}
117120

118121
@Command('make:repo {repoName} {modelFileName} {--without-interface}', {
@@ -147,8 +150,9 @@ export class CodegenCommand {
147150
_cli.success(`Successfully created ${filePath}`);
148151
} catch (e) {
149152
_cli.error(e['message']);
150-
return;
151153
}
154+
155+
process.exit();
152156
}
153157

154158
@Command('make:exception {name}', {
@@ -166,8 +170,9 @@ export class CodegenCommand {
166170
_cli.success(`Successfully created ${filePath}`);
167171
} catch (e) {
168172
_cli.error(e['message']);
169-
return;
170173
}
174+
175+
process.exit();
171176
}
172177

173178
@Command('make:resource {name}', { desc: 'Command to create a service' })
@@ -183,8 +188,9 @@ export class CodegenCommand {
183188
await CommandRunner.run(`make:service ${name}`, { silent: true });
184189
} catch (e) {
185190
_cli.error(e['message']);
186-
return;
187191
}
192+
193+
process.exit();
188194
}
189195

190196
@Command('make:event {name}', {
@@ -206,8 +212,9 @@ export class CodegenCommand {
206212
_cli.success(`Successfully created ${filePath}`);
207213
} catch (e) {
208214
_cli.error(e['message']);
209-
return;
210215
}
216+
217+
process.exit();
211218
}
212219

213220
@Command(
@@ -232,8 +239,9 @@ export class CodegenCommand {
232239
_cli.success(`Successfully created ${filePath}`);
233240
} catch (e) {
234241
_cli.error(e['message']);
235-
return;
236242
}
243+
244+
process.exit();
237245
}
238246

239247
@Command(
@@ -256,8 +264,9 @@ export class CodegenCommand {
256264
_cli.success(`Successfully created ${filePath}`);
257265
} catch (e) {
258266
_cli.error(e['message']);
259-
return;
260267
}
268+
269+
process.exit();
261270
}
262271

263272
@Command('make:mail {name}', {
@@ -278,7 +287,8 @@ export class CodegenCommand {
278287
_cli.success(`Successfully created ${filePath}`);
279288
} catch (e) {
280289
_cli.error(e['message']);
281-
return;
282290
}
291+
292+
process.exit();
283293
}
284294
}

lib/config/builder.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {
2+
NamespacedConfigMapKeys,
3+
NamespacedConfigMapValues,
4+
RegisterNamespaceReturnType,
5+
} from './options';
6+
7+
export class ConfigBuilder {
8+
static async build(
9+
namespaceObjects: RegisterNamespaceReturnType<string, any>[],
10+
): Promise<Map<string, any>> {
11+
const configMap = new Map();
12+
13+
for (const namespacedConfig of namespaceObjects) {
14+
const namespacedMap = new Map<
15+
NamespacedConfigMapKeys,
16+
NamespacedConfigMapValues
17+
>();
18+
namespacedMap.set('factory', namespacedConfig._.factory);
19+
namespacedMap.set('static', await namespacedConfig._.factory());
20+
namespacedMap.set('encrypt', namespacedConfig._.options.encrypt);
21+
configMap.set(namespacedConfig._.namespace, namespacedMap);
22+
}
23+
24+
return configMap;
25+
}
26+
}

lib/config/command.ts

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
1-
import { Injectable } from '@nestjs/common';
2-
import pc from 'picocolors';
31
import { Command, ConsoleIO } from '../console';
4-
import { Obj } from '../utils';
5-
import { Arr } from '../utils/array';
6-
import { columnify } from '../utils/columnify';
7-
import { IntentConfig } from './service';
2+
import { ConfigMap } from './options';
3+
import { CONFIG_FACTORY } from './constant';
4+
import pc from 'picocolors';
5+
import archy from 'archy';
6+
import { Inject } from '../foundation';
7+
import { jsonToArchy } from '../utils/console-helpers';
88

9-
@Injectable()
9+
@Command('config:view {--ns : Namespace of a particular config}', {
10+
desc: 'Command to view config for a given namespace',
11+
})
1012
export class ViewConfigCommand {
11-
constructor(private config: IntentConfig) {}
13+
constructor(@Inject(CONFIG_FACTORY) private config: ConfigMap) {}
1214

13-
@Command('config:view {namespace}', {
14-
desc: 'Command to view config for a given namespace',
15-
})
1615
async handle(_cli: ConsoleIO): Promise<void> {
17-
const namespace = _cli.argument<string>('namespace');
18-
const config = this.config.get(namespace);
19-
if (!config) {
20-
_cli.error(`config with ${namespace} namespace not found`);
16+
const nsFlag = _cli.option<string>('ns');
17+
const printNsToConsole = (namespace, obj) => {
18+
const values = obj.get('static') as Record<string, any>;
19+
console.log(
20+
archy(jsonToArchy(values, pc.bgGreen(pc.black(` ${namespace} `)))),
21+
);
22+
};
23+
24+
if (nsFlag) {
25+
const ns = this.config.get(nsFlag);
26+
printNsToConsole(nsFlag, ns);
2127
return;
2228
}
23-
const columnifiedConfig = columnify(
24-
Arr.toObj(Obj.entries(config), ['key', 'value']),
25-
);
26-
const printRows = [];
27-
for (const row of columnifiedConfig) {
28-
printRows.push([pc.green(row[0]), pc.yellow(row[1])].join(' '));
29-
}
3029

31-
// eslint-disable-next-line no-console
32-
console.log(printRows.join('\n'));
30+
// Example usage
31+
for (const [namespace, obj] of this.config.entries()) {
32+
printNsToConsole(namespace, obj);
33+
}
3334
}
3435
}

lib/config/constant.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const CONFIG_FACTORY = '@intentjs/config_factory';

lib/config/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export * from './builder';
2+
export * from './register-namespace';
3+
export * from './options';
4+
export * from './service';
5+
export * from './command';
6+
export * from './constant';

lib/config/options.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
export type RegisterNamespaceOptions = {
2+
encrypt?: boolean;
3+
};
4+
5+
export type BuildConfigFromNS<
6+
T extends RegisterNamespaceReturnType<any, any>[],
7+
> = {
8+
[N in T[number]['_']['namespace']]: Extract<
9+
T[number],
10+
{ _: { namespace: N } }
11+
>['$inferConfig'];
12+
};
13+
14+
export type RegisterNamespaceReturnType<
15+
N extends string,
16+
T extends Record<string, any>,
17+
> = {
18+
_: {
19+
namespace: N;
20+
options: RegisterNamespaceOptions;
21+
factory: () => T | Promise<T>;
22+
};
23+
$inferConfig: T;
24+
};
25+
26+
export type NamespacedConfigMapKeys = 'factory' | 'static' | 'encrypt';
27+
28+
export type NamespacedConfigMapValues =
29+
// eslint-disable-next-line @typescript-eslint/ban-types
30+
Function | object | boolean | string | number;
31+
32+
export type NamespacedConfigMap = Map<
33+
NamespacedConfigMapKeys,
34+
NamespacedConfigMapValues
35+
>;
36+
export type ConfigMap = Map<string, NamespacedConfigMap>;

lib/config/register-namespace.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { LiteralString } from '../type-helpers';
2+
import {
3+
RegisterNamespaceOptions,
4+
RegisterNamespaceReturnType,
5+
} from './options';
6+
7+
// eslint-disable-next-line @typescript-eslint/no-var-requires
8+
require('dotenv').config();
9+
10+
export const registerNamespace = <N extends string, T>(
11+
namespace: LiteralString<N>,
12+
factory: () => T | Promise<T>,
13+
options?: RegisterNamespaceOptions,
14+
): RegisterNamespaceReturnType<LiteralString<N>, T> => {
15+
return {
16+
_: {
17+
namespace,
18+
options: {
19+
encrypt: options?.encrypt ?? false,
20+
},
21+
factory,
22+
},
23+
$inferConfig: {} as T,
24+
};
25+
};

lib/config/service.ts

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,51 @@
1-
import { Injectable } from '@nestjs/common';
2-
import { ConfigService } from '@nestjs/config';
1+
import { Inject, Injectable } from '../foundation';
2+
import { DotNotation, GetNestedPropertyType } from '../type-helpers';
3+
import { Obj } from '../utils';
4+
import { CONFIG_FACTORY } from './constant';
5+
import { ConfigMap, NamespacedConfigMapValues } from './options';
6+
7+
type ConfigPaths<T> = DotNotation<T>;
38

49
@Injectable()
5-
export class IntentConfig {
6-
private static cachedConfig: Map<string, any>;
7-
private static config: ConfigService;
10+
export class ConfigService<G = undefined> {
11+
private static cachedConfig = new Map<string, any>();
12+
private static config: ConfigMap;
813

9-
constructor(private config: ConfigService) {
10-
IntentConfig.cachedConfig = new Map<string, any>();
11-
IntentConfig.config = config;
14+
constructor(@Inject(CONFIG_FACTORY) private config: ConfigMap) {
15+
ConfigService.cachedConfig = new Map<ConfigPaths<G>, any>();
16+
ConfigService.config = this.config;
1217
}
1318

14-
get<T = any>(key: string): T {
15-
if (IntentConfig.cachedConfig.has(key))
16-
return IntentConfig.cachedConfig.get(key);
17-
const value = this.config.get<T>(key);
18-
IntentConfig.cachedConfig.set(key, value);
19-
return value;
19+
get<P extends string = ConfigPaths<G>, F = any>(
20+
key: P,
21+
): GetNestedPropertyType<G, P, F> | Promise<GetNestedPropertyType<G, P, F>> {
22+
return ConfigService.get<G, P>(key);
2023
}
2124

22-
static get<T = any>(key: string): T {
23-
if (this.cachedConfig.has(key)) return this.cachedConfig.get(key);
24-
const value = this.config.get<T>(key);
25-
this.cachedConfig.set(key, value);
26-
return value;
25+
static get<C = undefined, P extends string = ConfigPaths<C>, F = any>(
26+
key: P,
27+
): GetNestedPropertyType<C, P, F> | Promise<GetNestedPropertyType<C, P, F>> {
28+
const cachedValue = ConfigService.cachedConfig.get(key);
29+
if (cachedValue) return cachedValue;
30+
31+
const [namespace, ...paths] = (key as string).split('.');
32+
const nsConfig = this.config.get(namespace);
33+
/**
34+
* Returns a null value if the namespace doesn't exist.
35+
*/
36+
if (!nsConfig) return null;
37+
38+
if (!paths.length) return nsConfig.get('static') as any;
39+
40+
const staticValues = nsConfig.get('static') as Omit<
41+
NamespacedConfigMapValues,
42+
'function'
43+
>;
44+
45+
const valueOnPath = Obj.get<any>(staticValues, paths.join('.'));
46+
if (valueOnPath) {
47+
this.cachedConfig.set(key as string, valueOnPath);
48+
}
49+
return valueOnPath;
2750
}
2851
}

0 commit comments

Comments
 (0)