@h3ravel/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/chunk-UZGBH6AC.js +776 -0
- package/dist/chunk-UZGBH6AC.js.map +1 -0
- package/dist/index.cjs +1860 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +352 -0
- package/dist/index.d.ts +352 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/src-E5IZSMT7.js +223 -0
- package/dist/src-E5IZSMT7.js.map +1 -0
- package/dist/src-HATPJSC4.js +19 -0
- package/dist/src-HATPJSC4.js.map +1 -0
- package/dist/src-KWAEFHNO.js +528 -0
- package/dist/src-KWAEFHNO.js.map +1 -0
- package/package.json +29 -0
- package/src/Application.ts +164 -0
- package/src/Container.ts +71 -0
- package/src/Contracts/.gitkeep +0 -0
- package/src/Contracts/BindingsContract.ts +37 -0
- package/src/Controller.ts +20 -0
- package/src/Exceptions/Handler.ts +1 -0
- package/src/Http/Kernel.ts +47 -0
- package/src/Providers/AppServiceProvider.ts +18 -0
- package/src/Providers/ViewServiceProvider.ts +19 -0
- package/src/ServiceProvider.ts +21 -0
- package/src/Utils/PathLoader.ts +46 -0
- package/src/index.ts +14 -0
- package/tests/.gitkeep +0 -0
- package/tsconfig.json +8 -0
- package/vite.config.ts +9 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Container } from './Container'
|
|
2
|
+
import { PathLoader } from './Utils/PathLoader'
|
|
3
|
+
import { ServiceProvider } from './ServiceProvider'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
export class Application extends Container {
|
|
7
|
+
paths = new PathLoader()
|
|
8
|
+
private booted = false
|
|
9
|
+
private versions = { app: '0', ts: '0' }
|
|
10
|
+
private basePath: string
|
|
11
|
+
|
|
12
|
+
private providers: ServiceProvider[] = []
|
|
13
|
+
protected externalProviders: Array<new (_app: Application) => ServiceProvider> = []
|
|
14
|
+
|
|
15
|
+
constructor(basePath: string) {
|
|
16
|
+
super()
|
|
17
|
+
this.basePath = basePath
|
|
18
|
+
this.setPath('base', basePath)
|
|
19
|
+
this.loadOptions()
|
|
20
|
+
this.registerBaseBindings()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register core bindings into the container
|
|
25
|
+
*/
|
|
26
|
+
protected registerBaseBindings () {
|
|
27
|
+
this.bind(Application, () => this)
|
|
28
|
+
this.bind('path.base', () => this.basePath)
|
|
29
|
+
this.bind('app.paths', () => this.paths)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Dynamically register all configured providers
|
|
34
|
+
*/
|
|
35
|
+
public async registerConfiguredProviders () {
|
|
36
|
+
const providers = await this.getAllProviders()
|
|
37
|
+
|
|
38
|
+
for (const ProviderClass of providers) {
|
|
39
|
+
if (!ProviderClass) continue
|
|
40
|
+
const provider = new ProviderClass(this)
|
|
41
|
+
await this.register(provider)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected async loadOptions () {
|
|
46
|
+
const app = await this.safeImport(this.getPath('base', 'package.json'))
|
|
47
|
+
const core = await this.safeImport('../package.json')
|
|
48
|
+
|
|
49
|
+
if (app && app.dependencies) {
|
|
50
|
+
this.versions.app = app.dependencies['@h3ravel/core']
|
|
51
|
+
}
|
|
52
|
+
if (core && core.devDependencies) {
|
|
53
|
+
this.versions.ts = app.devDependencies.typescript
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Load default and optional providers dynamically
|
|
59
|
+
*
|
|
60
|
+
* Auto-Registration Behavior
|
|
61
|
+
*
|
|
62
|
+
* Minimal App: Loads only core, config, http, router by default.
|
|
63
|
+
* Full-Stack App: Installs database, mail, queue, cache → they self-register via their providers.
|
|
64
|
+
*/
|
|
65
|
+
protected async getConfiguredProviders (): Promise<Array<new (_app: Application) => ServiceProvider>> {
|
|
66
|
+
return [
|
|
67
|
+
(await import('@h3ravel/core')).AppServiceProvider,
|
|
68
|
+
(await import('@h3ravel/http')).HttpServiceProvider,
|
|
69
|
+
(await import('@h3ravel/config')).ConfigServiceProvider,
|
|
70
|
+
(await import('@h3ravel/router')).RouteServiceProvider,
|
|
71
|
+
(await import('@h3ravel/router')).AssetsServiceProvider,
|
|
72
|
+
(await import('@h3ravel/core')).ViewServiceProvider,
|
|
73
|
+
(await this.safeImport('@h3ravel/database'))?.DatabaseServiceProvider,
|
|
74
|
+
(await this.safeImport('@h3ravel/cache'))?.CacheServiceProvider,
|
|
75
|
+
(await this.safeImport('@h3ravel/console'))?.ConsoleServiceProvider,
|
|
76
|
+
(await this.safeImport('@h3ravel/queue'))?.QueueServiceProvider,
|
|
77
|
+
(await this.safeImport('@h3ravel/mail'))?.MailServiceProvider,
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected async getAllProviders (): Promise<Array<new (_app: Application) => ServiceProvider>> {
|
|
82
|
+
const coreProviders = await this.getConfiguredProviders()
|
|
83
|
+
return [...coreProviders, ...this.externalProviders]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
registerProviders (providers: Array<new (_app: Application) => ServiceProvider>): void {
|
|
87
|
+
this.externalProviders.push(...providers)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Register a provider
|
|
92
|
+
*/
|
|
93
|
+
public async register (provider: ServiceProvider) {
|
|
94
|
+
await provider.register()
|
|
95
|
+
this.providers.push(provider)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Boot all providers after registration
|
|
100
|
+
*/
|
|
101
|
+
public async boot () {
|
|
102
|
+
if (this.booted) return
|
|
103
|
+
|
|
104
|
+
for (const provider of this.providers) {
|
|
105
|
+
if (provider.boot) {
|
|
106
|
+
await provider.boot()
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.booted = true
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Attempt to dynamically import an optional module
|
|
115
|
+
*/
|
|
116
|
+
private async safeImport (moduleName: string) {
|
|
117
|
+
try {
|
|
118
|
+
const mod = await import(moduleName)
|
|
119
|
+
return mod.default ?? mod
|
|
120
|
+
} catch {
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the base path of the app
|
|
127
|
+
*
|
|
128
|
+
* @returns
|
|
129
|
+
*/
|
|
130
|
+
getBasePath (): string {
|
|
131
|
+
return this.basePath
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Dynamically retrieves a path property from the class.
|
|
136
|
+
* Any property ending with "Path" is accessible automatically.
|
|
137
|
+
*
|
|
138
|
+
* @param name - The base name of the path property
|
|
139
|
+
* @returns
|
|
140
|
+
*/
|
|
141
|
+
getPath (name: Parameters<PathLoader['setPath']>[0], pth?: string) {
|
|
142
|
+
return path.join(this.paths.getPath(name, this.basePath), pth ?? '')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Programatically set the paths.
|
|
147
|
+
*
|
|
148
|
+
* @param name - The base name of the path property
|
|
149
|
+
* @param path - The new path
|
|
150
|
+
* @returns
|
|
151
|
+
*/
|
|
152
|
+
setPath (name: Parameters<PathLoader['setPath']>[0], path: string) {
|
|
153
|
+
return this.paths.setPath(name, path, this.basePath)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Returns the installed version of the system core and typescript.
|
|
158
|
+
*
|
|
159
|
+
* @returns
|
|
160
|
+
*/
|
|
161
|
+
getVersion (key: 'app' | 'ts') {
|
|
162
|
+
return this.versions[key]?.replaceAll(/\^|\~/g, '')
|
|
163
|
+
}
|
|
164
|
+
}
|
package/src/Container.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Bindings, UseKey } from "./Contracts/BindingsContract"
|
|
2
|
+
|
|
3
|
+
type IBinding = UseKey | (new (..._args: any[]) => unknown)
|
|
4
|
+
|
|
5
|
+
export class Container {
|
|
6
|
+
private bindings = new Map<IBinding, () => unknown>()
|
|
7
|
+
private singletons = new Map<IBinding, unknown>()
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Bind a transient service to the container
|
|
11
|
+
*/
|
|
12
|
+
bind<T> (key: new (...args: any[]) => T, factory: () => T): void
|
|
13
|
+
bind<T extends UseKey> (key: T, factory: () => Bindings[T]): void
|
|
14
|
+
bind<T extends UseKey> (
|
|
15
|
+
key: T,
|
|
16
|
+
factory: () => Bindings[T] | T
|
|
17
|
+
) {
|
|
18
|
+
this.bindings.set(key, factory)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Bind a singleton service to the container
|
|
23
|
+
*/
|
|
24
|
+
singleton<T extends UseKey> (
|
|
25
|
+
key: T | (new (..._args: any[]) => Bindings[T]),
|
|
26
|
+
factory: () => Bindings[T]
|
|
27
|
+
) {
|
|
28
|
+
this.bindings.set(key, () => {
|
|
29
|
+
if (!this.singletons.has(key)) {
|
|
30
|
+
this.singletons.set(key, factory())
|
|
31
|
+
}
|
|
32
|
+
return this.singletons.get(key)!
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a service from the container
|
|
38
|
+
*/
|
|
39
|
+
make<T extends UseKey> (key: T | (new (..._args: any[]) => Bindings[T])): Bindings[T] {
|
|
40
|
+
// 1️⃣ Direct factory binding
|
|
41
|
+
if (this.bindings.has(key)) {
|
|
42
|
+
return this.bindings.get(key)!() as Bindings[T]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2️⃣ If class constructor → auto-resolve via reflection
|
|
46
|
+
if (typeof key === 'function') {
|
|
47
|
+
return this.build(key)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
throw new Error(
|
|
51
|
+
`No binding found for key: ${typeof key === 'string' ? key : (key as any)?.name}`
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Automatically build a class with constructor dependency injection
|
|
57
|
+
*/
|
|
58
|
+
private build<T extends UseKey> (ClassType: new (..._args: any[]) => Bindings[T]): Bindings[T] {
|
|
59
|
+
const paramTypes: any[] = Reflect.getMetadata('design:paramtypes', ClassType) || []
|
|
60
|
+
const dependencies = paramTypes.map((dep) => this.make(dep))
|
|
61
|
+
return new ClassType(...dependencies)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if a service is registered
|
|
67
|
+
*/
|
|
68
|
+
has (key: UseKey): boolean {
|
|
69
|
+
return this.bindings.has(key)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DotNestedKeys, DotNestedValue } from "@h3ravel/support";
|
|
2
|
+
import type { H3, serve } from "h3";
|
|
3
|
+
|
|
4
|
+
import type { Edge } from "edge.js";
|
|
5
|
+
import { PathLoader } from "../Utils/PathLoader";
|
|
6
|
+
import type { Router } from "@h3ravel/router";
|
|
7
|
+
|
|
8
|
+
type RemoveIndexSignature<T> = {
|
|
9
|
+
[K in keyof T as string extends K
|
|
10
|
+
? never
|
|
11
|
+
: number extends K
|
|
12
|
+
? never
|
|
13
|
+
: K]: T[K]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type Bindings = {
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
env<T extends string> (): NodeJS.ProcessEnv
|
|
19
|
+
env<T extends string> (key: T, def?: any): any
|
|
20
|
+
view: Edge,
|
|
21
|
+
asset (key: string, def?: string): string,
|
|
22
|
+
router: Router
|
|
23
|
+
config: {
|
|
24
|
+
// get<X extends Record<string, any>> (): X
|
|
25
|
+
// get<X extends Record<string, any>, K extends DotNestedKeys<X>> (key: K, def?: any): DotNestedValue<X, K>
|
|
26
|
+
get<X extends Record<string, any>> (): X
|
|
27
|
+
get<X extends Record<string, any>, T extends Extract<keyof X, string>> (key: T, def?: any): X[T]
|
|
28
|
+
set<T extends string> (key: T, value: any): void
|
|
29
|
+
load?(): any
|
|
30
|
+
}
|
|
31
|
+
'http.app': H3
|
|
32
|
+
'path.base': string
|
|
33
|
+
'app.paths': PathLoader
|
|
34
|
+
'http.serve': typeof serve
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type UseKey = keyof RemoveIndexSignature<Bindings>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { HttpContext, IController } from '@h3ravel/http'
|
|
2
|
+
|
|
3
|
+
import { Application } from '.'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base controller class
|
|
7
|
+
*/
|
|
8
|
+
export abstract class Controller implements IController {
|
|
9
|
+
protected app: Application
|
|
10
|
+
|
|
11
|
+
constructor(app: Application) {
|
|
12
|
+
this.app = app
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public show (_ctx: HttpContext): any { return }
|
|
16
|
+
public index (_ctx: HttpContext): any { return }
|
|
17
|
+
public store (_ctx: HttpContext): any { return }
|
|
18
|
+
public update (_ctx: HttpContext): any { return }
|
|
19
|
+
public destroy (_ctx: HttpContext): any { return }
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default class { }
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { HttpContext, Middleware, Request, Response } from '@h3ravel/http'
|
|
2
|
+
|
|
3
|
+
import type { H3Event } from 'h3'
|
|
4
|
+
|
|
5
|
+
export class Kernel {
|
|
6
|
+
constructor(private middleware: Middleware[] = []) { }
|
|
7
|
+
|
|
8
|
+
async handle (event: H3Event, next: (ctx: HttpContext) => Promise<unknown>): Promise<unknown> {
|
|
9
|
+
const context: HttpContext = {
|
|
10
|
+
request: new Request(event),
|
|
11
|
+
response: new Response(event)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const result = await this.runMiddleware(context, () => next(context))
|
|
15
|
+
|
|
16
|
+
// Auto-set JSON header if plain object returned
|
|
17
|
+
if (result !== undefined && this.isPlainObject(result)) {
|
|
18
|
+
event.res.headers.set('Content-Type', 'application/json; charset=UTF-8')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async runMiddleware (context: HttpContext, next: (ctx: HttpContext) => Promise<unknown>) {
|
|
25
|
+
let index = -1
|
|
26
|
+
|
|
27
|
+
const runner = async (i: number): Promise<unknown> => {
|
|
28
|
+
if (i <= index) throw new Error('next() called multiple times')
|
|
29
|
+
index = i
|
|
30
|
+
const middleware = this.middleware[i]
|
|
31
|
+
|
|
32
|
+
if (middleware) {
|
|
33
|
+
return middleware.handle(context, () => runner(i + 1))
|
|
34
|
+
} else {
|
|
35
|
+
return next(context)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return runner(0)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private isPlainObject (value: unknown): value is Record<string, unknown> {
|
|
43
|
+
return typeof value === 'object' &&
|
|
44
|
+
value !== null &&
|
|
45
|
+
(value.constructor === Object || value.constructor === Array)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import 'reflect-metadata'
|
|
2
|
+
|
|
3
|
+
import { ServiceProvider } from '../ServiceProvider'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Bootstraps core services and bindings.
|
|
7
|
+
*
|
|
8
|
+
* Bind essential services to the container (logger, config repository).
|
|
9
|
+
* Register app-level singletons.
|
|
10
|
+
* Set up exception handling.
|
|
11
|
+
*
|
|
12
|
+
* Auto-Registered
|
|
13
|
+
*/
|
|
14
|
+
export class AppServiceProvider extends ServiceProvider {
|
|
15
|
+
register () {
|
|
16
|
+
// Core bindings
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Edge } from 'edge.js'
|
|
2
|
+
import { ServiceProvider } from '@h3ravel/core'
|
|
3
|
+
|
|
4
|
+
export class ViewServiceProvider extends ServiceProvider {
|
|
5
|
+
register (): void {
|
|
6
|
+
const config = this.app.make('config')
|
|
7
|
+
const edge = Edge.create({
|
|
8
|
+
cache: process.env.NODE_ENV === 'production'
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
edge.mount(this.app.getPath('views'))
|
|
12
|
+
|
|
13
|
+
edge.global('asset', this.app.make('asset'))
|
|
14
|
+
edge.global('config', config.get)
|
|
15
|
+
edge.global('app', this.app)
|
|
16
|
+
|
|
17
|
+
this.app.bind('view', () => edge)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Application } from './Application'
|
|
2
|
+
|
|
3
|
+
export abstract class ServiceProvider {
|
|
4
|
+
protected app: Application
|
|
5
|
+
|
|
6
|
+
constructor(app: Application) {
|
|
7
|
+
this.app = app
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Register bindings to the container.
|
|
12
|
+
* Runs before boot().
|
|
13
|
+
*/
|
|
14
|
+
abstract register (): void | Promise<void>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Perform post-registration booting of services.
|
|
18
|
+
* Runs after all providers have been registered.
|
|
19
|
+
*/
|
|
20
|
+
boot?(): void | Promise<void>
|
|
21
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import nodepath from "path"
|
|
2
|
+
|
|
3
|
+
type PathName = 'views' | 'routes' | 'assets' | 'base' | 'public' | 'storage' | 'config'
|
|
4
|
+
|
|
5
|
+
export class PathLoader {
|
|
6
|
+
private paths = {
|
|
7
|
+
base: '',
|
|
8
|
+
views: '/src/resources/views',
|
|
9
|
+
assets: '/public/assets',
|
|
10
|
+
routes: '/src/routes',
|
|
11
|
+
config: '/src/config',
|
|
12
|
+
public: '/public',
|
|
13
|
+
storage: '/src/storage',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Dynamically retrieves a path property from the class.
|
|
18
|
+
* Any property ending with "Path" is accessible automatically.
|
|
19
|
+
*
|
|
20
|
+
* @param name - The base name of the path property
|
|
21
|
+
* @param base - The base path to include to the path
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
24
|
+
getPath (name: PathName, base?: string): string {
|
|
25
|
+
if (base && name !== 'base') {
|
|
26
|
+
return nodepath.join(base, this.paths[name])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return this.paths[name]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Programatically set the paths.
|
|
34
|
+
*
|
|
35
|
+
* @param name - The base name of the path property
|
|
36
|
+
* @param path - The new path
|
|
37
|
+
* @param base - The base path to include to the path
|
|
38
|
+
*/
|
|
39
|
+
setPath (name: PathName, path: string, base?: string) {
|
|
40
|
+
if (base && name !== 'base') {
|
|
41
|
+
this.paths[name] = nodepath.join(base, path)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.paths[name] = path
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Automatically generated by barrelsby.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './Application';
|
|
6
|
+
export * from './Container';
|
|
7
|
+
export * from './Controller';
|
|
8
|
+
export * from './ServiceProvider';
|
|
9
|
+
export * from './Contracts/BindingsContract';
|
|
10
|
+
export * from './Exceptions/Handler';
|
|
11
|
+
export * from './Http/Kernel';
|
|
12
|
+
export * from './Providers/AppServiceProvider';
|
|
13
|
+
export * from './Providers/ViewServiceProvider';
|
|
14
|
+
export * from './Utils/PathLoader';
|
package/tests/.gitkeep
ADDED
|
File without changes
|
package/tsconfig.json
ADDED