@pulse-compute/provider-node 0.0.0 → 1.0.0-beta.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 +60 -1
- package/package.json +61 -6
- package/src/capabilities.js +56 -0
- package/src/compiler/assets-provider-proof.js +434 -0
- package/src/compiler/backend-json-request-body-proof.js +90 -0
- package/src/compiler/index.js +38 -0
- package/src/compiler/node-adapter.js +452 -0
- package/src/compiler/node-compiled-wasm-assets-lifecycle-proof.js +435 -0
- package/src/compiler/request-json-body-proof.js +181 -0
- package/src/compiler/route-handler-compiled-wasm-bridge.js +323 -0
- package/src/compiler/route-handler-effect-proof.js +215 -0
- package/src/compiler/route-handler-live-origin-proof.js +372 -0
- package/src/compiler/schema-decode-result-proof.js +180 -0
- package/src/compiler/schema-response-codec-proof.js +149 -0
- package/src/events/reference-adapter.js +145 -0
- package/src/index.js +57 -0
- package/src/javascript/assets-adapter.js +68 -0
- package/src/javascript/bindings-adapter.js +120 -0
- package/src/javascript/fetch-adapter.js +187 -0
- package/src/javascript/grip-broadcast.js +243 -0
- package/src/javascript/jwt-verifier.js +137 -0
- package/src/javascript/lifecycle.js +69 -0
- package/src/javascript/node-adapter.js +281 -0
- package/src/javascript/package-effects.js +78 -0
- package/src/javascript/runtime-host.js +165 -0
- package/src/javascript/source-package.js +439 -0
- package/src/javascript/support.js +205 -0
- package/src/javascript/target-support-policy.js +134 -0
- package/src/javascript/target.js +37 -0
- package/src/javascript/test-runtime.js +111 -0
- package/src/native/target.js +78 -0
- package/src/runtime/assets-provider.js +776 -0
- package/src/runtime/canonical-api-runtime.js +632 -0
- package/src/runtime/compiled-wasm-node-adapter-kv.js +15 -0
- package/src/runtime/index.js +17 -0
- package/src/runtime/jwt-verifier.js +264 -0
- package/src/runtime/route-handler-effects.js +757 -0
- package/src/toolchain.js +258 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
CRYPTO_ES256_GUEST_LINKED_IMPLEMENTATION,
|
|
5
|
+
CRYPTO_ES256_GUEST_LINKED_REALIZATION,
|
|
6
|
+
CRYPTO_GUEST_SOURCE_IMPLEMENTATION
|
|
7
|
+
} = require('@pulse-compute/wasm-contracts/crypto/contracts');
|
|
8
|
+
|
|
9
|
+
const NODE_NATIVE_JWT_VERIFIER_VERSION = 'pulse.node-native-jwt-verifier.v1';
|
|
10
|
+
const NODE_NATIVE_JWT_REALIZATIONS = Object.freeze({
|
|
11
|
+
HS256: Object.freeze({
|
|
12
|
+
algorithm: 'HS256',
|
|
13
|
+
realization: 'guest-source:pulse-hmac-as',
|
|
14
|
+
implementation: CRYPTO_GUEST_SOURCE_IMPLEMENTATION,
|
|
15
|
+
guestUnitRequired: false
|
|
16
|
+
}),
|
|
17
|
+
ES256: Object.freeze({
|
|
18
|
+
algorithm: 'ES256',
|
|
19
|
+
realization: CRYPTO_ES256_GUEST_LINKED_REALIZATION,
|
|
20
|
+
implementation: CRYPTO_ES256_GUEST_LINKED_IMPLEMENTATION,
|
|
21
|
+
guestUnitRequired: true
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
const NODE_NATIVE_JWT_REALIZATION = Object.freeze({
|
|
25
|
+
...NODE_NATIVE_JWT_REALIZATIONS.HS256,
|
|
26
|
+
semanticOwner: '@pulse-compute/crypto',
|
|
27
|
+
automaticFallback: false
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
let providerModule;
|
|
31
|
+
|
|
32
|
+
function dataRecord(value) {
|
|
33
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
34
|
+
let prototype;
|
|
35
|
+
try { prototype = Object.getPrototypeOf(value); }
|
|
36
|
+
catch { return undefined; }
|
|
37
|
+
if (prototype !== Object.prototype && prototype !== null) return undefined;
|
|
38
|
+
const output = new Map();
|
|
39
|
+
try {
|
|
40
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
41
|
+
if (typeof key !== 'string') return undefined;
|
|
42
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
43
|
+
if (
|
|
44
|
+
!descriptor
|
|
45
|
+
|| !descriptor.enumerable
|
|
46
|
+
|| !Object.prototype.hasOwnProperty.call(descriptor, 'value')
|
|
47
|
+
) return undefined;
|
|
48
|
+
output.set(key, descriptor.value);
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return output;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function loadJwtProvider() {
|
|
57
|
+
if (!providerModule) providerModule = import('@pulse-compute/jwt/provider');
|
|
58
|
+
return providerModule;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function requestActive(executionOptions, jwtProvider) {
|
|
62
|
+
const execution = dataRecord(executionOptions);
|
|
63
|
+
const signal = execution && execution.get('signal');
|
|
64
|
+
if (signal && signal.aborted) {
|
|
65
|
+
throw jwtProvider.jwtError('PULSE_JWT_OPERATION_FAILED', {
|
|
66
|
+
category: 'request-cancelled',
|
|
67
|
+
automaticFallback: false
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function requireCryptoVerifier(executionOptions, jwtProvider, algorithm) {
|
|
73
|
+
const execution = dataRecord(executionOptions);
|
|
74
|
+
const realization = dataRecord(execution && execution.get('cryptoRealization'));
|
|
75
|
+
const verifier = execution && execution.get('cryptoVerifier');
|
|
76
|
+
const expected = NODE_NATIVE_JWT_REALIZATIONS[algorithm];
|
|
77
|
+
const algorithms = realization && realization.get('algorithms');
|
|
78
|
+
const selected = Array.isArray(algorithms)
|
|
79
|
+
? algorithms.map(dataRecord).find((entry) => (
|
|
80
|
+
entry
|
|
81
|
+
&& expected
|
|
82
|
+
&& entry.get('algorithm') === expected.algorithm
|
|
83
|
+
&& entry.get('realization') === expected.realization
|
|
84
|
+
&& entry.get('implementation') === expected.implementation
|
|
85
|
+
&& entry.get('guestUnitRequired') === expected.guestUnitRequired
|
|
86
|
+
&& entry.get('available') === true
|
|
87
|
+
))
|
|
88
|
+
: undefined;
|
|
89
|
+
if (
|
|
90
|
+
!expected
|
|
91
|
+
|| !realization
|
|
92
|
+
|| realization.get('semanticOwner') !== '@pulse-compute/crypto'
|
|
93
|
+
|| realization.get('automaticFallback') !== false
|
|
94
|
+
|| !selected
|
|
95
|
+
|| !dataRecord(verifier)
|
|
96
|
+
) {
|
|
97
|
+
throw jwtProvider.jwtError('PULSE_JWT_TARGET_UNSUPPORTED', {
|
|
98
|
+
category: 'crypto-realization',
|
|
99
|
+
realization: expected && expected.realization,
|
|
100
|
+
implementation: expected && expected.implementation,
|
|
101
|
+
automaticFallback: false
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return verifier;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function keyArtifact(executionOptions, artifactId, jwtProvider) {
|
|
108
|
+
const execution = dataRecord(executionOptions);
|
|
109
|
+
const artifacts = execution && execution.get('packageArtifacts');
|
|
110
|
+
const matches = Array.isArray(artifacts)
|
|
111
|
+
? artifacts.filter((artifact) => {
|
|
112
|
+
const record = dataRecord(artifact);
|
|
113
|
+
return record && record.get('id') === artifactId;
|
|
114
|
+
})
|
|
115
|
+
: [];
|
|
116
|
+
if (matches.length !== 1) {
|
|
117
|
+
throw jwtProvider.jwtError('PULSE_JWT_KEY_INVALID', {
|
|
118
|
+
category: 'key-artifact',
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const artifact = dataRecord(matches[0]);
|
|
122
|
+
const data = artifact && dataRecord(artifact.get('data'));
|
|
123
|
+
if (
|
|
124
|
+
!artifact
|
|
125
|
+
|| artifact.get('version') !== 'pulse.jwt-es256-key-artifact.v1'
|
|
126
|
+
|| artifact.get('contractId') !== 'pulse.jwt'
|
|
127
|
+
|| artifact.get('package') !== '@pulse-compute/jwt'
|
|
128
|
+
|| artifact.get('kind') !== 'jwt-es256-static-public-key'
|
|
129
|
+
|| !data
|
|
130
|
+
) {
|
|
131
|
+
throw jwtProvider.jwtError('PULSE_JWT_KEY_INVALID', {
|
|
132
|
+
category: 'key-artifact',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return Object.freeze(Object.fromEntries(data));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function effectInput(effect, executionOptions, jwtProvider) {
|
|
139
|
+
const record = dataRecord(effect);
|
|
140
|
+
const payload = dataRecord(record && record.get('payload'));
|
|
141
|
+
const resource = dataRecord(record && record.get('resource'));
|
|
142
|
+
if (
|
|
143
|
+
!record
|
|
144
|
+
|| record.get('kind') !== 'jwt.verify'
|
|
145
|
+
|| record.get('contractId') !== 'pulse.jwt'
|
|
146
|
+
|| record.get('operation') !== 'verify'
|
|
147
|
+
|| !payload
|
|
148
|
+
|| !resource
|
|
149
|
+
) {
|
|
150
|
+
throw jwtProvider.jwtError('PULSE_JWT_OPERATION_FAILED', {
|
|
151
|
+
category: 'verifier-input'
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const authorization = payload.get('token');
|
|
155
|
+
if (authorization !== undefined && typeof authorization !== 'string') {
|
|
156
|
+
throw jwtProvider.jwtError('PULSE_JWT_BEARER_INVALID', {
|
|
157
|
+
category: 'authorization'
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const keyType = resource.get('keyType');
|
|
161
|
+
const binding = resource.get('secretBinding');
|
|
162
|
+
const artifactId = resource.get('keyArtifactId');
|
|
163
|
+
let key;
|
|
164
|
+
if (keyType === 'secret') {
|
|
165
|
+
if (
|
|
166
|
+
artifactId !== null
|
|
167
|
+
|| typeof binding !== 'string'
|
|
168
|
+
|| binding.length === 0
|
|
169
|
+
) throw jwtProvider.jwtError('PULSE_JWT_KEY_INVALID', {
|
|
170
|
+
category: 'secret-binding'
|
|
171
|
+
});
|
|
172
|
+
key = Object.freeze({ type: 'secret', binding });
|
|
173
|
+
} else if (keyType === 'jwk' || keyType === 'jwks') {
|
|
174
|
+
if (binding !== null || typeof artifactId !== 'string' || artifactId.length === 0) {
|
|
175
|
+
throw jwtProvider.jwtError('PULSE_JWT_KEY_INVALID', {
|
|
176
|
+
category: 'key-artifact'
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
key = keyArtifact(executionOptions, artifactId, jwtProvider);
|
|
180
|
+
if (key.type !== keyType) throw jwtProvider.jwtError('PULSE_JWT_KEY_INVALID', {
|
|
181
|
+
category: 'key-artifact'
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
throw jwtProvider.jwtError('PULSE_JWT_KEY_INVALID', { category: 'key' });
|
|
185
|
+
}
|
|
186
|
+
const policy = Object.fromEntries(
|
|
187
|
+
[...payload.entries()].filter(([name]) => name !== 'token')
|
|
188
|
+
);
|
|
189
|
+
return Object.freeze({
|
|
190
|
+
token: jwtProvider.bearerFromAuthorizationValue(authorization),
|
|
191
|
+
options: Object.freeze({
|
|
192
|
+
...policy,
|
|
193
|
+
key
|
|
194
|
+
})
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function createNodeNativeJwtVerify(baseOptions = {}) {
|
|
199
|
+
const secretLookup = baseOptions.secretLookup;
|
|
200
|
+
const captureWallClock = baseOptions.captureWallClock
|
|
201
|
+
|| (() => Object.freeze({
|
|
202
|
+
unixEpochSeconds: Date.now() / 1000,
|
|
203
|
+
trusted: true
|
|
204
|
+
}));
|
|
205
|
+
return async function verifyNodeNativeJwt(effect, executionOptions = {}) {
|
|
206
|
+
const jwtProvider = await loadJwtProvider();
|
|
207
|
+
requestActive(executionOptions, jwtProvider);
|
|
208
|
+
const input = effectInput(effect, executionOptions, jwtProvider);
|
|
209
|
+
const selectedCrypto = requireCryptoVerifier(
|
|
210
|
+
executionOptions,
|
|
211
|
+
jwtProvider,
|
|
212
|
+
input.options.algorithms[0]
|
|
213
|
+
);
|
|
214
|
+
const execution = dataRecord(executionOptions);
|
|
215
|
+
const executionClock = execution && execution.get('captureJwtWallClock');
|
|
216
|
+
const host = {
|
|
217
|
+
async captureWallClock() {
|
|
218
|
+
requestActive(executionOptions, jwtProvider);
|
|
219
|
+
const captured = await (
|
|
220
|
+
typeof executionClock === 'function'
|
|
221
|
+
? executionClock()
|
|
222
|
+
: captureWallClock()
|
|
223
|
+
);
|
|
224
|
+
requestActive(executionOptions, jwtProvider);
|
|
225
|
+
return captured;
|
|
226
|
+
},
|
|
227
|
+
resolveSecret: typeof secretLookup === 'function'
|
|
228
|
+
? async (binding) => {
|
|
229
|
+
requestActive(executionOptions, jwtProvider);
|
|
230
|
+
const secret = await secretLookup(binding, executionOptions);
|
|
231
|
+
requestActive(executionOptions, jwtProvider);
|
|
232
|
+
return secret;
|
|
233
|
+
}
|
|
234
|
+
: undefined,
|
|
235
|
+
registerSensitiveValue(value) {
|
|
236
|
+
const register = execution && execution.get('registerRedactionValue');
|
|
237
|
+
if (typeof register === 'function') register(value);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const validateSchemaValue = execution && execution.get('validateSchemaValue');
|
|
241
|
+
if (typeof validateSchemaValue === 'function') {
|
|
242
|
+
host.validateClaims = async (schemaId, claims, context) => {
|
|
243
|
+
requestActive(executionOptions, jwtProvider);
|
|
244
|
+
const result = await validateSchemaValue(schemaId, claims, context);
|
|
245
|
+
requestActive(executionOptions, jwtProvider);
|
|
246
|
+
return result;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const result = await jwtProvider.verifyJwtWithCrypto(
|
|
250
|
+
input,
|
|
251
|
+
Object.freeze(host),
|
|
252
|
+
selectedCrypto
|
|
253
|
+
);
|
|
254
|
+
requestActive(executionOptions, jwtProvider);
|
|
255
|
+
return result;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module.exports = Object.freeze({
|
|
260
|
+
NODE_NATIVE_JWT_VERIFIER_VERSION,
|
|
261
|
+
NODE_NATIVE_JWT_REALIZATION,
|
|
262
|
+
NODE_NATIVE_JWT_REALIZATIONS,
|
|
263
|
+
createNodeNativeJwtVerify
|
|
264
|
+
});
|