Skip to content

Commit d8f3596

Browse files
authored
chore(kumahq/container): allows us to use multiple containers at once (#5082)
Adds `createBuilder` function instead of `build` to let us use multiple containers in one application. Mostly just grunt work to get this done by passing through extra arguments to thing. Remember: I'm very close to replacing this entire file and the brandijs dependency so this is very "lets just get this done" mode, with an eye to replacing with my other better thing at some point. Signed-off-by: John Cowen <john.cowen@konghq.com>
1 parent 1efeec7 commit d8f3596

8 files changed

Lines changed: 107 additions & 75 deletions

File tree

package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/container/index.ts

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ import {
44
injected,
55
} from 'brandi'
66
import deepmerge from 'deepmerge'
7+
import { inject } from 'vue'
78

89
import type {
910
Token,
1011
TokenType,
1112
TokenValue,
1213
RequiredToken,
1314
OptionalToken,
15+
Container,
1416
} from 'brandi'
17+
import type { InjectionKey } from 'vue'
18+
1519

1620
export {
1721
token,
@@ -49,14 +53,51 @@ export type ServiceConfigurator<T = Record<string, Token>> = ($: T) => ServiceDe
4953
export type Alias<T extends (...args: never[]) => unknown> = (...rest: Parameters<T>) => ReturnType<T>
5054
export type Decorator<T extends TokenValue> = TokenType<T>
5155
export type ReturnDecorated<T extends TokenValue> = () => TokenType<T>
52-
export let container = createContainer()
53-
let labelMap = new WeakMap()
5456

5557
//
5658
export const merge = (...definitions: Array<ServiceDefinition[]>): ServiceDefinition[] => {
5759
return [...new Map([...definitions.flat()]).entries()]
5860
}
59-
const decorate = (entries: ServiceDefinition[]) => {
61+
export type Getter = <T extends TokenValue>(t: T) => TokenType<T>
62+
63+
export const injectionKey = Symbol() as InjectionKey<Getter>
64+
65+
export const createBuilder = () => {
66+
const container = createContainer()
67+
const labelMap = new WeakMap()
68+
return {
69+
build: (...entries: Array<ServiceDefinition[]>) => {
70+
const get = <T extends TokenValue>(token: T) => {
71+
try {
72+
return container.get(token)
73+
} catch (e) {
74+
console.error(`error resolving ${token.__d}`)
75+
throw e
76+
}
77+
}
78+
const merged = decorate(merge(...entries), get)
79+
merged.forEach(([t, config]) => service(t, config, container, labelMap))
80+
return get
81+
},
82+
destroy: () => { },
83+
injectionKey,
84+
container,
85+
}
86+
87+
}
88+
89+
export const createInjections = <T extends TokenValue[]>(...tokens: T): InjectionHooks<T> => {
90+
return tokens.map((token) => () => {
91+
const get = inject(injectionKey)
92+
if(typeof get !== 'undefined') {
93+
return get(token)
94+
}
95+
throw new Error(`Unable to find container for ${token.__d}`)
96+
}) as InjectionHooks<T>
97+
}
98+
99+
100+
const decorate = (entries: ServiceDefinition[], get: Getter) => {
60101
const map = new Map(entries)
61102
entries.forEach(([_token, definition]) => {
62103
if (typeof definition.decorates !== 'undefined') {
@@ -94,29 +135,8 @@ const decorate = (entries: ServiceDefinition[]) => {
94135
})
95136
return [...map.entries()]
96137
}
97-
export const get = <T extends TokenValue>(token: T): TokenType<T> => {
98-
try {
99-
return container.get(token)
100-
} catch (e) {
101-
console.error(`error resolving ${token.__d}`)
102-
throw e
103-
}
104-
}
105-
export const build = (...entries: Array<ServiceDefinition[]>): typeof get => {
106-
container = createContainer()
107-
labelMap = new WeakMap()
108-
const merged = decorate(merge(...entries))
109-
merged.forEach(item => service(...item))
110-
return get
111-
}
112-
113-
export const createInjections = <T extends TokenValue[]>(
114-
...tokens: T
115-
): InjectionHooks<T> =>
116-
tokens.map((token) => () => get(token)) as InjectionHooks<T>
117-
//
118138

119-
export const service = (t: Token, config: DependencyDefinition): void => {
139+
const service = (t: Token, config: DependencyDefinition, container: Container, labelMap: WeakMap<Token, Token[]>): void => {
120140
const bound = container.bind(t)
121141
switch (true) {
122142
case 'constant' in config:
@@ -134,27 +154,33 @@ export const service = (t: Token, config: DependencyDefinition): void => {
134154
config.labels.forEach((label: Token) => {
135155
if (!labelMap.has(label)) {
136156
labelMap.set(label, [])
137-
service(label, {
138-
service: () => {
139-
return labelMap.get(label).reduce((prev: unknown[], TOKEN: Token) => {
140-
try {
141-
const service = get(TOKEN)
142-
if (Array.isArray(service)) {
143-
return prev.concat(service)
144-
} else if (service instanceof Object) {
145-
return deepmerge(prev, service)
146-
} else {
147-
return prev
157+
service(
158+
label,
159+
{
160+
service: () => {
161+
// @ts-ignore
162+
return labelMap.get(label)!.reduce((prev: Token[], TOKEN) => {
163+
try {
164+
const service = container.get(TOKEN)
165+
if (Array.isArray(service)) {
166+
return prev.concat(service)
167+
} else if (service instanceof Object) {
168+
return deepmerge(prev, service)
169+
} else {
170+
return prev
171+
}
172+
} catch (e) {
173+
console.error(e)
174+
throw e
148175
}
149-
} catch (e) {
150-
console.error(e)
151-
throw e
152-
}
153-
}, [])
176+
}, [] as Token[])
177+
},
154178
},
155-
})
179+
container,
180+
labelMap,
181+
)
156182
}
157-
const tokens = labelMap.get(label)
183+
const tokens = labelMap.get(label)!
158184
tokens.push(t)
159185
})
160186
}
@@ -167,9 +193,3 @@ export const service = (t: Token, config: DependencyDefinition): void => {
167193
injected(...([config.service, ...config.arguments] as Parameters<typeof injected>))
168194
}
169195
}
170-
export const constant = <T>(func: T, config: { description: string }): Token<T> => {
171-
const t = token<T>(config.description)
172-
const bound = container.bind(t)
173-
bound.toConstant(func as Parameters<typeof bound.toConstant>[0])
174-
return t as Token<T>
175-
}

packages/container/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
},
1414
"dependencies": {
1515
"brandi": "^5.1.0",
16-
"deepmerge": "^4.3.1"
16+
"deepmerge": "^4.3.1",
17+
"vue": "^3"
1718
},
1819
"devDependencies": {
1920
"@kumahq/config": "*"

packages/kuma-gui/playwright/main.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { build, token } from '@kumahq/container'
1+
import { createBuilder, token } from '@kumahq/container'
22
import { setupSteps } from '@kumahq/gherkin-web/playwright'
33
import env from '@kumahq/settings/env'
44

@@ -15,6 +15,7 @@ export { test } from '@kumahq/gherkin-web/playwright'
1515
env: token<typeof env>('playwright.env'),
1616
vars: token('playwright.env.vars'),
1717
}
18+
const { build } = createBuilder()
1819
const get = build(
1920
// mocks
2021
fakeFs($),

packages/kuma-gui/src/app/application/debug.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
1-
import { token, get } from '@kumahq/container'
1+
import { token } from '@kumahq/container'
22
import { Cookie } from '@kumahq/fake-api'
33

44
import debugI18n from './services/i18n/DebugI18n'
55
import type { Env } from '@/app/application'
66
import type { ServiceDefinition, Token } from '@kumahq/container'
77

88
type I18n = ReturnType<typeof debugI18n>
9-
109
export const services = (app: Record<string, Token>, prefix = 'KUMA'): ServiceDefinition[] => [
1110
[token('i18n.debug'), {
12-
service: (i18n: () => I18n) => {
13-
const env = get(app.env) as Env
14-
// @ts-ignore PREFIX_I18N_DEBUG_ENABLED is debug only
15-
if (env(`${prefix}_I18N_DEBUG_ENABLED`).length > 0) {
16-
return debugI18n(i18n())
11+
service: (useI18n: () => I18n) => {
12+
const i18n = useI18n()
13+
return {
14+
...i18n,
15+
t: (...args: Parameters<typeof i18n['t']>) => {
16+
const cookies = Cookie.parse(document.cookie ?? '', { prefix: `${prefix}_` })
17+
const env = (key: string) => {
18+
return cookies[key] ?? ''
19+
}
20+
if (env(`${prefix}_I18N_DEBUG_ENABLED`).length > 0) {
21+
return args[0]
22+
}
23+
return i18n.t(...args)
24+
} ,
1725
}
18-
return i18n()
1926
},
2027
decorates: app.i18n,
2128
}],
2229
[token('env.debug'), {
23-
service: (getEnv: () => Env) => {
24-
25-
const env = getEnv()
26-
return (...[ key, ...rest ]: Parameters<Env>) => {
30+
service: (useEnv: () => Env) => {
31+
const env = useEnv()
32+
return (...[key, ...rest]: Parameters<Env>) => {
2733
// make sure you can't read/replace anything but PREFIX_*
28-
return Cookie.parse(document.cookie ?? '', { prefix: `${prefix}_`})[key] ?? env(key, ...rest)
34+
return Cookie.parse(document.cookie ?? '', { prefix: `${prefix}_` })[key] ?? env(key, ...rest)
2935
}
3036
},
3137
decorates: app.env,

packages/kuma-gui/src/app/kuma/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import KumaTargetRef from '@/app/kuma/components/kuma-target-ref/KumaTargetRef.v
1313
import { ApiError } from '@/app/kuma/services/kuma-api/ApiError'
1414
import KumaApi from '@/app/kuma/services/kuma-api/KumaApi'
1515
import { RestClient } from '@/app/kuma/services/kuma-api/RestClient'
16-
import { useRouter } from '@/app/vue'
1716
import type { ServiceDefinition } from '@kumahq/container'
1817
import type { DataSourcePool } from '@kumahq/data'
18+
import type { Router } from 'vue-router'
1919

2020
export * from './utils'
2121
export { Kri } from './kri'
@@ -59,7 +59,7 @@ function normalizeBaseUrl(url: string): string {
5959
return stripTrailingSlashes(url)
6060
}
6161

62-
const protocolHandler = (can: Can) => {
62+
const protocolHandler = (can: Can, router: Router) => {
6363
return (href: string) => {
6464
const kriProto = 'kri://'
6565
switch (true) {
@@ -138,7 +138,6 @@ const protocolHandler = (can: Can) => {
138138
}
139139
})()
140140
if (to) {
141-
const router = useRouter()
142141
try {
143142
return router.resolve(to).href
144143
} catch(e) {
@@ -158,19 +157,20 @@ const protocolHandler = (can: Can) => {
158157
export const services = (app: Record<string, Token>): ServiceDefinition[] => {
159158
return [
160159
[token('kuma.plugins'), {
161-
service: (i18n, can) => {
160+
service: (i18n, can, router) => {
162161
return [
163162
[Kongponents],
164163
[X, {
165164
i18n,
166-
protocolHandler: protocolHandler(can),
165+
protocolHandler: protocolHandler(can, router),
167166
routerElement: () => document.querySelector('.kuma-application'),
168167
}],
169168
]
170169
},
171170
arguments: [
172171
app.i18n,
173172
app.can,
173+
app.router,
174174
],
175175
labels: [
176176
app.plugins,

packages/kuma-gui/src/main.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Importing styles here enforces a consistent stylesheet order between the Vite development server and the production build. See https://github.com/vitejs/vite/issues/4890.
22
import './assets/styles/main.scss'
33

4-
import { build } from '@kumahq/container'
4+
import { createBuilder } from '@kumahq/container'
55
import { createApp } from 'vue'
66

77
import { services as application, TOKENS as APPLICATION } from '@/app/application'
@@ -32,6 +32,7 @@ async function mountVueApplication() {
3232
...KUMA,
3333
}
3434

35+
const { build, injectionKey } = createBuilder()
3536
const get = build(
3637
vue($),
3738
application($),
@@ -89,8 +90,9 @@ async function mountVueApplication() {
8990
const msw = await import('@/app/msw')
9091
await get(msw.TOKENS.msw)
9192
}
92-
const app = createApp((await import('./app/App.vue')).default);
93-
(await get($.app)(app)).mount('#app')
93+
const app = createApp((await import('./app/App.vue')).default)
94+
app.provide(injectionKey, get)
95+
;(await get($.app)(app)).mount('#app')
9496
}
9597

9698
mountVueApplication()

packages/kuma-gui/test-support/main.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// vitest's `setupFiles` property, please see `/vite.config.production.ts`
44

55
import { defaultKumaHtmlVars as htmlVars } from '@kumahq/config/vite'
6-
import { get, container, build } from '@kumahq/container'
6+
import { createBuilder } from '@kumahq/container'
77
import { beforeEach, afterEach } from 'vitest'
88

99
import { services as application, TOKENS as APPLICATION } from '@/app/application'
@@ -16,7 +16,8 @@ import { services as testing } from '@/app/vue/testing'
1616
...APPLICATION,
1717
...KUMA,
1818
}
19-
build(
19+
const { build, container } = createBuilder()
20+
const get = build(
2021
application($),
2122
vue($),
2223
testing($),

0 commit comments

Comments
 (0)