@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.
Files changed (38) hide show
  1. package/README.md +60 -1
  2. package/package.json +61 -6
  3. package/src/capabilities.js +56 -0
  4. package/src/compiler/assets-provider-proof.js +434 -0
  5. package/src/compiler/backend-json-request-body-proof.js +90 -0
  6. package/src/compiler/index.js +38 -0
  7. package/src/compiler/node-adapter.js +452 -0
  8. package/src/compiler/node-compiled-wasm-assets-lifecycle-proof.js +435 -0
  9. package/src/compiler/request-json-body-proof.js +181 -0
  10. package/src/compiler/route-handler-compiled-wasm-bridge.js +323 -0
  11. package/src/compiler/route-handler-effect-proof.js +215 -0
  12. package/src/compiler/route-handler-live-origin-proof.js +372 -0
  13. package/src/compiler/schema-decode-result-proof.js +180 -0
  14. package/src/compiler/schema-response-codec-proof.js +149 -0
  15. package/src/events/reference-adapter.js +145 -0
  16. package/src/index.js +57 -0
  17. package/src/javascript/assets-adapter.js +68 -0
  18. package/src/javascript/bindings-adapter.js +120 -0
  19. package/src/javascript/fetch-adapter.js +187 -0
  20. package/src/javascript/grip-broadcast.js +243 -0
  21. package/src/javascript/jwt-verifier.js +137 -0
  22. package/src/javascript/lifecycle.js +69 -0
  23. package/src/javascript/node-adapter.js +281 -0
  24. package/src/javascript/package-effects.js +78 -0
  25. package/src/javascript/runtime-host.js +165 -0
  26. package/src/javascript/source-package.js +439 -0
  27. package/src/javascript/support.js +205 -0
  28. package/src/javascript/target-support-policy.js +134 -0
  29. package/src/javascript/target.js +37 -0
  30. package/src/javascript/test-runtime.js +111 -0
  31. package/src/native/target.js +78 -0
  32. package/src/runtime/assets-provider.js +776 -0
  33. package/src/runtime/canonical-api-runtime.js +632 -0
  34. package/src/runtime/compiled-wasm-node-adapter-kv.js +15 -0
  35. package/src/runtime/index.js +17 -0
  36. package/src/runtime/jwt-verifier.js +264 -0
  37. package/src/runtime/route-handler-effects.js +757 -0
  38. 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
+ });