webspresso 0.0.107 → 0.0.108
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/.agents/AGENTS.md +4 -1
- package/.agents/CACHE.md +1 -0
- package/.agents/CONTEXT.md +152 -0
- package/.agents/ORM.md +74 -0
- package/.agents/SHUTDOWN.md +8 -1
- package/.agents/skills/webspresso-framework/SKILL.md +2 -1
- package/.agents/skills/webspresso-orm-and-database/SKILL.md +9 -0
- package/.agents/skills/webspresso-routing-and-api/SKILL.md +18 -2
- package/.agents/skills/webspresso-services/SKILL.md +36 -3
- package/AGENTS.md +6 -3
- package/core/app.js +7 -5
- package/core/applySchema.js +3 -72
- package/core/cache/lru.js +10 -0
- package/core/cache/tagged-lru.js +4 -0
- package/core/compileSchema.js +3 -68
- package/core/compression/compressible.js +53 -6
- package/core/compression/negotiate.js +31 -12
- package/core/context/app-context.js +201 -0
- package/core/context/index.js +55 -0
- package/core/context/request.js +241 -0
- package/core/events/dispatcher.js +144 -15
- package/core/events/signal.js +9 -0
- package/core/health/probes.js +12 -2
- package/core/http/index.js +10 -1
- package/core/http/pipeline.js +519 -368
- package/core/index.js +24 -1
- package/core/orm/eager-loader.js +90 -55
- package/core/orm/index.js +3 -39
- package/core/orm/model.js +6 -1
- package/core/orm/schema-helpers.js +59 -0
- package/core/orm/transaction.js +109 -14
- package/core/plugins/index.js +8 -0
- package/core/plugins/plugin-manager.js +1098 -0
- package/core/queue/manager.js +55 -27
- package/core/rate-limit/stores/memory.js +23 -10
- package/core/resilience/circuit-breaker.js +34 -14
- package/core/schema-cache.js +2 -64
- package/core/services/builtins/auth.js +273 -0
- package/core/services/builtins/exchange.js +229 -0
- package/core/services/builtins/file-manager.js +178 -0
- package/core/services/builtins/mail.js +134 -0
- package/core/services/builtins/media.js +196 -0
- package/core/services/builtins/system.js +219 -0
- package/core/services/discovery.js +133 -0
- package/core/services/executor.js +629 -0
- package/core/services/index.js +110 -5
- package/core/services/loader.js +161 -0
- package/core/services/memoize.js +408 -0
- package/core/services/registry.js +731 -0
- package/core/services/validator.js +424 -0
- package/core/shutdown/shutdown-manager.js +146 -85
- package/core/ssr/stream.js +109 -16
- package/core/stream/text-stream.js +45 -4
- package/core/utils/index.js +110 -0
- package/core/utils/timeout.js +4 -0
- package/core/validation/apply.js +74 -0
- package/core/validation/cache.js +66 -0
- package/core/validation/compiler.js +77 -0
- package/core/validation/index.js +22 -1
- package/index.d.ts +77 -4
- package/index.js +64 -20
- package/package.json +1 -1
- package/src/app-context.js +4 -196
- package/src/dev/typegen.js +1 -1
- package/src/plugin-manager.js +5 -1086
- package/src/server.js +4 -1
- package/src/services/builtins/auth.js +2 -273
- package/src/services/builtins/exchange.js +2 -229
- package/src/services/builtins/file-manager.js +2 -178
- package/src/services/builtins/mail.js +2 -134
- package/src/services/builtins/media.js +2 -196
- package/src/services/builtins/system.js +2 -219
- package/src/services/discovery.js +2 -133
- package/src/services/executor.js +2 -575
- package/src/services/index.js +4 -109
- package/src/services/loader.js +1 -119
- package/src/services/memoize.js +2 -408
- package/src/services/registry.js +1 -711
- package/src/services/validator.js +2 -424
- package/core/utils/array.js +0 -12
- package/core/utils/base64url.js +0 -8
- package/core/utils/ensure-leading-slash.js +0 -6
- package/core/utils/escape-html.js +0 -7
- package/core/utils/escape-regexp.js +0 -7
- package/core/utils/get-client-ip.js +0 -6
- package/core/utils/hmac.js +0 -6
- package/core/utils/random.js +0 -9
- package/core/utils/safe-compare.js +0 -3
- package/core/utils/sha256.js +0 -6
- package/core/utils/strip-trailing-slash.js +0 -6
- package/core/utils/walk.js +0 -7
package/core/compileSchema.js
CHANGED
|
@@ -1,69 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Schema Compiler
|
|
3
|
-
* Compiles schema definitions from API files
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { z: zForApi } = require('./validation');
|
|
7
|
-
const schemaCache = require('./schema-cache');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Compile schema from an API module
|
|
11
|
-
* @param {string} filePath - Absolute file path to API module
|
|
12
|
-
* @param {Object} apiModule - The loaded API module
|
|
13
|
-
* @returns {Object|null} Compiled schema object or null if no schema
|
|
14
|
-
*/
|
|
15
|
-
function compileSchema(filePath, apiModule) {
|
|
16
|
-
// Return cached schema if exists
|
|
17
|
-
if (schemaCache.has(filePath)) {
|
|
18
|
-
return schemaCache.get(filePath);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Check if module exports schema
|
|
22
|
-
const schemaFn = apiModule.schema;
|
|
23
|
-
|
|
24
|
-
// Schema is optional
|
|
25
|
-
if (schemaFn === undefined) {
|
|
26
|
-
schemaCache.set(filePath, null);
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Schema must be a function
|
|
31
|
-
if (typeof schemaFn !== 'function') {
|
|
32
|
-
throw new Error(`Schema in ${filePath} must be a function`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Call schema function with { z } (extended with z.nanoid, same as generateNanoid / zdb.nanoid)
|
|
36
|
-
const compiled = schemaFn({ z: zForApi });
|
|
37
|
-
|
|
38
|
-
// Validate compiled schema structure
|
|
39
|
-
if (compiled !== null && typeof compiled !== 'object') {
|
|
40
|
-
throw new Error(`Schema function in ${filePath} must return an object or null`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Cache and return
|
|
44
|
-
schemaCache.set(filePath, compiled);
|
|
45
|
-
return compiled;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Clear schema cache for a file (for hot-reload)
|
|
50
|
-
* @param {string} filePath - Absolute file path
|
|
51
|
-
*/
|
|
52
|
-
function invalidateSchema(filePath) {
|
|
53
|
-
schemaCache.del(filePath);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Clear all cached schemas
|
|
58
|
-
*/
|
|
59
|
-
function clearAllSchemas() {
|
|
60
|
-
schemaCache.clear();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = {
|
|
64
|
-
compileSchema,
|
|
65
|
-
invalidateSchema,
|
|
66
|
-
clearAllSchemas
|
|
67
|
-
};
|
|
68
|
-
|
|
1
|
+
'use strict';
|
|
69
2
|
|
|
3
|
+
// Backward-compatibility shim
|
|
4
|
+
module.exports = require('./validation/compiler');
|
|
@@ -11,7 +11,56 @@ const COMPRESSIBLE_TYPE_REGEX = /^(?:text\/[a-z0-9.+_-]+|application\/(?:javascr
|
|
|
11
11
|
const INCOMPRESSIBLE_TYPE_REGEX = /^(?:text\/event-stream|image\/(?:jpeg|png|gif|webp|avif|heic|heif)|video\/[a-z0-9.+_-]+|audio\/[a-z0-9.+_-]+|application\/(?:zip|gzip|x-gzip|x-bzip2|x-compress|x-7z-compressed|x-rar-compressed|pdf|wasm|octet-stream))$/i;
|
|
12
12
|
|
|
13
13
|
const compressibleCache = new Map();
|
|
14
|
-
const MAX_MIME_CACHE_SIZE =
|
|
14
|
+
const MAX_MIME_CACHE_SIZE = 512;
|
|
15
|
+
|
|
16
|
+
// Pre-fill standard common web & API MIME types for zero-lookup warmup
|
|
17
|
+
const PREFILLED_MIMES = [
|
|
18
|
+
['text/html', true],
|
|
19
|
+
['text/html; charset=utf-8', true],
|
|
20
|
+
['text/plain', true],
|
|
21
|
+
['text/plain; charset=utf-8', true],
|
|
22
|
+
['text/css', true],
|
|
23
|
+
['text/css; charset=utf-8', true],
|
|
24
|
+
['text/javascript', true],
|
|
25
|
+
['text/javascript; charset=utf-8', true],
|
|
26
|
+
['application/javascript', true],
|
|
27
|
+
['application/javascript; charset=utf-8', true],
|
|
28
|
+
['application/json', true],
|
|
29
|
+
['application/json; charset=utf-8', true],
|
|
30
|
+
['application/ld+json', true],
|
|
31
|
+
['application/manifest+json', true],
|
|
32
|
+
['application/graphql+json', true],
|
|
33
|
+
['application/geo+json', true],
|
|
34
|
+
['application/xml', true],
|
|
35
|
+
['application/atom+xml', true],
|
|
36
|
+
['application/rss+xml', true],
|
|
37
|
+
['application/xhtml+xml', true],
|
|
38
|
+
['image/svg+xml', true],
|
|
39
|
+
['text/event-stream', false],
|
|
40
|
+
['image/png', false],
|
|
41
|
+
['image/jpeg', false],
|
|
42
|
+
['image/webp', false],
|
|
43
|
+
['image/gif', false],
|
|
44
|
+
['image/avif', false],
|
|
45
|
+
['application/pdf', false],
|
|
46
|
+
['application/zip', false],
|
|
47
|
+
['application/gzip', false],
|
|
48
|
+
['application/octet-stream', false],
|
|
49
|
+
['video/mp4', false],
|
|
50
|
+
['audio/mpeg', false],
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
for (const [mime, val] of PREFILLED_MIMES) {
|
|
54
|
+
compressibleCache.set(mime, val);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function setCompressibleCache(key, value) {
|
|
58
|
+
if (compressibleCache.size >= MAX_MIME_CACHE_SIZE) {
|
|
59
|
+
const firstKey = compressibleCache.keys().next().value;
|
|
60
|
+
compressibleCache.delete(firstKey);
|
|
61
|
+
}
|
|
62
|
+
compressibleCache.set(key, value);
|
|
63
|
+
}
|
|
15
64
|
|
|
16
65
|
/**
|
|
17
66
|
* Determine if a Content-Type is compressible
|
|
@@ -33,13 +82,13 @@ function isCompressible(contentType) {
|
|
|
33
82
|
const mime = (semiIndex !== -1 ? contentType.slice(0, semiIndex) : contentType).trim().toLowerCase();
|
|
34
83
|
|
|
35
84
|
if (!mime) {
|
|
36
|
-
|
|
85
|
+
setCompressibleCache(contentType, false);
|
|
37
86
|
return false;
|
|
38
87
|
}
|
|
39
88
|
|
|
40
89
|
// Fast negative check
|
|
41
90
|
if (INCOMPRESSIBLE_TYPE_REGEX.test(mime)) {
|
|
42
|
-
|
|
91
|
+
setCompressibleCache(contentType, false);
|
|
43
92
|
return false;
|
|
44
93
|
}
|
|
45
94
|
|
|
@@ -51,9 +100,7 @@ function isCompressible(contentType) {
|
|
|
51
100
|
result = true;
|
|
52
101
|
}
|
|
53
102
|
|
|
54
|
-
|
|
55
|
-
compressibleCache.set(contentType, result);
|
|
56
|
-
}
|
|
103
|
+
setCompressibleCache(contentType, result);
|
|
57
104
|
|
|
58
105
|
return result;
|
|
59
106
|
}
|
|
@@ -71,9 +71,29 @@ function parseAcceptEncoding(header) {
|
|
|
71
71
|
|
|
72
72
|
const DEFAULT_SUPPORTED_ENCODINGS = Object.freeze(getDefaultSupportedEncodings());
|
|
73
73
|
const DEFAULT_SUPPORTED_STRING = DEFAULT_SUPPORTED_ENCODINGS.join(',');
|
|
74
|
+
const DEFAULT_PREFERRED_ENCODING = DEFAULT_SUPPORTED_ENCODINGS[0] || 'identity';
|
|
74
75
|
|
|
75
76
|
const selectEncodingCache = new Map();
|
|
76
|
-
const MAX_ENCODING_CACHE_SIZE =
|
|
77
|
+
const MAX_ENCODING_CACHE_SIZE = 256;
|
|
78
|
+
|
|
79
|
+
function setSelectEncodingCache(key, value) {
|
|
80
|
+
if (selectEncodingCache.size >= MAX_ENCODING_CACHE_SIZE) {
|
|
81
|
+
const firstKey = selectEncodingCache.keys().next().value;
|
|
82
|
+
selectEncodingCache.delete(firstKey);
|
|
83
|
+
}
|
|
84
|
+
selectEncodingCache.set(key, value);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Pre-fill common browser patterns for default configuration
|
|
88
|
+
if (DEFAULT_SUPPORTED_ENCODINGS.includes('br')) {
|
|
89
|
+
selectEncodingCache.set('gzip, deflate, br', 'br');
|
|
90
|
+
selectEncodingCache.set('gzip, deflate, br, zstd', 'br');
|
|
91
|
+
selectEncodingCache.set('br;q=1.0, gzip;q=0.8, *;q=0.1', 'br');
|
|
92
|
+
}
|
|
93
|
+
selectEncodingCache.set('gzip, deflate', 'gzip');
|
|
94
|
+
selectEncodingCache.set('gzip', 'gzip');
|
|
95
|
+
selectEncodingCache.set('*', DEFAULT_PREFERRED_ENCODING);
|
|
96
|
+
selectEncodingCache.set('identity', 'identity');
|
|
77
97
|
|
|
78
98
|
/**
|
|
79
99
|
* Select the best compression encoding given client header and server supported list
|
|
@@ -82,14 +102,17 @@ const MAX_ENCODING_CACHE_SIZE = 128;
|
|
|
82
102
|
* @returns {string|null} The selected encoding ('br', 'gzip', 'deflate', 'identity', or null)
|
|
83
103
|
*/
|
|
84
104
|
function selectEncoding(acceptEncoding, supportedEncodings) {
|
|
85
|
-
if (!acceptEncoding) {
|
|
105
|
+
if (!acceptEncoding || typeof acceptEncoding !== 'string') {
|
|
106
|
+
return 'identity';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const raw = acceptEncoding.trim();
|
|
110
|
+
if (!raw) {
|
|
86
111
|
return 'identity';
|
|
87
112
|
}
|
|
88
113
|
|
|
89
114
|
const isDefault = !supportedEncodings || !Array.isArray(supportedEncodings) || supportedEncodings.length === 0;
|
|
90
|
-
const cacheKey = isDefault
|
|
91
|
-
? `${acceptEncoding}|${DEFAULT_SUPPORTED_STRING}`
|
|
92
|
-
: `${acceptEncoding}|${supportedEncodings.join(',')}`;
|
|
115
|
+
const cacheKey = isDefault ? raw : `${raw}|${supportedEncodings.join(',')}`;
|
|
93
116
|
|
|
94
117
|
const cached = selectEncodingCache.get(cacheKey);
|
|
95
118
|
if (cached !== undefined) {
|
|
@@ -100,11 +123,9 @@ function selectEncoding(acceptEncoding, supportedEncodings) {
|
|
|
100
123
|
? DEFAULT_SUPPORTED_ENCODINGS
|
|
101
124
|
: supportedEncodings.map(e => e.toLowerCase()).filter(e => e !== 'br' || supportsBrotli());
|
|
102
125
|
|
|
103
|
-
const clientPreferences = parseAcceptEncoding(
|
|
126
|
+
const clientPreferences = parseAcceptEncoding(raw);
|
|
104
127
|
if (clientPreferences.length === 0) {
|
|
105
|
-
|
|
106
|
-
selectEncodingCache.set(cacheKey, 'identity');
|
|
107
|
-
}
|
|
128
|
+
setSelectEncodingCache(cacheKey, 'identity');
|
|
108
129
|
return 'identity';
|
|
109
130
|
}
|
|
110
131
|
|
|
@@ -157,9 +178,7 @@ function selectEncoding(acceptEncoding, supportedEncodings) {
|
|
|
157
178
|
result = wildcardQ > 0 ? 'identity' : null;
|
|
158
179
|
}
|
|
159
180
|
|
|
160
|
-
|
|
161
|
-
selectEncodingCache.set(cacheKey, result);
|
|
162
|
-
}
|
|
181
|
+
setSelectEncodingCache(cacheKey, result);
|
|
163
182
|
|
|
164
183
|
return result;
|
|
165
184
|
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide app context set by createApp() — use from API handlers, jobs, etc.
|
|
3
|
+
* @module src/app-context
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { circuitBreakerRegistry, retry } = require('../resilience');
|
|
7
|
+
|
|
8
|
+
/** @type {{ db: object|null, shutdownManager: object|null, serviceRegistry: object|null, events: object|null, mailer: object|null, cache: object|null, breakers: object|null }} */
|
|
9
|
+
let context = {
|
|
10
|
+
db: null,
|
|
11
|
+
shutdownManager: null,
|
|
12
|
+
serviceRegistry: null,
|
|
13
|
+
events: null,
|
|
14
|
+
mailer: null,
|
|
15
|
+
cache: null,
|
|
16
|
+
breakers: circuitBreakerRegistry,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Merge into the current context (typically called once from createApp).
|
|
21
|
+
* @param {{ db?: object|null, shutdownManager?: object|null, serviceRegistry?: object|null, events?: object|null, mailer?: object|null, cache?: object|null }} partial
|
|
22
|
+
*/
|
|
23
|
+
function setAppContext(partial) {
|
|
24
|
+
context = { ...context, ...partial };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @returns {{ db: object|null, shutdownManager: object|null, serviceRegistry: object|null, events: object|null, mailer: object|null }}
|
|
29
|
+
*/
|
|
30
|
+
function getAppContext() {
|
|
31
|
+
return context;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Database instance passed to createApp({ db }) (Knex + ORM helpers).
|
|
36
|
+
* @returns {object}
|
|
37
|
+
* @throws {Error} If createApp was not given a db instance
|
|
38
|
+
*/
|
|
39
|
+
function getDb() {
|
|
40
|
+
if (!context.db) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'No database registered. Create a DB with createDatabase(), then pass it to createApp({ db }). ' +
|
|
43
|
+
'Or use hasDb() before calling getDb().'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return context.db;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
function hasDb() {
|
|
53
|
+
return context.db != null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @returns {object|null}
|
|
58
|
+
*/
|
|
59
|
+
function getShutdownManager() {
|
|
60
|
+
return context.shutdownManager;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
function hasShutdownManager() {
|
|
67
|
+
return context.shutdownManager != null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get the registered ServiceRegistry instance
|
|
72
|
+
* @returns {object|null}
|
|
73
|
+
*/
|
|
74
|
+
function getServiceRegistry() {
|
|
75
|
+
return context.serviceRegistry;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if a ServiceRegistry is registered
|
|
80
|
+
* @returns {boolean}
|
|
81
|
+
*/
|
|
82
|
+
function hasServiceRegistry() {
|
|
83
|
+
return context.serviceRegistry != null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get the registered EventDispatcher instance
|
|
88
|
+
* @returns {object|null}
|
|
89
|
+
*/
|
|
90
|
+
function getEvents() {
|
|
91
|
+
return context.events;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if an EventDispatcher is registered
|
|
96
|
+
* @returns {boolean}
|
|
97
|
+
*/
|
|
98
|
+
function hasEvents() {
|
|
99
|
+
return context.events != null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Clear context (e.g. between tests).
|
|
104
|
+
*/
|
|
105
|
+
function resetAppContext() {
|
|
106
|
+
context = {
|
|
107
|
+
db: null,
|
|
108
|
+
shutdownManager: null,
|
|
109
|
+
serviceRegistry: null,
|
|
110
|
+
events: null,
|
|
111
|
+
mailer: null,
|
|
112
|
+
cache: null,
|
|
113
|
+
breakers: circuitBreakerRegistry,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @returns {object|null}
|
|
119
|
+
*/
|
|
120
|
+
function getMailer() {
|
|
121
|
+
return context.mailer;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @returns {boolean}
|
|
126
|
+
*/
|
|
127
|
+
function hasMailer() {
|
|
128
|
+
return context.mailer != null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @returns {object|null}
|
|
133
|
+
*/
|
|
134
|
+
function getCache() {
|
|
135
|
+
return context.cache;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
function hasCache() {
|
|
142
|
+
return context.cache != null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Express middleware: sets `req.db`, `req.service`, `req.events`, `req.mailer`, and `req.cache` from registered app context.
|
|
147
|
+
* File-based `pages/api/*` routes attach this automatically; use in `setupRoutes`
|
|
148
|
+
* for manually registered handlers that need `req.db` and `req.service`.
|
|
149
|
+
* @type {import('express').RequestHandler}
|
|
150
|
+
*/
|
|
151
|
+
function attachDbMiddleware(req, res, next) {
|
|
152
|
+
if (context.db != null) {
|
|
153
|
+
req.db = context.db;
|
|
154
|
+
}
|
|
155
|
+
if (context.events != null) {
|
|
156
|
+
req.events = context.events;
|
|
157
|
+
}
|
|
158
|
+
if (context.mailer != null) {
|
|
159
|
+
req.mailer = context.mailer;
|
|
160
|
+
}
|
|
161
|
+
if (context.cache != null) {
|
|
162
|
+
req.cache = context.cache;
|
|
163
|
+
}
|
|
164
|
+
req.breakers = context.breakers || circuitBreakerRegistry;
|
|
165
|
+
req.breaker = (name, opts) => (context.breakers || circuitBreakerRegistry).getBreaker(name, opts);
|
|
166
|
+
req.retry = retry;
|
|
167
|
+
if (context.serviceRegistry != null) {
|
|
168
|
+
req.service = (name, input, opts) =>
|
|
169
|
+
context.serviceRegistry.call(name, input, req.context || {
|
|
170
|
+
req,
|
|
171
|
+
res,
|
|
172
|
+
db: context.db,
|
|
173
|
+
events: context.events,
|
|
174
|
+
mailer: context.mailer,
|
|
175
|
+
cache: context.cache,
|
|
176
|
+
breakers: req.breakers,
|
|
177
|
+
breaker: req.breaker,
|
|
178
|
+
retry,
|
|
179
|
+
}, opts);
|
|
180
|
+
}
|
|
181
|
+
next();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
setAppContext,
|
|
186
|
+
getAppContext,
|
|
187
|
+
getDb,
|
|
188
|
+
hasDb,
|
|
189
|
+
getShutdownManager,
|
|
190
|
+
hasShutdownManager,
|
|
191
|
+
getServiceRegistry,
|
|
192
|
+
hasServiceRegistry,
|
|
193
|
+
getEvents,
|
|
194
|
+
hasEvents,
|
|
195
|
+
getMailer,
|
|
196
|
+
hasMailer,
|
|
197
|
+
getCache,
|
|
198
|
+
hasCache,
|
|
199
|
+
resetAppContext,
|
|
200
|
+
attachDbMiddleware,
|
|
201
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Webspresso Context Management Layer
|
|
5
|
+
* Process-wide application context and request-scoped AsyncLocalStorage ambient context.
|
|
6
|
+
*
|
|
7
|
+
* @module core/context
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const appContext = require('./app-context');
|
|
11
|
+
const requestContext = require('./request');
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
// App context (process-wide)
|
|
15
|
+
setAppContext: appContext.setAppContext,
|
|
16
|
+
getAppContext: appContext.getAppContext,
|
|
17
|
+
getDb: appContext.getDb,
|
|
18
|
+
hasDb: appContext.hasDb,
|
|
19
|
+
getShutdownManager: appContext.getShutdownManager,
|
|
20
|
+
hasShutdownManager: appContext.hasShutdownManager,
|
|
21
|
+
getServiceRegistry: appContext.getServiceRegistry,
|
|
22
|
+
hasServiceRegistry: appContext.hasServiceRegistry,
|
|
23
|
+
getEvents: appContext.getEvents,
|
|
24
|
+
hasEvents: appContext.hasEvents,
|
|
25
|
+
getMailer: appContext.getMailer,
|
|
26
|
+
hasMailer: appContext.hasMailer,
|
|
27
|
+
getCache: appContext.getCache,
|
|
28
|
+
hasCache: appContext.hasCache,
|
|
29
|
+
resetAppContext: appContext.resetAppContext,
|
|
30
|
+
attachDbMiddleware: appContext.attachDbMiddleware,
|
|
31
|
+
|
|
32
|
+
// Request & ambient execution context (AsyncLocalStorage)
|
|
33
|
+
requestContextStorage: requestContext.requestContextStorage,
|
|
34
|
+
runWithContext: requestContext.runWithContext,
|
|
35
|
+
withContext: requestContext.withContext,
|
|
36
|
+
getContext: requestContext.getContext,
|
|
37
|
+
useContext: requestContext.useContext,
|
|
38
|
+
useDb: requestContext.useDb,
|
|
39
|
+
useAuth: requestContext.useAuth,
|
|
40
|
+
useUser: requestContext.useUser,
|
|
41
|
+
useLogger: requestContext.useLogger,
|
|
42
|
+
useReq: requestContext.useReq,
|
|
43
|
+
useRes: requestContext.useRes,
|
|
44
|
+
useParams: requestContext.useParams,
|
|
45
|
+
useQuery: requestContext.useQuery,
|
|
46
|
+
useService: requestContext.useService,
|
|
47
|
+
useQueue: requestContext.useQueue,
|
|
48
|
+
useCache: requestContext.useCache,
|
|
49
|
+
useBreaker: requestContext.useBreaker,
|
|
50
|
+
useTransaction: requestContext.useTransaction,
|
|
51
|
+
|
|
52
|
+
// Namespace accessors
|
|
53
|
+
appContext,
|
|
54
|
+
requestContext,
|
|
55
|
+
};
|