@platformatic/runtime 3.32.0-alpha.0 → 3.32.0-alpha.1

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/config.d.ts CHANGED
@@ -42,7 +42,6 @@ export type PlatformaticRuntimeConfig = {
42
42
  maxHeapTotal?: number | string;
43
43
  maxYoungGeneration?: number | string;
44
44
  codeRangeSize?: number | string;
45
- noHeapCheck?: boolean | string;
46
45
  };
47
46
  dependencies?: string[];
48
47
  arguments?: string[];
@@ -79,6 +78,18 @@ export type PlatformaticRuntimeConfig = {
79
78
  )[];
80
79
  [k: string]: unknown;
81
80
  };
81
+ compileCache?:
82
+ | boolean
83
+ | {
84
+ /**
85
+ * Enable Node.js module compile cache for faster startup
86
+ */
87
+ enabled?: boolean;
88
+ /**
89
+ * Directory to store compile cache. Defaults to .plt/compile-cache in app root
90
+ */
91
+ directory?: string;
92
+ };
82
93
  };
83
94
  };
84
95
  };
@@ -109,7 +120,7 @@ export type PlatformaticRuntimeConfig = {
109
120
  };
110
121
  workersRestartDelay?: number | string;
111
122
  logger?: {
112
- level: (
123
+ level?: (
113
124
  | ("fatal" | "error" | "warn" | "info" | "debug" | "trace" | "silent")
114
125
  | {
115
126
  [k: string]: unknown;
@@ -213,7 +224,6 @@ export type PlatformaticRuntimeConfig = {
213
224
  maxHeapTotal?: number | string;
214
225
  maxYoungGeneration?: number | string;
215
226
  codeRangeSize?: number | string;
216
- noHeapCheck?: boolean | string;
217
227
  };
218
228
  undici?: {
219
229
  agentOptions?: {
@@ -265,6 +275,18 @@ export type PlatformaticRuntimeConfig = {
265
275
  maxSize?: number;
266
276
  maxEntrySize?: number;
267
277
  maxCount?: number;
278
+ /**
279
+ * Whitelist of origins to cache. Supports exact strings and regex patterns (e.g., "/https:\\/\\/.*\\.example\\.com/").
280
+ */
281
+ origins?: string[];
282
+ /**
283
+ * Default cache duration in seconds for responses without explicit expiration headers.
284
+ */
285
+ cacheByDefault?: number;
286
+ /**
287
+ * Cache type. "shared" caches may be shared between users, "private" caches are user-specific.
288
+ */
289
+ type?: "shared" | "private";
268
290
  [k: string]: unknown;
269
291
  };
270
292
  watch?: boolean | string;
@@ -503,4 +525,16 @@ export type PlatformaticRuntimeConfig = {
503
525
  [k: string]: string | [string, ...string[]];
504
526
  };
505
527
  };
528
+ compileCache?:
529
+ | boolean
530
+ | {
531
+ /**
532
+ * Enable Node.js module compile cache for faster startup
533
+ */
534
+ enabled?: boolean;
535
+ /**
536
+ * Directory to store compile cache. Defaults to .plt/compile-cache in app root
537
+ */
538
+ directory?: string;
539
+ };
506
540
  };
package/lib/runtime.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  parseMemorySize
11
11
  } from '@platformatic/foundation'
12
12
  import { ITC } from '@platformatic/itc'
13
+ import { client as metricsClient, collectProcessMetrics } from '@platformatic/metrics'
13
14
  import fastify from 'fastify'
14
15
  import { EventEmitter, once } from 'node:events'
15
16
  import { existsSync } from 'node:fs'
@@ -71,6 +72,21 @@ const kInspectorOptions = Symbol('plt.runtime.worker.inspectorOptions')
71
72
 
72
73
  const MAX_LISTENERS_COUNT = 100
73
74
 
75
+ function parseOrigins (origins) {
76
+ if (!origins) return undefined
77
+
78
+ return origins.map(origin => {
79
+ // Check if the origin is a regex pattern (starts and ends with /)
80
+ if (origin.startsWith('/') && origin.lastIndexOf('/') > 0) {
81
+ const lastSlash = origin.lastIndexOf('/')
82
+ const pattern = origin.slice(1, lastSlash)
83
+ const flags = origin.slice(lastSlash + 1)
84
+ return new RegExp(pattern, flags)
85
+ }
86
+ return origin
87
+ })
88
+ }
89
+
74
90
  const MAX_CONCURRENCY = 5
75
91
  const MAX_BOOTSTRAP_ATTEMPTS = 5
76
92
  const IMMEDIATE_RESTART_MAX_THRESHOLD = 10
@@ -119,6 +135,8 @@ export class Runtime extends EventEmitter {
119
135
 
120
136
  #channelCreationHook
121
137
 
138
+ #processMetricsRegistry
139
+
122
140
  constructor (config, context) {
123
141
  super()
124
142
  this.setMaxListeners(MAX_LISTENERS_COUNT)
@@ -197,6 +215,14 @@ export class Runtime extends EventEmitter {
197
215
  this.#metricsLabelName = 'applicationId'
198
216
  }
199
217
 
218
+ // Initialize process-level metrics registry in the main thread if metrics or management API is enabled
219
+ // These metrics are the same across all workers and only need to be collected once
220
+ // We need this for management API as it can request metrics even without explicit metrics config
221
+ if (config.metrics || config.managementApi) {
222
+ this.#processMetricsRegistry = new metricsClient.Registry()
223
+ collectProcessMetrics(this.#processMetricsRegistry)
224
+ }
225
+
200
226
  // Create the logger
201
227
  const [logger, destination, context] = await createLogger(config)
202
228
  this.logger = logger
@@ -346,6 +372,12 @@ export class Runtime extends EventEmitter {
346
372
  await this.#prometheusServer.close()
347
373
  }
348
374
 
375
+ // Clean up process metrics registry
376
+ if (this.#processMetricsRegistry) {
377
+ this.#processMetricsRegistry.clear()
378
+ this.#processMetricsRegistry = null
379
+ }
380
+
349
381
  if (this.#sharedHttpCache?.close) {
350
382
  await this.#sharedHttpCache.close()
351
383
  }
@@ -1059,6 +1091,12 @@ export class Runtime extends EventEmitter {
1059
1091
  async getMetrics (format = 'json') {
1060
1092
  let metrics = null
1061
1093
 
1094
+ // Get process-level metrics once from main thread registry (if available)
1095
+ let processMetricsJson = null
1096
+ if (this.#processMetricsRegistry) {
1097
+ processMetricsJson = await this.#processMetricsRegistry.getMetricsAsJSON()
1098
+ }
1099
+
1062
1100
  for (const worker of this.#workers.values()) {
1063
1101
  try {
1064
1102
  // The application might be temporarily unavailable
@@ -1066,6 +1104,7 @@ export class Runtime extends EventEmitter {
1066
1104
  continue
1067
1105
  }
1068
1106
 
1107
+ // Get thread-specific metrics from worker
1069
1108
  const applicationMetrics = await executeWithTimeout(
1070
1109
  sendViaITC(worker, 'getMetrics', format),
1071
1110
  this.#config.metrics?.timeout ?? 10000
@@ -1076,9 +1115,30 @@ export class Runtime extends EventEmitter {
1076
1115
  metrics = format === 'json' ? [] : ''
1077
1116
  }
1078
1117
 
1118
+ // Build worker labels including custom labels from metrics config
1119
+ const workerLabels = {
1120
+ ...this.#config.metrics?.labels,
1121
+ [this.#metricsLabelName]: worker[kApplicationId]
1122
+ }
1123
+ const workerId = worker[kWorkerId]
1124
+ if (workerId >= 0) {
1125
+ workerLabels.workerId = workerId
1126
+ }
1127
+
1079
1128
  if (format === 'json') {
1080
- metrics.push(...applicationMetrics)
1129
+ // Duplicate process metrics with worker labels and add to output
1130
+ if (processMetricsJson) {
1131
+ this.#applyLabelsToMetrics(processMetricsJson, workerLabels, metrics)
1132
+ }
1133
+ // Add worker's thread-specific metrics
1134
+ for (let i = 0; i < applicationMetrics.length; i++) {
1135
+ metrics.push(applicationMetrics[i])
1136
+ }
1081
1137
  } else {
1138
+ // Text format: format process metrics with worker labels
1139
+ if (processMetricsJson) {
1140
+ metrics += this.#formatProcessMetricsText(processMetricsJson, workerLabels)
1141
+ }
1082
1142
  metrics += applicationMetrics
1083
1143
  }
1084
1144
  }
@@ -1099,6 +1159,65 @@ export class Runtime extends EventEmitter {
1099
1159
  return { metrics }
1100
1160
  }
1101
1161
 
1162
+ // Apply labels to process metrics and push to output array (for JSON format)
1163
+ #applyLabelsToMetrics (processMetrics, labels, outputArray) {
1164
+ for (let i = 0; i < processMetrics.length; i++) {
1165
+ const metric = processMetrics[i]
1166
+ const newValues = []
1167
+ const values = metric.values
1168
+ for (let j = 0; j < values.length; j++) {
1169
+ const v = values[j]
1170
+ newValues.push({
1171
+ value: v.value,
1172
+ labels: { ...labels, ...v.labels },
1173
+ metricName: v.metricName
1174
+ })
1175
+ }
1176
+ outputArray.push({
1177
+ name: metric.name,
1178
+ help: metric.help,
1179
+ type: metric.type,
1180
+ aggregator: metric.aggregator,
1181
+ values: newValues
1182
+ })
1183
+ }
1184
+ }
1185
+
1186
+ // Format process metrics as Prometheus text format with labels
1187
+ #formatProcessMetricsText (processMetricsJson, labels) {
1188
+ let output = ''
1189
+
1190
+ for (let i = 0; i < processMetricsJson.length; i++) {
1191
+ const metric = processMetricsJson[i]
1192
+ const name = metric.name
1193
+ const help = metric.help
1194
+ const type = metric.type
1195
+
1196
+ // Add HELP and TYPE lines
1197
+ output += `# HELP ${name} ${help}\n`
1198
+ output += `# TYPE ${name} ${type}\n`
1199
+
1200
+ const values = metric.values
1201
+ for (let j = 0; j < values.length; j++) {
1202
+ const v = values[j]
1203
+ const combinedLabels = { ...labels, ...v.labels }
1204
+ const labelParts = []
1205
+
1206
+ for (const [key, val] of Object.entries(combinedLabels)) {
1207
+ // Escape label values for Prometheus format
1208
+ const escapedVal = String(val).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')
1209
+ labelParts.push(`${key}="${escapedVal}"`)
1210
+ }
1211
+
1212
+ const labelStr = labelParts.length > 0 ? `{${labelParts.join(',')}}` : ''
1213
+ const metricName = v.metricName || name
1214
+ output += `${metricName}${labelStr} ${v.value}\n`
1215
+ }
1216
+ }
1217
+
1218
+ return output
1219
+ }
1220
+
1102
1221
  async getFormattedMetrics () {
1103
1222
  try {
1104
1223
  const { metrics } = await this.getMetrics()
@@ -1349,7 +1468,7 @@ export class Runtime extends EventEmitter {
1349
1468
  elu = worker.performance.eventLoopUtilization(elu, previousELU)
1350
1469
  }
1351
1470
 
1352
- if (!features.node.worker.getHeapStatistics || options.noHeapCheck) {
1471
+ if (!features.node.worker.getHeapStatistics) {
1353
1472
  return { elu: elu.utilization, currentELU }
1354
1473
  }
1355
1474
 
@@ -1396,7 +1515,10 @@ export class Runtime extends EventEmitter {
1396
1515
  interceptors.push(
1397
1516
  undiciInterceptors.cache({
1398
1517
  store: this.#sharedHttpCache,
1399
- methods: config.httpCache.methods ?? ['GET', 'HEAD']
1518
+ methods: config.httpCache.methods ?? ['GET', 'HEAD'],
1519
+ origins: parseOrigins(config.httpCache.origins),
1520
+ cacheByDefault: config.httpCache.cacheByDefault,
1521
+ type: config.httpCache.type
1400
1522
  })
1401
1523
  )
1402
1524
  }
@@ -1564,7 +1686,8 @@ export class Runtime extends EventEmitter {
1564
1686
  codeRangeSizeMb
1565
1687
  },
1566
1688
  stdout: true,
1567
- stderr: true
1689
+ stderr: true,
1690
+ name: workerId
1568
1691
  })
1569
1692
 
1570
1693
  this.#handleWorkerStandardStreams(worker, applicationId, index)
@@ -1753,8 +1876,7 @@ export class Runtime extends EventEmitter {
1753
1876
  let health = null
1754
1877
  try {
1755
1878
  health = await this.getWorkerHealth(worker, {
1756
- previousELU: worker[kLastHealthCheckELU],
1757
- noHeapCheck: worker[kConfig]?.health?.noHeapCheck
1879
+ previousELU: worker[kLastHealthCheckELU]
1758
1880
  })
1759
1881
  } catch (err) {
1760
1882
  this.logger.error({ err }, `Failed to get health for ${errorLabel}.`)
@@ -1798,7 +1920,7 @@ export class Runtime extends EventEmitter {
1798
1920
 
1799
1921
  const healthConfig = worker[kConfig].health
1800
1922
 
1801
- let { maxELU, maxHeapUsed, maxHeapTotal, maxUnhealthyChecks, interval, noHeapCheck } = worker[kConfig].health
1923
+ let { maxELU, maxHeapUsed, maxHeapTotal, maxUnhealthyChecks, interval } = worker[kConfig].health
1802
1924
 
1803
1925
  if (typeof maxHeapTotal === 'string') {
1804
1926
  maxHeapTotal = parseMemorySize(maxHeapTotal)
@@ -1829,8 +1951,8 @@ export class Runtime extends EventEmitter {
1829
1951
 
1830
1952
  if (lastHealthMetrics) {
1831
1953
  const health = lastHealthMetrics.currentHealth
1832
- const memoryUsage = noHeapCheck ? 0 : health.heapUsed / maxHeapTotal
1833
- const unhealthy = health.elu > maxELU || (!noHeapCheck && memoryUsage > maxHeapUsed)
1954
+ const memoryUsage = health.heapUsed / maxHeapTotal
1955
+ const unhealthy = health.elu > maxELU || memoryUsage > maxHeapUsed
1834
1956
 
1835
1957
  this.emit('application:worker:health', {
1836
1958
  id: worker[kId],
@@ -1848,7 +1970,7 @@ export class Runtime extends EventEmitter {
1848
1970
  )
1849
1971
  }
1850
1972
 
1851
- if (!noHeapCheck && memoryUsage > maxHeapUsed) {
1973
+ if (memoryUsage > maxHeapUsed) {
1852
1974
  this.logger.error(
1853
1975
  `The ${errorLabel} is using ${(memoryUsage * 100).toFixed(2)} % of the memory, ` +
1854
1976
  `above the maximum allowed usage of ${(maxHeapUsed * 100).toFixed(2)} %.`
@@ -178,7 +178,23 @@ function createThreadInterceptor (runtimeConfig) {
178
178
  return threadDispatcher
179
179
  }
180
180
 
181
+ function parseOrigins (origins) {
182
+ if (!origins) return undefined
183
+
184
+ return origins.map(origin => {
185
+ // Check if the origin is a regex pattern (starts and ends with /)
186
+ if (origin.startsWith('/') && origin.lastIndexOf('/') > 0) {
187
+ const lastSlash = origin.lastIndexOf('/')
188
+ const pattern = origin.slice(1, lastSlash)
189
+ const flags = origin.slice(lastSlash + 1)
190
+ return new RegExp(pattern, flags)
191
+ }
192
+ return origin
193
+ })
194
+ }
195
+
181
196
  function createHttpCacheInterceptor (runtimeConfig) {
197
+ const httpCache = runtimeConfig.httpCache
182
198
  const cacheInterceptor = httpCacheInterceptor({
183
199
  store: new RemoteCacheStore({
184
200
  onRequest: opts => {
@@ -192,7 +208,10 @@ function createHttpCacheInterceptor (runtimeConfig) {
192
208
  },
193
209
  logger: globalThis.platformatic.logger
194
210
  }),
195
- methods: runtimeConfig.httpCache.methods ?? ['GET', 'HEAD'],
211
+ methods: httpCache.methods ?? ['GET', 'HEAD'],
212
+ origins: parseOrigins(httpCache.origins),
213
+ cacheByDefault: httpCache.cacheByDefault,
214
+ type: httpCache.type,
196
215
  logger: globalThis.platformatic.logger
197
216
  })
198
217
  return cacheInterceptor
@@ -9,7 +9,7 @@ import { EventEmitter } from 'node:events'
9
9
  import { ServerResponse } from 'node:http'
10
10
  import inspector from 'node:inspector'
11
11
  import { hostname } from 'node:os'
12
- import { resolve } from 'node:path'
12
+ import { join, resolve } from 'node:path'
13
13
  import { pathToFileURL } from 'node:url'
14
14
  import { threadId, workerData } from 'node:worker_threads'
15
15
  import pino from 'pino'
@@ -85,6 +85,57 @@ async function performPreloading (...sources) {
85
85
  }
86
86
  }
87
87
 
88
+ // Enable compile cache if configured (Node.js 22.1.0+)
89
+ async function setupCompileCache (runtimeConfig, applicationConfig, logger) {
90
+ // Normalize boolean shorthand: true -> { enabled: true }
91
+ const normalizeConfig = cfg => {
92
+ if (cfg === true) return { enabled: true }
93
+ if (cfg === false) return { enabled: false }
94
+ return cfg
95
+ }
96
+
97
+ // Merge runtime and app-level config (app overrides runtime)
98
+ const runtimeCache = normalizeConfig(runtimeConfig.compileCache)
99
+ const appCache = normalizeConfig(applicationConfig.compileCache)
100
+ const config = { ...runtimeCache, ...appCache }
101
+
102
+ if (!config.enabled) {
103
+ return
104
+ }
105
+
106
+ // Check if API is available (Node.js 22.1.0+)
107
+ let moduleApi
108
+ try {
109
+ moduleApi = await import('node:module')
110
+ if (typeof moduleApi.enableCompileCache !== 'function') {
111
+ return
112
+ }
113
+ } catch {
114
+ return
115
+ }
116
+
117
+ // Determine cache directory - use applicationConfig.path for the app root
118
+ const cacheDir = config.directory ?? join(applicationConfig.path, '.plt', 'compile-cache')
119
+
120
+ try {
121
+ const result = moduleApi.enableCompileCache(cacheDir)
122
+
123
+ const { compileCacheStatus } = moduleApi.constants ?? {}
124
+
125
+ if (result.status === compileCacheStatus?.ENABLED) {
126
+ logger.debug({ directory: result.directory }, 'Module compile cache enabled')
127
+ } else if (result.status === compileCacheStatus?.ALREADY_ENABLED) {
128
+ logger.debug({ directory: result.directory }, 'Module compile cache already enabled')
129
+ } else if (result.status === compileCacheStatus?.FAILED) {
130
+ logger.warn({ message: result.message }, 'Failed to enable module compile cache')
131
+ } else if (result.status === compileCacheStatus?.DISABLED) {
132
+ logger.debug('Module compile cache disabled via NODE_DISABLE_COMPILE_CACHE')
133
+ }
134
+ } catch (err) {
135
+ logger.warn({ err }, 'Error enabling module compile cache')
136
+ }
137
+ }
138
+
88
139
  async function main () {
89
140
  globalThis.fetch = fetch
90
141
  globalThis[kId] = threadId
@@ -94,10 +145,12 @@ async function main () {
94
145
  })
95
146
 
96
147
  const runtimeConfig = workerData.config
148
+ const applicationConfig = workerData.applicationConfig
97
149
 
98
- await performPreloading(runtimeConfig, workerData.applicationConfig)
150
+ // Enable compile cache early before loading user modules
151
+ await setupCompileCache(runtimeConfig, applicationConfig, globalThis.platformatic.logger)
99
152
 
100
- const applicationConfig = workerData.applicationConfig
153
+ await performPreloading(runtimeConfig, applicationConfig)
101
154
 
102
155
  // Load env file and mixin env vars from application config
103
156
  let envfile
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "3.32.0-alpha.0",
3
+ "version": "3.32.0-alpha.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -35,14 +35,14 @@
35
35
  "typescript": "^5.5.4",
36
36
  "undici-oidc-interceptor": "^0.5.0",
37
37
  "why-is-node-running": "^2.2.2",
38
- "@platformatic/composer": "3.32.0-alpha.0",
39
- "@platformatic/db": "3.32.0-alpha.0",
40
- "@platformatic/node": "3.32.0-alpha.0",
41
- "@platformatic/service": "3.32.0-alpha.0",
42
- "@platformatic/gateway": "3.32.0-alpha.0",
43
- "@platformatic/sql-graphql": "3.32.0-alpha.0",
44
- "@platformatic/wattpm-pprof-capture": "3.32.0-alpha.0",
45
- "@platformatic/sql-mapper": "3.32.0-alpha.0"
38
+ "@platformatic/composer": "3.32.0-alpha.1",
39
+ "@platformatic/db": "3.32.0-alpha.1",
40
+ "@platformatic/gateway": "3.32.0-alpha.1",
41
+ "@platformatic/node": "3.32.0-alpha.1",
42
+ "@platformatic/service": "3.32.0-alpha.1",
43
+ "@platformatic/sql-graphql": "3.32.0-alpha.1",
44
+ "@platformatic/sql-mapper": "3.32.0-alpha.1",
45
+ "@platformatic/wattpm-pprof-capture": "3.32.0-alpha.1"
46
46
  },
47
47
  "dependencies": {
48
48
  "@fastify/accepts": "^5.0.0",
@@ -71,12 +71,12 @@
71
71
  "undici": "^7.0.0",
72
72
  "undici-thread-interceptor": "^1.0.0",
73
73
  "ws": "^8.16.0",
74
- "@platformatic/basic": "3.32.0-alpha.0",
75
- "@platformatic/foundation": "3.32.0-alpha.0",
76
- "@platformatic/generators": "3.32.0-alpha.0",
77
- "@platformatic/itc": "3.32.0-alpha.0",
78
- "@platformatic/metrics": "3.32.0-alpha.0",
79
- "@platformatic/telemetry": "3.32.0-alpha.0"
74
+ "@platformatic/basic": "3.32.0-alpha.1",
75
+ "@platformatic/foundation": "3.32.0-alpha.1",
76
+ "@platformatic/generators": "3.32.0-alpha.1",
77
+ "@platformatic/itc": "3.32.0-alpha.1",
78
+ "@platformatic/metrics": "3.32.0-alpha.1",
79
+ "@platformatic/telemetry": "3.32.0-alpha.1"
80
80
  },
81
81
  "engines": {
82
82
  "node": ">=22.19.0"
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.32.0-alpha.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/3.32.0-alpha.1.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Runtime Config",
5
5
  "type": "object",
@@ -208,16 +208,6 @@
208
208
  "type": "string"
209
209
  }
210
210
  ]
211
- },
212
- "noHeapCheck": {
213
- "anyOf": [
214
- {
215
- "type": "boolean"
216
- },
217
- {
218
- "type": "string"
219
- }
220
- ]
221
211
  }
222
212
  },
223
213
  "additionalProperties": false
@@ -342,6 +332,28 @@
342
332
  }
343
333
  }
344
334
  }
335
+ },
336
+ "compileCache": {
337
+ "anyOf": [
338
+ {
339
+ "type": "boolean"
340
+ },
341
+ {
342
+ "type": "object",
343
+ "properties": {
344
+ "enabled": {
345
+ "type": "boolean",
346
+ "default": true,
347
+ "description": "Enable Node.js module compile cache for faster startup"
348
+ },
349
+ "directory": {
350
+ "type": "string",
351
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
352
+ }
353
+ },
354
+ "additionalProperties": false
355
+ }
356
+ ]
345
357
  }
346
358
  }
347
359
  }
@@ -532,16 +544,6 @@
532
544
  "type": "string"
533
545
  }
534
546
  ]
535
- },
536
- "noHeapCheck": {
537
- "anyOf": [
538
- {
539
- "type": "boolean"
540
- },
541
- {
542
- "type": "string"
543
- }
544
- ]
545
547
  }
546
548
  },
547
549
  "additionalProperties": false
@@ -666,6 +668,28 @@
666
668
  }
667
669
  }
668
670
  }
671
+ },
672
+ "compileCache": {
673
+ "anyOf": [
674
+ {
675
+ "type": "boolean"
676
+ },
677
+ {
678
+ "type": "object",
679
+ "properties": {
680
+ "enabled": {
681
+ "type": "boolean",
682
+ "default": true,
683
+ "description": "Enable Node.js module compile cache for faster startup"
684
+ },
685
+ "directory": {
686
+ "type": "string",
687
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
688
+ }
689
+ },
690
+ "additionalProperties": false
691
+ }
692
+ ]
669
693
  }
670
694
  }
671
695
  }
@@ -854,16 +878,6 @@
854
878
  "type": "string"
855
879
  }
