@platformatic/runtime 2.19.0-alpha.6 → 2.19.0-alpha.8
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 +12 -1
- package/lib/runtime.js +41 -3
- package/lib/schema.js +26 -0
- package/lib/shared-http-cache.js +39 -0
- package/lib/worker/http-cache.js +73 -0
- package/lib/worker/main.js +42 -11
- package/package.json +18 -16
- package/schema.json +30 -1
package/config.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and run json-schema-to-typescript to regenerate this file.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export type
|
|
8
|
+
export type HttpsSchemasPlatformaticDevPlatformaticRuntime2190Alpha8Json = {
|
|
9
9
|
[k: string]: unknown;
|
|
10
10
|
} & {
|
|
11
11
|
$schema?: string;
|
|
@@ -133,6 +133,17 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2190Alpha6Json = {
|
|
|
133
133
|
};
|
|
134
134
|
[k: string]: unknown;
|
|
135
135
|
};
|
|
136
|
+
httpCache?:
|
|
137
|
+
| boolean
|
|
138
|
+
| {
|
|
139
|
+
store?: string;
|
|
140
|
+
/**
|
|
141
|
+
* @minItems 1
|
|
142
|
+
*/
|
|
143
|
+
methods?: [string, ...string[]];
|
|
144
|
+
cacheTagsHeader?: string;
|
|
145
|
+
[k: string]: unknown;
|
|
146
|
+
};
|
|
136
147
|
watch?: boolean | string;
|
|
137
148
|
managementApi?:
|
|
138
149
|
| boolean
|
package/lib/runtime.js
CHANGED
|
@@ -16,6 +16,7 @@ const errors = require('./errors')
|
|
|
16
16
|
const { createLogger } = require('./logger')
|
|
17
17
|
const { startManagementApi } = require('./management-api')
|
|
18
18
|
const { startPrometheusServer } = require('./prom-server')
|
|
19
|
+
const { createSharedStore } = require('./shared-http-cache')
|
|
19
20
|
const { getRuntimeTmpDir } = require('./utils')
|
|
20
21
|
const { sendViaITC, waitEventFromITC } = require('./worker/itc')
|
|
21
22
|
const { RoundRobinMap } = require('./worker/round-robin-map.js')
|
|
@@ -66,6 +67,7 @@ class Runtime extends EventEmitter {
|
|
|
66
67
|
#inspectorServer
|
|
67
68
|
#workers
|
|
68
69
|
#restartingWorkers
|
|
70
|
+
#sharedHttpCache
|
|
69
71
|
|
|
70
72
|
constructor (configManager, runtimeLogsDir, env) {
|
|
71
73
|
super()
|
|
@@ -85,6 +87,7 @@ class Runtime extends EventEmitter {
|
|
|
85
87
|
})
|
|
86
88
|
this.#status = undefined
|
|
87
89
|
this.#restartingWorkers = new Map()
|
|
90
|
+
this.#sharedHttpCache = null
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
async init () {
|
|
@@ -139,6 +142,11 @@ class Runtime extends EventEmitter {
|
|
|
139
142
|
throw e
|
|
140
143
|
}
|
|
141
144
|
|
|
145
|
+
this.#sharedHttpCache = createSharedStore(
|
|
146
|
+
this.#configManager.dirname,
|
|
147
|
+
config.httpCache
|
|
148
|
+
)
|
|
149
|
+
|
|
142
150
|
this.#updateStatus('init')
|
|
143
151
|
}
|
|
144
152
|
|
|
@@ -267,6 +275,10 @@ class Runtime extends EventEmitter {
|
|
|
267
275
|
this.#loggerDestination = null
|
|
268
276
|
}
|
|
269
277
|
|
|
278
|
+
if (this.#sharedHttpCache?.close) {
|
|
279
|
+
await this.#sharedHttpCache.close()
|
|
280
|
+
}
|
|
281
|
+
|
|
270
282
|
this.#updateStatus('closed')
|
|
271
283
|
}
|
|
272
284
|
|
|
@@ -729,6 +741,23 @@ class Runtime extends EventEmitter {
|
|
|
729
741
|
return createReadStream(filePath)
|
|
730
742
|
}
|
|
731
743
|
|
|
744
|
+
async invalidateHttpCache (options = {}) {
|
|
745
|
+
const { keys, tags } = options
|
|
746
|
+
|
|
747
|
+
if (!this.#sharedHttpCache) return
|
|
748
|
+
|
|
749
|
+
const promises = []
|
|
750
|
+
if (keys && keys.length > 0) {
|
|
751
|
+
promises.push(this.#sharedHttpCache.deleteKeys(keys))
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (tags && tags.length > 0) {
|
|
755
|
+
promises.push(this.#sharedHttpCache.deleteTags(tags))
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
await Promise.all(promises)
|
|
759
|
+
}
|
|
760
|
+
|
|
732
761
|
async sendCommandToService (id, name, message) {
|
|
733
762
|
const service = await this.#getServiceById(id)
|
|
734
763
|
|
|
@@ -885,9 +914,18 @@ class Runtime extends EventEmitter {
|
|
|
885
914
|
port: worker,
|
|
886
915
|
handlers: {
|
|
887
916
|
getServiceMeta: this.getServiceMeta.bind(this),
|
|
888
|
-
listServices: () =>
|
|
889
|
-
|
|
890
|
-
|
|
917
|
+
listServices: () => this.#servicesIds,
|
|
918
|
+
getServices: this.getServices.bind(this),
|
|
919
|
+
getHttpCacheValue: opts => this.#sharedHttpCache.getValue(opts.request),
|
|
920
|
+
setHttpCacheValue: opts => this.#sharedHttpCache.setValue(
|
|
921
|
+
opts.request,
|
|
922
|
+
opts.response,
|
|
923
|
+
opts.payload
|
|
924
|
+
),
|
|
925
|
+
deleteHttpCacheValue: opts => this.#sharedHttpCache.delete(
|
|
926
|
+
opts.request
|
|
927
|
+
),
|
|
928
|
+
invalidateHttpCache: opts => this.invalidateHttpCache(opts),
|
|
891
929
|
}
|
|
892
930
|
})
|
|
893
931
|
worker[kITC].listen()
|
package/lib/schema.js
CHANGED
|
@@ -194,6 +194,32 @@ const platformaticRuntimeSchema = {
|
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
},
|
|
197
|
+
httpCache: {
|
|
198
|
+
oneOf: [
|
|
199
|
+
{
|
|
200
|
+
type: 'boolean'
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
type: 'object',
|
|
204
|
+
properties: {
|
|
205
|
+
store: {
|
|
206
|
+
type: 'string'
|
|
207
|
+
},
|
|
208
|
+
methods: {
|
|
209
|
+
type: 'array',
|
|
210
|
+
items: {
|
|
211
|
+
type: 'string'
|
|
212
|
+
},
|
|
213
|
+
default: ['GET', 'HEAD'],
|
|
214
|
+
minItems: 1
|
|
215
|
+
},
|
|
216
|
+
cacheTagsHeader: {
|
|
217
|
+
type: 'string'
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
},
|
|
197
223
|
watch: {
|
|
198
224
|
anyOf: [
|
|
199
225
|
{
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { join } = require('node:path')
|
|
4
|
+
const { createRequire } = require('node:module')
|
|
5
|
+
const MemoryCacheStore = require('@platformatic/undici-cache-memory')
|
|
6
|
+
|
|
7
|
+
function createSharedStore (projectDir, httpCacheConfig = {}) {
|
|
8
|
+
const runtimeRequire = createRequire(join(projectDir, 'file'))
|
|
9
|
+
|
|
10
|
+
const { store, ...storeConfig } = httpCacheConfig
|
|
11
|
+
const CacheStore = store ? runtimeRequire(store) : MemoryCacheStore
|
|
12
|
+
|
|
13
|
+
class SharedCacheStore extends CacheStore {
|
|
14
|
+
async getValue (req) {
|
|
15
|
+
const cachedValue = await this.get(req)
|
|
16
|
+
if (!cachedValue) return null
|
|
17
|
+
|
|
18
|
+
const { body, ...response } = cachedValue
|
|
19
|
+
|
|
20
|
+
let payload = ''
|
|
21
|
+
for await (const chunk of body) {
|
|
22
|
+
payload += chunk
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { response, payload }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setValue (req, opts, data) {
|
|
29
|
+
const writeStream = this.createWriteStream(req, opts)
|
|
30
|
+
writeStream.write(data)
|
|
31
|
+
writeStream.end()
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new SharedCacheStore(storeConfig)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { createSharedStore }
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Readable, Writable } = require('node:stream')
|
|
4
|
+
const { kITC } = require('./symbols')
|
|
5
|
+
|
|
6
|
+
class RemoteCacheStore {
|
|
7
|
+
async get (request) {
|
|
8
|
+
const itc = globalThis[kITC]
|
|
9
|
+
if (!itc) return
|
|
10
|
+
|
|
11
|
+
const cachedValue = await itc.send('getHttpCacheValue', {
|
|
12
|
+
request: this.#sanitizeRequest(request)
|
|
13
|
+
})
|
|
14
|
+
if (!cachedValue) return
|
|
15
|
+
|
|
16
|
+
const readable = new Readable({
|
|
17
|
+
read () {}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
Object.defineProperty(readable, 'value', {
|
|
21
|
+
get () { return cachedValue.response }
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
readable.push(cachedValue.payload)
|
|
25
|
+
readable.push(null)
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
...cachedValue.response,
|
|
29
|
+
body: readable
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
createWriteStream (request, response) {
|
|
34
|
+
const itc = globalThis[kITC]
|
|
35
|
+
if (!itc) throw new Error('Cannot write to cache without an ITC instance')
|
|
36
|
+
|
|
37
|
+
let payload = ''
|
|
38
|
+
|
|
39
|
+
request = this.#sanitizeRequest(request)
|
|
40
|
+
|
|
41
|
+
return new Writable({
|
|
42
|
+
write (chunk, encoding, callback) {
|
|
43
|
+
payload += chunk
|
|
44
|
+
callback()
|
|
45
|
+
},
|
|
46
|
+
final (callback) {
|
|
47
|
+
itc.send('setHttpCacheValue', { request, response, payload })
|
|
48
|
+
.then(() => callback())
|
|
49
|
+
.catch((err) => callback(err))
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
delete (request) {
|
|
55
|
+
const itc = globalThis[kITC]
|
|
56
|
+
if (!itc) throw new Error('Cannot delete from cache without an ITC instance')
|
|
57
|
+
|
|
58
|
+
request = this.#sanitizeRequest(request)
|
|
59
|
+
itc.send('deleteHttpCacheValue', { request })
|
|
60
|
+
// TODO: return a Promise
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#sanitizeRequest (request) {
|
|
64
|
+
return {
|
|
65
|
+
origin: request.origin,
|
|
66
|
+
method: request.method,
|
|
67
|
+
path: request.path,
|
|
68
|
+
headers: request.headers
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = RemoteCacheStore
|
package/lib/worker/main.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { EventEmitter } = require('node:events')
|
|
4
3
|
const { createRequire } = require('node:module')
|
|
5
4
|
const { hostname } = require('node:os')
|
|
6
5
|
const { join } = require('node:path')
|
|
@@ -11,9 +10,11 @@ const diagnosticChannel = require('node:diagnostics_channel')
|
|
|
11
10
|
const { ServerResponse } = require('node:http')
|
|
12
11
|
|
|
13
12
|
const pino = require('pino')
|
|
14
|
-
const { fetch, setGlobalDispatcher, Agent } = require('undici')
|
|
13
|
+
const { fetch, setGlobalDispatcher, getGlobalDispatcher, Agent } = require('undici')
|
|
15
14
|
const { wire } = require('undici-thread-interceptor')
|
|
15
|
+
const undici = require('undici')
|
|
16
16
|
|
|
17
|
+
const RemoteCacheStore = require('./http-cache')
|
|
17
18
|
const { PlatformaticApp } = require('./app')
|
|
18
19
|
const { setupITC } = require('./itc')
|
|
19
20
|
const loadInterceptors = require('./interceptors')
|
|
@@ -36,10 +37,7 @@ globalThis[kId] = threadId
|
|
|
36
37
|
let app
|
|
37
38
|
|
|
38
39
|
const config = workerData.config
|
|
39
|
-
globalThis.platformatic = Object.assign(globalThis.platformatic ?? {}, {
|
|
40
|
-
logger: createLogger(),
|
|
41
|
-
events: new EventEmitter()
|
|
42
|
-
})
|
|
40
|
+
globalThis.platformatic = Object.assign(globalThis.platformatic ?? {}, { logger: createLogger() })
|
|
43
41
|
|
|
44
42
|
function handleUnhandled (type, err) {
|
|
45
43
|
const label =
|
|
@@ -96,10 +94,34 @@ async function main () {
|
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
const dispatcherOpts = { ...config.undici }
|
|
98
|
+
|
|
99
|
+
if (Object.keys(interceptors).length > 0) {
|
|
100
|
+
const clientInterceptors = []
|
|
101
|
+
const poolInterceptors = []
|
|
102
|
+
|
|
103
|
+
if (interceptors.Agent) {
|
|
104
|
+
clientInterceptors.push(...interceptors.Agent)
|
|
105
|
+
poolInterceptors.push(...interceptors.Agent)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (interceptors.Pool) {
|
|
109
|
+
poolInterceptors.push(...interceptors.Pool)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (interceptors.Client) {
|
|
113
|
+
clientInterceptors.push(...interceptors.Client)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
dispatcherOpts.factory = (origin, opts) => {
|
|
117
|
+
return opts && opts.connections === 1
|
|
118
|
+
? new undici.Client(origin, opts).compose(clientInterceptors)
|
|
119
|
+
: new undici.Pool(origin, opts).compose(poolInterceptors)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const globalDispatcher = new Agent(dispatcherOpts)
|
|
124
|
+
.compose(composedInterceptors)
|
|
103
125
|
|
|
104
126
|
setGlobalDispatcher(globalDispatcher)
|
|
105
127
|
|
|
@@ -113,6 +135,15 @@ async function main () {
|
|
|
113
135
|
...hooks
|
|
114
136
|
})
|
|
115
137
|
|
|
138
|
+
if (config.httpCache) {
|
|
139
|
+
setGlobalDispatcher(
|
|
140
|
+
getGlobalDispatcher().compose(undici.interceptors.cache({
|
|
141
|
+
store: new RemoteCacheStore(),
|
|
142
|
+
methods: config.httpCache.methods ?? ['GET', 'HEAD']
|
|
143
|
+
}))
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
116
147
|
// If the service is an entrypoint and runtime server config is defined, use it.
|
|
117
148
|
let serverConfig = null
|
|
118
149
|
if (config.server && service.entrypoint) {
|
|
@@ -159,7 +190,7 @@ async function main () {
|
|
|
159
190
|
|
|
160
191
|
if (service.entrypoint && config.basePath) {
|
|
161
192
|
const meta = await app.stackable.getMeta()
|
|
162
|
-
if (!meta.wantsAbsoluteUrls) {
|
|
193
|
+
if (!meta.composer.wantsAbsoluteUrls) {
|
|
163
194
|
stripBasePath(config.basePath)
|
|
164
195
|
}
|
|
165
196
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "2.19.0-alpha.
|
|
3
|
+
"version": "2.19.0-alpha.8",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,18 +35,19 @@
|
|
|
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": "2.19.0-alpha.
|
|
39
|
-
"@platformatic/db": "2.19.0-alpha.
|
|
40
|
-
"@platformatic/node": "2.19.0-alpha.
|
|
41
|
-
"@platformatic/
|
|
42
|
-
"@platformatic/
|
|
43
|
-
"@platformatic/sql-
|
|
38
|
+
"@platformatic/composer": "2.19.0-alpha.8",
|
|
39
|
+
"@platformatic/db": "2.19.0-alpha.8",
|
|
40
|
+
"@platformatic/node": "2.19.0-alpha.8",
|
|
41
|
+
"@platformatic/service": "2.19.0-alpha.8",
|
|
42
|
+
"@platformatic/sql-graphql": "2.19.0-alpha.8",
|
|
43
|
+
"@platformatic/sql-mapper": "2.19.0-alpha.8"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@fastify/error": "^4.0.0",
|
|
47
47
|
"@fastify/websocket": "^11.0.0",
|
|
48
48
|
"@hapi/topo": "^6.0.2",
|
|
49
49
|
"@platformatic/http-metrics": "^0.2.1",
|
|
50
|
+
"@platformatic/undici-cache-memory": "^0.8.1",
|
|
50
51
|
"@watchable/unpromise": "^1.0.2",
|
|
51
52
|
"boring-name-generator": "^1.0.3",
|
|
52
53
|
"change-case-all": "^2.1.0",
|
|
@@ -67,16 +68,17 @@
|
|
|
67
68
|
"prom-client": "^15.1.2",
|
|
68
69
|
"semgrator": "^0.3.0",
|
|
69
70
|
"tail-file-stream": "^0.2.0",
|
|
70
|
-
"
|
|
71
|
-
"undici
|
|
71
|
+
"thread-cpu-usage": "^0.2.0",
|
|
72
|
+
"undici": "^7.0.0",
|
|
73
|
+
"undici-thread-interceptor": "^0.10.0",
|
|
72
74
|
"ws": "^8.16.0",
|
|
73
|
-
"@platformatic/
|
|
74
|
-
"@platformatic/
|
|
75
|
-
"@platformatic/
|
|
76
|
-
"@platformatic/
|
|
77
|
-
"@platformatic/telemetry": "2.19.0-alpha.
|
|
78
|
-
"@platformatic/
|
|
79
|
-
"@platformatic/
|
|
75
|
+
"@platformatic/config": "2.19.0-alpha.8",
|
|
76
|
+
"@platformatic/basic": "2.19.0-alpha.8",
|
|
77
|
+
"@platformatic/itc": "2.19.0-alpha.8",
|
|
78
|
+
"@platformatic/generators": "2.19.0-alpha.8",
|
|
79
|
+
"@platformatic/telemetry": "2.19.0-alpha.8",
|
|
80
|
+
"@platformatic/ts-compiler": "2.19.0-alpha.8",
|
|
81
|
+
"@platformatic/utils": "2.19.0-alpha.8"
|
|
80
82
|
},
|
|
81
83
|
"scripts": {
|
|
82
84
|
"test": "npm run lint && borp --concurrency=1 --timeout=300000 && tsd",
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.19.0-alpha.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.19.0-alpha.8.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -840,6 +840,35 @@
|
|
|
840
840
|
}
|
|
841
841
|
}
|
|
842
842
|
},
|
|
843
|
+
"httpCache": {
|
|
844
|
+
"oneOf": [
|
|
845
|
+
{
|
|
846
|
+
"type": "boolean"
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
"type": "object",
|
|
850
|
+
"properties": {
|
|
851
|
+
"store": {
|
|
852
|
+
"type": "string"
|
|
853
|
+
},
|
|
854
|
+
"methods": {
|
|
855
|
+
"type": "array",
|
|
856
|
+
"items": {
|
|
857
|
+
"type": "string"
|
|
858
|
+
},
|
|
859
|
+
"default": [
|
|
860
|
+
"GET",
|
|
861
|
+
"HEAD"
|
|
862
|
+
],
|
|
863
|
+
"minItems": 1
|
|
864
|
+
},
|
|
865
|
+
"cacheTagsHeader": {
|
|
866
|
+
"type": "string"
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
]
|
|
871
|
+
},
|
|
843
872
|
"watch": {
|
|
844
873
|
"anyOf": [
|
|
845
874
|
{
|