@pulse-compute/wasm-contracts 0.0.0 → 1.0.0-beta.2
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 +35 -2
- package/package.json +124 -6
- package/src/assets/contracts.js +296 -0
- package/src/config/provider-resolution.js +54 -0
- package/src/config/runtime-provider-kv.js +73 -0
- package/src/config/symbolic-refs.js +192 -0
- package/src/crypto/contracts.js +647 -0
- package/src/definitions/config-schema.js +30 -0
- package/src/definitions/handler-roles.js +66 -0
- package/src/definitions/path-grammar.js +11 -0
- package/src/definitions/router-api.js +149 -0
- package/src/diagnostics/codes.js +42 -0
- package/src/diagnostics/reporter.js +20 -0
- package/src/diagnostics.js +139 -0
- package/src/dispatch-table.js +23 -0
- package/src/entities/catalog.d.ts +7 -0
- package/src/entities/catalog.js +87 -0
- package/src/entities/contracts.d.ts +95 -0
- package/src/entities/contracts.js +122 -0
- package/src/entities/json-rpc.d.ts +27 -0
- package/src/entities/json-rpc.js +69 -0
- package/src/entities/runtime.d.ts +67 -0
- package/src/entities/runtime.js +288 -0
- package/src/events/contracts.js +456 -0
- package/src/execution-plan.js +42 -0
- package/src/grip/contracts.js +106 -0
- package/src/handler/backend-json-request-body.js +58 -0
- package/src/handler/beta-lifecycle-golden.js +97 -0
- package/src/handler/canonical-native-plan.js +143 -0
- package/src/handler/canonical-native-runtime.js +168 -0
- package/src/handler/canonical-runtime.js +13 -0
- package/src/handler/context.js +131 -0
- package/src/handler/ctx-contract.js +71 -0
- package/src/handler/doctor-readiness-aggregator.js +67 -0
- package/src/handler/effects.js +446 -0
- package/src/handler/io-lifecycle.js +134 -0
- package/src/handler/lifecycle-authoring-alignment.js +56 -0
- package/src/handler/lifecycle-readiness-aggregate.js +93 -0
- package/src/handler/reference-node-lifecycle-golden.js +73 -0
- package/src/handler/request-json-body.js +147 -0
- package/src/handler/runtime-api.js +471 -0
- package/src/handler/schema-decode-result.js +135 -0
- package/src/handler/schema-response-codec.js +141 -0
- package/src/handler/surface-contract.js +601 -0
- package/src/handler/types-contract.js +51 -0
- package/src/handler-table.js +28 -0
- package/src/host/backend-capabilities.js +24 -0
- package/src/host/capabilities.js +273 -0
- package/src/host/channel-broadcaster.js +60 -0
- package/src/host/json-body.js +80 -0
- package/src/host/request-result-headers.js +38 -0
- package/src/host/streaming-passthrough.js +90 -0
- package/src/host/wasm-host-abi.js +154 -0
- package/src/index.js +75 -0
- package/src/jwt/contracts.js +427 -0
- package/src/library/api-surface.js +99 -0
- package/src/library/contracts.js +273 -0
- package/src/library/manifest.js +317 -0
- package/src/library/second-package-owned-lowering.js +39 -0
- package/src/library/sidecars.js +147 -0
- package/src/logging.js +89 -0
- package/src/package/package-contract.js +975 -0
- package/src/package/package-contract.schema.json +303 -0
- package/src/package/runtime-pulse-live.js +100 -0
- package/src/path-table.js +12 -0
- package/src/path.js +275 -0
- package/src/project/config-factory.js +48 -0
- package/src/project/config-plan.js +409 -0
- package/src/project/config-runtime.js +46 -0
- package/src/project/javascript-application.js +212 -0
- package/src/project/javascript-core-capabilities.js +44 -0
- package/src/project/native-optimization.js +9 -0
- package/src/project/package-ownership.js +242 -0
- package/src/project/package-product-projection.js +171 -0
- package/src/project/reachable-graph-identity.js +219 -0
- package/src/project/reachable-graph-projections.js +279 -0
- package/src/project/reachable-graph-resolver.js +303 -0
- package/src/project/reachable-graph-v2.js +430 -0
- package/src/project/reachable-graph-v2.schema.json +285 -0
- package/src/project/reachable-graph.js +199 -0
- package/src/project/reachable-graph.schema.json +222 -0
- package/src/project/target-support-evidence.js +559 -0
- package/src/provider/canonical-provider.js +239 -0
- package/src/provider/final-wasm-policy.js +87 -0
- package/src/provider/toolchain.js +771 -0
- package/src/route-plan.js +43 -0
- package/src/s3/contracts.js +12 -0
- package/src/schema-json/compile.js +63 -0
- package/src/schema-json/contracts.js +10 -0
- package/src/schema-json/generic-parser.js +62 -0
- package/src/schema-json/registry.js +359 -0
- package/src/schema-json/semantic-trace.js +241 -0
- package/src/schema-json/v1.js +65 -0
- package/src/schema-json/v2.js +110 -0
- package/src/sha256.js +104 -0
- package/src/spine.js +381 -0
- package/src/stable-id.js +142 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ABI_VERSION = 'pulsewasm.wasm-host-abi.v4';
|
|
4
|
+
const ABI_PHASE = '12D';
|
|
5
|
+
|
|
6
|
+
const METHOD_CODES = Object.freeze({
|
|
7
|
+
UNKNOWN: 0,
|
|
8
|
+
GET: 1,
|
|
9
|
+
POST: 2,
|
|
10
|
+
PUT: 3,
|
|
11
|
+
PATCH: 4,
|
|
12
|
+
DELETE: 5,
|
|
13
|
+
OPTIONS: 6,
|
|
14
|
+
HEAD: 7
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const RESULT_KINDS = Object.freeze({
|
|
18
|
+
RESULT_NONE: 0,
|
|
19
|
+
RESULT_TEXT: 1,
|
|
20
|
+
RESULT_JSON: 2,
|
|
21
|
+
RESULT_BINARY: 3,
|
|
22
|
+
RESULT_EMPTY: 4,
|
|
23
|
+
RESULT_STREAM: 5
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const NEXT_OUTCOMES = Object.freeze({
|
|
27
|
+
NEXT_NONE: 0,
|
|
28
|
+
NEXT_NORMAL: 1,
|
|
29
|
+
NEXT_ERROR: 2
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function fn(name, params, returns = 'void', notes = []) {
|
|
33
|
+
return { name, params, returns, notes };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildWasmHostAbiExportGroups() {
|
|
37
|
+
const memory = [
|
|
38
|
+
fn('memory', [], 'memory', ['Exported linear memory.']),
|
|
39
|
+
fn('pulse_alloc', [{ name: 'byteLength', type: 'i32' }], 'i32', ['Allocates host-visible memory.']),
|
|
40
|
+
fn('pulse_free', [{ name: 'ptr', type: 'i32' }, { name: 'byteLength', type: 'i32' }], 'void', ['Releases memory previously allocated through pulse_alloc when supported.'])
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const lifecycle = [
|
|
44
|
+
fn('pulse_init', [], 'void', ['Initializes runtime globals.']),
|
|
45
|
+
fn('pulse_reset', [], 'void', ['Clears transient dispatch/result/error state.'])
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const dispatch = [
|
|
49
|
+
fn('pulse_execute_request', [{ name: 'methodCode', type: 'i32' }, { name: 'pathPtr', type: 'i32' }, { name: 'pathLen', type: 'i32' }], 'i32', ['Executes request routing with a UTF-8 full path.']),
|
|
50
|
+
fn('pulse_execute_connect', [{ name: 'methodCode', type: 'i32' }, { name: 'pathPtr', type: 'i32' }, { name: 'pathLen', type: 'i32' }], 'i32', ['Executes explicit connect lifecycle handlers for the matched scope.']),
|
|
51
|
+
fn('pulse_execute_disconnect', [{ name: 'methodCode', type: 'i32' }, { name: 'pathPtr', type: 'i32' }, { name: 'pathLen', type: 'i32' }], 'i32', ['Executes explicit disconnect lifecycle handlers for the matched scope.'])
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const result = [
|
|
55
|
+
fn('pulse_result_status', [], 'i32'),
|
|
56
|
+
fn('pulse_result_kind', [], 'i32'),
|
|
57
|
+
fn('pulse_result_body_ref', [], 'i32'),
|
|
58
|
+
fn('pulse_result_body_ptr', [], 'i32'),
|
|
59
|
+
fn('pulse_result_body_len', [], 'i32'),
|
|
60
|
+
fn('pulse_result_headers_ref', [], 'i32'),
|
|
61
|
+
fn('pulse_result_error_ref', [], 'i32'),
|
|
62
|
+
fn('pulse_result_stream_ref', [], 'i32'),
|
|
63
|
+
fn('pulse_result_response_ref', [], 'i32')
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const headers = [
|
|
67
|
+
fn('pulse_request_header_first', [{ name: 'ctxRef', type: 'i32' }, { name: 'namePtr', type: 'i32' }, { name: 'nameLen', type: 'i32' }], 'i32', ['Returns first matching request header StringRef, or 0.']),
|
|
68
|
+
fn('pulse_request_header_count', [{ name: 'ctxRef', type: 'i32' }, { name: 'namePtr', type: 'i32' }, { name: 'nameLen', type: 'i32' }], 'i32'),
|
|
69
|
+
fn('pulse_request_header_at', [{ name: 'ctxRef', type: 'i32' }, { name: 'namePtr', type: 'i32' }, { name: 'nameLen', type: 'i32' }, { name: 'index', type: 'i32' }], 'i32', ['Returns matching request header StringRef by repeated-header index, or 0.']),
|
|
70
|
+
fn('pulse_response_header_set', [{ name: 'ctxRef', type: 'i32' }, { name: 'namePtr', type: 'i32' }, { name: 'nameLen', type: 'i32' }, { name: 'valuePtr', type: 'i32' }, { name: 'valueLen', type: 'i32' }], 'void'),
|
|
71
|
+
fn('pulse_response_header_append', [{ name: 'ctxRef', type: 'i32' }, { name: 'namePtr', type: 'i32' }, { name: 'nameLen', type: 'i32' }, { name: 'valuePtr', type: 'i32' }, { name: 'valueLen', type: 'i32' }], 'void'),
|
|
72
|
+
fn('pulse_response_header_delete', [{ name: 'ctxRef', type: 'i32' }, { name: 'namePtr', type: 'i32' }, { name: 'nameLen', type: 'i32' }], 'void'),
|
|
73
|
+
fn('pulse_result_header_count', [], 'i32'),
|
|
74
|
+
fn('pulse_result_header_name_ptr', [{ name: 'index', type: 'i32' }], 'i32'),
|
|
75
|
+
fn('pulse_result_header_name_len', [{ name: 'index', type: 'i32' }], 'i32'),
|
|
76
|
+
fn('pulse_result_header_value_ptr', [{ name: 'index', type: 'i32' }], 'i32'),
|
|
77
|
+
fn('pulse_result_header_value_len', [{ name: 'index', type: 'i32' }], 'i32'),
|
|
78
|
+
fn('pulse_result_header_mode', [{ name: 'index', type: 'i32' }], 'i32', ['1 = set/override, 2 = append.']),
|
|
79
|
+
fn('pulse_result_header_delete_count', [], 'i32'),
|
|
80
|
+
fn('pulse_result_header_delete_name_ptr', [{ name: 'index', type: 'i32' }], 'i32'),
|
|
81
|
+
fn('pulse_result_header_delete_name_len', [{ name: 'index', type: 'i32' }], 'i32')
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const jsonBody = [
|
|
85
|
+
fn('pulse_request_body_text', [{ name: 'ctxRef', type: 'i32' }], 'i32', ['Returns request body text StringRef; body access is explicit and lazy.']),
|
|
86
|
+
fn('pulse_request_body_len', [{ name: 'ctxRef', type: 'i32' }], 'i32'),
|
|
87
|
+
fn('pulse_request_json_parse', [{ name: 'ctxRef', type: 'i32' }], 'i32', ['Returns JsonParseResultRef; parsing errors are explicit, not thrown.']),
|
|
88
|
+
fn('pulse_json_result_ok', [{ name: 'resultRef', type: 'i32' }], 'i32'),
|
|
89
|
+
fn('pulse_json_result_value', [{ name: 'resultRef', type: 'i32' }], 'i32'),
|
|
90
|
+
fn('pulse_json_result_error', [{ name: 'resultRef', type: 'i32' }], 'i32'),
|
|
91
|
+
fn('pulse_json_kind', [{ name: 'valueRef', type: 'i32' }], 'i32'),
|
|
92
|
+
fn('pulse_json_object_has', [{ name: 'valueRef', type: 'i32' }, { name: 'keyPtr', type: 'i32' }, { name: 'keyLen', type: 'i32' }], 'i32'),
|
|
93
|
+
fn('pulse_json_object_get', [{ name: 'valueRef', type: 'i32' }, { name: 'keyPtr', type: 'i32' }, { name: 'keyLen', type: 'i32' }], 'i32'),
|
|
94
|
+
fn('pulse_json_array_len', [{ name: 'valueRef', type: 'i32' }], 'i32'),
|
|
95
|
+
fn('pulse_json_array_get', [{ name: 'valueRef', type: 'i32' }, { name: 'index', type: 'i32' }], 'i32'),
|
|
96
|
+
fn('pulse_json_as_string', [{ name: 'valueRef', type: 'i32' }], 'i32'),
|
|
97
|
+
fn('pulse_json_as_f64', [{ name: 'valueRef', type: 'i32' }], 'f64'),
|
|
98
|
+
fn('pulse_json_as_bool', [{ name: 'valueRef', type: 'i32' }], 'i32'),
|
|
99
|
+
fn('pulse_result_json_text', [{ name: 'ctxRef', type: 'i32' }, { name: 'statusCode', type: 'i32' }, { name: 'jsonPtr', type: 'i32' }, { name: 'jsonLen', type: 'i32' }], 'void', ['Sets RESULT_JSON and Content-Type: application/json.'])
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const stringRefs = [
|
|
103
|
+
fn('pulse_string_ptr', [{ name: 'stringRef', type: 'i32' }], 'i32'),
|
|
104
|
+
fn('pulse_string_len', [{ name: 'stringRef', type: 'i32' }], 'i32'),
|
|
105
|
+
fn('pulse_string_is_null', [{ name: 'stringRef', type: 'i32' }], 'i32')
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
const ctxOps = [
|
|
109
|
+
fn('pulse_ctx_param', [{ name: 'ctxRef', type: 'i32' }, { name: 'namePtr', type: 'i32' }, { name: 'nameLen', type: 'i32' }], 'i32', ['Returns a StringRef, or 0 when missing.']),
|
|
110
|
+
fn('pulse_ctx_state_get', [{ name: 'ctxRef', type: 'i32' }, { name: 'keyPtr', type: 'i32' }, { name: 'keyLen', type: 'i32' }], 'i32', ['Returns a StringRef, or 0 when missing.']),
|
|
111
|
+
fn('pulse_ctx_state_set', [{ name: 'ctxRef', type: 'i32' }, { name: 'keyPtr', type: 'i32' }, { name: 'keyLen', type: 'i32' }, { name: 'valuePtr', type: 'i32' }, { name: 'valueLen', type: 'i32' }], 'void'),
|
|
112
|
+
fn('pulse_result_text', [{ name: 'ctxRef', type: 'i32' }, { name: 'statusCode', type: 'i32' }, { name: 'bodyPtr', type: 'i32' }, { name: 'bodyLen', type: 'i32' }], 'void'),
|
|
113
|
+
fn('pulse_result_empty', [{ name: 'ctxRef', type: 'i32' }, { name: 'statusCode', type: 'i32' }], 'void'),
|
|
114
|
+
fn('pulse_result_stream', [{ name: 'ctxRef', type: 'i32' }, { name: 'statusCode', type: 'i32' }, { name: 'streamRef', type: 'i32' }], 'void', ['Sets RESULT_STREAM from an opaque host-owned stream ref.']),
|
|
115
|
+
fn('pulse_result_from_response', [{ name: 'ctxRef', type: 'i32' }, { name: 'responseRef', type: 'i32' }], 'void', ['Sets RESULT_STREAM from an opaque host-owned response ref; host preserves/merges status, headers, and stream.']),
|
|
116
|
+
fn('pulse_next', [{ name: 'ctxRef', type: 'i32' }], 'void'),
|
|
117
|
+
fn('pulse_next_error', [{ name: 'ctxRef', type: 'i32' }, { name: 'errorRef', type: 'i32' }], 'void')
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
const errors = [
|
|
121
|
+
fn('pulse_error', [{ name: 'codePtr', type: 'i32' }, { name: 'codeLen', type: 'i32' }, { name: 'messagePtr', type: 'i32' }, { name: 'messageLen', type: 'i32' }, { name: 'statusCode', type: 'i32' }], 'i32'),
|
|
122
|
+
fn('pulse_error_with_cause', [{ name: 'codePtr', type: 'i32' }, { name: 'codeLen', type: 'i32' }, { name: 'messagePtr', type: 'i32' }, { name: 'messageLen', type: 'i32' }, { name: 'statusCode', type: 'i32' }, { name: 'causeRef', type: 'i32' }], 'i32'),
|
|
123
|
+
fn('pulse_error_code_ref', [{ name: 'errorRef', type: 'i32' }], 'i32'),
|
|
124
|
+
fn('pulse_error_message_ref', [{ name: 'errorRef', type: 'i32' }], 'i32'),
|
|
125
|
+
fn('pulse_error_status_code', [{ name: 'errorRef', type: 'i32' }], 'i32'),
|
|
126
|
+
fn('pulse_error_cause_ref', [{ name: 'errorRef', type: 'i32' }], 'i32')
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const channel = [
|
|
130
|
+
fn('pulse_channel_count', [{ name: 'channelsRef', type: 'i32' }], 'i32'),
|
|
131
|
+
fn('pulse_channel_ref_at', [{ name: 'channelsRef', type: 'i32' }, { name: 'index', type: 'i32' }], 'i32')
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
return { memory, lifecycle, dispatch, result, headers, jsonBody, stringRefs, ctxOps, errors, channel };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function flattenWasmHostAbiExports(groups) {
|
|
138
|
+
return Object.entries(groups).flatMap(([group, functions]) => functions.map((item) => ({ group, ...item })));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function listWasmHostAbiExports() {
|
|
142
|
+
return flattenWasmHostAbiExports(buildWasmHostAbiExportGroups());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = {
|
|
146
|
+
ABI_VERSION,
|
|
147
|
+
ABI_PHASE,
|
|
148
|
+
METHOD_CODES,
|
|
149
|
+
RESULT_KINDS,
|
|
150
|
+
NEXT_OUTCOMES,
|
|
151
|
+
buildWasmHostAbiExportGroups,
|
|
152
|
+
flattenWasmHostAbiExports,
|
|
153
|
+
listWasmHostAbiExports
|
|
154
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
handlerSchemaDecodeResult: require('./handler/schema-decode-result.js'),
|
|
5
|
+
handlerSchemaResponseCodec: require('./handler/schema-response-codec.js'),
|
|
6
|
+
diagnostics: require('./diagnostics.js'),
|
|
7
|
+
diagnosticCodes: require('./diagnostics/codes.js'),
|
|
8
|
+
diagnosticReporter: require('./diagnostics/reporter.js'),
|
|
9
|
+
stableId: require('./stable-id.js'),
|
|
10
|
+
logging: require('./logging.js'),
|
|
11
|
+
configSchema: require('./definitions/config-schema.js'),
|
|
12
|
+
handlerRoles: require('./definitions/handler-roles.js'),
|
|
13
|
+
routerApi: require('./definitions/router-api.js'),
|
|
14
|
+
path: require('./path.js'),
|
|
15
|
+
pathGrammar: require('./definitions/path-grammar.js'),
|
|
16
|
+
routePlan: require('./route-plan.js'),
|
|
17
|
+
dispatchTable: require('./dispatch-table.js'),
|
|
18
|
+
executionPlan: require('./execution-plan.js'),
|
|
19
|
+
pathTable: require('./path-table.js'),
|
|
20
|
+
handlerTable: require('./handler-table.js'),
|
|
21
|
+
handlerEffects: require('./handler/effects.js'),
|
|
22
|
+
handlerContext: require('./handler/context.js'),
|
|
23
|
+
handlerIoLifecycle: require('./handler/io-lifecycle.js'),
|
|
24
|
+
requestJsonBody: require('./handler/request-json-body.js'),
|
|
25
|
+
schemaDecodeResult: require('./handler/schema-decode-result.js'),
|
|
26
|
+
schemaResponseCodec: require('./handler/schema-response-codec.js'),
|
|
27
|
+
backendJsonRequestBody: require('./handler/backend-json-request-body.js'),
|
|
28
|
+
referenceNodeLifecycleGolden: require('./handler/reference-node-lifecycle-golden.js'),
|
|
29
|
+
betaLifecycleGolden: require('./handler/beta-lifecycle-golden.js'),
|
|
30
|
+
lifecycleAuthoringAlignment: require('./handler/lifecycle-authoring-alignment.js'),
|
|
31
|
+
lifecycleReadinessAggregate: require('./handler/lifecycle-readiness-aggregate.js'),
|
|
32
|
+
doctorReadinessAggregator: require('./handler/doctor-readiness-aggregator.js'),
|
|
33
|
+
runtimeApi: require('./handler/runtime-api.js'),
|
|
34
|
+
canonicalRuntime: require('./handler/canonical-runtime.js'),
|
|
35
|
+
canonicalNativePlan: require('./handler/canonical-native-plan.js'),
|
|
36
|
+
canonicalNativeRuntime: require('./handler/canonical-native-runtime.js'),
|
|
37
|
+
canonicalProvider: require('./provider/canonical-provider.js'),
|
|
38
|
+
assetsContracts: require('./assets/contracts.js'),
|
|
39
|
+
gripContracts: require('./grip/contracts.js'),
|
|
40
|
+
jwtContracts: require('./jwt/contracts.js'),
|
|
41
|
+
entitiesContracts: require('./entities/contracts.js'),
|
|
42
|
+
entitiesRuntime: require('./entities/runtime.js'),
|
|
43
|
+
entitiesJsonRpc: require('./entities/json-rpc.js'),
|
|
44
|
+
entitiesCatalog: require('./entities/catalog.js'),
|
|
45
|
+
hostCapabilities: require('./host/capabilities.js'),
|
|
46
|
+
hostWasmAbi: require('./host/wasm-host-abi.js'),
|
|
47
|
+
requestResultHeaders: require('./host/request-result-headers.js'),
|
|
48
|
+
channelBroadcaster: require('./host/channel-broadcaster.js'),
|
|
49
|
+
jsonBody: require('./host/json-body.js'),
|
|
50
|
+
streamingPassthrough: require('./host/streaming-passthrough.js'),
|
|
51
|
+
backendCapabilities: require('./host/backend-capabilities.js'),
|
|
52
|
+
libraryContracts: require('./library/contracts.js'),
|
|
53
|
+
libraryManifest: require('./library/manifest.js'),
|
|
54
|
+
secondPackageOwnedLowering: require('./library/second-package-owned-lowering.js'),
|
|
55
|
+
librarySidecars: require('./library/sidecars.js'),
|
|
56
|
+
libraryApiSurface: require('./library/api-surface.js'),
|
|
57
|
+
schemaJsonContracts: require('./schema-json/contracts.js'),
|
|
58
|
+
schemaJsonV1: require('./schema-json/v1.js'),
|
|
59
|
+
schemaJsonV2: require('./schema-json/v2.js'),
|
|
60
|
+
schemaJsonCompile: require('./schema-json/compile.js'),
|
|
61
|
+
schemaJsonGenericParser: require('./schema-json/generic-parser.js'),
|
|
62
|
+
configSymbolicRefs: require('./config/symbolic-refs.js'),
|
|
63
|
+
configProviderResolution: require('./config/provider-resolution.js'),
|
|
64
|
+
runtimeProviderKv: require('./config/runtime-provider-kv.js'),
|
|
65
|
+
compilerSpine: require('./spine.js'),
|
|
66
|
+
handlerSurface: require('./handler/surface-contract.js'),
|
|
67
|
+
ctxContract: require('./handler/ctx-contract.js'),
|
|
68
|
+
handlerTypes: require('./handler/types-contract.js'),
|
|
69
|
+
projectConfigPlan: require('./project/config-plan.js'),
|
|
70
|
+
projectConfigRuntime: require('./project/config-runtime.js'),
|
|
71
|
+
reachableGraph: require('./project/reachable-graph.js'),
|
|
72
|
+
packageOwnership: require('./project/package-ownership.js'),
|
|
73
|
+
packageContract: require('./package/package-contract.js'),
|
|
74
|
+
targetSupportEvidence: require('./project/target-support-evidence.js')
|
|
75
|
+
};
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
CRYPTO_SEMANTIC_OWNER
|
|
5
|
+
} = require('../crypto/contracts.js');
|
|
6
|
+
const {
|
|
7
|
+
PACKAGE_CRYPTO_REQUIREMENT_VERSION
|
|
8
|
+
} = require('../package/package-contract.js');
|
|
9
|
+
|
|
10
|
+
const JWT_CONTRACT_VERSION = 'pulse.jwt-contract.v1';
|
|
11
|
+
const JWT_CANONICAL_EFFECT_VERSION = 'pulse.canonical-package-effect.v1';
|
|
12
|
+
const JWT_LOWERING_PLAN_VERSION = 'pulse.jwt-lowering-plan.v1';
|
|
13
|
+
const JWT_LOWERING_PLAN_ARTIFACT = 'jwt-lowering-plan.json';
|
|
14
|
+
const JWT_SIDECAR_ABI_VERSION = 'pulse.jwt-sidecar-abi.v1';
|
|
15
|
+
const JWT_CRYPTO_COMPOSITION_VERSION = 'pulse.jwt-crypto-composition.v1';
|
|
16
|
+
const JWT_CONTRACT_ID = 'pulse.jwt';
|
|
17
|
+
const JWT_PACKAGE_NAME = '@pulse-compute/jwt';
|
|
18
|
+
|
|
19
|
+
const JWT_ALGORITHMS = Object.freeze(['HS256', 'RS256', 'ES256', 'EdDSA']);
|
|
20
|
+
const JWT_IMPLEMENTED_ALGORITHMS = Object.freeze(['HS256', 'ES256']);
|
|
21
|
+
const JWT_KEY_TYPES = Object.freeze(['secret', 'jwk', 'jwks']);
|
|
22
|
+
const JWT_IMPLEMENTED_KEY_TYPES = Object.freeze(['secret', 'jwk', 'jwks']);
|
|
23
|
+
const JWT_NATIVE_REALIZATION_KINDS = Object.freeze(['crypto-composed']);
|
|
24
|
+
const JWT_ALGORITHM_CAPABILITIES = Object.freeze({
|
|
25
|
+
HS256: 'jwt.verify.hs256',
|
|
26
|
+
RS256: 'jwt.verify.rs256',
|
|
27
|
+
ES256: 'jwt.verify.es256',
|
|
28
|
+
EdDSA: 'jwt.verify.eddsa'
|
|
29
|
+
});
|
|
30
|
+
const JWT_PACKAGE_INTRINSICS = Object.freeze({
|
|
31
|
+
bearer: Object.freeze({
|
|
32
|
+
name: 'jwt.bearer-request-header',
|
|
33
|
+
nativeIntrinsic: 'request.header',
|
|
34
|
+
compilerName: '__pulse_jwt_bearer_request_header',
|
|
35
|
+
valueKind: 'string-or-undefined',
|
|
36
|
+
argumentIndexes: Object.freeze([]),
|
|
37
|
+
staticArguments: Object.freeze(['authorization'])
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
const JWT_DIAGNOSTIC_CODES = Object.freeze({
|
|
41
|
+
ARGUMENT_SHAPE_UNSUPPORTED: 'PULSE_JWT_ARGUMENT_SHAPE_UNSUPPORTED',
|
|
42
|
+
CONTEXT_UNSUPPORTED: 'PULSE_JWT_CONTEXT_UNSUPPORTED',
|
|
43
|
+
TOKEN_SOURCE_UNSUPPORTED: 'PULSE_JWT_TOKEN_SOURCE_UNSUPPORTED',
|
|
44
|
+
BEARER_REQUEST_UNSUPPORTED: 'PULSE_JWT_BEARER_REQUEST_UNSUPPORTED',
|
|
45
|
+
BEARER_PLACEMENT_UNSUPPORTED: 'PULSE_JWT_BEARER_PLACEMENT_UNSUPPORTED',
|
|
46
|
+
PLACEMENT_UNSUPPORTED: 'PULSE_JWT_PLACEMENT_UNSUPPORTED',
|
|
47
|
+
RESULT_ACCESS_UNSUPPORTED: 'PULSE_JWT_RESULT_ACCESS_UNSUPPORTED',
|
|
48
|
+
OPTIONS_LITERAL_REQUIRED: 'PULSE_JWT_OPTIONS_LITERAL_REQUIRED',
|
|
49
|
+
OPTIONS_PROPERTY_UNSUPPORTED: 'PULSE_JWT_OPTIONS_PROPERTY_UNSUPPORTED',
|
|
50
|
+
ALGORITHMS_LITERAL_REQUIRED: 'PULSE_JWT_ALGORITHMS_LITERAL_REQUIRED',
|
|
51
|
+
ALGORITHMS_EMPTY: 'PULSE_JWT_ALGORITHMS_EMPTY',
|
|
52
|
+
ALGORITHM_UNSUPPORTED: 'PULSE_JWT_ALGORITHM_UNSUPPORTED',
|
|
53
|
+
KEY_LITERAL_REQUIRED: 'PULSE_JWT_KEY_LITERAL_REQUIRED',
|
|
54
|
+
KEY_TYPE_UNSUPPORTED: 'PULSE_JWT_KEY_TYPE_UNSUPPORTED',
|
|
55
|
+
KEY_ALGORITHM_MISMATCH: 'PULSE_JWT_KEY_ALGORITHM_MISMATCH',
|
|
56
|
+
JWK_INVALID: 'PULSE_JWT_JWK_INVALID',
|
|
57
|
+
PRIVATE_JWK_UNSUPPORTED: 'PULSE_JWT_PRIVATE_JWK_UNSUPPORTED',
|
|
58
|
+
JWKS_LIMIT_EXCEEDED: 'PULSE_JWT_JWKS_LIMIT_EXCEEDED',
|
|
59
|
+
JWKS_AMBIGUOUS: 'PULSE_JWT_JWKS_AMBIGUOUS',
|
|
60
|
+
POLICY_LITERAL_REQUIRED: 'PULSE_JWT_POLICY_LITERAL_REQUIRED',
|
|
61
|
+
POLICY_LIMIT_EXCEEDED: 'PULSE_JWT_POLICY_LIMIT_EXCEEDED',
|
|
62
|
+
SCHEMA_LITERAL_REQUIRED: 'PULSE_JWT_SCHEMA_LITERAL_REQUIRED',
|
|
63
|
+
SCHEMA_UNKNOWN: 'PULSE_JWT_SCHEMA_UNKNOWN',
|
|
64
|
+
SCHEMA_UNREALIZED: 'PULSE_JWT_SCHEMA_UNREALIZED',
|
|
65
|
+
TARGET_ALGORITHM_UNSUPPORTED: 'PULSE_JWT_TARGET_ALGORITHM_UNSUPPORTED',
|
|
66
|
+
TARGET_KEY_UNSUPPORTED: 'PULSE_JWT_TARGET_KEY_UNSUPPORTED',
|
|
67
|
+
TARGET_CLOCK_UNAVAILABLE: 'PULSE_JWT_TARGET_CLOCK_UNAVAILABLE',
|
|
68
|
+
REALIZATION_UNSUPPORTED: 'PULSE_JWT_REALIZATION_UNSUPPORTED',
|
|
69
|
+
REALIZATION_AMBIGUOUS: 'PULSE_JWT_REALIZATION_AMBIGUOUS',
|
|
70
|
+
FACADE_USE_UNSUPPORTED: 'PULSE_JWT_FACADE_USE_UNSUPPORTED'
|
|
71
|
+
});
|
|
72
|
+
const JWT_RESOURCE_LIMITS = Object.freeze({
|
|
73
|
+
tokenBytes: 16 * 1024,
|
|
74
|
+
protectedHeaderBytes: 4 * 1024,
|
|
75
|
+
claimsBytes: 16 * 1024,
|
|
76
|
+
jwksEntries: 16,
|
|
77
|
+
requiredClaimNames: 32,
|
|
78
|
+
algorithms: 4,
|
|
79
|
+
clockToleranceSeconds: 300,
|
|
80
|
+
jsonDepth: 32
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const JWT_ES256_KEY_NORMALIZATION_CONTRACT = Object.freeze({
|
|
84
|
+
version: 'pulse.jwt.es256-key-normalization.v1',
|
|
85
|
+
status: 'executable-g3',
|
|
86
|
+
algorithm: 'ES256',
|
|
87
|
+
inlineJwk: Object.freeze({
|
|
88
|
+
requiredMembers: Object.freeze(['kty', 'crv', 'x', 'y']),
|
|
89
|
+
optionalMembers: Object.freeze(['alg', 'use', 'key_ops', 'kid']),
|
|
90
|
+
exactValues: Object.freeze({
|
|
91
|
+
kty: 'EC',
|
|
92
|
+
crv: 'P-256',
|
|
93
|
+
alg: 'absent-or-ES256',
|
|
94
|
+
use: 'absent-or-sig',
|
|
95
|
+
key_ops: 'absent-or-exactly-verify'
|
|
96
|
+
}),
|
|
97
|
+
coordinateEncoding: 'canonical-unpadded-base64url',
|
|
98
|
+
coordinateBytes: 32,
|
|
99
|
+
privateMembersRejected: true,
|
|
100
|
+
unknownMembersRejected: true,
|
|
101
|
+
certificateMembersRejected: true
|
|
102
|
+
}),
|
|
103
|
+
jwks: Object.freeze({
|
|
104
|
+
kind: 'bounded-static-inline',
|
|
105
|
+
entriesMaximum: JWT_RESOURCE_LIMITS.jwksEntries,
|
|
106
|
+
remoteDiscovery: false,
|
|
107
|
+
fetch: false,
|
|
108
|
+
cache: false,
|
|
109
|
+
refresh: false,
|
|
110
|
+
duplicateKid: 'reject-entire-set',
|
|
111
|
+
selection: 'kid-exact-or-single-eligible-key',
|
|
112
|
+
selectedKeyFailure: 'terminal-no-retry'
|
|
113
|
+
}),
|
|
114
|
+
cryptoAdapter: Object.freeze({
|
|
115
|
+
publicKeyType: 'p256-public-key-bytes',
|
|
116
|
+
publicKeyEncoding: 'x-y-big-endian',
|
|
117
|
+
publicKeyBytes: 64,
|
|
118
|
+
signatureEncoding: 'jose-r-s-big-endian',
|
|
119
|
+
signatureBytes: 64,
|
|
120
|
+
derAccepted: false,
|
|
121
|
+
highS: 'accepted-if-otherwise-valid'
|
|
122
|
+
}),
|
|
123
|
+
exactSigningInput: true,
|
|
124
|
+
claimsBeforeAuthenticity: false,
|
|
125
|
+
automaticFallback: false
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const JWT_VERIFY_OPERATION = Object.freeze({
|
|
129
|
+
version: JWT_CANONICAL_EFFECT_VERSION,
|
|
130
|
+
contractId: JWT_CONTRACT_ID,
|
|
131
|
+
package: JWT_PACKAGE_NAME,
|
|
132
|
+
import: JWT_PACKAGE_NAME,
|
|
133
|
+
kind: 'jwt.verify',
|
|
134
|
+
providerKind: 'jwt',
|
|
135
|
+
operation: 'verify',
|
|
136
|
+
capability: 'jwt.verify',
|
|
137
|
+
result: 'jwt-verification',
|
|
138
|
+
placement: 'variable',
|
|
139
|
+
clock: 'provider-wall-clock'
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const JWT_EXECUTION_REQUIREMENTS = Object.freeze({
|
|
143
|
+
containment: 'request-owned-effect',
|
|
144
|
+
cancellation: 'request-lifecycle-signal',
|
|
145
|
+
secretResolution: 'provider-owned-named-secret-authority',
|
|
146
|
+
clock: 'one-trusted-provider-wall-clock-capture',
|
|
147
|
+
schemaValidation: 'project-codecs-decode-in-memory',
|
|
148
|
+
resultProjection: 'detached-immutable-jwt-verification',
|
|
149
|
+
redaction: Object.freeze([
|
|
150
|
+
'token',
|
|
151
|
+
'signature',
|
|
152
|
+
'key-material',
|
|
153
|
+
'complete-claims',
|
|
154
|
+
'secret-value'
|
|
155
|
+
])
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const JWT_NATIVE_LOWERING_CONTRACT = Object.freeze({
|
|
159
|
+
planVersion: JWT_LOWERING_PLAN_VERSION,
|
|
160
|
+
sidecarAbiVersion: JWT_SIDECAR_ABI_VERSION,
|
|
161
|
+
cryptoCompositionVersion: JWT_CRYPTO_COMPOSITION_VERSION,
|
|
162
|
+
operation: 'jwt.verify',
|
|
163
|
+
tokenSource: 'bearer-request-header',
|
|
164
|
+
result: 'jwt-verification',
|
|
165
|
+
targetCapabilities: Object.freeze([
|
|
166
|
+
'jwt.verify',
|
|
167
|
+
'jwt.verify.hs256',
|
|
168
|
+
'secret.get',
|
|
169
|
+
'time.wall-clock'
|
|
170
|
+
]),
|
|
171
|
+
realizationKinds: JWT_NATIVE_REALIZATION_KINDS,
|
|
172
|
+
realizationBinding: 'crypto-owned-before-artifact-generation',
|
|
173
|
+
implementationStatus: 'crypto-composed-hs256-es256',
|
|
174
|
+
implementedCapabilities: Object.freeze([
|
|
175
|
+
'jwt.verify',
|
|
176
|
+
'jwt.verify.hs256',
|
|
177
|
+
'jwt.verify.es256',
|
|
178
|
+
'secret.get',
|
|
179
|
+
'time.wall-clock'
|
|
180
|
+
]),
|
|
181
|
+
implementedAlgorithms: JWT_IMPLEMENTED_ALGORITHMS,
|
|
182
|
+
implementedKeyTypes: JWT_IMPLEMENTED_KEY_TYPES,
|
|
183
|
+
realization: 'guest-source:pulse-hmac-as',
|
|
184
|
+
implementation: 'pulse-hmac-as.v1',
|
|
185
|
+
realizations: Object.freeze({
|
|
186
|
+
HS256: Object.freeze({
|
|
187
|
+
realization: 'guest-source:pulse-hmac-as',
|
|
188
|
+
implementation: 'pulse-hmac-as.v1',
|
|
189
|
+
guestUnitRequired: false
|
|
190
|
+
}),
|
|
191
|
+
ES256: Object.freeze({
|
|
192
|
+
realization: 'guest-linked:pulse-es256-rustcrypto-p256',
|
|
193
|
+
implementation: 'rustcrypto.p256-0.13.2.ecdsa-0.16.9.sha2-0.10.9.v1',
|
|
194
|
+
guestUnitRequired: true
|
|
195
|
+
})
|
|
196
|
+
}),
|
|
197
|
+
cryptoSemanticOwner: CRYPTO_SEMANTIC_OWNER,
|
|
198
|
+
portableVerifierImplemented: false,
|
|
199
|
+
hostVerifyImplemented: false,
|
|
200
|
+
guestUnitRequiredForHs256: false,
|
|
201
|
+
guestUnitRequiredForEs256: true,
|
|
202
|
+
automaticFallback: false
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const JWT_TARGET_REALIZATIONS = Object.freeze({
|
|
206
|
+
node: Object.freeze({
|
|
207
|
+
javascript: Object.freeze({
|
|
208
|
+
status: 'eligible',
|
|
209
|
+
algorithm: 'HS256',
|
|
210
|
+
realization: 'runtime-builtin',
|
|
211
|
+
implementation: 'webcrypto.subtle.hmac-sha-256.v1',
|
|
212
|
+
algorithms: JWT_IMPLEMENTED_ALGORITHMS,
|
|
213
|
+
keyTypes: JWT_IMPLEMENTED_KEY_TYPES,
|
|
214
|
+
clock: 'node-provider-wall-clock',
|
|
215
|
+
automaticFallback: false
|
|
216
|
+
}),
|
|
217
|
+
native: Object.freeze({
|
|
218
|
+
status: 'eligible',
|
|
219
|
+
algorithm: 'HS256',
|
|
220
|
+
realization: 'guest-source:pulse-hmac-as',
|
|
221
|
+
implementation: 'pulse-hmac-as.v1',
|
|
222
|
+
algorithms: JWT_IMPLEMENTED_ALGORITHMS,
|
|
223
|
+
keyTypes: JWT_IMPLEMENTED_KEY_TYPES,
|
|
224
|
+
clock: 'node-provider-wall-clock',
|
|
225
|
+
guestUnitRequired: false,
|
|
226
|
+
automaticFallback: false
|
|
227
|
+
})
|
|
228
|
+
}),
|
|
229
|
+
fastly: Object.freeze({
|
|
230
|
+
javascript: Object.freeze({
|
|
231
|
+
status: 'eligible',
|
|
232
|
+
algorithm: 'HS256',
|
|
233
|
+
realization: 'runtime-builtin',
|
|
234
|
+
implementation: 'webcrypto.subtle.hmac-sha-256.v1',
|
|
235
|
+
algorithms: JWT_IMPLEMENTED_ALGORITHMS,
|
|
236
|
+
keyTypes: JWT_IMPLEMENTED_KEY_TYPES,
|
|
237
|
+
clock: 'fastly-provider-wall-clock',
|
|
238
|
+
automaticFallback: false
|
|
239
|
+
}),
|
|
240
|
+
native: Object.freeze({
|
|
241
|
+
status: 'eligible',
|
|
242
|
+
algorithms: JWT_IMPLEMENTED_ALGORITHMS,
|
|
243
|
+
keyTypes: JWT_IMPLEMENTED_KEY_TYPES,
|
|
244
|
+
realizations: JWT_NATIVE_LOWERING_CONTRACT.realizations,
|
|
245
|
+
guestUnitRequiredForEs256: true,
|
|
246
|
+
automaticFallback: false
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
class PulseJwtContractError extends Error {
|
|
252
|
+
constructor(message, detail) {
|
|
253
|
+
super(message);
|
|
254
|
+
this.name = 'PulseJwtContractError';
|
|
255
|
+
this.code = 'PULSE_JWT_OPERATION_FAILED';
|
|
256
|
+
this.detail = Object.freeze({ category: 'canonical-operation', ...detail });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function ordinaryObject(value) {
|
|
261
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
262
|
+
const prototype = Object.getPrototypeOf(value);
|
|
263
|
+
return prototype === Object.prototype || prototype === null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function dataProperty(value, name) {
|
|
267
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
268
|
+
return descriptor && descriptor.enumerable
|
|
269
|
+
&& Object.prototype.hasOwnProperty.call(descriptor, 'value')
|
|
270
|
+
? descriptor.value
|
|
271
|
+
: undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function assertOperationIdentity(effect) {
|
|
275
|
+
if (!ordinaryObject(effect)
|
|
276
|
+
|| effect.contractId !== JWT_VERIFY_OPERATION.contractId
|
|
277
|
+
|| effect.package !== JWT_VERIFY_OPERATION.package
|
|
278
|
+
|| effect.kind !== JWT_VERIFY_OPERATION.kind
|
|
279
|
+
|| effect.providerKind !== JWT_VERIFY_OPERATION.providerKind
|
|
280
|
+
|| effect.operation !== JWT_VERIFY_OPERATION.operation
|
|
281
|
+
|| effect.capability !== JWT_VERIFY_OPERATION.capability
|
|
282
|
+
|| effect.result !== JWT_VERIFY_OPERATION.result) {
|
|
283
|
+
throw new PulseJwtContractError(
|
|
284
|
+
'Pulse JWT provider received an invalid canonical verification operation.',
|
|
285
|
+
{ field: 'identity' }
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function normalizeJwtVerifyEffect(effect) {
|
|
291
|
+
assertOperationIdentity(effect);
|
|
292
|
+
const payload = dataProperty(effect, 'payload');
|
|
293
|
+
if (!ordinaryObject(payload)) {
|
|
294
|
+
throw new PulseJwtContractError(
|
|
295
|
+
'Pulse JWT verification requires an ordinary operation payload.',
|
|
296
|
+
{ field: 'payload' }
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
const payloadKeys = Reflect.ownKeys(payload);
|
|
300
|
+
if (payloadKeys.length !== 2 || !payloadKeys.includes('token') || !payloadKeys.includes('options')) {
|
|
301
|
+
throw new PulseJwtContractError(
|
|
302
|
+
'Pulse JWT verification received an invalid operation payload.',
|
|
303
|
+
{ field: 'payload' }
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const token = dataProperty(payload, 'token');
|
|
307
|
+
const options = dataProperty(payload, 'options');
|
|
308
|
+
if (typeof token !== 'string' || !ordinaryObject(options)) {
|
|
309
|
+
throw new PulseJwtContractError(
|
|
310
|
+
'Pulse JWT verification received an invalid token or options payload.',
|
|
311
|
+
{ field: typeof token !== 'string' ? 'token' : 'options' }
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
return Object.freeze({ token, options });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function jwtVerifyProviderRequirements(options = {}) {
|
|
318
|
+
const requirements = ['jwt.verify', 'time.wall-clock'];
|
|
319
|
+
const algorithms = Array.isArray(options.algorithms) ? options.algorithms : [];
|
|
320
|
+
for (const algorithm of algorithms) {
|
|
321
|
+
const suffix = String(algorithm).toLowerCase();
|
|
322
|
+
if (JWT_ALGORITHMS.includes(String(algorithm)) && !requirements.includes(`jwt.verify.${suffix}`)) {
|
|
323
|
+
requirements.push(`jwt.verify.${suffix}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const key = ordinaryObject(options.key) ? options.key : {};
|
|
327
|
+
if (key.type === 'secret') requirements.push('secret.get');
|
|
328
|
+
if (typeof options.claimsSchema === 'string') requirements.push('schema.decode');
|
|
329
|
+
return Object.freeze(requirements);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function jwtAlgorithmCapability(algorithm) {
|
|
333
|
+
return JWT_ALGORITHM_CAPABILITIES[String(algorithm)];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function jwtVerifyNativeRequirements(options = {}) {
|
|
337
|
+
const requirements = ['jwt.verify', 'time.wall-clock'];
|
|
338
|
+
const algorithms = Array.isArray(options.algorithms) ? options.algorithms : [];
|
|
339
|
+
for (const algorithm of algorithms) {
|
|
340
|
+
const capability = jwtAlgorithmCapability(algorithm);
|
|
341
|
+
if (capability && !requirements.includes(capability)) requirements.push(capability);
|
|
342
|
+
}
|
|
343
|
+
const key = ordinaryObject(options.key) ? options.key : {};
|
|
344
|
+
if (key.type === 'secret') requirements.push('secret.get');
|
|
345
|
+
if (typeof options.claimsSchema === 'string') requirements.push('schema.decode');
|
|
346
|
+
return Object.freeze(requirements);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function jwtCryptoRequirements(algorithms = []) {
|
|
350
|
+
if (!Array.isArray(algorithms)) {
|
|
351
|
+
throw new PulseJwtContractError(
|
|
352
|
+
'JWT crypto demand requires a static algorithm array.',
|
|
353
|
+
{ field: 'algorithms' }
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const normalized = [...new Set(algorithms.map(String))].sort();
|
|
357
|
+
if (normalized.some((algorithm) => !JWT_ALGORITHMS.includes(algorithm))) {
|
|
358
|
+
throw new PulseJwtContractError(
|
|
359
|
+
'JWT crypto demand contains an unsupported JWT algorithm.',
|
|
360
|
+
{ field: 'algorithms' }
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
if (normalized.length === 0) return Object.freeze([]);
|
|
364
|
+
return Object.freeze([
|
|
365
|
+
Object.freeze({
|
|
366
|
+
version: PACKAGE_CRYPTO_REQUIREMENT_VERSION,
|
|
367
|
+
requestedBy: JWT_PACKAGE_NAME,
|
|
368
|
+
semanticOwner: CRYPTO_SEMANTIC_OWNER,
|
|
369
|
+
reachable: true,
|
|
370
|
+
algorithms: Object.freeze(normalized)
|
|
371
|
+
})
|
|
372
|
+
]);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function jwtVerifyRedactedProjection(options = {}) {
|
|
376
|
+
const key = ordinaryObject(options.key) ? options.key : {};
|
|
377
|
+
const keyCount = key.type === 'jwks' && Array.isArray(key.keys)
|
|
378
|
+
? key.keys.length
|
|
379
|
+
: (key.type === 'jwk' || key.type === 'secret' ? 1 : 0);
|
|
380
|
+
return Object.freeze({
|
|
381
|
+
version: JWT_CONTRACT_VERSION,
|
|
382
|
+
contractId: JWT_CONTRACT_ID,
|
|
383
|
+
operation: 'verify',
|
|
384
|
+
algorithms: Object.freeze(Array.isArray(options.algorithms)
|
|
385
|
+
? options.algorithms.filter((entry) => JWT_ALGORITHMS.includes(String(entry))).map(String)
|
|
386
|
+
: []),
|
|
387
|
+
keyType: JWT_KEY_TYPES.includes(String(key.type)) ? String(key.type) : 'unknown',
|
|
388
|
+
keyCount,
|
|
389
|
+
hasIssuer: options.issuer !== undefined,
|
|
390
|
+
hasAudience: options.audience !== undefined,
|
|
391
|
+
hasSubject: options.subject !== undefined,
|
|
392
|
+
hasClaimsSchema: typeof options.claimsSchema === 'string',
|
|
393
|
+
providerRequirements: jwtVerifyProviderRequirements(options)
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
module.exports = Object.freeze({
|
|
398
|
+
JWT_CONTRACT_VERSION,
|
|
399
|
+
JWT_CANONICAL_EFFECT_VERSION,
|
|
400
|
+
JWT_LOWERING_PLAN_VERSION,
|
|
401
|
+
JWT_LOWERING_PLAN_ARTIFACT,
|
|
402
|
+
JWT_SIDECAR_ABI_VERSION,
|
|
403
|
+
JWT_CRYPTO_COMPOSITION_VERSION,
|
|
404
|
+
JWT_CONTRACT_ID,
|
|
405
|
+
JWT_PACKAGE_NAME,
|
|
406
|
+
JWT_ALGORITHMS,
|
|
407
|
+
JWT_IMPLEMENTED_ALGORITHMS,
|
|
408
|
+
JWT_KEY_TYPES,
|
|
409
|
+
JWT_IMPLEMENTED_KEY_TYPES,
|
|
410
|
+
JWT_NATIVE_REALIZATION_KINDS,
|
|
411
|
+
JWT_ALGORITHM_CAPABILITIES,
|
|
412
|
+
JWT_PACKAGE_INTRINSICS,
|
|
413
|
+
JWT_DIAGNOSTIC_CODES,
|
|
414
|
+
JWT_RESOURCE_LIMITS,
|
|
415
|
+
JWT_ES256_KEY_NORMALIZATION_CONTRACT,
|
|
416
|
+
JWT_VERIFY_OPERATION,
|
|
417
|
+
JWT_EXECUTION_REQUIREMENTS,
|
|
418
|
+
JWT_NATIVE_LOWERING_CONTRACT,
|
|
419
|
+
JWT_TARGET_REALIZATIONS,
|
|
420
|
+
PulseJwtContractError,
|
|
421
|
+
normalizeJwtVerifyEffect,
|
|
422
|
+
jwtVerifyProviderRequirements,
|
|
423
|
+
jwtAlgorithmCapability,
|
|
424
|
+
jwtVerifyNativeRequirements,
|
|
425
|
+
jwtCryptoRequirements,
|
|
426
|
+
jwtVerifyRedactedProjection
|
|
427
|
+
});
|