856
880
  ]
857
- },
858
- "noHeapCheck": {
859
- "anyOf": [
860
- {
861
- "type": "boolean"
862
- },
863
- {
864
- "type": "string"
865
- }
866
- ]
867
881
  }
868
882
  },
869
883
  "additionalProperties": false
@@ -988,6 +1002,28 @@
988
1002
  }
989
1003
  }
990
1004
  }
1005
+ },
1006
+ "compileCache": {
1007
+ "anyOf": [
1008
+ {
1009
+ "type": "boolean"
1010
+ },
1011
+ {
1012
+ "type": "object",
1013
+ "properties": {
1014
+ "enabled": {
1015
+ "type": "boolean",
1016
+ "default": true,
1017
+ "description": "Enable Node.js module compile cache for faster startup"
1018
+ },
1019
+ "directory": {
1020
+ "type": "string",
1021
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
1022
+ }
1023
+ },
1024
+ "additionalProperties": false
1025
+ }
1026
+ ]
991
1027
  }
992
1028
  }
993
1029
  }
@@ -1176,16 +1212,6 @@
1176
1212
  "type": "string"
1177
1213
  }
1178
1214
  ]
1179
- },
1180
- "noHeapCheck": {
1181
- "anyOf": [
1182
- {
1183
- "type": "boolean"
1184
- },
1185
- {
1186
- "type": "string"
1187
- }
1188
- ]
1189
1215
  }
