@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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +750 -0
  3. package/build/.gitkeep +0 -0
  4. package/build/config.d.ts +4 -0
  5. package/build/config.d.ts.map +1 -0
  6. package/build/config.js +15 -0
  7. package/build/config.js.map +1 -0
  8. package/build/consts.d.ts +50 -0
  9. package/build/consts.d.ts.map +1 -0
  10. package/build/consts.js +56 -0
  11. package/build/consts.js.map +1 -0
  12. package/build/context.d.ts +3 -0
  13. package/build/context.d.ts.map +1 -0
  14. package/build/context.js +203 -0
  15. package/build/context.js.map +1 -0
  16. package/build/helper.d.ts +8 -0
  17. package/build/helper.d.ts.map +1 -0
  18. package/build/helper.js +31 -0
  19. package/build/helper.js.map +1 -0
  20. package/build/index.d.ts +7 -0
  21. package/build/index.d.ts.map +1 -0
  22. package/build/index.js +6 -0
  23. package/build/index.js.map +1 -0
  24. package/build/service.d.ts +7 -0
  25. package/build/service.d.ts.map +1 -0
  26. package/build/service.js +27 -0
  27. package/build/service.js.map +1 -0
  28. package/build/types.d.ts +80 -0
  29. package/build/types.d.ts.map +1 -0
  30. package/build/types.js +2 -0
  31. package/build/types.js.map +1 -0
  32. package/build/utils/context.d.ts +11 -0
  33. package/build/utils/context.d.ts.map +1 -0
  34. package/build/utils/context.js +15 -0
  35. package/build/utils/context.js.map +1 -0
  36. package/build/utils/index.d.ts +3 -0
  37. package/build/utils/index.d.ts.map +1 -0
  38. package/build/utils/index.js +3 -0
  39. package/build/utils/index.js.map +1 -0
  40. package/build/utils/layer.d.ts +6 -0
  41. package/build/utils/layer.d.ts.map +1 -0
  42. package/build/utils/layer.js +12 -0
  43. package/build/utils/layer.js.map +1 -0
  44. package/package.json +31 -0
  45. package/src/config.ts +17 -0
  46. package/src/consts.ts +58 -0
  47. package/src/context.ts +261 -0
  48. package/src/helper.ts +43 -0
  49. package/src/index.ts +7 -0
  50. package/src/service.ts +49 -0
  51. package/src/types.ts +96 -0
  52. package/src/utils/context.ts +34 -0
  53. package/src/utils/index.ts +3 -0
  54. package/src/utils/layer.ts +17 -0
  55. package/tsconfig.json +14 -0
  56. 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,3 @@
1
+ export * from './context.js';
2
+ export * from './layer.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,3 @@
1
+ export * from './context.js';
2
+ export * from './layer.js';
3
+ //# sourceMappingURL=index.js.map
@@ -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
@@ -0,0 +1,7 @@
1
+
2
+ export type * from './types.js'
3
+ export * from './consts.js'
4
+ export * from './context.js'
5
+ export * from './service.js'
6
+ export * from './helper.js'
7
+ export * from './config.js'
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,3 @@
1
+
2
+ export * from './context.js'
3
+ export * from './layer.js'
@@ -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'