@owlmeans/context 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/README.md +750 -0
- package/build/.gitkeep +0 -0
- package/build/config.d.ts +4 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +15 -0
- package/build/config.js.map +1 -0
- package/build/consts.d.ts +50 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +56 -0
- package/build/consts.js.map +1 -0
- package/build/context.d.ts +3 -0
- package/build/context.d.ts.map +1 -0
- package/build/context.js +203 -0
- package/build/context.js.map +1 -0
- package/build/helper.d.ts +8 -0
- package/build/helper.d.ts.map +1 -0
- package/build/helper.js +31 -0
- package/build/helper.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +6 -0
- package/build/index.js.map +1 -0
- package/build/service.d.ts +7 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +27 -0
- package/build/service.js.map +1 -0
- package/build/types.d.ts +80 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/context.d.ts +11 -0
- package/build/utils/context.d.ts.map +1 -0
- package/build/utils/context.js +15 -0
- package/build/utils/context.js.map +1 -0
- package/build/utils/index.d.ts +3 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +3 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/layer.d.ts +6 -0
- package/build/utils/layer.d.ts.map +1 -0
- package/build/utils/layer.js +12 -0
- package/build/utils/layer.js.map +1 -0
- package/package.json +31 -0
- package/src/config.ts +17 -0
- package/src/consts.ts +58 -0
- package/src/context.ts +261 -0
- package/src/helper.ts +43 -0
- package/src/index.ts +7 -0
- package/src/service.ts +49 -0
- package/src/types.ts +96 -0
- package/src/utils/context.ts +34 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/layer.ts +17 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/utils/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAmC,MAAM,cAAc,CAAA;AAIrE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,UAAsB,EAAE,EAAE,CAAC,mBAAmB,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAA;AAEnH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAoB,EAAE,KAAsB,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAA;AAEvG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAc,EAAE,MAAa,EAAW,EAAE,CACxE,CAAC,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;AAEtF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,QAAuB,EAAE,KAAY,EAAE,EAAE,CAC3E,QAAQ,CAAC,MAAM,IAAI,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;AAE3F,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,KAAK,CAAC,MAAM;IACZ,KAAK,CAAC,MAAM;IACZ,KAAK,CAAC,OAAO;IACb,KAAK,CAAC,MAAM;IACZ,KAAK,CAAC,IAAI;CACX,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAA0C,EAAE,KAAY,EAAE,EAAU,EAAE,EAAE,CACrG,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAE3E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,OAAU,EACV,WAAyC,EACzC,IAAoB,EACpB,KAAsB,EACtB,IAAyC,EACzC,EAAE,CAAC,OAAO,CAAC,GAAG,CACd,WAAW,CAAC,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,EAAC,UAAU,EAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAO,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CACpH,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Layer } from '../consts';
|
|
2
|
+
export interface InLayer<T> extends Record<Layer, Record<string, T>> {
|
|
3
|
+
}
|
|
4
|
+
export declare const initializeLayer: <T>(inLayer: InLayer<T>, layer: Layer, id: string | undefined) => string;
|
|
5
|
+
export declare const DEFAULT = "default";
|
|
6
|
+
//# sourceMappingURL=layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../src/utils/layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAEjC,MAAM,WAAW,OAAO,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CAAI;AAExE,eAAO,MAAM,eAAe,GAAI,CAAC,WAAW,OAAO,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,MAAM,GAAG,SAAS,KAAG,MAU9F,CAAA;AAED,eAAO,MAAM,OAAO,YAAY,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const initializeLayer = (inLayer, layer, id) => {
|
|
2
|
+
if (inLayer[layer] == null) {
|
|
3
|
+
inLayer[layer] = {};
|
|
4
|
+
}
|
|
5
|
+
id = id ?? DEFAULT;
|
|
6
|
+
if (inLayer[layer][id] == null) {
|
|
7
|
+
inLayer[layer][id] = {};
|
|
8
|
+
}
|
|
9
|
+
return id;
|
|
10
|
+
};
|
|
11
|
+
export const DEFAULT = 'default';
|
|
12
|
+
//# sourceMappingURL=layer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer.js","sourceRoot":"","sources":["../../src/utils/layer.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAI,OAAmB,EAAE,KAAY,EAAE,EAAsB,EAAU,EAAE;IACtG,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;IACrB,CAAC;IACD,EAAE,GAAG,EAAE,IAAI,OAAO,CAAA;IAClB,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EAAO,CAAA;IAC9B,CAAC;IAED,OAAO,EAAE,CAAA;AACX,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/context",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc -b",
|
|
7
|
+
"dev": "sleep 150 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
8
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty"
|
|
9
|
+
},
|
|
10
|
+
"main": "build/index.js",
|
|
11
|
+
"module": "build/index.js",
|
|
12
|
+
"types": "build/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./build/index.js",
|
|
16
|
+
"require": "./build/index.js",
|
|
17
|
+
"default": "./build/index.js",
|
|
18
|
+
"module": "./build/index.js",
|
|
19
|
+
"types": "./build/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"nodemon": "^3.1.7",
|
|
24
|
+
"npm-check": "^6.0.1",
|
|
25
|
+
"typescript": "^5.6.3"
|
|
26
|
+
},
|
|
27
|
+
"private": false,
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AppType, Layer } from './consts.js'
|
|
2
|
+
import type { BasicConfig } from './types.js'
|
|
3
|
+
|
|
4
|
+
export const makeBasicConfig = <C extends BasicConfig>(type: AppType, service: string, cfg?: Partial<C>): C => {
|
|
5
|
+
const config = {
|
|
6
|
+
ready: false,
|
|
7
|
+
service,
|
|
8
|
+
layer: type === AppType.Frontend ? Layer.Service : Layer.System,
|
|
9
|
+
type,
|
|
10
|
+
debug: {},
|
|
11
|
+
services: {},
|
|
12
|
+
records: [],
|
|
13
|
+
...cfg
|
|
14
|
+
} as C
|
|
15
|
+
|
|
16
|
+
return config
|
|
17
|
+
}
|
package/src/consts.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
export enum Layer {
|
|
3
|
+
System = 'system',
|
|
4
|
+
Global = 'global',
|
|
5
|
+
Service = 'service',
|
|
6
|
+
Entity = 'entity',
|
|
7
|
+
User = 'user'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export enum AppType {
|
|
11
|
+
Backend = 'backend',
|
|
12
|
+
Frontend = 'frontend',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export enum MiddlewareType {
|
|
16
|
+
Config = 'config',
|
|
17
|
+
Context = 'context'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export enum ContextStage {
|
|
21
|
+
Configuration = 'configuration',
|
|
22
|
+
Loading = 'loading',
|
|
23
|
+
Ready = 'ready'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export enum MiddlewareStage {
|
|
27
|
+
Configuration = 'configuration',
|
|
28
|
+
Loading = 'loading',
|
|
29
|
+
Ready = 'ready',
|
|
30
|
+
Switching = 'switching'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const CONFIG_RECORD = 'records'
|
|
34
|
+
|
|
35
|
+
export const EMPTY_ENTITY = '[UNDEFINED_ENTITY]'
|
|
36
|
+
export const EMPTY_PROFILE = '[UNDEFINED_PROFILE]'
|
|
37
|
+
|
|
38
|
+
// Idioms to compose app
|
|
39
|
+
/**
|
|
40
|
+
* It's used to define intermedieate base route
|
|
41
|
+
*/
|
|
42
|
+
export const ROOT = 'root'
|
|
43
|
+
/**
|
|
44
|
+
* It's used to define the home final route (default route in the app)
|
|
45
|
+
*/
|
|
46
|
+
export const HOME = 'home'
|
|
47
|
+
/**
|
|
48
|
+
* It's used to define intermediate area where authentication isn't required
|
|
49
|
+
*/
|
|
50
|
+
export const GUEST = 'guest'
|
|
51
|
+
/**
|
|
52
|
+
* It's used to define intermediate area where authentication is required
|
|
53
|
+
*/
|
|
54
|
+
export const BASE = 'base'
|
|
55
|
+
/**
|
|
56
|
+
* It's used to show user some fallbacke error screen when things gone completely wrong
|
|
57
|
+
*/
|
|
58
|
+
export const CRASH = 'crash'
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { ContextStage, Layer, MiddlewareStage, MiddlewareType } from './consts.js'
|
|
2
|
+
import type { BasicConfig, BasicContext, Middleware, BasicModule, BasicResource, Service } from './types.js'
|
|
3
|
+
// import { applyMiddlewares, getAllServices, getMiddlerwareKey, isResourceAvailable, layersOrder } from './utils/context.js'
|
|
4
|
+
import { applyMiddlewares, getAllServices, getMiddlerwareKey, isResourceAvailable } from './utils/context.js'
|
|
5
|
+
import { DEFAULT, InLayer, initializeLayer } from './utils/layer.js'
|
|
6
|
+
|
|
7
|
+
type Module = BasicModule
|
|
8
|
+
|
|
9
|
+
export const makeBasicContext = <C extends BasicConfig>(cfg: C): BasicContext<C> => {
|
|
10
|
+
const services = {} as InLayer<Record<string, Service>>
|
|
11
|
+
const modules = {} as InLayer<Record<string, Module>>
|
|
12
|
+
const resources = {} as InLayer<Record<string, BasicResource>>
|
|
13
|
+
const middlewares: Record<string, Middleware[]> = {}
|
|
14
|
+
const allServices: Record<string, Service> = {}
|
|
15
|
+
const allModules: Record<string, Module> = {}
|
|
16
|
+
const allResources: Record<string, BasicResource> = {}
|
|
17
|
+
|
|
18
|
+
let configure: (res: boolean) => void
|
|
19
|
+
let initialize: (resv: boolean) => void
|
|
20
|
+
|
|
21
|
+
const configured = new Promise<boolean>(resolve => { configure = resolve })
|
|
22
|
+
const initialized = new Promise<boolean>(resolve => { initialize = resolve })
|
|
23
|
+
|
|
24
|
+
// const contexts: Record<string, BasicContext<C>> = {}
|
|
25
|
+
|
|
26
|
+
const inLazyInit = new Set<Service>()
|
|
27
|
+
|
|
28
|
+
const context: BasicContext<C> = {
|
|
29
|
+
cfg,
|
|
30
|
+
|
|
31
|
+
stage: ContextStage.Configuration,
|
|
32
|
+
|
|
33
|
+
waitForConfigured: () => configured,
|
|
34
|
+
|
|
35
|
+
waitForInitialized: () => initialized,
|
|
36
|
+
|
|
37
|
+
configure: <T>() => {
|
|
38
|
+
if (context.stage !== ContextStage.Configuration) {
|
|
39
|
+
configure(true)
|
|
40
|
+
return context as T
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void (async () => {
|
|
44
|
+
await applyMiddlewares<C, BasicContext<C>>(context, middlewares, MiddlewareType.Config, MiddlewareStage.Configuration)
|
|
45
|
+
|
|
46
|
+
context.stage = ContextStage.Loading
|
|
47
|
+
configure(true)
|
|
48
|
+
})()
|
|
49
|
+
|
|
50
|
+
return context as T
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
init: async <T>() => {
|
|
54
|
+
await configured
|
|
55
|
+
|
|
56
|
+
if (context.stage !== ContextStage.Loading) {
|
|
57
|
+
return context as T
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await applyMiddlewares<C, BasicContext<C>>(context, middlewares, MiddlewareType.Context, MiddlewareStage.Configuration)
|
|
61
|
+
|
|
62
|
+
await getAllServices(services, context.cfg.layer, context.cfg.layerId ?? DEFAULT).reduce(
|
|
63
|
+
async (previous, service) => {
|
|
64
|
+
await previous
|
|
65
|
+
if (!service.initialized) {
|
|
66
|
+
if (service.init != null) {
|
|
67
|
+
await service.init()
|
|
68
|
+
}
|
|
69
|
+
if (service.init == null && service.lazyInit == null) {
|
|
70
|
+
service.initialized = true
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
, Promise.resolve())
|
|
75
|
+
|
|
76
|
+
await applyMiddlewares<C, BasicContext<C>>(context, middlewares, MiddlewareType.Config, MiddlewareStage.Loading)
|
|
77
|
+
|
|
78
|
+
const id = initializeLayer(resources, context.cfg.layer, context.cfg.layerId ?? DEFAULT)
|
|
79
|
+
await Object.values(resources[context.cfg.layer][id]).reduce(async (previous, resource) => {
|
|
80
|
+
await previous
|
|
81
|
+
if (isResourceAvailable(resource, context.cfg.layer)) {
|
|
82
|
+
await resource.init?.()
|
|
83
|
+
}
|
|
84
|
+
}, Promise.resolve())
|
|
85
|
+
|
|
86
|
+
await applyMiddlewares<C, BasicContext<C>>(context, middlewares, MiddlewareType.Context, MiddlewareStage.Loading)
|
|
87
|
+
|
|
88
|
+
context.stage = ContextStage.Ready
|
|
89
|
+
context.cfg.ready = true
|
|
90
|
+
initialize(true)
|
|
91
|
+
|
|
92
|
+
void applyMiddlewares<C, BasicContext<C>>(context, middlewares, MiddlewareType.Context, MiddlewareStage.Ready)
|
|
93
|
+
|
|
94
|
+
return context as T
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
registerService: <T>(service: Service) => {
|
|
98
|
+
const id = initializeLayer(services, context.cfg.layer, context.cfg.layerId)
|
|
99
|
+
service = service.registerContext(context)
|
|
100
|
+
|
|
101
|
+
if (allServices[service.alias] == null) {
|
|
102
|
+
allServices[service.alias] = service
|
|
103
|
+
}
|
|
104
|
+
services[context.cfg.layer][id][service.alias] = service
|
|
105
|
+
|
|
106
|
+
return context as T
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
registerModule: <T>(module: Module) => {
|
|
110
|
+
const id = initializeLayer(modules, context.cfg.layer, context.cfg.layerId)
|
|
111
|
+
module = module.registerContext(context)
|
|
112
|
+
if (allModules[module.alias] == null) {
|
|
113
|
+
allModules[module.alias] = module
|
|
114
|
+
}
|
|
115
|
+
modules[context.cfg.layer][id][module.alias] = module
|
|
116
|
+
|
|
117
|
+
return context as T
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
registerModules: <T>(modules: Module[]) => {
|
|
121
|
+
modules.forEach(module => context.registerModule(module))
|
|
122
|
+
return context as T
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
registerResource: <T>(resource: BasicResource) => {
|
|
126
|
+
const id = initializeLayer(resources, context.cfg.layer, context.cfg.layerId)
|
|
127
|
+
resource = resource.registerContext(context)
|
|
128
|
+
if (allResources[resource.alias] == null) {
|
|
129
|
+
allResources[resource.alias] = resource
|
|
130
|
+
}
|
|
131
|
+
resources[context.cfg.layer][id][resource.alias] = resource
|
|
132
|
+
|
|
133
|
+
return context as T
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
registerMiddleware: <T>(middleware: Middleware) => {
|
|
137
|
+
const key = getMiddlerwareKey(middleware)
|
|
138
|
+
if (!(key in middlewares)) {
|
|
139
|
+
middlewares[key] = []
|
|
140
|
+
}
|
|
141
|
+
middlewares[key].push(middleware)
|
|
142
|
+
|
|
143
|
+
return context as T
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
get config() {
|
|
147
|
+
return configured.then(() => context.cfg)
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
service: <T>(alias: string) => {
|
|
151
|
+
const id = initializeLayer(services, context.cfg.layer, context.cfg.layerId)
|
|
152
|
+
|
|
153
|
+
let _service: Service
|
|
154
|
+
if (services[context.cfg.layer]?.[id]?.[alias] != null) {
|
|
155
|
+
_service = services[context.cfg.layer][id][alias]
|
|
156
|
+
} else if (services[Layer.Global]?.[id]?.[alias] != null) {
|
|
157
|
+
_service = services[context.cfg.layer][id][alias]
|
|
158
|
+
} else {
|
|
159
|
+
throw new SyntaxError(`Service ${alias} not found in layer ${context.cfg.layer}`)
|
|
160
|
+
}
|
|
161
|
+
if (!_service.initialized) {
|
|
162
|
+
if (_service.lazyInit != null) {
|
|
163
|
+
if (!inLazyInit.has(_service)) {
|
|
164
|
+
inLazyInit.add(_service)
|
|
165
|
+
_service.lazyInit()
|
|
166
|
+
.then(() => _service.initialized = true)
|
|
167
|
+
.finally(() => inLazyInit.delete(_service))
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
throw new SyntaxError(`Service ${alias} is not initialized`)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return _service as T
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
module: <T>(alias: string) => {
|
|
177
|
+
const id = initializeLayer(modules, context.cfg.layer, context.cfg.layerId)
|
|
178
|
+
if (modules[context.cfg.layer][id][alias] != null) {
|
|
179
|
+
return modules[context.cfg.layer][id][alias] as T
|
|
180
|
+
}
|
|
181
|
+
throw new SyntaxError(`Module ${alias} not found`)
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
resource: <T extends BasicResource>(alias: string) => {
|
|
185
|
+
const id = initializeLayer(resources, context.cfg.layer, context.cfg.layerId)
|
|
186
|
+
if (resources[context.cfg.layer][id][alias] != null) {
|
|
187
|
+
const resource = resources[context.cfg.layer][id][alias] as T
|
|
188
|
+
|
|
189
|
+
return resource
|
|
190
|
+
}
|
|
191
|
+
throw new SyntaxError(`Resource ${alias} not found`)
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
modules: <T>() => {
|
|
195
|
+
const id = initializeLayer(modules, context.cfg.layer, context.cfg.layerId)
|
|
196
|
+
return Object.values(modules[context.cfg.layer][id]) as T[]
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @TODO Unfortunatly module cloning between contexts is quite unstable.
|
|
201
|
+
* And it looks like in some cases it relies on modules that aren't really clonned.
|
|
202
|
+
* The problem is that the not cloned modules are changing their context accross all context
|
|
203
|
+
* layers and we can't rely on such behaviour.
|
|
204
|
+
* So we disabled context update as a whole and all functionality that relies on it
|
|
205
|
+
* cause in curent release mixed tenancy is not in use in any project and it looks like
|
|
206
|
+
* there is no short term plans to use it some way.
|
|
207
|
+
*/
|
|
208
|
+
updateContext: async <T>(_id?: string, _layer?: Layer) => {
|
|
209
|
+
return context as T
|
|
210
|
+
|
|
211
|
+
// const index = layersOrder.indexOf(context.cfg.layer)
|
|
212
|
+
// layer = layer ?? (layersOrder[index + 1] != null ? layersOrder[index + 1] : undefined)
|
|
213
|
+
// if (layer == null) {
|
|
214
|
+
// throw new SyntaxError("There is no next layer to switch to")
|
|
215
|
+
// }
|
|
216
|
+
|
|
217
|
+
// if ([Layer.Entity, Layer.User].includes(layer) && id == null) {
|
|
218
|
+
// throw new SyntaxError(`Cannot switch to layer ${layer} without id`)
|
|
219
|
+
// }
|
|
220
|
+
|
|
221
|
+
// id = initializeLayer(resources, layer, id)
|
|
222
|
+
|
|
223
|
+
// // Cache initalized context layers
|
|
224
|
+
// const key = `${layer}:${id}`
|
|
225
|
+
// if (key in contexts) {
|
|
226
|
+
// return contexts[key] as T
|
|
227
|
+
// }
|
|
228
|
+
|
|
229
|
+
// const _config: C = JSON.parse(JSON.stringify(context.cfg))
|
|
230
|
+
// _config.layer = layer
|
|
231
|
+
// _config.ready = false
|
|
232
|
+
// _config.layerId = id
|
|
233
|
+
// // @TODO we need to make a rule to store all makeContext methods after they are applied
|
|
234
|
+
// const _context = context.makeContext != null ? context.makeContext(_config) : makeBasicContext(_config)
|
|
235
|
+
|
|
236
|
+
// Object.values(middlewares).flatMap(middlewares => middlewares)
|
|
237
|
+
// .forEach(middleware => _context.registerMiddleware(middleware))
|
|
238
|
+
|
|
239
|
+
// await applyMiddlewares<C, BasicContext<C>>(_context, middlewares, MiddlewareType.Config, MiddlewareStage.Switching, { layer, id })
|
|
240
|
+
|
|
241
|
+
// Object.values(allServices).forEach(service => _context.registerService(service))
|
|
242
|
+
|
|
243
|
+
// Object.values(allModules).forEach(module => _context.registerModule(module))
|
|
244
|
+
|
|
245
|
+
// Object.values(allResources).forEach(resource => _context.registerResource(resource))
|
|
246
|
+
|
|
247
|
+
// await applyMiddlewares<C, BasicContext<C>>(_context, middlewares, MiddlewareType.Context, MiddlewareStage.Switching, { layer, id })
|
|
248
|
+
|
|
249
|
+
// _context.stage = ContextStage.Loading
|
|
250
|
+
// await _context.configure().init()
|
|
251
|
+
|
|
252
|
+
// return (contexts[key] = _context) as T
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
hasResource: alias => allResources[alias] != null,
|
|
256
|
+
|
|
257
|
+
hasService: alias => allServices[alias] != null
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return context
|
|
261
|
+
}
|
package/src/helper.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Layer } from './consts.js'
|
|
2
|
+
import type { Contextual, BasicContext, BasicConfig } from './types.js'
|
|
3
|
+
|
|
4
|
+
export const appendContextual = <T extends Contextual>(alias: string, contextual: Partial<T>): T => {
|
|
5
|
+
contextual.alias = alias
|
|
6
|
+
contextual.registerContext = <T, C extends BasicConfig>(context: BasicContext<C>) => {
|
|
7
|
+
const _contextual = contextual.ctx == null
|
|
8
|
+
? contextual
|
|
9
|
+
: contextual.reinitializeContext != null
|
|
10
|
+
? contextual.reinitializeContext(context) : contextual
|
|
11
|
+
|
|
12
|
+
// We do NOT update context when it's the same contextual with already set context
|
|
13
|
+
// Actuall we just do not need to switch context in many on the level of context consumer
|
|
14
|
+
if (_contextual.ctx == null) {
|
|
15
|
+
_contextual.ctx = context
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return _contextual as T
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
contextual.assertCtx = (
|
|
22
|
+
location => assertContext(contextual.ctx, location ?? contextual.alias)
|
|
23
|
+
) as typeof contextual.assertCtx
|
|
24
|
+
|
|
25
|
+
return contextual as T
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// export type UpdContextType<C extends Config, T extends Context<C>, ExtraType> = T & ExtraType
|
|
29
|
+
|
|
30
|
+
// export type UpdConfigType<T extends Config, ExtraType> = T & ExtraType
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @throws {SyntaxError}
|
|
34
|
+
*/
|
|
35
|
+
export const assertContext = <C extends BasicConfig, T extends BasicContext<C>>(ctx: T | BasicContext<BasicConfig> | undefined, location?: string): T => {
|
|
36
|
+
if (ctx == null) {
|
|
37
|
+
throw new SyntaxError(`Context not found in ${location}`)
|
|
38
|
+
}
|
|
39
|
+
return ctx as T
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const isContextWithoutIds = <C extends BasicConfig, T extends BasicContext<C>>(context: T): boolean =>
|
|
43
|
+
[Layer.System, Layer.Global, Layer.Service].includes(context.cfg.layer)
|
package/src/index.ts
ADDED
package/src/service.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { appendContextual } from './helper.js'
|
|
2
|
+
import type { Contextual, InitializedService, LazyService, Service } from './types.js'
|
|
3
|
+
|
|
4
|
+
enum TypeToMethod {
|
|
5
|
+
Lazy = 'lazyInit',
|
|
6
|
+
Initialized = 'init'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface InitMethod<S extends Service> {
|
|
10
|
+
(service: S): () => Promise<void>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface CreateService<S extends Service> {
|
|
14
|
+
(alias: string, service: Partial<S>, init?: InitMethod<S>): S
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const _createService = <S extends Service>(type: TypeToMethod): CreateService<S> => (alias, service, init) => {
|
|
18
|
+
if (service.registerContext == null) {
|
|
19
|
+
appendContextual(alias, service as Contextual)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let initialize: (res: boolean) => void
|
|
23
|
+
const ready = new Promise<boolean>(resolve => { initialize = resolve })
|
|
24
|
+
|
|
25
|
+
service.alias = alias
|
|
26
|
+
|
|
27
|
+
service.initialized = false
|
|
28
|
+
|
|
29
|
+
init = init ?? (() => async () => {
|
|
30
|
+
service.initialized = true
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
service[type] = async () => {
|
|
34
|
+
await init(service as S)()
|
|
35
|
+
initialize(true)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
service.ready = () => ready
|
|
39
|
+
|
|
40
|
+
return service as S
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const createService = <S extends InitializedService>(
|
|
44
|
+
alias: string, service: Partial<S>, init?: InitMethod<S>
|
|
45
|
+
): S => _createService<S>(TypeToMethod.Initialized)(alias, service, init)
|
|
46
|
+
|
|
47
|
+
export const createLazyService = <S extends LazyService>(
|
|
48
|
+
alias: string, service: Partial<S>, init?: InitMethod<S>
|
|
49
|
+
): S => _createService<S>(TypeToMethod.Lazy)(alias, service, init)
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { AppType, ContextStage, Layer, MiddlewareStage, MiddlewareType, CONFIG_RECORD } from './consts.js'
|
|
2
|
+
|
|
3
|
+
export interface BasicConfig {
|
|
4
|
+
ready: boolean
|
|
5
|
+
service: string
|
|
6
|
+
// Is used as username of the service (e.g. for looking up for autehntication keys)
|
|
7
|
+
alias?: string
|
|
8
|
+
layer: Layer
|
|
9
|
+
type: AppType
|
|
10
|
+
layerId?: string
|
|
11
|
+
services?: Record<string, Object>
|
|
12
|
+
[CONFIG_RECORD]?: ConfigRecord[]
|
|
13
|
+
debug?: {
|
|
14
|
+
all?: boolean
|
|
15
|
+
[section: string]: boolean | undefined
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ConfigRecord extends ConfigRecordItem {
|
|
20
|
+
id: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type MaybeArray<T> = T | T[]
|
|
24
|
+
|
|
25
|
+
interface ConfigRecordItem extends Record<
|
|
26
|
+
string,
|
|
27
|
+
MaybeArray<ConfigRecordItem | boolean | string | number | null | undefined>> {
|
|
28
|
+
recordType?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface Contextual {
|
|
32
|
+
ctx?: BasicContext<any>
|
|
33
|
+
alias: string
|
|
34
|
+
reinitializeContext?: <T extends Contextual>(context: BasicContext<any>) => T // This method is a fix to replace context inside closed scope
|
|
35
|
+
registerContext: <T extends Contextual, C extends BasicConfig>(context: BasicContext<C>) => T
|
|
36
|
+
assertCtx: <C extends BasicConfig, T extends BasicContext<C>>(location?: string) => T
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Service extends Contextual {
|
|
40
|
+
layers?: Layer[]
|
|
41
|
+
initialized: boolean
|
|
42
|
+
init?: () => Promise<void>
|
|
43
|
+
lazyInit?: () => Promise<void>
|
|
44
|
+
ready?: () => Promise<boolean>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface InitializedService extends Service {
|
|
48
|
+
init: () => Promise<void>
|
|
49
|
+
ready: () => Promise<boolean>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface LazyService extends Service {
|
|
53
|
+
lazyInit: () => Promise<void>
|
|
54
|
+
ready: () => Promise<boolean>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface BasicModule extends Contextual {
|
|
58
|
+
_module: true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface BasicResource extends Contextual {
|
|
62
|
+
layers?: Layer[]
|
|
63
|
+
init?: () => Promise<void> // After context switch the resource should be able to reinitialize
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface Middleware {
|
|
67
|
+
type: MiddlewareType
|
|
68
|
+
stage: MiddlewareStage
|
|
69
|
+
apply: <C extends BasicConfig, T extends BasicContext<C>>(context: T, args?: Record<string, string | undefined>) => Promise<void>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface BasicContext<C extends BasicConfig> {
|
|
73
|
+
cfg: C
|
|
74
|
+
stage: ContextStage
|
|
75
|
+
waitForConfigured: () => Promise<boolean>
|
|
76
|
+
waitForInitialized: () => Promise<boolean>
|
|
77
|
+
configure: <T extends BasicContext<C>>() => T
|
|
78
|
+
init: <T extends BasicContext<C>>() => Promise<T>
|
|
79
|
+
updateContext: <T extends BasicContext<C>>(id?: string, to?: Layer) => Promise<T>
|
|
80
|
+
registerService: <T extends BasicContext<C>>(service: Service) => T
|
|
81
|
+
registerModule: <T extends BasicContext<C>>(module: BasicModule) => T
|
|
82
|
+
registerModules: <T extends BasicContext<C>>(module: BasicModule[]) => T
|
|
83
|
+
registerResource: <T extends BasicContext<C>>(resource: BasicResource) => T
|
|
84
|
+
registerMiddleware: <T extends BasicContext<C>>(middleware: Middleware) => T
|
|
85
|
+
|
|
86
|
+
get config(): Promise<C>
|
|
87
|
+
service: <T extends Service>(alias: string) => T
|
|
88
|
+
module: <T extends BasicModule>(alias: string) => T
|
|
89
|
+
resource: <T extends BasicResource>(alias: string) => T
|
|
90
|
+
hasResource: (alias: string) => boolean
|
|
91
|
+
hasService: (alias: string) => boolean
|
|
92
|
+
|
|
93
|
+
modules: <T extends BasicModule>() => T[]
|
|
94
|
+
|
|
95
|
+
makeContext?: <T extends BasicContext<C>>(cfg: C) => T
|
|
96
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Layer, MiddlewareStage, MiddlewareType } from '../consts.js'
|
|
2
|
+
import type { BasicConfig, BasicContext, Middleware, BasicResource, Service } from '../types.js'
|
|
3
|
+
import type { InLayer } from './layer.js'
|
|
4
|
+
|
|
5
|
+
export const getMiddlerwareKey = (middleware: Middleware) => createMiddlewareKey(middleware.type, middleware.stage)
|
|
6
|
+
|
|
7
|
+
export const createMiddlewareKey = (type: MiddlewareType, stage: MiddlewareStage) => `${type}:${stage}`
|
|
8
|
+
|
|
9
|
+
export const isLayerIherited = (current: Layer, parent: Layer): boolean =>
|
|
10
|
+
(current === parent) || (layersOrder.indexOf(parent) > layersOrder.indexOf(current))
|
|
11
|
+
|
|
12
|
+
export const isResourceAvailable = (resource: BasicResource, layer: Layer) =>
|
|
13
|
+
resource.layers == null || resource.layers.some(_layer => isLayerIherited(_layer, layer))
|
|
14
|
+
|
|
15
|
+
export const layersOrder = [
|
|
16
|
+
Layer.System,
|
|
17
|
+
Layer.Global,
|
|
18
|
+
Layer.Service,
|
|
19
|
+
Layer.Entity,
|
|
20
|
+
Layer.User
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
export const getAllServices = (services: InLayer<Record<string, Service>>, layer: Layer, id: string) =>
|
|
24
|
+
services?.[layer]?.[id] != null ? Object.values(services[layer][id]) : []
|
|
25
|
+
|
|
26
|
+
export const applyMiddlewares = <C extends BasicConfig, T extends BasicContext<C>>(
|
|
27
|
+
context: T,
|
|
28
|
+
middlewares: Record<string, Middleware[]>,
|
|
29
|
+
type: MiddlewareType,
|
|
30
|
+
stage: MiddlewareStage,
|
|
31
|
+
args?: Record<string, string | undefined>
|
|
32
|
+
) => Promise.all(
|
|
33
|
+
middlewares[createMiddlewareKey(type, stage)]?.map(async middleware => middleware.apply<C, T>(context, args)) ?? []
|
|
34
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Layer } from '../consts'
|
|
2
|
+
|
|
3
|
+
export interface InLayer<T> extends Record<Layer, Record<string, T>> { }
|
|
4
|
+
|
|
5
|
+
export const initializeLayer = <T>(inLayer: InLayer<T>, layer: Layer, id: string | undefined): string => {
|
|
6
|
+
if (inLayer[layer] == null) {
|
|
7
|
+
inLayer[layer] = {}
|
|
8
|
+
}
|
|
9
|
+
id = id ?? DEFAULT
|
|
10
|
+
if (inLayer[layer][id] == null) {
|
|
11
|
+
inLayer[layer][id] = {} as T
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return id
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const DEFAULT = 'default'
|