1190
1216
  },
1191
1217
  "additionalProperties": false
@@ -1310,6 +1336,28 @@
1310
1336
  }
1311
1337
  }
1312
1338
  }
1339
+ },
1340
+ "compileCache": {
1341
+ "anyOf": [
1342
+ {
1343
+ "type": "boolean"
1344
+ },
1345
+ {
1346
+ "type": "object",
1347
+ "properties": {
1348
+ "enabled": {
1349
+ "type": "boolean",
1350
+ "default": true,
1351
+ "description": "Enable Node.js module compile cache for faster startup"
1352
+ },
1353
+ "directory": {
1354
+ "type": "string",
1355
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
1356
+ }
1357
+ },
1358
+ "additionalProperties": false
1359
+ }
1360
+ ]
1313
1361
  }
1314
1362
  }
1315
1363
  }
@@ -1389,7 +1437,6 @@
1389
1437
  "properties": {
1390
1438
  "level": {
1391
1439
  "type": "string",
1392
- "default": "info",
1393
1440
  "oneOf": [
1394
1441
  {
1395
1442
  "enum": [
@@ -1536,9 +1583,6 @@
1536
1583
  "default": true
1537
1584
  }
1538
1585
  },
1539
- "required": [
1540
- "level"
1541
- ],
1542
1586
  "default": {},
1543
1587
  "additionalProperties": true
1544
1588
  },
@@ -1834,17 +1878,6 @@
1834
1878
  }
1835
1879
  ],
