@ontemper/sdk 0.8.0 → 0.8.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/README.md +55 -2
- package/dist/As2Resource.d.ts +431 -76
- package/dist/As2Resource.d.ts.map +1 -1
- package/dist/As2Resource.js +31 -30
- package/dist/As2Resource.js.map +1 -1
- package/dist/As2Transport.d.ts.map +1 -1
- package/dist/As2Transport.js +3 -1
- package/dist/As2Transport.js.map +1 -1
- package/dist/As2Wire.d.ts +39 -0
- package/dist/As2Wire.d.ts.map +1 -1
- package/dist/Contract/RuntimeApi.d.ts +19 -0
- package/dist/Contract/RuntimeApi.d.ts.map +1 -1
- package/dist/Contract/RuntimeApi.js +1 -1
- package/dist/Contract/RuntimeApi.js.map +1 -1
- package/dist/DatabaseTunnelAuthorization.d.ts +3 -0
- package/dist/DatabaseTunnelAuthorization.d.ts.map +1 -0
- package/dist/DatabaseTunnelAuthorization.js +12 -0
- package/dist/DatabaseTunnelAuthorization.js.map +1 -0
- package/dist/HttpApiResource.d.ts +2916 -177
- package/dist/HttpApiResource.d.ts.map +1 -1
- package/dist/HttpApiResource.js +82 -60
- package/dist/HttpApiResource.js.map +1 -1
- package/dist/Kafka.d.ts +89 -0
- package/dist/Kafka.d.ts.map +1 -0
- package/dist/Kafka.js +297 -0
- package/dist/Kafka.js.map +1 -0
- package/dist/Manifest.d.ts +1 -1
- package/dist/Manifest.js +1 -1
- package/dist/PostgreSql.d.ts +18 -0
- package/dist/PostgreSql.d.ts.map +1 -0
- package/dist/PostgreSql.js +40 -0
- package/dist/PostgreSql.js.map +1 -0
- package/dist/PostgreSqlPool.d.ts +34 -0
- package/dist/PostgreSqlPool.d.ts.map +1 -0
- package/dist/PostgreSqlPool.js +133 -0
- package/dist/PostgreSqlPool.js.map +1 -0
- package/dist/PostgreSqlTimeouts.d.ts +6 -0
- package/dist/PostgreSqlTimeouts.d.ts.map +1 -0
- package/dist/PostgreSqlTimeouts.js +17 -0
- package/dist/PostgreSqlTimeouts.js.map +1 -0
- package/dist/PostgreSqlTunnel.d.ts +25 -0
- package/dist/PostgreSqlTunnel.d.ts.map +1 -0
- package/dist/PostgreSqlTunnel.js +114 -0
- package/dist/PostgreSqlTunnel.js.map +1 -0
- package/dist/ResourceRecords.d.ts +1086 -0
- package/dist/ResourceRecords.d.ts.map +1 -0
- package/dist/ResourceRecords.js +303 -0
- package/dist/ResourceRecords.js.map +1 -0
- package/dist/Resources.d.ts +13 -0
- package/dist/Resources.d.ts.map +1 -0
- package/dist/Resources.js +13 -0
- package/dist/Resources.js.map +1 -0
- package/dist/RuntimeResources.d.ts +15 -1
- package/dist/RuntimeResources.d.ts.map +1 -1
- package/dist/RuntimeResources.js +42 -13
- package/dist/RuntimeResources.js.map +1 -1
- package/dist/TransportResource.d.ts +1 -32
- package/dist/TransportResource.d.ts.map +1 -1
- package/dist/TransportResource.js +1 -19
- package/dist/TransportResource.js.map +1 -1
- package/package.json +37 -4
package/dist/Kafka.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { constants, createPrivateKey, createSign, randomUUID } from 'node:crypto';
|
|
2
|
+
import { Option, Redacted, Schema } from 'effect';
|
|
3
|
+
export class OptionalKafkaDependencyError extends Error {
|
|
4
|
+
constructor(packageName) {
|
|
5
|
+
super(`Kafka authentication requires the optional package ${packageName}.`);
|
|
6
|
+
this.name = 'OptionalKafkaDependencyError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class UnsupportedKafkaAuthError extends Error {
|
|
10
|
+
constructor(field) {
|
|
11
|
+
super(`Kafka record uses an unsupported ${field}. Upgrade @ontemper/sdk to support it.`);
|
|
12
|
+
this.name = 'UnsupportedKafkaAuthError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// STS credentials are reused until this long before they expire.
|
|
16
|
+
const ASSUMED_ROLE_REFRESH_MARGIN_MS = 60_000;
|
|
17
|
+
const OAUTH_TOKEN_REQUEST_TIMEOUT_MS = 15_000;
|
|
18
|
+
const base64Url = (value) => Buffer.from(value).toString('base64url');
|
|
19
|
+
const formEncode = (value) => encodeURIComponent(value).replace(/%20/g, '+');
|
|
20
|
+
function privateKeyAssertion(tokenRequest, now) {
|
|
21
|
+
const clientAuthentication = tokenRequest.clientAuthentication;
|
|
22
|
+
if (clientAuthentication.type !== 'private_key_jwt') {
|
|
23
|
+
throw new UnsupportedKafkaAuthError('auth.tokenRequest.clientAuthentication');
|
|
24
|
+
}
|
|
25
|
+
const header = {
|
|
26
|
+
alg: clientAuthentication.algorithm,
|
|
27
|
+
typ: 'JWT',
|
|
28
|
+
};
|
|
29
|
+
if (clientAuthentication.keyId !== undefined) {
|
|
30
|
+
header.kid = clientAuthentication.keyId;
|
|
31
|
+
}
|
|
32
|
+
const issuedAt = Math.floor(now / 1000);
|
|
33
|
+
const claims = {
|
|
34
|
+
iss: tokenRequest.clientId,
|
|
35
|
+
sub: tokenRequest.clientId,
|
|
36
|
+
aud: tokenRequest.tokenUrl,
|
|
37
|
+
exp: issuedAt + 60,
|
|
38
|
+
iat: issuedAt,
|
|
39
|
+
jti: randomUUID(),
|
|
40
|
+
};
|
|
41
|
+
const signed = `${base64Url(JSON.stringify(header))}.${base64Url(JSON.stringify(claims))}`;
|
|
42
|
+
const signature = createSign('SHA256');
|
|
43
|
+
signature.update(signed);
|
|
44
|
+
signature.end();
|
|
45
|
+
const value = clientAuthentication.algorithm === 'ES256'
|
|
46
|
+
? signature.sign({ key: Redacted.value(clientAuthentication.privateKey), dsaEncoding: 'ieee-p1363' })
|
|
47
|
+
: clientAuthentication.algorithm === 'PS256'
|
|
48
|
+
? signature.sign({
|
|
49
|
+
key: Redacted.value(clientAuthentication.privateKey),
|
|
50
|
+
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
51
|
+
saltLength: constants.RSA_PSS_SALTLEN_DIGEST,
|
|
52
|
+
})
|
|
53
|
+
: signature.sign(Redacted.value(clientAuthentication.privateKey));
|
|
54
|
+
return `${signed}.${base64Url(value)}`;
|
|
55
|
+
}
|
|
56
|
+
function oauthBearerProvider(tokenRequest, options) {
|
|
57
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
58
|
+
const now = options.now ?? Date.now;
|
|
59
|
+
let cached;
|
|
60
|
+
// Concurrent broker authentications share one in-flight token request.
|
|
61
|
+
let pendingFetch = null;
|
|
62
|
+
const fetchToken = async () => {
|
|
63
|
+
const clientAuthentication = tokenRequest.clientAuthentication;
|
|
64
|
+
if (!['none', 'basic', 'body', 'query', 'private_key_jwt'].includes(clientAuthentication.type)) {
|
|
65
|
+
throw new UnsupportedKafkaAuthError('auth.tokenRequest.clientAuthentication');
|
|
66
|
+
}
|
|
67
|
+
const parameters = new URLSearchParams({ ...tokenRequest.extraParams, grant_type: 'client_credentials' });
|
|
68
|
+
if (tokenRequest.scope !== undefined)
|
|
69
|
+
parameters.set('scope', tokenRequest.scope);
|
|
70
|
+
if (tokenRequest.audience !== undefined)
|
|
71
|
+
parameters.set('audience', tokenRequest.audience);
|
|
72
|
+
const headers = {};
|
|
73
|
+
const tokenUrl = new URL(tokenRequest.tokenUrl);
|
|
74
|
+
switch (clientAuthentication.type) {
|
|
75
|
+
case 'none':
|
|
76
|
+
parameters.set('client_id', tokenRequest.clientId);
|
|
77
|
+
break;
|
|
78
|
+
case 'basic':
|
|
79
|
+
headers.Authorization = `Basic ${Buffer.from(`${formEncode(tokenRequest.clientId)}:${formEncode(Redacted.value(clientAuthentication.clientSecret))}`).toString('base64')}`;
|
|
80
|
+
break;
|
|
81
|
+
case 'body':
|
|
82
|
+
parameters.set('client_id', tokenRequest.clientId);
|
|
83
|
+
parameters.set('client_secret', Redacted.value(clientAuthentication.clientSecret));
|
|
84
|
+
break;
|
|
85
|
+
case 'query':
|
|
86
|
+
tokenUrl.searchParams.set('client_id', tokenRequest.clientId);
|
|
87
|
+
tokenUrl.searchParams.set('client_secret', Redacted.value(clientAuthentication.clientSecret));
|
|
88
|
+
break;
|
|
89
|
+
case 'private_key_jwt':
|
|
90
|
+
parameters.set('client_id', tokenRequest.clientId);
|
|
91
|
+
parameters.set('client_assertion_type', 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer');
|
|
92
|
+
parameters.set('client_assertion', privateKeyAssertion(tokenRequest, now()));
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
const isJson = tokenRequest.tokenRequestFormat === 'json';
|
|
96
|
+
headers['Content-Type'] = isJson ? 'application/json' : 'application/x-www-form-urlencoded';
|
|
97
|
+
const response = await fetchImpl(tokenUrl, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers,
|
|
100
|
+
body: isJson ? JSON.stringify(Object.fromEntries(parameters)) : parameters,
|
|
101
|
+
redirect: 'error',
|
|
102
|
+
signal: AbortSignal.timeout(OAUTH_TOKEN_REQUEST_TIMEOUT_MS),
|
|
103
|
+
});
|
|
104
|
+
if (!response.ok)
|
|
105
|
+
throw new Error(`OAuth token request failed with status ${response.status}.`);
|
|
106
|
+
const body = await response.json();
|
|
107
|
+
const parsed = decodeTokenResponse(body);
|
|
108
|
+
if (Option.isNone(parsed))
|
|
109
|
+
throw new Error('OAuth token response did not include an access token.');
|
|
110
|
+
const accessToken = parsed.value.access_token;
|
|
111
|
+
const expiresIn = Option.getOrUndefined(decodeExpiresIn(parsed.value.expires_in));
|
|
112
|
+
// Without a stated lifetime the token's expiry is unknown, so each authentication requests a fresh one.
|
|
113
|
+
cached =
|
|
114
|
+
expiresIn === undefined
|
|
115
|
+
? undefined
|
|
116
|
+
: { value: accessToken, expiresAt: now() + Math.max(0, expiresIn - 30) * 1000 };
|
|
117
|
+
return { value: accessToken };
|
|
118
|
+
};
|
|
119
|
+
return () => {
|
|
120
|
+
if (cached !== undefined && cached.expiresAt > now())
|
|
121
|
+
return Promise.resolve({ value: cached.value });
|
|
122
|
+
if (pendingFetch !== null)
|
|
123
|
+
return pendingFetch;
|
|
124
|
+
const request = (async () => {
|
|
125
|
+
try {
|
|
126
|
+
return await fetchToken();
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
pendingFetch = null;
|
|
130
|
+
}
|
|
131
|
+
})();
|
|
132
|
+
pendingFetch = request;
|
|
133
|
+
return request;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const decodeTokenResponse = Schema.decodeUnknownOption(Schema.Struct({
|
|
137
|
+
access_token: Schema.String.check(Schema.isMinLength(1)),
|
|
138
|
+
expires_in: Schema.optionalKey(Schema.Unknown),
|
|
139
|
+
}));
|
|
140
|
+
// A missing, non-numeric, or non-positive expires_in reads as absent, so the token is not cached.
|
|
141
|
+
const decodeExpiresIn = Schema.decodeUnknownOption(Schema.Finite.check(Schema.isGreaterThan(0)));
|
|
142
|
+
function mskOAuthBearerProvider(resource, options) {
|
|
143
|
+
const auth = resource.auth;
|
|
144
|
+
if (auth.type !== 'aws_msk_iam')
|
|
145
|
+
throw new UnsupportedKafkaAuthError('auth.type');
|
|
146
|
+
const loadMskSigner = options.loadMskSigner ??
|
|
147
|
+
(async () => {
|
|
148
|
+
// The optional peer is absent in workflows that do not use MSK IAM, so loading it must remain lazy.
|
|
149
|
+
try {
|
|
150
|
+
return await import('aws-msk-iam-sasl-signer-js');
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (error instanceof Error && 'code' in error && error.code === 'ERR_MODULE_NOT_FOUND') {
|
|
154
|
+
throw new OptionalKafkaDependencyError('aws-msk-iam-sasl-signer-js');
|
|
155
|
+
}
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
const loadCredentialProviders = options.loadCredentialProviders ??
|
|
160
|
+
(async () => {
|
|
161
|
+
// The optional peer is loaded only when a record asks the client to assume an AWS role.
|
|
162
|
+
try {
|
|
163
|
+
return await import('@aws-sdk/credential-providers');
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (error instanceof Error && 'code' in error && error.code === 'ERR_MODULE_NOT_FOUND') {
|
|
167
|
+
throw new OptionalKafkaDependencyError('@aws-sdk/credential-providers');
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const now = options.now ?? Date.now;
|
|
173
|
+
// One STS provider per config, and its credentials reused until shortly before expiry, so each broker
|
|
174
|
+
// authentication does not send its own AssumeRole call.
|
|
175
|
+
let assumeRoleProvider;
|
|
176
|
+
let assumedCredentials;
|
|
177
|
+
const loadAssumeRoleProvider = async (params) => {
|
|
178
|
+
try {
|
|
179
|
+
return (await loadCredentialProviders()).fromTemporaryCredentials({
|
|
180
|
+
params,
|
|
181
|
+
clientConfig: { region: auth.region },
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
// A failed load is retried on the next authentication instead of caching the rejection.
|
|
186
|
+
assumeRoleProvider = undefined;
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const assumedRoleCredentials = async (params) => {
|
|
191
|
+
if (assumedCredentials !== undefined && assumedCredentials.refreshAt > now())
|
|
192
|
+
return assumedCredentials.credentials;
|
|
193
|
+
assumeRoleProvider ??= loadAssumeRoleProvider(params);
|
|
194
|
+
const { expiration, ...credentials } = await (await assumeRoleProvider)();
|
|
195
|
+
assumedCredentials =
|
|
196
|
+
expiration === undefined
|
|
197
|
+
? undefined
|
|
198
|
+
: { credentials, refreshAt: expiration.getTime() - ASSUMED_ROLE_REFRESH_MARGIN_MS };
|
|
199
|
+
return credentials;
|
|
200
|
+
};
|
|
201
|
+
return async () => {
|
|
202
|
+
const signer = await loadMskSigner();
|
|
203
|
+
const awsAuth = auth.awsAuth;
|
|
204
|
+
if (!['access_key', 'assume_role', 'ambient'].includes(awsAuth.type)) {
|
|
205
|
+
throw new UnsupportedKafkaAuthError('auth.awsAuth');
|
|
206
|
+
}
|
|
207
|
+
const token = awsAuth.type === 'access_key'
|
|
208
|
+
? await signer.generateAuthTokenFromCredentialsProvider({
|
|
209
|
+
region: auth.region,
|
|
210
|
+
awsCredentialsProvider: async () => {
|
|
211
|
+
const creds = {
|
|
212
|
+
accessKeyId: awsAuth.accessKeyId,
|
|
213
|
+
secretAccessKey: Redacted.value(awsAuth.secretAccessKey),
|
|
214
|
+
};
|
|
215
|
+
if (awsAuth.sessionToken !== undefined)
|
|
216
|
+
creds.sessionToken = Redacted.value(awsAuth.sessionToken);
|
|
217
|
+
return creds;
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
: awsAuth.type === 'assume_role'
|
|
221
|
+
? await signer.generateAuthTokenFromCredentialsProvider({
|
|
222
|
+
region: auth.region,
|
|
223
|
+
awsCredentialsProvider: () => {
|
|
224
|
+
const params = { RoleArn: awsAuth.roleArn, RoleSessionName: 'temper-kafka' };
|
|
225
|
+
if (awsAuth.externalId !== undefined)
|
|
226
|
+
params.ExternalId = awsAuth.externalId;
|
|
227
|
+
return assumedRoleCredentials(params);
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
: await signer.generateAuthToken({ region: auth.region });
|
|
231
|
+
return { value: token.token };
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// A bad endpoint or key would otherwise surface only at the first broker authentication, deep inside KafkaJS.
|
|
235
|
+
function assertTokenRequestUsable(tokenRequest) {
|
|
236
|
+
const tokenUrl = URL.canParse(tokenRequest.tokenUrl) ? new URL(tokenRequest.tokenUrl) : undefined;
|
|
237
|
+
if (tokenUrl?.protocol !== 'https:') {
|
|
238
|
+
throw new Error('Kafka OAuth auth.tokenRequest.tokenUrl must be an https URL.');
|
|
239
|
+
}
|
|
240
|
+
const clientAuthentication = tokenRequest.clientAuthentication;
|
|
241
|
+
if (clientAuthentication.type !== 'private_key_jwt')
|
|
242
|
+
return;
|
|
243
|
+
try {
|
|
244
|
+
createPrivateKey(Redacted.value(clientAuthentication.privateKey));
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
throw new Error('Kafka OAuth auth.tokenRequest.clientAuthentication.privateKey is not a valid PEM private key.');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/** Maps a resolved Kafka record to the options accepted by `new Kafka(...)`. */
|
|
251
|
+
export function kafkaConfig(resource, options = {}) {
|
|
252
|
+
if (resource.brokers.some((broker) => broker.startsWith('['))) {
|
|
253
|
+
throw new Error('KafkaJS does not support bracketed IPv6 bootstrap broker addresses; configure a hostname or IPv4 address.');
|
|
254
|
+
}
|
|
255
|
+
let ssl;
|
|
256
|
+
if (resource.tls !== undefined) {
|
|
257
|
+
const tlsOptions = {};
|
|
258
|
+
if (resource.tls.caCertificate !== undefined)
|
|
259
|
+
tlsOptions.ca = resource.tls.caCertificate;
|
|
260
|
+
if (resource.tls.clientCertificate !== undefined)
|
|
261
|
+
tlsOptions.cert = resource.tls.clientCertificate;
|
|
262
|
+
if (resource.tls.clientKey !== undefined)
|
|
263
|
+
tlsOptions.key = Redacted.value(resource.tls.clientKey);
|
|
264
|
+
if (resource.tls.clientKeyPassphrase !== undefined) {
|
|
265
|
+
tlsOptions.passphrase = Redacted.value(resource.tls.clientKeyPassphrase);
|
|
266
|
+
}
|
|
267
|
+
if (resource.tls.serverName !== undefined)
|
|
268
|
+
tlsOptions.servername = resource.tls.serverName;
|
|
269
|
+
ssl = Object.keys(tlsOptions).length === 0 ? true : tlsOptions;
|
|
270
|
+
}
|
|
271
|
+
const auth = resource.auth;
|
|
272
|
+
if (!['none', 'sasl_plain', 'sasl_scram', 'oauthbearer', 'aws_msk_iam'].includes(auth.type)) {
|
|
273
|
+
throw new UnsupportedKafkaAuthError('auth.type');
|
|
274
|
+
}
|
|
275
|
+
if (auth.type === 'oauthbearer')
|
|
276
|
+
assertTokenRequestUsable(auth.tokenRequest);
|
|
277
|
+
const sasl = auth.type === 'none'
|
|
278
|
+
? undefined
|
|
279
|
+
: auth.type === 'sasl_plain'
|
|
280
|
+
? { mechanism: 'plain', username: auth.username, password: Redacted.value(auth.password) }
|
|
281
|
+
: auth.type === 'sasl_scram'
|
|
282
|
+
? auth.mechanism === 'SCRAM-SHA-256'
|
|
283
|
+
? { mechanism: 'scram-sha-256', username: auth.username, password: Redacted.value(auth.password) }
|
|
284
|
+
: { mechanism: 'scram-sha-512', username: auth.username, password: Redacted.value(auth.password) }
|
|
285
|
+
: auth.type === 'oauthbearer'
|
|
286
|
+
? { mechanism: 'oauthbearer', oauthBearerProvider: oauthBearerProvider(auth.tokenRequest, options) }
|
|
287
|
+
: { mechanism: 'oauthbearer', oauthBearerProvider: mskOAuthBearerProvider(resource, options) };
|
|
288
|
+
const config = { brokers: [...resource.brokers] };
|
|
289
|
+
if (resource.clientId !== undefined)
|
|
290
|
+
config.clientId = resource.clientId;
|
|
291
|
+
if (ssl !== undefined)
|
|
292
|
+
config.ssl = ssl;
|
|
293
|
+
if (sasl !== undefined)
|
|
294
|
+
config.sasl = sasl;
|
|
295
|
+
return config;
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=Kafka.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Kafka.js","sourceRoot":"","sources":["../src/Kafka.ts"],"names":[],"mappings":"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"}
|
package/dist/Manifest.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HttpApi, type HttpApiGroup } from 'effect/http-api';
|
|
2
2
|
import { type WorkflowManifest } from './Contract/Manifest.js';
|
|
3
|
-
export declare const SDK_VERSION = "0.8.
|
|
3
|
+
export declare const SDK_VERSION = "0.8.1";
|
|
4
4
|
export declare const RUNTIME_API_VERSION = 1;
|
|
5
5
|
/** The route the SDK serves for platform probes; an API declaring `GET` on it collides with it on every request. */
|
|
6
6
|
export declare const HEALTHCHECK_PATH = "/healthcheck";
|
package/dist/Manifest.js
CHANGED
|
@@ -5,7 +5,7 @@ import { declarationOf } from './Resource.js';
|
|
|
5
5
|
import { Trigger } from './Trigger.js';
|
|
6
6
|
const decodeTrigger = Schema.decodeUnknownSync(TriggerMetadata, { onExcessProperty: 'error' });
|
|
7
7
|
const identityOf = (resource) => `${resource.kind}:${resource.kind === 'resourceType' ? resource.type : resource.key}`;
|
|
8
|
-
export const SDK_VERSION = '0.8.
|
|
8
|
+
export const SDK_VERSION = '0.8.1';
|
|
9
9
|
export const RUNTIME_API_VERSION = 1;
|
|
10
10
|
/** The route the SDK serves for platform probes; an API declaring `GET` on it collides with it on every request. */
|
|
11
11
|
export const HEALTHCHECK_PATH = '/healthcheck';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL Resource pools, exported from `@temper/workflow/resources`. A pool needs the `pg` package, and a
|
|
3
|
+
* builder's tunnel to a Temper-managed database needs `ws`; both load only when a pool is built.
|
|
4
|
+
*/
|
|
5
|
+
import { Effect, type Scope } from 'effect';
|
|
6
|
+
import type pg from 'pg';
|
|
7
|
+
import type { InvalidResourceError } from './ResourcesErrors.js';
|
|
8
|
+
import { type ReadError } from './RuntimeResources.js';
|
|
9
|
+
export { PostgresqlResource } from './PostgreSqlPool.js';
|
|
10
|
+
/** What opening a pool can fail with; a rejected tunnel fails each connection instead, never the open. */
|
|
11
|
+
export type PostgresqlPoolError = ReadError | InvalidResourceError;
|
|
12
|
+
/**
|
|
13
|
+
* The pool for the PostgreSQL Resource `key`, ended when the scope closes; a client still checked out after 15 s
|
|
14
|
+
* is left open and logged. Native TCP, except in a builder (`TEMPER_DATABASE_TUNNEL=websocket`) for a
|
|
15
|
+
* Temper-managed database, which goes through the Resources tunnel.
|
|
16
|
+
*/
|
|
17
|
+
export declare const makePostgresqlPool: (key: string) => Effect.Effect<pg.Pool, PostgresqlPoolError, Scope.Scope>;
|
|
18
|
+
//# sourceMappingURL=PostgreSql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgreSql.d.ts","sourceRoot":"","sources":["../src/PostgreSql.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAoB,MAAM,EAAU,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEtE,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAGzB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,0GAA0G;AAC1G,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,oBAAoB,CAAC;AAqCnE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAC7C,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL Resource pools, exported from `@temper/workflow/resources`. A pool needs the `pg` package, and a
|
|
3
|
+
* builder's tunnel to a Temper-managed database needs `ws`; both load only when a pool is built.
|
|
4
|
+
*/
|
|
5
|
+
import { Config, Duration, Effect, Option } from 'effect';
|
|
6
|
+
import { FetchHttpClient, HttpClient } from 'effect/http';
|
|
7
|
+
import { databaseTunnelUrl } from './DatabaseTunnelAuthorization.js';
|
|
8
|
+
import { endPool, makeResourcePool, tunnelEnabled } from './PostgreSqlPool.js';
|
|
9
|
+
import { makeRuntimeResourceClient } from './RuntimeResources.js';
|
|
10
|
+
export { PostgresqlResource } from './PostgreSqlPool.js';
|
|
11
|
+
// A token in its refresh window waits for the provider's next publish. pg's connect timeout is 10 s, so the wait
|
|
12
|
+
// ends with the connection attempt instead of holding the process open.
|
|
13
|
+
const TUNNEL_AUTHORIZATION_TIMEOUT = Duration.seconds(10);
|
|
14
|
+
const open = (rawKey) => Effect.gen(function* () {
|
|
15
|
+
const key = rawKey.trim();
|
|
16
|
+
// A workflow's own HttpClient when there is one; resolver calls disable its tracing either way.
|
|
17
|
+
const httpClient = yield* Effect.serviceOption(HttpClient.HttpClient);
|
|
18
|
+
const client = yield* makeRuntimeResourceClient.pipe(Option.isSome(httpClient)
|
|
19
|
+
? Effect.provideService(HttpClient.HttpClient, httpClient.value)
|
|
20
|
+
: Effect.provide(FetchHttpClient.layer));
|
|
21
|
+
const tunnel = tunnelEnabled(yield* Config.option(Config.String('TEMPER_DATABASE_TUNNEL')).pipe(Effect.orElseSucceed(() => Option.none())));
|
|
22
|
+
const context = yield* Effect.context();
|
|
23
|
+
return yield* makeResourcePool(key, {
|
|
24
|
+
resolve: client.resolve,
|
|
25
|
+
inspect: client.inspect,
|
|
26
|
+
authorizeTunnel: tunnel
|
|
27
|
+
? (tunnelKey) => Effect.runPromiseWith(context)(Effect.all({ url: databaseTunnelUrl(tunnelKey), token: client.tunnelToken }).pipe(Effect.timeoutOrElse({
|
|
28
|
+
duration: TUNNEL_AUTHORIZATION_TIMEOUT,
|
|
29
|
+
orElse: () => Effect.fail(new Error('No runtime token for the database tunnel within 10 s.')),
|
|
30
|
+
})))
|
|
31
|
+
: undefined,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* The pool for the PostgreSQL Resource `key`, ended when the scope closes; a client still checked out after 15 s
|
|
36
|
+
* is left open and logged. Native TCP, except in a builder (`TEMPER_DATABASE_TUNNEL=websocket`) for a
|
|
37
|
+
* Temper-managed database, which goes through the Resources tunnel.
|
|
38
|
+
*/
|
|
39
|
+
export const makePostgresqlPool = (key) => Effect.acquireRelease(open(key), (pool) => endPool(pool));
|
|
40
|
+
//# sourceMappingURL=PostgreSql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgreSql.js","sourceRoot":"","sources":["../src/PostgreSql.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,yBAAyB,EAAkB,MAAM,uBAAuB,CAAC;AAElF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAKzD,iHAAiH;AACjH,wEAAwE;AACxE,MAAM,4BAA4B,GAAG,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAE1D,MAAM,IAAI,GAAG,CAAC,MAAc,EAA+C,EAAE,CAC3E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC1B,gGAAgG;IAChG,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAClD,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;QACvB,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC;QAChE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAC1C,CAAC;IACF,MAAM,MAAM,GAAG,aAAa,CAC1B,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAC9G,CAAC;IACF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAS,CAAC;IAC/C,OAAO,KAAK,CAAC,CAAC,gBAAgB,CAAC,GAAG,EAAE;QAClC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,eAAe,EAAE,MAAM;YACrB,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CACZ,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAC5B,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,iBAAiB,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAC/E,MAAM,CAAC,aAAa,CAAC;gBACnB,QAAQ,EAAE,4BAA4B;gBACtC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;aAC9F,CAAC,CACH,CACF;YACL,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAA4D,EAAE,CAC1G,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Duration, Effect, Option, Schema } from 'effect';
|
|
2
|
+
import type pg from 'pg';
|
|
3
|
+
import { InvalidResourceError } from './ResourcesErrors.js';
|
|
4
|
+
import type { InspectedResource, ReadError } from './RuntimeResources.js';
|
|
5
|
+
export { PostgresqlResource } from './ResourceRecords.js';
|
|
6
|
+
/** Where a pool reads its Resource: the runtime API in a workflow, fixed answers in tests. */
|
|
7
|
+
export interface ResourceSource {
|
|
8
|
+
readonly resolve: (key: string) => Effect.Effect<Schema.Json, ReadError | InvalidResourceError>;
|
|
9
|
+
/** The only read whose 404 means that no record answers the key. */
|
|
10
|
+
readonly inspect: (key: string) => Effect.Effect<Pick<InspectedResource, 'metadata'>, ReadError>;
|
|
11
|
+
/** Set only in builders (`TEMPER_DATABASE_TUNNEL=websocket`); never cache what it returns. */
|
|
12
|
+
readonly authorizeTunnel?: ((key: string) => Promise<{
|
|
13
|
+
readonly url: string;
|
|
14
|
+
readonly token: string;
|
|
15
|
+
}>) | undefined;
|
|
16
|
+
}
|
|
17
|
+
/** A rotated password reaches new connections after this, as a declaration's 30 s read cache allows. */
|
|
18
|
+
export declare const PASSWORD_REFRESH_MS = 30000;
|
|
19
|
+
/**
|
|
20
|
+
* Inspects, then resolves the Resource and builds an unconnected pool. `ResourceNotFoundError` means only that no
|
|
21
|
+
* record answers the key. Warnings go through the caller's logger and secret masks, as do the password's masks.
|
|
22
|
+
*/
|
|
23
|
+
export declare const makeResourcePool: (key: string, source: ResourceSource) => Effect.Effect<pg.Pool, ReadError | InvalidResourceError>;
|
|
24
|
+
export declare const tunnelEnabled: (value: Option.Option<string>) => boolean;
|
|
25
|
+
/** Outlasts the pool's 10 s statement timeout, so a query in flight when the scope closes can still finish. */
|
|
26
|
+
export declare const POOL_END_TIMEOUT: Duration.Input;
|
|
27
|
+
/**
|
|
28
|
+
* Ends the pool, waiting at most `timeout`. pg-pool's `end()` waits for every checked-out client, so a leaked
|
|
29
|
+
* `pool.connect()` would otherwise hold its scope open; past the bound the release logs a warning and returns.
|
|
30
|
+
*/
|
|
31
|
+
export declare const endPool: (pool: {
|
|
32
|
+
readonly end: () => Promise<void>;
|
|
33
|
+
}, timeout?: Duration.Input) => Effect.Effect<void>;
|
|
34
|
+
//# sourceMappingURL=PostgreSqlPool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgreSqlPool.d.ts","sourceRoot":"","sources":["../src/PostgreSqlPool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAuB,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC3F,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAIzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,oBAAoB,CAAC,CAAC;IAChG,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;IACjG,8FAA8F;IAC9F,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAC;CACrH;AAqED,wGAAwG;AACxG,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAa1C;;;GAGG;AACH,eAAO,MAAM,gBAAgB,QACtB,MAAM,UACH,cAAc,KACrB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,oBAAoB,CAwDtD,CAAC;AAEL,eAAO,MAAM,aAAa,UAAW,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAwC,CAAC;AAEnG,+GAA+G;AAC/G,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,KAAoB,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,OAAO,SACZ;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,YAClC,QAAQ,CAAC,KAAK,KACtB,MAAM,CAAC,MAAM,CAAC,IAAI,CAUlB,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Clock, Effect, Option, Predicate, Redacted, Schema } from 'effect';
|
|
2
|
+
import { observePostgreSqlTimeouts } from './PostgreSqlTimeouts.js';
|
|
3
|
+
import { registerSecrets } from './Redaction.js';
|
|
4
|
+
import { PostgresqlResource } from './ResourceRecords.js';
|
|
5
|
+
import { InvalidResourceError } from './ResourcesErrors.js';
|
|
6
|
+
export { PostgresqlResource } from './ResourceRecords.js';
|
|
7
|
+
const validate = Schema.toStandardSchemaV1(PostgresqlResource)['~standard'].validate;
|
|
8
|
+
// Names only the failing paths: a schema message can quote the value, which may be a secret.
|
|
9
|
+
const decode = (key, value) => Effect.suspend(() => {
|
|
10
|
+
const result = validate(value);
|
|
11
|
+
if (result instanceof Promise)
|
|
12
|
+
return Effect.die(new Error('PostgresqlResource decodes synchronously'));
|
|
13
|
+
if (result.issues === undefined)
|
|
14
|
+
return Effect.succeed(result.value);
|
|
15
|
+
const paths = result.issues.map((issue) => (issue.path ?? []).map((part) => String(Predicate.isObject(part) ? part.key : part)).join('.') || '(root)');
|
|
16
|
+
return Effect.fail(new InvalidResourceError({
|
|
17
|
+
target: `key ${key}`,
|
|
18
|
+
message: `Resource ${key} does not match the expected schema: invalid ${[...new Set(paths)].join(', ')}`,
|
|
19
|
+
}));
|
|
20
|
+
});
|
|
21
|
+
function sslConfig(resource) {
|
|
22
|
+
const { tls } = resource;
|
|
23
|
+
const clientCertificate = tls?.clientCertificate
|
|
24
|
+
? {
|
|
25
|
+
cert: tls.clientCertificate,
|
|
26
|
+
key: tls.clientKey === undefined ? undefined : Redacted.value(tls.clientKey),
|
|
27
|
+
passphrase: tls.clientKeyPassphrase === undefined ? undefined : Redacted.value(tls.clientKeyPassphrase),
|
|
28
|
+
}
|
|
29
|
+
: {};
|
|
30
|
+
switch (resource.sslmode) {
|
|
31
|
+
case 'disable':
|
|
32
|
+
return false;
|
|
33
|
+
case 'require':
|
|
34
|
+
return { rejectUnauthorized: false, ...clientCertificate };
|
|
35
|
+
case 'verify-ca':
|
|
36
|
+
return {
|
|
37
|
+
rejectUnauthorized: true,
|
|
38
|
+
ca: tls?.caCertificate,
|
|
39
|
+
...clientCertificate,
|
|
40
|
+
checkServerIdentity: () => undefined,
|
|
41
|
+
};
|
|
42
|
+
case 'verify-full':
|
|
43
|
+
return { rejectUnauthorized: true, ca: tls?.caCertificate, ...clientCertificate, servername: tls?.serverName };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// RDS IAM needs a token signer and an SSH tunnel needs a bastion client; neither runs inside this pool.
|
|
47
|
+
const resourcePassword = (key, resource) => {
|
|
48
|
+
const refuse = (message) => Effect.fail(new InvalidResourceError({ target: `key ${key}`, message }));
|
|
49
|
+
if (resource.sshTunnel)
|
|
50
|
+
return refuse(`PostgreSQL Resource ${key} needs an SSH tunnel, which this SDK does not open.`);
|
|
51
|
+
if (resource.auth.type === 'rds_iam') {
|
|
52
|
+
return refuse(`PostgreSQL Resource ${key} uses RDS IAM authentication, which this SDK does not support.`);
|
|
53
|
+
}
|
|
54
|
+
return Effect.succeed(resource.auth.type === 'password' ? Redacted.value(resource.auth.password) : '');
|
|
55
|
+
};
|
|
56
|
+
// `pg` and `ws` are optional peers, so they load when a pool is built: importing `./resources` needs neither.
|
|
57
|
+
const loadOptional = (name, load) => Effect.tryPromise({
|
|
58
|
+
try: load,
|
|
59
|
+
catch: () => new Error(`PostgreSQL Resource pools need the ${name} package: add ${name} to the workflow dependencies.`),
|
|
60
|
+
}).pipe(Effect.orDie);
|
|
61
|
+
const loadPg = loadOptional('pg', () => import('pg').then((module) => module.default));
|
|
62
|
+
const loadTunnel = loadOptional('ws', () => import('./PostgreSqlTunnel.js'));
|
|
63
|
+
/** A rotated password reaches new connections after this, as a declaration's 30 s read cache allows. */
|
|
64
|
+
export const PASSWORD_REFRESH_MS = 30_000;
|
|
65
|
+
const ManagedPlacement = Schema.Struct({ managedDatabasePlacementId: Schema.String });
|
|
66
|
+
const isManaged = Schema.is(ManagedPlacement);
|
|
67
|
+
// The runtime API answers a missing Variable reference with the same 404 as a missing record, so after a found
|
|
68
|
+
// `inspect` a resolve 404 names a broken record, never an absent one.
|
|
69
|
+
const brokenReference = (key) => new InvalidResourceError({
|
|
70
|
+
target: `key ${key}`,
|
|
71
|
+
message: `Resource ${key} exists, but a value it references, such as its password Variable, does not resolve in this environment.`,
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Inspects, then resolves the Resource and builds an unconnected pool. `ResourceNotFoundError` means only that no
|
|
75
|
+
* record answers the key. Warnings go through the caller's logger and secret masks, as do the password's masks.
|
|
76
|
+
*/
|
|
77
|
+
export const makeResourcePool = (key, source) => Effect.gen(function* () {
|
|
78
|
+
const record = yield* source.inspect(key);
|
|
79
|
+
const resource = yield* source.resolve(key).pipe(Effect.catchTag('ResourceNotFoundError', () => Effect.fail(brokenReference(key))), Effect.flatMap((value) => decode(key, value)));
|
|
80
|
+
let password = yield* resourcePassword(key, resource);
|
|
81
|
+
yield* registerSecrets([password]);
|
|
82
|
+
let passwordReadAt = yield* Clock.currentTimeMillis;
|
|
83
|
+
// A failed re-read keeps the last password, so an outage never fails a connection the old password still opens.
|
|
84
|
+
const currentPassword = Effect.gen(function* () {
|
|
85
|
+
const now = yield* Clock.currentTimeMillis;
|
|
86
|
+
if (now - passwordReadAt < PASSWORD_REFRESH_MS)
|
|
87
|
+
return password;
|
|
88
|
+
passwordReadAt = now;
|
|
89
|
+
const next = yield* source.resolve(key).pipe(Effect.flatMap((value) => decode(key, value)), Effect.flatMap((record) => resourcePassword(key, record)), Effect.tap((value) => registerSecrets([value])), Effect.option);
|
|
90
|
+
if (Option.isSome(next))
|
|
91
|
+
password = next.value;
|
|
92
|
+
return password;
|
|
93
|
+
});
|
|
94
|
+
const authorize = source.authorizeTunnel;
|
|
95
|
+
const managed = isManaged(record.metadata);
|
|
96
|
+
const driver = yield* loadPg;
|
|
97
|
+
const TunnelStream = managed && authorize ? (yield* loadTunnel).PostgreSqlTunnelStream : undefined;
|
|
98
|
+
const context = yield* Effect.context();
|
|
99
|
+
const warn = (event, message, fields = {}) => {
|
|
100
|
+
Effect.runForkWith(context)(Effect.logWarning(message).pipe(Effect.annotateLogs({ event, ...fields })));
|
|
101
|
+
};
|
|
102
|
+
// Each physical connection reads current runtime authorization, including connections opened after token renewal.
|
|
103
|
+
const stream = TunnelStream && authorize ? () => new TunnelStream(() => authorize(key)) : undefined;
|
|
104
|
+
// A callback prevents pg from substituting ambient PGPASSWORD/pgpass for an empty Resource password, and pg calls
|
|
105
|
+
// it for each new connection.
|
|
106
|
+
const pool = observePostgreSqlTimeouts(driver, new driver.Pool({
|
|
107
|
+
stream,
|
|
108
|
+
host: resource.host,
|
|
109
|
+
port: resource.port,
|
|
110
|
+
user: resource.user,
|
|
111
|
+
password: () => Effect.runPromiseWith(context)(currentPassword),
|
|
112
|
+
database: resource.dbname,
|
|
113
|
+
ssl: sslConfig(resource),
|
|
114
|
+
max: 5,
|
|
115
|
+
connectionTimeoutMillis: 10_000,
|
|
116
|
+
idleTimeoutMillis: 30_000,
|
|
117
|
+
statement_timeout: 10_000,
|
|
118
|
+
}), () => warn('temper.database.query.timeout', 'PostgreSQL statement timed out.', { code: '57014' }));
|
|
119
|
+
// The error and its attached client stay out of the line: both can carry server text and login details.
|
|
120
|
+
pool.on('error', () => warn('temper.database.connection.error', 'An idle PostgreSQL connection closed.'));
|
|
121
|
+
return pool;
|
|
122
|
+
});
|
|
123
|
+
export const tunnelEnabled = (value) => Option.contains(value, 'websocket');
|
|
124
|
+
/** Outlasts the pool's 10 s statement timeout, so a query in flight when the scope closes can still finish. */
|
|
125
|
+
export const POOL_END_TIMEOUT = '15 seconds';
|
|
126
|
+
/**
|
|
127
|
+
* Ends the pool, waiting at most `timeout`. pg-pool's `end()` waits for every checked-out client, so a leaked
|
|
128
|
+
* `pool.connect()` would otherwise hold its scope open; past the bound the release logs a warning and returns.
|
|
129
|
+
*/
|
|
130
|
+
export const endPool = (pool, timeout = POOL_END_TIMEOUT) => Effect.promise(() => pool.end().catch(() => undefined)).pipe(Effect.timeoutOption(timeout), Effect.flatMap((ended) => Option.isSome(ended)
|
|
131
|
+
? Effect.void
|
|
132
|
+
: Effect.logWarning('A PostgreSQL pool did not end in time: a client was not released.').pipe(Effect.annotateLogs({ event: 'temper.database.pool.end.timeout' }))));
|
|
133
|
+
//# sourceMappingURL=PostgreSqlPool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgreSqlPool.js","sourceRoot":"","sources":["../src/PostgreSqlPool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type pg from 'pg';
|
|
2
|
+
/** The `pg` classes the observer checks against; the caller passes the module it loaded. */
|
|
3
|
+
export type PgClasses = Pick<typeof pg, 'Client' | 'DatabaseError'>;
|
|
4
|
+
/** Calls `report` once per statement timeout on any connection of the pool, without changing query results. */
|
|
5
|
+
export declare const observePostgreSqlTimeouts: (driver: PgClasses, pool: pg.Pool, report: () => void) => pg.Pool;
|
|
6
|
+
//# sourceMappingURL=PostgreSqlTimeouts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgreSqlTimeouts.d.ts","sourceRoot":"","sources":["../src/PostgreSqlTimeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,4FAA4F;AAC5F,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,GAAG,eAAe,CAAC,CAAC;AAEpE,+GAA+G;AAC/G,eAAO,MAAM,yBAAyB,WAAY,SAAS,QAAQ,EAAE,CAAC,IAAI,UAAU,MAAM,IAAI,KAAG,EAAE,CAAC,IAenG,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Calls `report` once per statement timeout on any connection of the pool, without changing query results. */
|
|
2
|
+
export const observePostgreSqlTimeouts = (driver, pool, report) => {
|
|
3
|
+
pool.on('connect', (client) => {
|
|
4
|
+
if (!(client instanceof driver.Client) || !client.connection)
|
|
5
|
+
return;
|
|
6
|
+
// Wire errors cover pool, checked-out client, and callback queries without changing their return values.
|
|
7
|
+
client.connection.on('errorMessage', (error) => {
|
|
8
|
+
if (error instanceof driver.DatabaseError &&
|
|
9
|
+
error.code === '57014' &&
|
|
10
|
+
error.message.includes('statement timeout')) {
|
|
11
|
+
report();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
return pool;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=PostgreSqlTimeouts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgreSqlTimeouts.js","sourceRoot":"","sources":["../src/PostgreSqlTimeouts.ts"],"names":[],"mappings":"AAKA,+GAA+G;AAC/G,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,MAAiB,EAAE,IAAa,EAAE,MAAkB,EAAW,EAAE;IACzG,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;QAC5B,IAAI,CAAC,CAAC,MAAM,YAAY,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU;YAAE,OAAO;QACrE,yGAAyG;QACzG,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,KAAY,EAAE,EAAE;YACpD,IACE,KAAK,YAAY,MAAM,CAAC,aAAa;gBACrC,KAAK,CAAC,IAAI,KAAK,OAAO;gBACtB,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAC3C,CAAC;gBACD,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
|