@pulse-compute/wasm-host-runtime 1.0.0-beta.1 → 1.0.0-beta.3
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
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
> **Install directly:** No for application projects; it is installed transitively.<br>
|
|
7
7
|
> **Supported entry points:** `None for application authors.`<br>
|
|
8
8
|
> **Stability:** Internal compiler/runtime interface synchronized with this release set.<br>
|
|
9
|
-
> **
|
|
9
|
+
> **npm:** [`@pulse-compute/wasm-host-runtime`](https://www.npmjs.com/package/@pulse-compute/wasm-host-runtime)<br>
|
|
10
|
+
> **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.3/packages/implementation-packages/)
|
|
10
11
|
>
|
|
11
|
-
> This release-status block is generated from the synchronized `Pulse 1.0.0-beta.
|
|
12
|
+
> This release-status block is generated from the synchronized `Pulse 1.0.0-beta.3` package policy.
|
|
12
13
|
<!-- pulse-package-status:end -->
|
|
13
14
|
|
|
14
15
|
Package-owned PulseWasm host-runtime build-time builders and local runtime helpers.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pulse-compute/wasm-host-runtime",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "^22.14.0 || ^24.0.0"
|
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
"./runtime/canonical-native-host": "./src/runtime/canonical-native-host.js"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@pulse-compute/
|
|
32
|
-
"@pulse-compute/
|
|
33
|
-
"@pulse-compute/wasm-
|
|
31
|
+
"@pulse-compute/crypto": "1.0.0-beta.3",
|
|
32
|
+
"@pulse-compute/runtime": "1.0.0-beta.3",
|
|
33
|
+
"@pulse-compute/wasm-build-support": "1.0.0-beta.3",
|
|
34
|
+
"@pulse-compute/wasm-contracts": "1.0.0-beta.3",
|
|
35
|
+
"@pulse-compute/wasm-schema-json": "1.0.0-beta.3"
|
|
34
36
|
},
|
|
35
37
|
"publishConfig": {
|
|
36
38
|
"access": "public"
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
"url": "git+https://github.com/pulse-compute/pulse.git",
|
|
47
49
|
"directory": "wasm/packages/host-runtime"
|
|
48
50
|
},
|
|
49
|
-
"homepage": "https://pulsecompute.io/v1.0.0-beta.
|
|
51
|
+
"homepage": "https://pulsecompute.io/v1.0.0-beta.3/packages/implementation-packages/",
|
|
50
52
|
"bugs": {
|
|
51
53
|
"url": "https://github.com/pulse-compute/pulse/issues"
|
|
52
54
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const portableKv = require('@pulse-compute/runtime/host');
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
function loadRuntimeApi() {
|
|
@@ -886,6 +887,7 @@ function normalizeProviderEffect(effect, schemaCodecs, options = {}) {
|
|
|
886
887
|
if (!effect || typeof effect !== 'object') {
|
|
887
888
|
throw new CanonicalRuntimeError('CanonicalEffectProtocolError', 'PULSE_CANONICAL_EFFECT_PROTOCOL', 'Canonical provider effect must be an object.', { effect });
|
|
888
889
|
}
|
|
890
|
+
if (portableKv.isConditionalKv(effect.kind)) return Object.freeze({ ...portableKv.admitConditionalKv(effect, options), id: effect.id, groupKey: effect.groupKey, source: effect.source });
|
|
889
891
|
if (effect.kind === 'fetch') return normalizeFetchEffect(effect, schemaCodecs, options);
|
|
890
892
|
const id = String(effect.id || '');
|
|
891
893
|
if (!id) throw new CanonicalRuntimeError('CanonicalEffectProtocolError', 'PULSE_CANONICAL_EFFECT_PROTOCOL', 'Canonical provider effect requires an id.', { effect });
|
|
@@ -980,6 +982,7 @@ function effectTraceStart(adapterId, executionId, normalized, sensitiveValues) {
|
|
|
980
982
|
headers: redactedHeaders(normalized.init.headers, sensitiveValues)
|
|
981
983
|
});
|
|
982
984
|
if (normalized.kind === 'config.get' || normalized.kind === 'secret.get') return Object.freeze({ ...base, name: redactString(normalized.name, sensitiveValues) });
|
|
985
|
+
if (portableKv.isConditionalKv(normalized.kind)) return Object.freeze({ ...base, key: '<redacted>', generation: '<redacted>', value: '<redacted>' });
|
|
983
986
|
if (normalized.kind === 'kv.get' || normalized.kind === 'kv.put') return Object.freeze({ ...base, store: redactString(normalized.store, sensitiveValues), key: redactString(normalized.key, sensitiveValues) });
|
|
984
987
|
if (normalized.kind === 'event.emit') return Object.freeze({ ...base, eventType: redactString(normalized.frame.type, sensitiveValues), schemaId: normalized.frame.schemaId });
|
|
985
988
|
if (normalized.package) return Object.freeze({ ...base, package: normalized.package, contractId: normalized.contractId, operation: normalized.operation });
|
|
@@ -991,6 +994,7 @@ function effectTraceResolved(adapterId, executionId, normalized, value) {
|
|
|
991
994
|
if (normalized.kind === 'fetch') return Object.freeze({ ...base, status: value.status, bodyClass: value.bodyClass });
|
|
992
995
|
if (normalized.kind === 'secret.get') return Object.freeze({ ...base, hit: value !== undefined, value: '<redacted>' });
|
|
993
996
|
if (normalized.kind === 'config.get') return Object.freeze({ ...base, hit: value !== undefined });
|
|
997
|
+
if (portableKv.isConditionalKv(normalized.kind)) return Object.freeze({ ...base, status: value.status });
|
|
994
998
|
if (normalized.kind === 'kv.get') return Object.freeze({ ...base, hit: value !== undefined });
|
|
995
999
|
if (normalized.kind === 'kv.put') return Object.freeze({ ...base, stored: Boolean(value) });
|
|
996
1000
|
if (normalized.kind === 'event.emit') return Object.freeze({ ...base, accepted: value === undefined });
|
|
@@ -1288,6 +1292,7 @@ function normalizeProviderAdapter(input = {}) {
|
|
|
1288
1292
|
version: String(input.version || `pulse.canonical-${id}-provider.v1`),
|
|
1289
1293
|
createCapabilities: typeof input.createCapabilities === 'function' ? input.createCapabilities : () => ({}),
|
|
1290
1294
|
dispatchEffect,
|
|
1295
|
+
prepareConditionalKv: typeof input.prepareConditionalKv === 'function' ? input.prepareConditionalKv.bind(input) : undefined,
|
|
1291
1296
|
resultMetadata: typeof input.resultMetadata === 'function' ? input.resultMetadata : () => undefined,
|
|
1292
1297
|
disposeExecution: typeof input.disposeExecution === 'function' ? input.disposeExecution : () => undefined
|
|
1293
1298
|
});
|
|
@@ -1361,10 +1366,15 @@ function createCanonicalHostRuntime(options = {}) {
|
|
|
1361
1366
|
|
|
1362
1367
|
async function dispatchOne(effect) {
|
|
1363
1368
|
const normalized = normalizeProviderEffect(effect, schemaCodecs, runtimeOptions);
|
|
1369
|
+
const conditional = portableKv.isConditionalKv(normalized.kind);
|
|
1370
|
+
if (conditional) portableKv.registerKvRedactions(normalized, (value) => sensitiveValues.add(value));
|
|
1364
1371
|
recordTrace(effectTraceStart(adapter.id, executionId, normalized, sensitiveValues));
|
|
1365
1372
|
dispatchedEffects += 1;
|
|
1366
1373
|
try {
|
|
1367
|
-
|
|
1374
|
+
const effectExecution = { ...options, ...executionOptions, metadata, executionId, trace: traceSink, registerRedactionValue: (value) => sensitiveValues.add(value), onKvObservation: recordTrace };
|
|
1375
|
+
let snapshot = conditional
|
|
1376
|
+
? await portableKv.executeConditionalKv(normalized, adapter.prepareConditionalKv || ((_admitted, kvExecution) => () => adapter.dispatchEffect(normalized, kvExecution)), effectExecution, runtimeOptions)
|
|
1377
|
+
: await adapter.dispatchEffect(normalized, effectExecution);
|
|
1368
1378
|
if (normalized.kind === 'config.get' || normalized.kind === 'secret.get') {
|
|
1369
1379
|
if (snapshot !== undefined && typeof snapshot !== 'string') {
|
|
1370
1380
|
throw new CanonicalRuntimeError(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const portableKv = require('@pulse-compute/runtime/host');
|
|
2
3
|
|
|
3
4
|
const crypto = require('node:crypto');
|
|
4
5
|
const {
|
|
@@ -81,6 +82,7 @@ function normalizeProviderAdapter(input) {
|
|
|
81
82
|
if (!input || typeof input !== 'object') {
|
|
82
83
|
return Object.freeze({
|
|
83
84
|
id: 'unconfigured',
|
|
85
|
+
prepareConditionalKv() { return undefined; },
|
|
84
86
|
version: 'pulse.canonical-native-unconfigured-provider.v1',
|
|
85
87
|
async dispatchEffect(effect) {
|
|
86
88
|
throw new CanonicalNativeHostError(`No provider adapter is configured for native effect ${effect.kind}.`, 'PULSE_PROVIDER_CAPABILITY_MISSING', { effectId: effect.id, kind: effect.kind });
|
|
@@ -104,6 +106,7 @@ function normalizeProviderAdapter(input) {
|
|
|
104
106
|
id,
|
|
105
107
|
version: String(input.version || `pulse.canonical-native-${id}-provider.v1`),
|
|
106
108
|
dispatchEffect,
|
|
109
|
+
prepareConditionalKv: typeof input.prepareConditionalKv === 'function' ? input.prepareConditionalKv.bind(input) : undefined,
|
|
107
110
|
resultMetadata: typeof input.resultMetadata === 'function' ? input.resultMetadata.bind(input) : () => undefined,
|
|
108
111
|
disposeExecution: typeof input.disposeExecution === 'function' ? input.disposeExecution.bind(input) : () => undefined
|
|
109
112
|
});
|
|
@@ -247,6 +250,7 @@ function nativeEffect(planEffect, payload) {
|
|
|
247
250
|
}
|
|
248
251
|
if (planEffect.kind === 'config.get' || planEffect.kind === 'secret.get') return { ...base, name: payload.name };
|
|
249
252
|
if (planEffect.kind === 'kv.get' || planEffect.kind === 'kv.put') return { ...base, store: kvStoreName(payload.store), key: payload.key, value: planEffect.kind === 'kv.put' ? payload.value : undefined };
|
|
253
|
+
if (portableKv.isConditionalKv(planEffect.kind)) return { ...base, store: kvStoreName(payload.store), key: payload.key, value: payload.value, generation: payload.generation };
|
|
250
254
|
if (planEffect.kind === 'event.emit') {
|
|
251
255
|
return {
|
|
252
256
|
...base,
|
|
@@ -724,7 +728,9 @@ function instantiateCanonicalNativeModule(compiled, options = {}) {
|
|
|
724
728
|
const effect = plan.effects && plan.effects[index];
|
|
725
729
|
if (!effect) throw new CanonicalNativeHostError(`Native module requested unknown effect index ${index}.`, 'PULSE_CANONICAL_NATIVE_EFFECT_INDEX_INVALID', { effectIndex: index });
|
|
726
730
|
const payload = value(payloadHandle);
|
|
727
|
-
|
|
731
|
+
const kvAdmission = portableKv.isConditionalKv(effect.kind) ? portableKv.admitConditionalKv(nativeEffect(effect, payload), options) : undefined;
|
|
732
|
+
if (kvAdmission) portableKv.registerKvRedactions(kvAdmission, (value) => sensitiveValues.add(value));
|
|
733
|
+
pending.push(Object.freeze({ index, effect, payload, ...(kvAdmission ? { kvAdmission } : {}) }));
|
|
728
734
|
}
|
|
729
735
|
};
|
|
730
736
|
|
|
@@ -782,7 +788,7 @@ function instantiateCanonicalNativeModule(compiled, options = {}) {
|
|
|
782
788
|
throw new CanonicalNativeHostError(`Native host cannot prepare an unknown effect index ${effectIndex}.`, 'PULSE_CANONICAL_NATIVE_EFFECT_INDEX_INVALID', { effectIndex });
|
|
783
789
|
}
|
|
784
790
|
|
|
785
|
-
let result = rawResult;
|
|
791
|
+
let result = portableKv.isConditionalKv(effect.kind) ? portableKv.normalizeConditionalKvResult(effect, rawResult, options) : rawResult;
|
|
786
792
|
if (effect.kind === 'event.emit' && result !== undefined) {
|
|
787
793
|
throw new canonicalRuntime.CanonicalRuntimeError(
|
|
788
794
|
'EventAcceptanceError',
|
|
@@ -992,7 +998,11 @@ async function executeCanonicalNativeInvocation(compiled, options = {}, invocati
|
|
|
992
998
|
if (eventMode && options.signal && options.signal.aborted) {
|
|
993
999
|
throw nativeEventCancelled('Native event execution was cancelled before effect dispatch.');
|
|
994
1000
|
}
|
|
995
|
-
const rawEffect = nativeEffect(entry.effect, entry.payload);
|
|
1001
|
+
const rawEffect = { ...nativeEffect(entry.effect, entry.payload), ...entry.kvAdmission };
|
|
1002
|
+
const conditional = portableKv.isConditionalKv(rawEffect.kind);
|
|
1003
|
+
const privateEffect = conditional || ((controller.plan.packages && controller.plan.packages.effects) || []).some((item) =>
|
|
1004
|
+
item.package === entry.effect.package && item.contractId === entry.effect.contractId && item.operation === entry.effect.operation
|
|
1005
|
+
&& item.redaction && Object.keys(item.redaction).length > 0);
|
|
996
1006
|
const normalized = canonicalRuntime.normalizeProviderEffect(rawEffect, controller.schemaCodecs, {
|
|
997
1007
|
...options,
|
|
998
1008
|
strict: options.strict === undefined
|
|
@@ -1002,8 +1012,8 @@ async function executeCanonicalNativeInvocation(compiled, options = {}, invocati
|
|
|
1002
1012
|
target: 'native',
|
|
1003
1013
|
provider: adapter.id
|
|
1004
1014
|
});
|
|
1005
|
-
controller.trace.push(Object.freeze(redactValue({ type: 'native-effect-start', executionId, provider: adapter.id, effectId: normalized.id, kind: normalized.kind, payload: entry.payload }, controller.sensitiveValues)));
|
|
1006
|
-
const
|
|
1015
|
+
controller.trace.push(Object.freeze(redactValue({ type: 'native-effect-start', executionId, provider: adapter.id, effectId: normalized.id, kind: normalized.kind, payload: privateEffect ? '<redacted>' : entry.payload }, controller.sensitiveValues)));
|
|
1016
|
+
const effectExecution = {
|
|
1007
1017
|
...options,
|
|
1008
1018
|
executionId,
|
|
1009
1019
|
metadata: compiled && compiled.plan ? compiled.plan.canonical : controller.plan.canonical,
|
|
@@ -1018,9 +1028,13 @@ async function executeCanonicalNativeInvocation(compiled, options = {}, invocati
|
|
|
1018
1028
|
validateSchemaValue(schemaId, value, context = {}) {
|
|
1019
1029
|
return controller.schemaCodecs.decode(String(schemaId), value, context.source || 'package-effect');
|
|
1020
1030
|
}
|
|
1021
|
-
}
|
|
1031
|
+
};
|
|
1032
|
+
const rawResult = conditional
|
|
1033
|
+
? await portableKv.executeConditionalKv(normalized, adapter.prepareConditionalKv || ((_admitted, kvExecution) => () => adapter.dispatchEffect(normalized, kvExecution)),
|
|
1034
|
+
{ ...effectExecution, onKvObservation: (event) => controller.trace.push(event) }, options)
|
|
1035
|
+
: await raceNativeEventSignal(adapter.dispatchEffect(normalized, effectExecution), eventMode ? options.signal : undefined);
|
|
1022
1036
|
const result = controller.prepareEffectResult(entry.index, rawResult);
|
|
1023
|
-
controller.trace.push(Object.freeze(redactValue({ type: 'native-effect-resolved', executionId, provider: adapter.id, effectId: normalized.id, kind: normalized.kind, result: entry.effect.kind === 'secret.get' ? '<redacted>' : result && typeof result.toJSON === 'function' ? result.toJSON() : result }, controller.sensitiveValues)));
|
|
1037
|
+
controller.trace.push(Object.freeze(redactValue({ type: 'native-effect-resolved', executionId, provider: adapter.id, effectId: normalized.id, kind: normalized.kind, result: privateEffect || entry.effect.kind === 'secret.get' ? '<redacted>' : result && typeof result.toJSON === 'function' ? result.toJSON() : result }, controller.sensitiveValues)));
|
|
1024
1038
|
resolutionOrder.push(entry.effect.id);
|
|
1025
1039
|
return { entry, result };
|
|
1026
1040
|
}));
|
|
@@ -244,7 +244,13 @@ function createNativeGuestSourceCryptoVerifier(input = {}) {
|
|
|
244
244
|
&& memory.buffer.byteLength === ES256_MEMORY_BYTES
|
|
245
245
|
&& typeof verifyEs256 === 'function'
|
|
246
246
|
);
|
|
247
|
+
const byteAlgorithms = ['SHA-256', 'HMAC-SHA256'].filter((algorithm) =>
|
|
248
|
+
selectedAlgorithm(plan, algorithm, HS256_REALIZATION, 'guest-source', CRYPTO_GUEST_SOURCE_IMPLEMENTATION));
|
|
249
|
+
const digestMac = byteAlgorithms.length > 0
|
|
250
|
+
? require('@pulse-compute/crypto/pulsewasm-native').bindNativeDigestMac(moduleExports) : undefined;
|
|
247
251
|
const selectedRealizations = Object.freeze([
|
|
252
|
+
...byteAlgorithms.map((algorithm) => Object.freeze({ algorithm, realization: HS256_REALIZATION,
|
|
253
|
+
implementation: CRYPTO_GUEST_SOURCE_IMPLEMENTATION, guestUnitRequired: false, available: Boolean(digestMac) })),
|
|
248
254
|
...(selected ? [Object.freeze({
|
|
249
255
|
algorithm: 'HS256',
|
|
250
256
|
realization: HS256_REALIZATION,
|
|
@@ -271,6 +277,10 @@ function createNativeGuestSourceCryptoVerifier(input = {}) {
|
|
|
271
277
|
});
|
|
272
278
|
|
|
273
279
|
const verifier = Object.freeze({
|
|
280
|
+
...(digestMac ? { bytes: Object.freeze({
|
|
281
|
+
...(byteAlgorithms.includes('SHA-256') ? { sha256: digestMac.sha256 } : {}),
|
|
282
|
+
...(byteAlgorithms.includes('HMAC-SHA256') ? { hmacSha256: digestMac.hmacSha256 } : {})
|
|
283
|
+
}) } : {}),
|
|
274
284
|
mac: Object.freeze({
|
|
275
285
|
verify(request) {
|
|
276
286
|
const normalized = normalizeRequest(request);
|