1836
1880
  "default": 268435456
1837
- },
1838
- "noHeapCheck": {
1839
- "anyOf": [
1840
- {
1841
- "type": "boolean"
1842
- },
1843
- {
1844
- "type": "string"
1845
- }
1846
- ],
1847
- "default": false
1848
1881
  }
1849
1882
  },
1850
1883
  "additionalProperties": false
@@ -1976,6 +2009,26 @@
1976
2009
  },
1977
2010
  "maxCount": {
1978
2011
  "type": "integer"
2012
+ },
2013
+ "origins": {
2014
+ "type": "array",
2015
+ "items": {
2016
+ "type": "string"
2017
+ },
2018
+ "description": "Whitelist of origins to cache. Supports exact strings and regex patterns (e.g., \"/https:\\\\/\\\\/.*\\\\.example\\\\.com/\")."
2019
+ },
2020
+ "cacheByDefault": {
2021
+ "type": "integer",
2022
+ "description": "Default cache duration in seconds for responses without explicit expiration headers."
2023
+ },
2024
+ "type": {
2025
+ "type": "string",
2026
+ "enum": [
2027
+ "shared",
2028
+ "private"
2029
+ ],
2030
+ "default": "shared",
2031
+ "description": "Cache type. \"shared\" caches may be shared between users, \"private\" caches are user-specific."
1979
2032
  }
1980
2033
  }
1981
2034
  }
@@ -2621,6 +2674,28 @@
2621
2674
  "deny"
2622
2675
  ],
2623
2676
  "additionalProperties": false
2677
+ },
2678
+ "compileCache": {
2679
+ "anyOf": [
2680
+ {
2681
+ "type": "boolean"
2682
+ },
2683
+ {
2684
+ "type": "object",
2685
+ "properties": {
2686
+ "enabled": {
2687
+ "type": "boolean",
2688
+ "default": true,
2689
+ "description": "Enable Node.js module compile cache for faster startup"
2690
+ },
2691
+ "directory": {
2692
+ "type": "string",
2693
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
2694
+ }
2695
+ },
2696
+ "additionalProperties": false
2697
+ }
2698
+ ]
2624
2699
  }
2625
2700
  },
2626
2701
  "anyOf": [