@zintrust/core 0.1.20 → 0.1.22
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/package.json +2 -1
- package/src/boot/Application.d.ts.map +1 -1
- package/src/boot/Application.js +48 -10
- package/src/boot/bootstrap.js +2 -0
- package/src/cli/commands/MigrateCommand.d.ts.map +1 -1
- package/src/cli/commands/MigrateCommand.js +37 -4
- package/src/cli/d1/D1SqlMigrations.d.ts.map +1 -1
- package/src/cli/d1/D1SqlMigrations.js +10 -4
- package/src/cli/scaffolding/ControllerGenerator.js +4 -4
- package/src/cli/scaffolding/GovernanceScaffolder.js +1 -1
- package/src/cli/scaffolding/MigrationGenerator.js +1 -1
- package/src/cli/scaffolding/ModelGenerator.d.ts +1 -1
- package/src/cli/scaffolding/ModelGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/ModelGenerator.js +11 -3
- package/src/cli/scaffolding/RouteGenerator.js +1 -1
- package/src/cli/scaffolding/ServiceScaffolder.js +4 -4
- package/src/config/broadcast.d.ts +14 -28
- package/src/config/broadcast.d.ts.map +1 -1
- package/src/config/broadcast.js +69 -35
- package/src/config/cache.d.ts +13 -45
- package/src/config/cache.d.ts.map +1 -1
- package/src/config/cache.js +69 -25
- package/src/config/database.d.ts +22 -64
- package/src/config/database.d.ts.map +1 -1
- package/src/config/database.js +99 -31
- package/src/config/env.d.ts +6 -0
- package/src/config/env.d.ts.map +1 -1
- package/src/config/env.js +7 -0
- package/src/config/index.d.ts +32 -136
- package/src/config/index.d.ts.map +1 -1
- package/src/config/mail.d.ts +19 -55
- package/src/config/mail.d.ts.map +1 -1
- package/src/config/mail.js +63 -21
- package/src/config/middleware.d.ts +24 -0
- package/src/config/middleware.d.ts.map +1 -1
- package/src/config/middleware.js +72 -52
- package/src/config/notification.d.ts +14 -27
- package/src/config/notification.d.ts.map +1 -1
- package/src/config/notification.js +82 -36
- package/src/config/queue.d.ts +21 -51
- package/src/config/queue.d.ts.map +1 -1
- package/src/config/queue.js +72 -27
- package/src/config/storage.d.ts +27 -34
- package/src/config/storage.d.ts.map +1 -1
- package/src/config/storage.js +97 -56
- package/src/config/type.d.ts +23 -1
- package/src/config/type.d.ts.map +1 -1
- package/src/config/type.js +10 -1
- package/src/http/parsers/MultipartParser.d.ts.map +1 -1
- package/src/http/parsers/MultipartParser.js +69 -42
- package/src/index.d.ts +9 -5
- package/src/index.d.ts.map +1 -1
- package/src/index.js +1 -0
- package/src/microservices/PostgresAdapter.d.ts.map +1 -1
- package/src/microservices/PostgresAdapter.js +0 -1
- package/src/migrations/MigrationLoader.d.ts +1 -1
- package/src/migrations/MigrationLoader.d.ts.map +1 -1
- package/src/migrations/Migrator.d.ts +3 -3
- package/src/migrations/Migrator.d.ts.map +1 -1
- package/src/migrations/Migrator.js +1 -1
- package/src/migrations/MigratorFactory.d.ts +1 -1
- package/src/migrations/MigratorFactory.d.ts.map +1 -1
- package/src/migrations/MigratorFactory.js +21 -5
- package/src/migrations/enum/index.d.ts +93 -0
- package/src/migrations/enum/index.d.ts.map +1 -0
- package/src/migrations/enum/index.js +92 -0
- package/src/migrations/schema/Blueprint.d.ts +1 -1
- package/src/migrations/schema/Blueprint.d.ts.map +1 -1
- package/src/migrations/schema/Blueprint.js +27 -25
- package/src/migrations/schema/Schema.d.ts +1 -1
- package/src/migrations/schema/Schema.d.ts.map +1 -1
- package/src/migrations/schema/Schema.js +4 -3
- package/src/migrations/schema/SchemaCompiler.d.ts +1 -1
- package/src/migrations/schema/SchemaCompiler.d.ts.map +1 -1
- package/src/migrations/schema/SchemaCompiler.js +99 -91
- package/src/migrations/schema/index.d.ts +4 -4
- package/src/migrations/schema/index.d.ts.map +1 -1
- package/src/migrations/schema/index.js +3 -3
- package/src/migrations/schema/types.d.ts +2 -1
- package/src/migrations/schema/types.d.ts.map +1 -1
- package/src/node-singletons/fs.d.ts +1 -1
- package/src/node-singletons/fs.d.ts.map +1 -1
- package/src/node-singletons/fs.js +1 -1
- package/src/orm/ConnectionManager.d.ts +6 -4
- package/src/orm/ConnectionManager.d.ts.map +1 -1
- package/src/orm/ConnectionManager.js +213 -75
- package/src/orm/Database.d.ts +4 -2
- package/src/orm/Database.d.ts.map +1 -1
- package/src/orm/Database.js +110 -67
- package/src/orm/DatabaseAdapter.d.ts +4 -2
- package/src/orm/DatabaseAdapter.d.ts.map +1 -1
- package/src/orm/DatabaseAdapter.js +17 -0
- package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -1
- package/src/orm/DatabaseRuntimeRegistration.js +12 -0
- package/src/orm/Model.d.ts.map +1 -1
- package/src/orm/Model.js +24 -2
- package/src/orm/QueryBuilder.d.ts +1 -1
- package/src/orm/QueryBuilder.d.ts.map +1 -1
- package/src/orm/QueryBuilder.js +4 -3
- package/src/orm/adapters/D1Adapter.d.ts.map +1 -1
- package/src/orm/adapters/D1Adapter.js +2 -1
- package/src/orm/adapters/D1RemoteAdapter.d.ts.map +1 -1
- package/src/orm/adapters/D1RemoteAdapter.js +2 -1
- package/src/orm/adapters/MySQLAdapter.d.ts.map +1 -1
- package/src/orm/adapters/MySQLAdapter.js +2 -1
- package/src/orm/adapters/SQLServerAdapter.d.ts.map +1 -1
- package/src/orm/adapters/SQLServerAdapter.js +2 -1
- package/src/orm/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/src/orm/adapters/SQLiteAdapter.js +3 -2
- package/src/orm/migrations/MigrationStore.d.ts.map +1 -1
- package/src/performance/Optimizer.d.ts +6 -6
- package/src/performance/Optimizer.d.ts.map +1 -1
- package/src/performance/Optimizer.js +174 -52
- package/src/profiling/RequestProfiler.d.ts.map +1 -1
- package/src/profiling/RequestProfiler.js +3 -1
- package/src/routing/doc.d.ts +4 -5
- package/src/routing/doc.d.ts.map +1 -1
- package/src/routing/doc.js +35 -20
- package/src/routing/publicRoot.d.ts +9 -0
- package/src/routing/publicRoot.d.ts.map +1 -1
- package/src/routing/publicRoot.js +63 -2
- package/src/runtime/StartupConfigFileRegistry.d.ts +21 -0
- package/src/runtime/StartupConfigFileRegistry.d.ts.map +1 -0
- package/src/runtime/StartupConfigFileRegistry.js +43 -0
- package/src/runtime/useFileLoader.d.ts +26 -0
- package/src/runtime/useFileLoader.d.ts.map +1 -0
- package/src/runtime/useFileLoader.js +188 -0
- package/src/scripts/TemplateSync.js +4 -4
- package/src/security/XssProtection.d.ts.map +1 -1
- package/src/security/XssProtection.js +62 -14
- package/src/templates/project/basic/config/broadcast.ts.tpl +33 -17
- package/src/templates/project/basic/config/cache.ts.tpl +35 -17
- package/src/templates/project/basic/config/database.ts.tpl +68 -32
- package/src/templates/project/basic/config/logging/HttpLogger.ts.tpl +7 -114
- package/src/templates/project/basic/config/mail.ts.tpl +59 -13
- package/src/templates/project/basic/config/notification.ts.tpl +28 -17
- package/src/templates/project/basic/config/queue.ts.tpl +49 -17
- package/src/templates/project/basic/config/storage.ts.tpl +55 -18
- package/src/templates/project/basic/config/type.ts.tpl +0 -1
- package/src/templates/project/basic/src/index.ts.tpl +3 -0
- package/src/templates/project/basic/config/logging/KvLogger.ts.tpl +0 -181
- package/src/templates/project/basic/config/logging/SlackLogger.ts.tpl +0 -156
|
@@ -59,8 +59,52 @@ function createCacheState(cacheDir, ttlMs, maxEntries) {
|
|
|
59
59
|
cacheDir,
|
|
60
60
|
ttlMs,
|
|
61
61
|
maxEntries,
|
|
62
|
+
pendingWrites: new Map(),
|
|
62
63
|
};
|
|
63
64
|
}
|
|
65
|
+
async function ensureCacheDir(cacheDir) {
|
|
66
|
+
try {
|
|
67
|
+
await fs.fsPromises.access(cacheDir);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
await fs.fsPromises.mkdir(cacheDir, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function flushPendingWrites(state) {
|
|
74
|
+
if (state.pendingWrites.size === 0) {
|
|
75
|
+
state.flushTimer = undefined;
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const pending = state.pendingWrites;
|
|
79
|
+
state.pendingWrites = new Map();
|
|
80
|
+
state.flushTimer = undefined;
|
|
81
|
+
try {
|
|
82
|
+
await ensureCacheDir(state.cacheDir);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
Logger.error('Failed to ensure cache directory before flush', error);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const writes = Array.from(pending.entries()).map(async ([key, entry]) => {
|
|
89
|
+
const file = path.join(state.cacheDir, `${key}.json`);
|
|
90
|
+
await fs.fsPromises.writeFile(file, entry.payload);
|
|
91
|
+
});
|
|
92
|
+
await Promise.all(writes);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
function scheduleCacheWrite(state, key, payload) {
|
|
96
|
+
state.pendingWrites.set(key, { payload });
|
|
97
|
+
if (state.flushTimer !== undefined)
|
|
98
|
+
return;
|
|
99
|
+
state.flushTimer = setTimeout(() => {
|
|
100
|
+
void flushPendingWrites(state).catch((error) => {
|
|
101
|
+
Logger.error('Failed to flush generation cache writes', error);
|
|
102
|
+
});
|
|
103
|
+
}, 50);
|
|
104
|
+
if (isUnrefableTimer(state.flushTimer)) {
|
|
105
|
+
state.flushTimer.unref();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
64
108
|
function initializeCacheState(state) {
|
|
65
109
|
loadFromDisk(state);
|
|
66
110
|
}
|
|
@@ -95,26 +139,26 @@ function startCacheCleanup(state) {
|
|
|
95
139
|
function createCacheInstance(state) {
|
|
96
140
|
return {
|
|
97
141
|
/**
|
|
98
|
-
* Get from cache
|
|
142
|
+
* Get from cache (async)
|
|
99
143
|
*/
|
|
100
|
-
get(type, params) {
|
|
144
|
+
async get(type, params) {
|
|
101
145
|
const key = getCacheKey(type, params);
|
|
102
146
|
const entry = state.cache.get(key);
|
|
103
147
|
if (entry === undefined)
|
|
104
|
-
return null;
|
|
148
|
+
return Promise.resolve(null); //NoSONAR
|
|
105
149
|
// Check TTL
|
|
106
150
|
if (Date.now() - entry.timestamp > state.ttlMs) {
|
|
107
151
|
state.cache.delete(key);
|
|
108
152
|
const file = path.join(state.cacheDir, `${key}.json`);
|
|
109
153
|
deleteFileNonBlocking(file);
|
|
110
|
-
return null;
|
|
154
|
+
return Promise.resolve(null); //NoSONAR
|
|
111
155
|
}
|
|
112
|
-
return entry.code;
|
|
156
|
+
return Promise.resolve(entry.code); //NoSONAR
|
|
113
157
|
},
|
|
114
158
|
/**
|
|
115
|
-
* Set in cache
|
|
159
|
+
* Set in cache (async)
|
|
116
160
|
*/
|
|
117
|
-
set(type, params, code) {
|
|
161
|
+
async set(type, params, code) {
|
|
118
162
|
const key = getCacheKey(type, params);
|
|
119
163
|
// If key already exists, delete first so insertion order updates for LRU
|
|
120
164
|
if (state.cache.has(key))
|
|
@@ -134,27 +178,34 @@ function createCacheInstance(state) {
|
|
|
134
178
|
deleteFileNonBlocking(file);
|
|
135
179
|
}
|
|
136
180
|
}
|
|
181
|
+
const payload = JSON.stringify({ code, timestamp: Date.now() }, null, 2);
|
|
182
|
+
scheduleCacheWrite(state, key, payload);
|
|
137
183
|
},
|
|
138
184
|
/**
|
|
139
|
-
* Save cache to disk
|
|
185
|
+
* Save cache to disk (async)
|
|
140
186
|
*/
|
|
141
|
-
save() {
|
|
142
|
-
saveCacheToDisk(state);
|
|
187
|
+
async save() {
|
|
188
|
+
await saveCacheToDisk(state);
|
|
143
189
|
},
|
|
144
190
|
/**
|
|
145
|
-
* Clear cache
|
|
191
|
+
* Clear cache (async)
|
|
146
192
|
*/
|
|
147
|
-
clear() {
|
|
193
|
+
async clear() {
|
|
148
194
|
if (state.cleanupInterval) {
|
|
149
195
|
clearInterval(state.cleanupInterval);
|
|
150
196
|
state.cleanupInterval = undefined;
|
|
151
197
|
}
|
|
152
|
-
|
|
198
|
+
if (state.flushTimer) {
|
|
199
|
+
clearTimeout(state.flushTimer);
|
|
200
|
+
state.flushTimer = undefined;
|
|
201
|
+
}
|
|
202
|
+
state.pendingWrites.clear();
|
|
203
|
+
await clearCache(state);
|
|
153
204
|
},
|
|
154
205
|
/**
|
|
155
|
-
* Get cache statistics
|
|
206
|
+
* Get cache statistics (async)
|
|
156
207
|
*/
|
|
157
|
-
getStats() {
|
|
208
|
+
async getStats() {
|
|
158
209
|
return getCacheStats(state);
|
|
159
210
|
},
|
|
160
211
|
};
|
|
@@ -166,38 +217,65 @@ function attachCacheStateForTests(instance, state) {
|
|
|
166
217
|
});
|
|
167
218
|
}
|
|
168
219
|
/**
|
|
169
|
-
* Save cache to disk
|
|
220
|
+
* Save cache to disk (async)
|
|
170
221
|
*/
|
|
171
|
-
function saveCacheToDisk(state) {
|
|
172
|
-
|
|
173
|
-
|
|
222
|
+
async function saveCacheToDisk(state) {
|
|
223
|
+
try {
|
|
224
|
+
const flushedEnsured = await flushPendingWrites(state);
|
|
225
|
+
if (!flushedEnsured) {
|
|
226
|
+
await ensureCacheDir(state.cacheDir);
|
|
227
|
+
}
|
|
228
|
+
const writes = Array.from(state.cache.entries()).map(async ([key, entry]) => {
|
|
229
|
+
const file = path.join(state.cacheDir, `${key}.json`);
|
|
230
|
+
await fs.fsPromises.writeFile(file, JSON.stringify(entry, null, 2));
|
|
231
|
+
});
|
|
232
|
+
await Promise.all(writes);
|
|
174
233
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
fs.writeFileSync(file, JSON.stringify(entry, null, 2));
|
|
234
|
+
catch (error) {
|
|
235
|
+
Logger.error('Failed to save cache to disk', error);
|
|
178
236
|
}
|
|
179
237
|
}
|
|
180
238
|
/**
|
|
181
|
-
* Clear cache
|
|
239
|
+
* Clear cache (async)
|
|
182
240
|
*/
|
|
183
|
-
function clearCache(state) {
|
|
241
|
+
async function clearCache(state) {
|
|
184
242
|
state.cache.clear();
|
|
185
|
-
|
|
186
|
-
|
|
243
|
+
state.pendingWrites.clear();
|
|
244
|
+
try {
|
|
245
|
+
try {
|
|
246
|
+
await fs.fsPromises.access(state.cacheDir);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return; // Dir doesn't exist
|
|
250
|
+
}
|
|
251
|
+
await fs.fsPromises.rm(state.cacheDir, { recursive: true });
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
Logger.error('Failed to clear cache', error);
|
|
187
255
|
}
|
|
188
256
|
}
|
|
189
257
|
/**
|
|
190
|
-
* Get cache statistics
|
|
258
|
+
* Get cache statistics (async)
|
|
191
259
|
*/
|
|
192
|
-
function getCacheStats(state) {
|
|
260
|
+
async function getCacheStats(state) {
|
|
193
261
|
let diskUsage = 0;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
262
|
+
try {
|
|
263
|
+
try {
|
|
264
|
+
await fs.fsPromises.access(state.cacheDir);
|
|
265
|
+
const files = await fs.fsPromises.readdir(state.cacheDir);
|
|
266
|
+
const sizes = await Promise.all(files.map(async (file) => {
|
|
267
|
+
const stats = await fs.fsPromises.stat(path.join(state.cacheDir, file));
|
|
268
|
+
return stats.size;
|
|
269
|
+
}));
|
|
270
|
+
diskUsage = sizes.reduce((sum, size) => sum + size, 0);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// ignore
|
|
199
274
|
}
|
|
200
275
|
}
|
|
276
|
+
catch {
|
|
277
|
+
// ignore
|
|
278
|
+
}
|
|
201
279
|
return {
|
|
202
280
|
size: diskUsage,
|
|
203
281
|
entries: state.cache.size,
|
|
@@ -242,24 +320,39 @@ function toInt32(value) {
|
|
|
242
320
|
return uint32 > 2147483647 ? uint32 - 4294967296 : uint32;
|
|
243
321
|
}
|
|
244
322
|
/**
|
|
245
|
-
* Load cache from disk
|
|
323
|
+
* Load cache from disk (async)
|
|
246
324
|
*/
|
|
247
|
-
function loadFromDisk(state) {
|
|
248
|
-
|
|
325
|
+
async function loadFromDisk(state) {
|
|
326
|
+
try {
|
|
249
327
|
try {
|
|
250
|
-
|
|
251
|
-
for (const file of files) {
|
|
252
|
-
if (file.endsWith('.json') === true) {
|
|
253
|
-
const content = fs.readFileSync(path.join(state.cacheDir, file), 'utf-8');
|
|
254
|
-
const data = JSON.parse(content);
|
|
255
|
-
state.cache.set(file.replace('.json', ''), data);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
328
|
+
await fs.fsPromises.access(state.cacheDir);
|
|
258
329
|
}
|
|
259
|
-
catch
|
|
260
|
-
|
|
330
|
+
catch {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const files = await fs.fsPromises.readdir(state.cacheDir);
|
|
334
|
+
const jsonFiles = files.filter((file) => file.endsWith('.json') === true);
|
|
335
|
+
const parsedEntries = await Promise.all(jsonFiles.map(async (file) => {
|
|
336
|
+
const filePath = path.join(state.cacheDir, file);
|
|
337
|
+
const content = await fs.fsPromises.readFile(filePath, 'utf-8');
|
|
338
|
+
try {
|
|
339
|
+
const data = JSON.parse(content);
|
|
340
|
+
return { key: file.replace('.json', ''), data };
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// ignore corrupted files
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
}));
|
|
347
|
+
for (const entry of parsedEntries) {
|
|
348
|
+
if (entry !== null) {
|
|
349
|
+
state.cache.set(entry.key, entry.data);
|
|
350
|
+
}
|
|
261
351
|
}
|
|
262
352
|
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
Logger.error(`Failed to load cache from disk: ${err instanceof Error ? err.message : String(err)}`);
|
|
355
|
+
}
|
|
263
356
|
}
|
|
264
357
|
/**
|
|
265
358
|
* Lazy Module Loader - Load dependencies only when needed
|
|
@@ -474,17 +567,36 @@ async function generateInParallel(stats, generators, batchSize) {
|
|
|
474
567
|
*/
|
|
475
568
|
async function generateWithCache(cache, stats, type, params, generatorFn) {
|
|
476
569
|
// Try cache
|
|
477
|
-
|
|
570
|
+
let cached = null;
|
|
571
|
+
try {
|
|
572
|
+
cached = await cache.get(type, params);
|
|
573
|
+
}
|
|
574
|
+
catch (err) {
|
|
575
|
+
Logger.warn('GenerationCache.get failed; treating as cache miss', {
|
|
576
|
+
type,
|
|
577
|
+
error: err instanceof Error ? err.message : String(err),
|
|
578
|
+
});
|
|
579
|
+
}
|
|
478
580
|
if (cached !== null) {
|
|
479
|
-
|
|
480
|
-
|
|
581
|
+
try {
|
|
582
|
+
stats.cacheHits++;
|
|
583
|
+
return JSON.parse(cached);
|
|
584
|
+
}
|
|
585
|
+
catch (err) {
|
|
586
|
+
Logger.warn('Failed to parse cached generation result; treating as cache miss', {
|
|
587
|
+
type,
|
|
588
|
+
error: err instanceof Error ? err.message : String(err),
|
|
589
|
+
});
|
|
590
|
+
}
|
|
481
591
|
}
|
|
482
592
|
// Generate
|
|
483
593
|
const startTime = performance.now();
|
|
484
594
|
const result = await generatorFn();
|
|
485
595
|
const duration = performance.now() - startTime;
|
|
486
|
-
// Cache result
|
|
487
|
-
cache
|
|
596
|
+
// Cache result (fire-and-forget)
|
|
597
|
+
void cache
|
|
598
|
+
.set(type, params, JSON.stringify(result))
|
|
599
|
+
.catch((err) => Logger.error('GenerationCache.set failed', err));
|
|
488
600
|
stats.cacheMisses++;
|
|
489
601
|
stats.savedTime += duration;
|
|
490
602
|
return result;
|
|
@@ -492,6 +604,16 @@ async function generateWithCache(cache, stats, type, params, generatorFn) {
|
|
|
492
604
|
/**
|
|
493
605
|
* Get optimization statistics
|
|
494
606
|
*/
|
|
607
|
+
function getCacheStatusSync(cache) {
|
|
608
|
+
const state = cache[GENERATION_CACHE_STATE_SYMBOL];
|
|
609
|
+
const map = state?.cache;
|
|
610
|
+
if (!(map instanceof Map))
|
|
611
|
+
return { size: 0, keys: [] };
|
|
612
|
+
return {
|
|
613
|
+
size: map.size,
|
|
614
|
+
keys: Array.from(map.keys()),
|
|
615
|
+
};
|
|
616
|
+
}
|
|
495
617
|
function getOptimizerStats(cache, stats) {
|
|
496
618
|
const total = stats.cacheHits + stats.cacheMisses;
|
|
497
619
|
const hitRate = total > 0 ? (stats.cacheHits / total) * 100 : 0;
|
|
@@ -501,7 +623,7 @@ function getOptimizerStats(cache, stats) {
|
|
|
501
623
|
hitRate: `${hitRate.toFixed(1)}%`,
|
|
502
624
|
parallelRuns: stats.parallelRuns,
|
|
503
625
|
estimatedSavedTime: `${stats.savedTime.toFixed(2)}ms`,
|
|
504
|
-
cacheStatus: cache
|
|
626
|
+
cacheStatus: getCacheStatusSync(cache),
|
|
505
627
|
};
|
|
506
628
|
}
|
|
507
629
|
export default PerformanceOptimizer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestProfiler.d.ts","sourceRoot":"","sources":["../../../src/profiling/RequestProfiler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"RequestProfiler.d.ts","sourceRoot":"","sources":["../../../src/profiling/RequestProfiler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAa,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAC/B,cAAc,IAAI,YAAY,CAAC;IAC/B,aAAa,IAAI,WAAW,CAAC;IAC7B,iBAAiB,IAAI,eAAe,CAAC;IACrC,cAAc,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAAC;CAChD;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe;IAC1B;;OAEG;cACO,gBAAgB;EAsE1B,CAAC;AAmBH;;GAEG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,eAAe,eAAe,CAAC"}
|
|
@@ -49,13 +49,15 @@ export const RequestProfiler = Object.freeze({
|
|
|
49
49
|
const queriesExecuted = queryLog.length;
|
|
50
50
|
const patterns = n1Detector.detect(queryLog);
|
|
51
51
|
const memoryDelta = memoryProfiler.delta();
|
|
52
|
-
|
|
52
|
+
const report = {
|
|
53
53
|
duration,
|
|
54
54
|
queriesExecuted,
|
|
55
55
|
n1Patterns: patterns,
|
|
56
56
|
memoryDelta,
|
|
57
57
|
timestamp: new Date(),
|
|
58
58
|
};
|
|
59
|
+
queryLogger.clear('profiling');
|
|
60
|
+
return report;
|
|
59
61
|
},
|
|
60
62
|
generateReport(profile) {
|
|
61
63
|
const n1Section = formatN1Section(profile.n1Patterns);
|
package/src/routing/doc.d.ts
CHANGED
|
@@ -8,21 +8,20 @@ export { MIME_TYPES_MAP } from './common';
|
|
|
8
8
|
/**
|
|
9
9
|
* Find the package root directory
|
|
10
10
|
*/
|
|
11
|
-
export { findPackageRoot, getFrameworkPublicRoots, getPublicRoot } from './publicRoot';
|
|
11
|
+
export { findPackageRoot, findPackageRootAsync, getFrameworkPublicRoots, getFrameworkPublicRootsAsync, getPublicRoot, getPublicRootAsync, } from './publicRoot';
|
|
12
12
|
/**
|
|
13
13
|
* Set relaxed CSP headers for docs (allows external assets like Tailwind CDN, Google Fonts)
|
|
14
14
|
*/
|
|
15
15
|
export declare const setDocumentationCSPHeaders: (response: IResponse) => void;
|
|
16
16
|
/**
|
|
17
|
-
* Serve a documentation static file
|
|
18
|
-
* Returns true if file was served, false if not found
|
|
17
|
+
* Serve a documentation static file (async)
|
|
19
18
|
*/
|
|
20
|
-
export declare const
|
|
19
|
+
export declare const serveDocumentationFileAsync: (urlPath: string, response: IResponse) => Promise<boolean>;
|
|
21
20
|
export declare const registerDocRoutes: (router: IRouter) => void;
|
|
22
21
|
declare const _default: {
|
|
23
22
|
registerDocRoutes: (router: IRouter) => void;
|
|
24
23
|
setDocumentationCSPHeaders: (response: IResponse) => void;
|
|
25
|
-
|
|
24
|
+
serveDocumentationFileAsync: (urlPath: string, response: IResponse) => Promise<boolean>;
|
|
26
25
|
};
|
|
27
26
|
export default _default;
|
|
28
27
|
//# sourceMappingURL=doc.d.ts.map
|
package/src/routing/doc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../../src/routing/doc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAMhD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG/C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;GAEG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../../src/routing/doc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAMhD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG/C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,aAAa,EACb,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAiB7B;;GAEG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,SAAS,KAAG,IAWhE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,SAAS,MAAM,EACf,UAAU,SAAS,KAClB,OAAO,CAAC,OAAO,CAgDjB,CAAC;AASF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,IAMnD,CAAC;;gCANwC,OAAO,KAAG,IAAI;2CA5EH,SAAS,KAAG,IAAI;2CAiB1D,MAAM,YACL,SAAS,KAClB,OAAO,CAAC,OAAO,CAAC;;AAiEnB,wBAIE"}
|
package/src/routing/doc.js
CHANGED
|
@@ -8,19 +8,16 @@ import * as fs from '../node-singletons/fs.js';
|
|
|
8
8
|
import * as path from '../node-singletons/path.js';
|
|
9
9
|
import { MIME_TYPES_MAP, resolveSafePath, tryDecodeURIComponent } from './common.js';
|
|
10
10
|
import { ErrorRouting } from './error.js';
|
|
11
|
-
import {
|
|
11
|
+
import { getPublicRootAsync } from './publicRoot.js';
|
|
12
12
|
import { Router } from './Router.js';
|
|
13
13
|
export { MIME_TYPES_MAP } from './common.js';
|
|
14
14
|
/**
|
|
15
15
|
* Find the package root directory
|
|
16
16
|
*/
|
|
17
17
|
// Backward-compatible re-exports
|
|
18
|
-
export { findPackageRoot, getFrameworkPublicRoots, getPublicRoot } from './publicRoot.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*/
|
|
22
|
-
const mapStaticPath = (urlPath) => {
|
|
23
|
-
const publicRoot = getPublicRoot();
|
|
18
|
+
export { findPackageRoot, findPackageRootAsync, getFrameworkPublicRoots, getFrameworkPublicRootsAsync, getPublicRoot, getPublicRootAsync, } from './publicRoot.js';
|
|
19
|
+
const mapStaticPathAsync = async (urlPath) => {
|
|
20
|
+
const publicRoot = await getPublicRootAsync();
|
|
24
21
|
const normalize = (p) => (p.startsWith('/') ? p.slice(1) : p);
|
|
25
22
|
if (urlPath === '/doc' || urlPath === '/doc/')
|
|
26
23
|
return publicRoot;
|
|
@@ -45,28 +42,42 @@ export const setDocumentationCSPHeaders = (response) => {
|
|
|
45
42
|
"font-src 'self' data: https://fonts.gstatic.com;");
|
|
46
43
|
};
|
|
47
44
|
/**
|
|
48
|
-
* Serve a documentation static file
|
|
49
|
-
* Returns true if file was served, false if not found
|
|
45
|
+
* Serve a documentation static file (async)
|
|
50
46
|
*/
|
|
51
|
-
export const
|
|
52
|
-
let filePath =
|
|
47
|
+
export const serveDocumentationFileAsync = async (urlPath, response) => {
|
|
48
|
+
let filePath = await mapStaticPathAsync(urlPath);
|
|
53
49
|
if (filePath === undefined) {
|
|
54
50
|
return false;
|
|
55
51
|
}
|
|
56
52
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
try {
|
|
54
|
+
const stats = await fs.fsPromises.stat(filePath);
|
|
55
|
+
if (stats.isDirectory()) {
|
|
56
|
+
filePath = path.join(filePath, 'index.html');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// ignore
|
|
59
61
|
}
|
|
60
|
-
|
|
62
|
+
const exists = async (p) => {
|
|
63
|
+
try {
|
|
64
|
+
await fs.fsPromises.access(p);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
if (!(await exists(filePath)) && !path.extname(filePath)) {
|
|
61
72
|
const htmlPath = `${filePath}.html`;
|
|
62
|
-
if (
|
|
73
|
+
if (await exists(htmlPath)) {
|
|
63
74
|
filePath = htmlPath;
|
|
64
75
|
}
|
|
65
76
|
}
|
|
66
|
-
if (
|
|
77
|
+
if (await exists(filePath)) {
|
|
67
78
|
const ext = path.extname(filePath).toLowerCase();
|
|
68
79
|
const contentType = MIME_TYPES_MAP[ext] || 'application/octet-stream';
|
|
69
|
-
const content = fs.
|
|
80
|
+
const content = await fs.fsPromises.readFile(filePath);
|
|
70
81
|
response.setStatus(200);
|
|
71
82
|
response.setHeader('Content-Type', contentType);
|
|
72
83
|
response.send(content);
|
|
@@ -78,10 +89,10 @@ export const serveDocumentationFile = (urlPath, response) => {
|
|
|
78
89
|
}
|
|
79
90
|
return false;
|
|
80
91
|
};
|
|
81
|
-
const handleDocRequest = (req, res) => {
|
|
92
|
+
const handleDocRequest = async (req, res) => {
|
|
82
93
|
setDocumentationCSPHeaders(res);
|
|
83
94
|
const urlPath = req.getPath();
|
|
84
|
-
if (
|
|
95
|
+
if (await serveDocumentationFileAsync(urlPath, res))
|
|
85
96
|
return;
|
|
86
97
|
ErrorRouting.handleNotFound(req, res);
|
|
87
98
|
};
|
|
@@ -92,4 +103,8 @@ export const registerDocRoutes = (router) => {
|
|
|
92
103
|
// Greedy path match for nested assets like /doc/assets/app.js
|
|
93
104
|
Router.get(router, '/doc/:path*', handleDocRequest);
|
|
94
105
|
};
|
|
95
|
-
export default {
|
|
106
|
+
export default {
|
|
107
|
+
registerDocRoutes,
|
|
108
|
+
setDocumentationCSPHeaders,
|
|
109
|
+
serveDocumentationFileAsync,
|
|
110
|
+
};
|
|
@@ -6,13 +6,22 @@
|
|
|
6
6
|
* Find the package root directory.
|
|
7
7
|
*/
|
|
8
8
|
export declare const findPackageRoot: (startDir: string) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Find the package root directory (async).
|
|
11
|
+
*/
|
|
12
|
+
export declare const findPackageRootAsync: (startDir: string) => Promise<string>;
|
|
9
13
|
/**
|
|
10
14
|
* Framework public roots (dist/public preferred).
|
|
11
15
|
*/
|
|
12
16
|
export declare const getFrameworkPublicRoots: () => string[];
|
|
17
|
+
export declare const getFrameworkPublicRootsAsync: () => Promise<string[]>;
|
|
13
18
|
/**
|
|
14
19
|
* Resolve the effective public root.
|
|
15
20
|
* Prefers app-local `public/` when present; otherwise falls back to framework public roots.
|
|
16
21
|
*/
|
|
17
22
|
export declare const getPublicRoot: () => string;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the effective public root (async).
|
|
25
|
+
*/
|
|
26
|
+
export declare const getPublicRootAsync: () => Promise<string>;
|
|
18
27
|
//# sourceMappingURL=publicRoot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publicRoot.d.ts","sourceRoot":"","sources":["../../../src/routing/publicRoot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,KAAG,
|
|
1
|
+
{"version":3,"file":"publicRoot.d.ts","sourceRoot":"","sources":["../../../src/routing/publicRoot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,KAAG,MAalD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAkB3E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,QAAO,MAAM,EAIhD,CAAC;AAEF,eAAO,MAAM,4BAA4B,QAAa,OAAO,CAAC,MAAM,EAAE,CAIrE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,QAAO,MAgBhC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,MAAM,CA+BzD,CAAC"}
|
|
@@ -11,8 +11,13 @@ import * as path from '../node-singletons/path.js';
|
|
|
11
11
|
export const findPackageRoot = (startDir) => {
|
|
12
12
|
let current = startDir;
|
|
13
13
|
for (let i = 0; i < 10; i++) {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
try {
|
|
15
|
+
if (fs.existsSync(path.join(current, 'package.json')))
|
|
16
|
+
return current;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// ignore access errors
|
|
20
|
+
}
|
|
16
21
|
const parent = path.dirname(current);
|
|
17
22
|
if (parent === current)
|
|
18
23
|
break;
|
|
@@ -20,6 +25,27 @@ export const findPackageRoot = (startDir) => {
|
|
|
20
25
|
}
|
|
21
26
|
return startDir;
|
|
22
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Find the package root directory (async).
|
|
30
|
+
*/
|
|
31
|
+
export const findPackageRootAsync = async (startDir) => {
|
|
32
|
+
const findUp = async (current, depth) => {
|
|
33
|
+
if (depth >= 10)
|
|
34
|
+
return startDir;
|
|
35
|
+
try {
|
|
36
|
+
await fs.fsPromises.access(path.join(current, 'package.json'));
|
|
37
|
+
return current;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// ignore
|
|
41
|
+
}
|
|
42
|
+
const parent = path.dirname(current);
|
|
43
|
+
if (parent === current)
|
|
44
|
+
return startDir;
|
|
45
|
+
return findUp(parent, depth + 1);
|
|
46
|
+
};
|
|
47
|
+
return findUp(startDir, 0);
|
|
48
|
+
};
|
|
23
49
|
/**
|
|
24
50
|
* Framework public roots (dist/public preferred).
|
|
25
51
|
*/
|
|
@@ -28,6 +54,11 @@ export const getFrameworkPublicRoots = () => {
|
|
|
28
54
|
const packageRoot = findPackageRoot(thisDir);
|
|
29
55
|
return [path.join(packageRoot, 'dist/public'), path.join(packageRoot, 'public')];
|
|
30
56
|
};
|
|
57
|
+
export const getFrameworkPublicRootsAsync = async () => {
|
|
58
|
+
const thisDir = esmDirname(import.meta.url);
|
|
59
|
+
const packageRoot = await findPackageRootAsync(thisDir);
|
|
60
|
+
return [path.join(packageRoot, 'dist/public'), path.join(packageRoot, 'public')];
|
|
61
|
+
};
|
|
31
62
|
/**
|
|
32
63
|
* Resolve the effective public root.
|
|
33
64
|
* Prefers app-local `public/` when present; otherwise falls back to framework public roots.
|
|
@@ -47,3 +78,33 @@ export const getPublicRoot = () => {
|
|
|
47
78
|
}
|
|
48
79
|
return candidates[0];
|
|
49
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the effective public root (async).
|
|
83
|
+
*/
|
|
84
|
+
export const getPublicRootAsync = async () => {
|
|
85
|
+
const appRoots = [path.join(process.cwd(), 'public')];
|
|
86
|
+
const fwRoots = await getFrameworkPublicRootsAsync();
|
|
87
|
+
const candidates = [...appRoots, ...fwRoots];
|
|
88
|
+
const exists = async (p) => {
|
|
89
|
+
try {
|
|
90
|
+
await fs.fsPromises.access(p);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const hasIndex = async (root) => exists(path.join(root, 'index.html'));
|
|
98
|
+
const checks = await Promise.all(candidates.map(async (candidate) => {
|
|
99
|
+
const rootExists = await exists(candidate);
|
|
100
|
+
const indexExists = rootExists ? await hasIndex(candidate) : false;
|
|
101
|
+
return { candidate, rootExists, indexExists };
|
|
102
|
+
}));
|
|
103
|
+
const withIndex = checks.find((c) => c.indexExists);
|
|
104
|
+
if (withIndex)
|
|
105
|
+
return withIndex.candidate;
|
|
106
|
+
const firstExisting = checks.find((c) => c.rootExists);
|
|
107
|
+
if (firstExisting)
|
|
108
|
+
return firstExisting.candidate;
|
|
109
|
+
return candidates[0];
|
|
110
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const StartupConfigFile: {
|
|
2
|
+
readonly Broadcast: "config/broadcast.ts";
|
|
3
|
+
readonly Cache: "config/cache.ts";
|
|
4
|
+
readonly Database: "config/database.ts";
|
|
5
|
+
readonly Mail: "config/mail.ts";
|
|
6
|
+
readonly Middleware: "config/middleware.ts";
|
|
7
|
+
readonly Notification: "config/notification.ts";
|
|
8
|
+
readonly Queue: "config/queue.ts";
|
|
9
|
+
readonly Storage: "config/storage.ts";
|
|
10
|
+
};
|
|
11
|
+
export type StartupConfigFileTyps = typeof StartupConfigFile.Broadcast | typeof StartupConfigFile.Cache | typeof StartupConfigFile.Database | typeof StartupConfigFile.Mail | typeof StartupConfigFile.Middleware | typeof StartupConfigFile.Notification | typeof StartupConfigFile.Queue | typeof StartupConfigFile.Storage;
|
|
12
|
+
export declare const StartupConfigFileRegistry: Readonly<{
|
|
13
|
+
preload(files: readonly StartupConfigFileTyps[]): Promise<void>;
|
|
14
|
+
isPreloaded(): boolean;
|
|
15
|
+
get<T>(file: StartupConfigFileTyps): T | undefined;
|
|
16
|
+
has(file: StartupConfigFileTyps): boolean;
|
|
17
|
+
/** Intended for tests only. */
|
|
18
|
+
clear(): void;
|
|
19
|
+
}>;
|
|
20
|
+
export default StartupConfigFileRegistry;
|
|
21
|
+
//# sourceMappingURL=StartupConfigFileRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StartupConfigFileRegistry.d.ts","sourceRoot":"","sources":["../../../src/runtime/StartupConfigFileRegistry.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB;;;;;;;;;CASpB,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAC7B,OAAO,iBAAiB,CAAC,SAAS,GAClC,OAAO,iBAAiB,CAAC,KAAK,GAC9B,OAAO,iBAAiB,CAAC,QAAQ,GACjC,OAAO,iBAAiB,CAAC,IAAI,GAC7B,OAAO,iBAAiB,CAAC,UAAU,GACnC,OAAO,iBAAiB,CAAC,YAAY,GACrC,OAAO,iBAAiB,CAAC,KAAK,GAC9B,OAAO,iBAAiB,CAAC,OAAO,CAAC;AAKrC,eAAO,MAAM,yBAAyB;mBACf,SAAS,qBAAqB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;mBAetD,OAAO;QAIlB,CAAC,QAAQ,qBAAqB,GAAG,CAAC,GAAG,SAAS;cAIxC,qBAAqB,GAAG,OAAO;IAIzC,+BAA+B;aACtB,IAAI;EAIb,CAAC;AAEH,eAAe,yBAAyB,CAAC"}
|