@pulse-compute/provider-fastly 1.0.0-beta.1 → 1.0.0-beta.10
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 +7 -6
- package/dist/config-schema.json +30 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +11 -10
- package/src/build/canonical-target.js +35 -2
- package/src/build/digest-native.js +25 -0
- package/src/build/effect-invocations.js +49 -0
- package/src/build/embedded-assets.as.ts +32 -0
- package/src/build/entities-native.js +6 -11
- package/src/build/kv-native.as.ts +331 -0
- package/src/build/kv-native.js +27 -0
- package/src/build/native-application-errors.js +159 -0
- package/src/build/native-binary-resources.js +30 -0
- package/src/build/native-body-forwarding.as.ts +341 -0
- package/src/build/native-body-forwarding.js +69 -0
- package/src/build/native-dynamic-backends.as.ts +115 -0
- package/src/build/native-dynamic-backends.js +52 -0
- package/src/build/native-fetch-deadlines.as.ts +164 -0
- package/src/build/native-fetch-deadlines.js +27 -0
- package/src/build/native-fetch-errors.js +38 -0
- package/src/build/native-handle-ownership.js +127 -0
- package/src/build/native-header-lookup.js +50 -0
- package/src/build/native-http-effects.js +155 -32
- package/src/build/native-platform-capabilities.js +755 -210
- package/src/build/native-request-body.js +44 -0
- package/src/build/native-request-headers.js +93 -0
- package/src/build/native-send-error-detail.as.ts +12 -0
- package/src/build/native-stdio.js +45 -0
- package/src/build/native-string-values.js +143 -0
- package/src/build/native-transport-details.as.ts +84 -0
- package/src/build/native-value-budget.js +90 -0
- package/src/build/package-native-application.js +114 -0
- package/src/build/request-budget.as.ts +50 -0
- package/src/build/request-budget.js +47 -0
- package/src/build/s3-body.as.ts +97 -0
- package/src/build/s3-native.as.ts +228 -0
- package/src/build/s3-native.js +30 -0
- package/src/build/schema-admission.js +83 -0
- package/src/build/schema-nested-json.js +93 -0
- package/src/build/schema-scalar-record.js +41 -0
- package/src/build/time-native.js +49 -0
- package/src/build/wasm-budget.js +28 -0
- package/src/config-api.js +6 -0
- package/src/config-schema.json +30 -0
- package/src/index.ts +25 -0
- package/src/javascript/jwt-verifier.js +14 -4
- package/src/javascript/logging.js +11 -2
- package/src/javascript/package-effects.js +5 -2
- package/src/javascript/runtime-host.js +7 -3
- package/src/javascript/source-package.js +4 -2
- package/src/javascript/target-support-policy.js +20 -3
- package/src/javascript/target.js +17 -1
- package/src/javascript/test-runtime.js +1 -0
- package/src/provider-contract.js +22 -6
- package/src/runtime/canonical-api-runtime.js +6 -0
- package/src/testing/conditional-kv-host.js +155 -0
- package/src/testing/native-dynamic-backends-host.js +39 -0
- package/src/testing/native-http-effects-host.js +9 -1
- package/src/testing/native-platform-capabilities-host.js +349 -24
- package/src/toolchain/body-forwarding.js +25 -0
- package/src/toolchain/config.js +16 -0
- package/src/toolchain/index.js +48 -5
- package/src/toolchain/logging.js +18 -0
- package/src/toolchain/native-execution.js +78 -0
- package/src/toolchain/s3.js +26 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { schemaHasScalarRecord } = require('@pulse-compute/wasm-contracts/schema-json/registry');
|
|
4
|
+
|
|
5
|
+
function hasScalarRecords(plan) {
|
|
6
|
+
return (plan.schemas?.registry?.schemas || []).some(schema => schemaHasScalarRecord(schema.root));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Both Fastly Native realizations validate handles before json-as sees them.
|
|
10
|
+
function scalarRecordProjectionLines(node) {
|
|
11
|
+
const limits = node.limits;
|
|
12
|
+
return [
|
|
13
|
+
' if (input.kind != PULSE_VALUE_OBJECT || (checkDuplicates && input.duplicateJsonKeys)) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 56, -1); return 0 }',
|
|
14
|
+
` if (input.keys.length > ${limits.maxKeys}) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 56, -1); return 0 }`,
|
|
15
|
+
' const keys = input.keys.slice().sort()',
|
|
16
|
+
' const output = host_value_object()',
|
|
17
|
+
' let bytes = 2',
|
|
18
|
+
' for (let i = 0; i < keys.length; i++) {',
|
|
19
|
+
' const key = keys[i]',
|
|
20
|
+
` if (key.length > ${limits.maxKeyLength}) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 56, -1); return 0 }`,
|
|
21
|
+
' const keyHandle = __pulse_fastly_string_value(key)',
|
|
22
|
+
' const itemHandle = host_value_property(valueHandle, keyHandle)',
|
|
23
|
+
' const item = __pulse_fastly_value(itemHandle)',
|
|
24
|
+
' let size = 0',
|
|
25
|
+
' if (item.kind == PULSE_VALUE_STRING) {',
|
|
26
|
+
` if (item.text.length > ${limits.maxStringLength}) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 56, -1); return 0 }`,
|
|
27
|
+
' size = __pulse_scalar_record_string_bytes(item.text)',
|
|
28
|
+
' } else if (item.kind == PULSE_VALUE_NUMBER && isFinite(item.number)) {',
|
|
29
|
+
` size = ${limits.numberBytes}`,
|
|
30
|
+
' } else if (item.kind == PULSE_VALUE_BOOLEAN) size = item.boolean != 0 ? 4 : 5',
|
|
31
|
+
' else if (item.kind == PULSE_VALUE_NULL) size = 4',
|
|
32
|
+
' else { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 56, -1); return 0 }',
|
|
33
|
+
' bytes += (i == 0 ? 0 : 1) + __pulse_scalar_record_string_bytes(key) + 1 + size',
|
|
34
|
+
` if (bytes > ${limits.maxBytes}) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 56, -1); return 0 }`,
|
|
35
|
+
' host_value_object_set(output, keyHandle, itemHandle)',
|
|
36
|
+
' }',
|
|
37
|
+
' return output'
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { hasScalarRecords, scalarRecordProjectionLines };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Provider-owned realtime sampling. The portable guest still suspends through
|
|
4
|
+
// effect_begin; no clock import is exposed to application-authored code.
|
|
5
|
+
function timeNativeSource(plan) {
|
|
6
|
+
if (!(plan.effects || []).some(effect => effect.kind === 'time.now')) return '';
|
|
7
|
+
return `
|
|
8
|
+
function __pulse_time_pad(value: i32, width: i32): string {
|
|
9
|
+
let text = value.toString()
|
|
10
|
+
while (text.length < width) text = "0" + text
|
|
11
|
+
return text
|
|
12
|
+
}
|
|
13
|
+
function __pulse_time_iso(ms: u64): string {
|
|
14
|
+
// Gregorian civil date from whole days since the Unix epoch.
|
|
15
|
+
const z = <i32>(ms / 86400000) + 719468
|
|
16
|
+
const era = z / 146097
|
|
17
|
+
const doe = z - era * 146097
|
|
18
|
+
const yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365
|
|
19
|
+
let year = yoe + era * 400
|
|
20
|
+
const doy = doe - (365 * yoe + yoe / 4 - yoe / 100)
|
|
21
|
+
const mp = (5 * doy + 2) / 153
|
|
22
|
+
const day = doy - (153 * mp + 2) / 5 + 1
|
|
23
|
+
const month = mp + (mp < 10 ? 3 : -9)
|
|
24
|
+
if (month <= 2) year += 1
|
|
25
|
+
const rest = <i32>(ms % 86400000)
|
|
26
|
+
return __pulse_time_pad(year, 4) + "-" + __pulse_time_pad(month, 2) + "-" + __pulse_time_pad(day, 2)
|
|
27
|
+
+ "T" + __pulse_time_pad(rest / 3600000, 2) + ":" + __pulse_time_pad((rest / 60000) % 60, 2)
|
|
28
|
+
+ ":" + __pulse_time_pad((rest / 1000) % 60, 2) + "." + __pulse_time_pad(rest % 1000, 3) + "Z"
|
|
29
|
+
}
|
|
30
|
+
function __pulse_fastly_time_begin(effectIndex: i32, payload: __PulseFastlyValue): void {
|
|
31
|
+
const out = new StaticArray<u64>(1)
|
|
32
|
+
const status = wasi_snapshot_preview1_clock_time_get(0, 1000000, changetype<usize>(out))
|
|
33
|
+
const result = host_value_object()
|
|
34
|
+
if (status != 0) {
|
|
35
|
+
host_value_object_set(result, __pulse_fastly_string_value("status"), __pulse_fastly_string_value("failed"))
|
|
36
|
+
host_value_object_set(result, __pulse_fastly_string_value("reason"), __pulse_fastly_string_value("unavailable"))
|
|
37
|
+
} else {
|
|
38
|
+
// Divide integer nanoseconds before conversion to f64 to retain millisecond precision.
|
|
39
|
+
const ms = unchecked(out[0]) / 1000000
|
|
40
|
+
host_value_object_set(result, __pulse_fastly_string_value("status"), __pulse_fastly_string_value("ok"))
|
|
41
|
+
host_value_object_set(result, __pulse_fastly_string_value("unixEpochMs"), host_value_number(<f64>ms))
|
|
42
|
+
host_value_object_set(result, __pulse_fastly_string_value("iso8601"), __pulse_fastly_string_value(__pulse_time_iso(ms)))
|
|
43
|
+
}
|
|
44
|
+
__pulse_fastly_ready_effect(effectIndex, result)
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = Object.freeze({ timeNativeSource });
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { fastlyConfigDefault } = require('../config-schema.js');
|
|
4
|
+
const DEFAULT_FASTLY_MAX_WASM_BYTES = fastlyConfigDefault('fastly.build.maxWasmBytes');
|
|
5
|
+
|
|
6
|
+
function normalizeFastlyMaxWasmBytes(value) {
|
|
7
|
+
if (value === undefined) return DEFAULT_FASTLY_MAX_WASM_BYTES;
|
|
8
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
9
|
+
const error = new TypeError('fastly.build.maxWasmBytes must be a positive safe integer byte count.');
|
|
10
|
+
error.code = 'PULSE_FASTLY_MAX_WASM_BYTES_INVALID';
|
|
11
|
+
throw error;
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function assertFastlyWasmBudget(bytes, value) {
|
|
17
|
+
const maxBytes = normalizeFastlyMaxWasmBytes(value);
|
|
18
|
+
if (bytes > maxBytes) {
|
|
19
|
+
const error = new Error('Generated Fastly Native module exceeds the configured Pulse compilation budget.');
|
|
20
|
+
error.name = 'FastlyNativePlatformCapabilitiesError';
|
|
21
|
+
error.code = 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_WASM_TOO_LARGE';
|
|
22
|
+
error.detail = Object.freeze({ bytes, maxBytes });
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
return maxBytes;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = Object.freeze({ DEFAULT_FASTLY_MAX_WASM_BYTES, normalizeFastlyMaxWasmBytes, assertFastlyWasmBudget });
|
package/src/config-api.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { normalizeFastlyLogEndpoint } = require('./toolchain/logging.js');
|
|
4
|
+
const { normalizeFastlyMaxWasmBytes } = require('./build/wasm-budget.js');
|
|
3
5
|
const { fastlyConfigDefault } = require('./config-schema.js');
|
|
4
6
|
|
|
5
7
|
const FASTLY_PROVIDER_API_VERSION = 'pulse.provider-fastly-api.v2';
|
|
@@ -38,8 +40,11 @@ function fastly(options = {}) {
|
|
|
38
40
|
const normalizedGrip = options.grip === undefined ? undefined : gripBindings(options.grip);
|
|
39
41
|
return Object.freeze({
|
|
40
42
|
kind: 'fastly',
|
|
43
|
+
...(options.bodyForwarding === undefined ? {} : {bodyForwarding: require('./toolchain/body-forwarding.js').normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs)}),
|
|
44
|
+
...(options.maxDurationMs === undefined ? {} : { maxDurationMs: require('@pulse-compute/runtime/host').normalizeRequestDuration(options.maxDurationMs) }),
|
|
41
45
|
version: FASTLY_PROVIDER_API_VERSION,
|
|
42
46
|
bindings: Object.freeze({
|
|
47
|
+
...(options.logEndpoint === undefined ? {} : { logEndpoint: normalizeFastlyLogEndpoint(options.logEndpoint) }),
|
|
43
48
|
configStore: String(options.configStore ?? fastlyConfigDefault('fastly.configStore')),
|
|
44
49
|
secretStore: String(options.secretStore ?? fastlyConfigDefault('fastly.secretStore')),
|
|
45
50
|
kv: stringMap(options.kv),
|
|
@@ -48,6 +53,7 @@ function fastly(options = {}) {
|
|
|
48
53
|
dynamicBackends: options.dynamicBackends === undefined ? fastlyConfigDefault('fastly.dynamicBackends') : options.dynamicBackends === true
|
|
49
54
|
}),
|
|
50
55
|
build: Object.freeze({
|
|
56
|
+
maxWasmBytes: normalizeFastlyMaxWasmBytes(options.maxWasmBytes),
|
|
51
57
|
name: String(options.name ?? fastlyConfigDefault('fastly.name')),
|
|
52
58
|
description: String(options.description ?? fastlyConfigDefault('fastly.description')),
|
|
53
59
|
authors: Object.freeze((Array.isArray(options.authors) ? options.authors : fastlyConfigDefault('fastly.authors')).map(String)),
|
package/src/config-schema.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "pulse.provider-fastly-config-schema.v1",
|
|
3
3
|
"fields": {
|
|
4
|
+
"fastly.bindings.s3": {
|
|
5
|
+
"kind": "object",
|
|
6
|
+
"default": {}
|
|
7
|
+
},
|
|
4
8
|
"fastly.configStore": {
|
|
5
9
|
"kind": "string",
|
|
6
10
|
"default": "pulse_config"
|
|
@@ -68,6 +72,32 @@
|
|
|
68
72
|
"fastly.local.networkFetch": {
|
|
69
73
|
"kind": "boolean",
|
|
70
74
|
"default": false
|
|
75
|
+
},
|
|
76
|
+
"fastly.maxDurationMs": {
|
|
77
|
+
"kind": "optional-number"
|
|
78
|
+
},
|
|
79
|
+
"fastly.build.maxWasmBytes": {
|
|
80
|
+
"kind": "positive-safe-integer",
|
|
81
|
+
"default": 4194304
|
|
82
|
+
},
|
|
83
|
+
"fastly.maxWasmBytes": {
|
|
84
|
+
"kind": "positive-safe-integer"
|
|
85
|
+
},
|
|
86
|
+
"fastly.bodyForwarding": {
|
|
87
|
+
"kind": "object"
|
|
88
|
+
},
|
|
89
|
+
"fastly.bodyForwarding.requestMaxBytes": {
|
|
90
|
+
"kind": "integer",
|
|
91
|
+
"minimum": 1,
|
|
92
|
+
"maximum": 1048576
|
|
93
|
+
},
|
|
94
|
+
"fastly.bodyForwarding.responseMaxBytes": {
|
|
95
|
+
"kind": "integer",
|
|
96
|
+
"minimum": 1,
|
|
97
|
+
"maximum": 1048576
|
|
98
|
+
},
|
|
99
|
+
"fastly.logEndpoint": {
|
|
100
|
+
"kind": "optional-string"
|
|
71
101
|
}
|
|
72
102
|
}
|
|
73
103
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const { normalizeFastlyLogEndpoint } = require('../src/toolchain/logging.js') as { normalizeFastlyLogEndpoint(value?: string): string | undefined };
|
|
2
|
+
const { normalizeBodyForwarding } = require('../src/toolchain/body-forwarding.js') as { normalizeBodyForwarding(value: PulseFastlyBodyForwardingOptions | undefined, maxDurationMs?: number): PulseFastlyBodyForwardingOptions | undefined };
|
|
3
|
+
const { normalizeFastlyMaxWasmBytes } = require('../src/build/wasm-budget.js') as { normalizeFastlyMaxWasmBytes(value?: number): number };
|
|
4
|
+
const { normalizeRequestDuration } = require('@pulse-compute/runtime/host') as { normalizeRequestDuration(value?: number): number | undefined };
|
|
1
5
|
import FASTLY_CONFIG_SCHEMA from './config-schema.json';
|
|
2
6
|
|
|
3
7
|
type FastlySchemaRule = Readonly<{ kind: string; default?: unknown }>;
|
|
@@ -22,7 +26,19 @@ export interface PulseFastlyLocalOptions {
|
|
|
22
26
|
readonly networkFetch?: boolean;
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
export interface PulseFastlyBodyForwardingOptions {
|
|
30
|
+
readonly requestMaxBytes: number;
|
|
31
|
+
readonly responseMaxBytes: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
export interface PulseFastlyProviderOptions {
|
|
35
|
+
/** Inclusive final Native Wasm byte budget; default 4 MiB. */
|
|
36
|
+
readonly maxWasmBytes?: number;
|
|
37
|
+
readonly maxDurationMs?: number;
|
|
38
|
+
/** Experimental Native metered pump; final close hands delivery to Fastly. */
|
|
39
|
+
readonly bodyForwarding?: PulseFastlyBodyForwardingOptions;
|
|
40
|
+
/** Named production log stream; omitted preserves stdout. */
|
|
41
|
+
readonly logEndpoint?: string;
|
|
26
42
|
readonly configStore?: string;
|
|
27
43
|
readonly secretStore?: string;
|
|
28
44
|
readonly kv?: Readonly<Record<string, string>>;
|
|
@@ -36,9 +52,13 @@ export interface PulseFastlyProviderOptions {
|
|
|
36
52
|
}
|
|
37
53
|
|
|
38
54
|
export interface PulseFastlyProviderConfig {
|
|
55
|
+
readonly maxDurationMs?: number;
|
|
56
|
+
/** Experimental Native metered pump; final close hands delivery to Fastly. */
|
|
57
|
+
readonly bodyForwarding?: PulseFastlyBodyForwardingOptions;
|
|
39
58
|
readonly kind: 'fastly';
|
|
40
59
|
readonly version: typeof FASTLY_PROVIDER_API_VERSION;
|
|
41
60
|
readonly bindings: {
|
|
61
|
+
readonly logEndpoint?: string;
|
|
42
62
|
readonly configStore: string;
|
|
43
63
|
readonly secretStore: string;
|
|
44
64
|
readonly kv: Readonly<Record<string, string>>;
|
|
@@ -57,6 +77,7 @@ export interface PulseFastlyProviderConfig {
|
|
|
57
77
|
readonly dynamicBackends: boolean;
|
|
58
78
|
};
|
|
59
79
|
readonly build: {
|
|
80
|
+
readonly maxWasmBytes: number;
|
|
60
81
|
readonly name: string;
|
|
61
82
|
readonly description: string;
|
|
62
83
|
readonly authors: readonly string[];
|
|
@@ -97,8 +118,11 @@ export function fastly(options: PulseFastlyProviderOptions = {}): PulseFastlyPro
|
|
|
97
118
|
const normalizedGrip = options.grip === undefined ? undefined : gripBindings(options.grip);
|
|
98
119
|
return Object.freeze({
|
|
99
120
|
kind: 'fastly',
|
|
121
|
+
maxDurationMs: normalizeRequestDuration(options.maxDurationMs),
|
|
122
|
+
bodyForwarding: normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs),
|
|
100
123
|
version: FASTLY_PROVIDER_API_VERSION,
|
|
101
124
|
bindings: Object.freeze({
|
|
125
|
+
...(options.logEndpoint === undefined ? {} : { logEndpoint: normalizeFastlyLogEndpoint(options.logEndpoint) }),
|
|
102
126
|
configStore: String(options.configStore ?? fastlyDefaults['fastly.configStore']),
|
|
103
127
|
secretStore: String(options.secretStore ?? fastlyDefaults['fastly.secretStore']),
|
|
104
128
|
kv: stringMap(options.kv),
|
|
@@ -107,6 +131,7 @@ export function fastly(options: PulseFastlyProviderOptions = {}): PulseFastlyPro
|
|
|
107
131
|
dynamicBackends: options.dynamicBackends === undefined ? Boolean(fastlyDefaults['fastly.dynamicBackends']) : options.dynamicBackends === true
|
|
108
132
|
}),
|
|
109
133
|
build: Object.freeze({
|
|
134
|
+
maxWasmBytes: normalizeFastlyMaxWasmBytes(options.maxWasmBytes),
|
|
110
135
|
name: String(options.name ?? fastlyDefaults['fastly.name']),
|
|
111
136
|
description: String(options.description ?? fastlyDefaults['fastly.description']),
|
|
112
137
|
authors: Object.freeze((options.authors ?? (Array.isArray(fastlyDefaults['fastly.authors']) ? fastlyDefaults['fastly.authors'] : [])).map(String)),
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
+
normalizeJwtSignEffect,
|
|
4
5
|
normalizeJwtVerifyEffect
|
|
5
6
|
} = require('@pulse-compute/wasm-contracts/jwt/contracts');
|
|
6
7
|
const {
|
|
7
8
|
CRYPTO_ES256_RUNTIME_BUILTIN_IMPLEMENTATION,
|
|
9
|
+
CRYPTO_RS256_RUNTIME_BUILTIN_IMPLEMENTATION,
|
|
8
10
|
CRYPTO_RUNTIME_BUILTIN_IMPLEMENTATION
|
|
9
11
|
} = require('@pulse-compute/wasm-contracts/crypto/contracts');
|
|
10
12
|
|
|
@@ -16,6 +18,7 @@ const FASTLY_JAVASCRIPT_JWT_REALIZATIONS = Object.freeze({
|
|
|
16
18
|
implementation: CRYPTO_RUNTIME_BUILTIN_IMPLEMENTATION,
|
|
17
19
|
automaticFallback: false
|
|
18
20
|
}),
|
|
21
|
+
RS256: Object.freeze({ algorithm: 'RS256', realization: 'runtime-builtin', implementation: CRYPTO_RS256_RUNTIME_BUILTIN_IMPLEMENTATION, automaticFallback: false }),
|
|
19
22
|
ES256: Object.freeze({
|
|
20
23
|
algorithm: 'ES256',
|
|
21
24
|
realization: 'runtime-builtin',
|
|
@@ -83,12 +86,19 @@ function createFastlyJavascriptJwtVerify(options = {}) {
|
|
|
83
86
|
return async function verifyFastlyJavascriptJwt(effect, execution) {
|
|
84
87
|
const { jwtProvider, cryptoProvider } = await loadRuntimeModules();
|
|
85
88
|
assertRequestActive(execution, jwtProvider);
|
|
86
|
-
const
|
|
87
|
-
|
|
89
|
+
const signing = effect.operation === 'sign';
|
|
90
|
+
const input = signing ? normalizeJwtSignEffect(effect) : normalizeJwtVerifyEffect(effect);
|
|
91
|
+
if (!signing && execution && typeof execution.registerRedactionValue === 'function') {
|
|
88
92
|
execution.registerRedactionValue(input.token);
|
|
89
93
|
}
|
|
90
94
|
assertRequestActive(execution, jwtProvider);
|
|
91
|
-
const selectedCrypto =
|
|
95
|
+
const selectedCrypto = signing
|
|
96
|
+
? (input.options.algorithm === 'RS256'
|
|
97
|
+
? require('@pulse-compute/crypto/provider').bindJavascriptRs256Signer()
|
|
98
|
+
: input.options.algorithm === 'ES256'
|
|
99
|
+
? require('@pulse-compute/crypto/provider').bindJavascriptEs256Signer()
|
|
100
|
+
: require('@pulse-compute/crypto/provider').bindJavascriptDigestMac(['HMAC-SHA256']))
|
|
101
|
+
: assertRuntimeBuiltin(cryptoProvider, jwtProvider);
|
|
92
102
|
const host = {
|
|
93
103
|
async captureWallClock() {
|
|
94
104
|
assertRequestActive(execution, jwtProvider);
|
|
@@ -118,7 +128,7 @@ function createFastlyJavascriptJwtVerify(options = {}) {
|
|
|
118
128
|
return result;
|
|
119
129
|
};
|
|
120
130
|
}
|
|
121
|
-
const result = await jwtProvider.verifyJwtWithCrypto(
|
|
131
|
+
const result = await (signing ? jwtProvider.signJwtWithCrypto : jwtProvider.verifyJwtWithCrypto)(
|
|
122
132
|
input,
|
|
123
133
|
Object.freeze(host),
|
|
124
134
|
selectedCrypto
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { normalizeFastlyLogEndpoint, MAX_LOG_BYTES } = require('../toolchain/logging.js');
|
|
4
|
+
|
|
3
5
|
const FASTLY_JAVASCRIPT_LOGGING_VERSION = 'pulse.fastly-javascript-logging.v1';
|
|
4
6
|
|
|
5
7
|
function createFastlyJavascriptLogEmitter(options = {}) {
|
|
8
|
+
const endpoint = normalizeFastlyLogEndpoint(options.logEndpoint);
|
|
6
9
|
const output = options.console || globalThis.console;
|
|
7
10
|
const methods = Object.freeze({
|
|
8
11
|
1: 'error',
|
|
@@ -11,12 +14,18 @@ function createFastlyJavascriptLogEmitter(options = {}) {
|
|
|
11
14
|
4: 'debug'
|
|
12
15
|
});
|
|
13
16
|
return function fastlyJavascriptLog(level, message, event) {
|
|
14
|
-
if (!output) return;
|
|
15
17
|
const method = methods[Number(level)] || 'log';
|
|
18
|
+
const record = `[pulse][${event && event.name || method}] ${String(message)}`;
|
|
19
|
+
if (endpoint !== undefined) {
|
|
20
|
+
if (new TextEncoder().encode(record).byteLength > MAX_LOG_BYTES) return;
|
|
21
|
+
try { new options.Logger(endpoint).log(record); } catch { /* Best effort, no fallback or retry. */ }
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!output) return;
|
|
16
25
|
const write = typeof output[method] === 'function'
|
|
17
26
|
? output[method]
|
|
18
27
|
: (typeof output.log === 'function' ? output.log : undefined);
|
|
19
|
-
if (write) write.call(output,
|
|
28
|
+
try { if (write) write.call(output, record); } catch { /* Logging does not change the HTTP outcome. */ }
|
|
20
29
|
};
|
|
21
30
|
}
|
|
22
31
|
|
|
@@ -15,10 +15,12 @@ function unavailable(effect = {}) {
|
|
|
15
15
|
function createFastlyJavascriptPackageEffectCapabilities(options = {}) {
|
|
16
16
|
const assetsLookup = options.assetsLookup;
|
|
17
17
|
const gripBroadcast = options.gripBroadcast;
|
|
18
|
+
const digestText = require('@pulse-compute/crypto/provider').createJavascriptTextDigest(options);
|
|
18
19
|
const jwtVerify = options.jwtVerify;
|
|
19
20
|
return Object.freeze({
|
|
20
21
|
version: FASTLY_JAVASCRIPT_PACKAGE_EFFECTS_VERSION,
|
|
21
22
|
effect(effect, execution) {
|
|
23
|
+
if (effect && effect.contractId === 'pulse.crypto' && effect.operation === 'digestText') return digestText(effect, execution);
|
|
22
24
|
if (effect && effect.contractId === 'pulse.assets' && effect.operation === 'lookup') {
|
|
23
25
|
if (typeof assetsLookup !== 'function') return unavailable(effect);
|
|
24
26
|
return assetsLookup(effect.payload, execution);
|
|
@@ -33,7 +35,7 @@ function createFastlyJavascriptPackageEffectCapabilities(options = {}) {
|
|
|
33
35
|
}
|
|
34
36
|
return gripBroadcast(effect.payload, execution);
|
|
35
37
|
}
|
|
36
|
-
if (effect && effect.contractId === 'pulse.jwt' && effect.operation
|
|
38
|
+
if (effect && effect.contractId === 'pulse.jwt' && ['verify', 'sign'].includes(effect.operation)) {
|
|
37
39
|
if (typeof jwtVerify !== 'function') return unavailable(effect);
|
|
38
40
|
return jwtVerify(effect, execution);
|
|
39
41
|
}
|
|
@@ -49,7 +51,8 @@ function withFastlyPackageEffectCapabilities(capabilities, options = {}) {
|
|
|
49
51
|
...base,
|
|
50
52
|
effect(effect, execution) {
|
|
51
53
|
if (effect && (
|
|
52
|
-
effect.contractId === 'pulse.
|
|
54
|
+
effect.contractId === 'pulse.crypto'
|
|
55
|
+
|| effect.contractId === 'pulse.assets'
|
|
53
56
|
|| effect.contractId === 'pulse.grip'
|
|
54
57
|
|| effect.contractId === 'pulse.jwt'
|
|
55
58
|
)) {
|
|
@@ -45,10 +45,10 @@ function createFastlyJavascriptCapabilities(options = {}) {
|
|
|
45
45
|
: undefined;
|
|
46
46
|
return withFastlyPackageEffectCapabilities(
|
|
47
47
|
withFastlyFetchCapability(
|
|
48
|
-
withFastlyBindingCapabilities(options.capabilities, { ...options, bindingCapabilities }),
|
|
48
|
+
withFastlyBindingCapabilities({ time: () => runtimeHost.readWallTime(options.wallClock === undefined ? () => Date.now() : options.wallClock), ...options.capabilities }, { ...options, bindingCapabilities }),
|
|
49
49
|
{ ...options, fetchImplementation: options.fetchImplementation }
|
|
50
50
|
),
|
|
51
|
-
{ assetsLookup, gripBroadcast, jwtVerify }
|
|
51
|
+
{ assetsLookup, gripBroadcast, jwtVerify, digestSubtle: options.digestSubtle }
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -57,6 +57,9 @@ function requestHeaderPairs(request) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
async function executeFastlyJavascriptApplication(application, request, options = {}) {
|
|
60
|
+
if (options.maxDurationMs !== undefined || options.requestBudget !== undefined) {
|
|
61
|
+
throw new runtimeHost.PulseRuntimeContractError('PULSE_REQUEST_DURATION_UNSUPPORTED', 'Fastly JavaScript does not implement the request deadline; select Native.');
|
|
62
|
+
}
|
|
60
63
|
const capabilities = options.effectAdapter === undefined
|
|
61
64
|
? createFastlyJavascriptCapabilities(options)
|
|
62
65
|
: undefined;
|
|
@@ -77,12 +80,13 @@ async function executeFastlyJavascriptApplication(application, request, options
|
|
|
77
80
|
maxKvValueBytes: options.maxKvValueBytes,
|
|
78
81
|
maxKvValueDepth: options.maxKvValueDepth,
|
|
79
82
|
maxKvValueEntries: options.maxKvValueEntries,
|
|
83
|
+
binaryResources: options.binaryResources,
|
|
80
84
|
schemaCodecs: options.schemaCodecs,
|
|
81
85
|
strict: options.strict === true,
|
|
82
86
|
target: 'javascript',
|
|
83
87
|
provider: 'fastly',
|
|
84
88
|
reporting: options.reporting,
|
|
85
|
-
log: options.log || createFastlyJavascriptLogEmitter({ console: options.console }),
|
|
89
|
+
log: options.log || createFastlyJavascriptLogEmitter({ console: options.console, logEndpoint: options.bindings?.logEndpoint, Logger: options.apis?.Logger }),
|
|
86
90
|
redactionValues: options.redactionValues,
|
|
87
91
|
onLogObservation: options.onLogObservation,
|
|
88
92
|
onJsonTrace: options.onJsonTrace,
|
|
@@ -153,6 +153,7 @@ function abortControllerCompatibilitySource() {
|
|
|
153
153
|
function bootstrapSource(deployment, runtimeConfig) {
|
|
154
154
|
return [
|
|
155
155
|
"import { enforceExplicitBackends } from 'fastly:backend';",
|
|
156
|
+
"import { Logger } from 'fastly:logger';",
|
|
156
157
|
"import { ConfigStore } from 'fastly:config-store';",
|
|
157
158
|
"import { KVStore } from 'fastly:kv-store';",
|
|
158
159
|
"import { SecretStore } from 'fastly:secret-store';",
|
|
@@ -165,7 +166,7 @@ function bootstrapSource(deployment, runtimeConfig) {
|
|
|
165
166
|
'if (pulseRuntimeConfig.bindings.dynamicBackends !== true) enforceExplicitBackends();',
|
|
166
167
|
'export const handler = createFastlyJavascriptHandler(application, {',
|
|
167
168
|
' ...pulseRuntimeConfig,',
|
|
168
|
-
' apis: { ConfigStore, SecretStore, KVStore },',
|
|
169
|
+
' apis: { ConfigStore, SecretStore, KVStore, Logger },',
|
|
169
170
|
' fetchImplementation: globalThis.fetch.bind(globalThis),',
|
|
170
171
|
' console: globalThis.console,',
|
|
171
172
|
' schemaCodecs',
|
|
@@ -332,6 +333,7 @@ function bundleFastlyJavascriptApplication(plan, projectRoot, schemaBundle, opti
|
|
|
332
333
|
]
|
|
333
334
|
: ['const schemaCodecs = null;']),
|
|
334
335
|
'const { createFastlyJavascriptHandler } = fastlyRuntime;',
|
|
336
|
+
...(options.binaryResources ? [require('@pulse-compute/wasm-host-runtime/compiler/binary-resources').resourceJavascriptSource(options.binaryResources, 'application', 'resourceHost'), "import resourceHost from '@pulse-compute/runtime/host';"] : []),
|
|
335
337
|
'export { application, createFastlyJavascriptHandler, schemaCodecs };',
|
|
336
338
|
'export default application;',
|
|
337
339
|
''
|
|
@@ -501,7 +503,7 @@ function writeFastlyJavascriptSourcePackage(options = {}) {
|
|
|
501
503
|
});
|
|
502
504
|
const schemaBundle = options.schemaBundle;
|
|
503
505
|
const schemaActive = Boolean(schemaBundle && schemaBundle.active);
|
|
504
|
-
const bundle = bundleFastlyJavascriptApplication(plan, projectRoot, schemaBundle);
|
|
506
|
+
const bundle = bundleFastlyJavascriptApplication(plan, projectRoot, schemaBundle, { binaryResources: options.binaryResources });
|
|
505
507
|
const providerConfig = options.providerConfig || {};
|
|
506
508
|
const providerBindings = providerConfig.bindings && typeof providerConfig.bindings === 'object'
|
|
507
509
|
? providerConfig.bindings
|
|
@@ -16,6 +16,11 @@ function blockedRestriction(id, restriction) {
|
|
|
16
16
|
|
|
17
17
|
function classifyFastlyJavascriptCapability(id, restrictions = {}) {
|
|
18
18
|
const capability = String(id);
|
|
19
|
+
if (capability === 'request.body.transform') return decision(capability, 'blocked', 'fastly-body-transform-unavailable', 'provider-fastly');
|
|
20
|
+
if (capability === 'request.body.forward' || capability === 'req.body') return decision(capability, 'blocked', 'fastly-incoming-body-forwarding-unavailable', 'provider-fastly');
|
|
21
|
+
if (capability === 'crypto.digestText') return decision(capability, 'eligible', 'fastly-javascript-crypto-sha256-runtime-builtin', 'provider-fastly');
|
|
22
|
+
if (['kv.getVersioned', 'kv.insertIfAbsent', 'kv.compareAndSwap', 'kv.list', 'kv.delete'].includes(capability)) return decision(capability, 'blocked', 'fastly-conditional-kv-incomplete', 'provider-fastly');
|
|
23
|
+
if (['s3.head', 's3.getText', 's3.putText', 's3.getBody'].includes(capability)) return decision(capability, 'blocked', 'fastly-javascript-s3-raw-headers-unavailable', 'provider-fastly');
|
|
19
24
|
if (isJavascriptCoreCapability(capability)) {
|
|
20
25
|
return decision(capability, 'eligible', 'fastly-request-router-runtime', 'provider-fastly');
|
|
21
26
|
}
|
|
@@ -79,9 +84,11 @@ function classifyFastlyJavascriptCapability(id, restrictions = {}) {
|
|
|
79
84
|
return decision(capability, 'eligible', 'fastly-javascript-provider-wall-clock-authority', 'provider-fastly');
|
|
80
85
|
}
|
|
81
86
|
if (
|
|
82
|
-
capability === 'jwt.
|
|
87
|
+
capability === 'jwt.sign'
|
|
88
|
+
|| capability === 'jwt.verify'
|
|
83
89
|
|| capability === 'jwt.verify.hs256'
|
|
84
90
|
|| capability === 'jwt.verify.es256'
|
|
91
|
+
|| capability === 'jwt.verify.rs256'
|
|
85
92
|
) {
|
|
86
93
|
return blockedRestriction(capability, restrictions.jwt)
|
|
87
94
|
|| decision(capability, 'eligible', 'fastly-javascript-jwt-crypto-runtime-builtin', 'provider-fastly');
|
|
@@ -100,6 +107,9 @@ function classifyFastlyJavascriptCapability(id, restrictions = {}) {
|
|
|
100
107
|
|
|
101
108
|
function classifyFastlyJavascriptProviderRequirement(id, restrictions = {}) {
|
|
102
109
|
const requirement = String(id);
|
|
110
|
+
if (requirement === 'crypto.digestText') return decision(requirement, 'eligible', 'fastly-javascript-crypto-sha256-runtime-builtin', 'provider-fastly');
|
|
111
|
+
if (['kv.getVersioned', 'kv.insertIfAbsent', 'kv.compareAndSwap', 'kv.list', 'kv.delete'].includes(requirement)) return decision(requirement, 'blocked', 'fastly-conditional-kv-incomplete', 'provider-fastly');
|
|
112
|
+
if (['s3.head', 's3.getText', 's3.putText', 's3.getBody'].includes(requirement)) return decision(requirement, 'blocked', 'fastly-javascript-s3-raw-headers-unavailable', 'provider-fastly');
|
|
103
113
|
if (requirement === 'request' || ['response.json', 'response.text', 'response.custom'].includes(requirement)) {
|
|
104
114
|
return decision(requirement, 'eligible', 'fastly-request-response-lifecycle', 'provider-fastly');
|
|
105
115
|
}
|
|
@@ -141,9 +151,11 @@ function classifyFastlyJavascriptProviderRequirement(id, restrictions = {}) {
|
|
|
141
151
|
return decision(requirement, 'eligible', 'fastly-javascript-provider-wall-clock-authority', 'provider-fastly');
|
|
142
152
|
}
|
|
143
153
|
if (
|
|
144
|
-
requirement === 'jwt.
|
|
154
|
+
requirement === 'jwt.sign'
|
|
155
|
+
|| requirement === 'jwt.verify'
|
|
145
156
|
|| requirement === 'jwt.verify.hs256'
|
|
146
157
|
|| requirement === 'jwt.verify.es256'
|
|
158
|
+
|| requirement === 'jwt.verify.rs256'
|
|
147
159
|
) {
|
|
148
160
|
return blockedRestriction(requirement, restrictions.jwt)
|
|
149
161
|
|| decision(requirement, 'eligible', 'fastly-javascript-jwt-crypto-runtime-builtin', 'provider-fastly');
|
|
@@ -165,6 +177,11 @@ function restriction(reasonId, configuration) {
|
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
function fastlyJavascriptProjectRestrictions(compiled, project) {
|
|
180
|
+
if (project.providerConfig?.maxDurationMs !== undefined) {
|
|
181
|
+
const error = new TypeError('fastly.maxDurationMs requires the Native target; Fastly JavaScript does not implement the request deadline.');
|
|
182
|
+
error.code = 'PULSE_REQUEST_DURATION_UNSUPPORTED';
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
168
185
|
const bindings = project.providerConfig && project.providerConfig.bindings || {};
|
|
169
186
|
const backends = bindings.backends && typeof bindings.backends === 'object' ? bindings.backends : {};
|
|
170
187
|
const kv = bindings.kv && typeof bindings.kv === 'object' ? bindings.kv : {};
|
|
@@ -177,7 +194,7 @@ function fastlyJavascriptProjectRestrictions(compiled, project) {
|
|
|
177
194
|
const secretSites = sites.filter((entry) => entry.kind === 'secret.get');
|
|
178
195
|
const jwtSites = sites.filter((entry) => (
|
|
179
196
|
entry.contractId === 'pulse.jwt'
|
|
180
|
-
&& (
|
|
197
|
+
&& (['verify', 'sign'].includes(entry.operation) || ['jwt.verify', 'jwt.sign'].includes(entry.kind))
|
|
181
198
|
));
|
|
182
199
|
const output = {};
|
|
183
200
|
|
package/src/javascript/target.js
CHANGED
|
@@ -14,6 +14,16 @@ const FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR = normalizeJavascriptTargetDescriptor(
|
|
|
14
14
|
crypto: defineCryptoTargetCapabilities({
|
|
15
15
|
target: 'javascript',
|
|
16
16
|
algorithms: [{
|
|
17
|
+
algorithm: 'HMAC-SHA256',
|
|
18
|
+
realization: 'runtime-builtin',
|
|
19
|
+
implemented: true,
|
|
20
|
+
status: 'implemented-jwt-sign'
|
|
21
|
+
}, {
|
|
22
|
+
algorithm: 'SHA-256',
|
|
23
|
+
realization: 'runtime-builtin',
|
|
24
|
+
implemented: true,
|
|
25
|
+
status: 'implemented-d1'
|
|
26
|
+
}, {
|
|
17
27
|
algorithm: 'HS256',
|
|
18
28
|
realization: 'runtime-builtin',
|
|
19
29
|
implemented: true,
|
|
@@ -23,6 +33,11 @@ const FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR = normalizeJavascriptTargetDescriptor(
|
|
|
23
33
|
realization: 'runtime-builtin',
|
|
24
34
|
implemented: true,
|
|
25
35
|
status: 'implemented-h4'
|
|
36
|
+
}, {
|
|
37
|
+
algorithm: 'RS256',
|
|
38
|
+
realization: 'runtime-builtin',
|
|
39
|
+
implemented: true,
|
|
40
|
+
status: 'implemented-h4'
|
|
26
41
|
}]
|
|
27
42
|
}),
|
|
28
43
|
capabilities: {
|
|
@@ -45,7 +60,7 @@ const FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR = normalizeJavascriptTargetDescriptor(
|
|
|
45
60
|
kv: true,
|
|
46
61
|
parallel: true
|
|
47
62
|
},
|
|
48
|
-
packages: ['pulse.assets', 'pulse.grip', 'pulse.jwt'],
|
|
63
|
+
packages: ['pulse.assets', 'pulse.crypto', 'pulse.grip', 'pulse.jwt'],
|
|
49
64
|
restrictions: {
|
|
50
65
|
fetch: 'origin-backend-map-or-dynamic-opt-in',
|
|
51
66
|
kv: 'logical-store-map-required',
|
|
@@ -57,6 +72,7 @@ const FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR = normalizeJavascriptTargetDescriptor(
|
|
|
57
72
|
supported: true,
|
|
58
73
|
default: 'info',
|
|
59
74
|
levels: ['off', 'error', 'warn', 'info', 'debug'],
|
|
75
|
+
destinations: ['stdout', 'configured-named-endpoint'],
|
|
60
76
|
destination: 'provider-owned',
|
|
61
77
|
betaDefault: 'stdio-log-tail'
|
|
62
78
|
},
|
|
@@ -331,6 +331,7 @@ async function executeFastlyJavascriptLocalRequest(application, request, options
|
|
|
331
331
|
maxRequestBodyBytes: options.maxRequestBodyBytes ?? options.maxBodyBytes,
|
|
332
332
|
maxFetchBodyBytes: options.maxFetchBodyBytes ?? options.maxBodyBytes,
|
|
333
333
|
maxStructuredBodyBytes: options.maxStructuredBodyBytes,
|
|
334
|
+
binaryResources: options.binaryResources,
|
|
334
335
|
schemaCodecs: options.schemaCodecs,
|
|
335
336
|
strict: options.strict === true,
|
|
336
337
|
reporting: options.reporting,
|
package/src/provider-contract.js
CHANGED
|
@@ -12,27 +12,40 @@ const FASTLY_PROVIDER_DESCRIPTOR = canonical.normalizeDescriptor({
|
|
|
12
12
|
deployable: true,
|
|
13
13
|
localExecution: true,
|
|
14
14
|
capabilities: [
|
|
15
|
-
'request', 'response.json', 'response.text', 'response.custom', 'fetch',
|
|
16
|
-
'config.get', 'secret.get', 'kv.get', 'kv.put', 'assets.lookup',
|
|
17
|
-
'grip.channel', 'grip.hold', 'grip.publish', 'grip.broadcast', 'jwt.verify',
|
|
18
|
-
'opaque.pass-through'
|
|
15
|
+
'request', 'request.body.forward', 'response.json', 'response.text', 'response.custom', 'fetch',
|
|
16
|
+
'time.wall-clock', 'crypto.digestText', 'config.get', 'secret.get', 'kv.get', 'kv.put', 'kv.getVersioned', 'kv.insertIfAbsent', 'kv.compareAndSwap', 'kv.list', 'kv.delete', 'assets.lookup',
|
|
17
|
+
'grip.channel', 'grip.hold', 'grip.publish', 'grip.broadcast', 'jwt.verify', 'jwt.sign',
|
|
18
|
+
'opaque.pass-through', 's3.head', 's3.getText', 's3.putText', 's3.getBody'
|
|
19
19
|
],
|
|
20
20
|
lowering: {
|
|
21
21
|
request: 'fastly.compute.request',
|
|
22
|
+
'request.body.forward': 'fastly.native.body-forwarding',
|
|
22
23
|
'response.json': 'fastly.compute.response.json',
|
|
23
24
|
'response.text': 'fastly.compute.response.text',
|
|
24
25
|
'response.custom': 'fastly.compute.response.custom',
|
|
25
26
|
fetch: 'fastly.backend.fetch',
|
|
27
|
+
'time.wall-clock': 'wasi.clock_time_get.realtime',
|
|
28
|
+
'crypto.digestText': 'pulse.crypto.guest-source.sha256',
|
|
26
29
|
'config.get': 'fastly.config-store.get',
|
|
27
30
|
'secret.get': 'fastly.secret-store.get',
|
|
28
31
|
'kv.get': 'fastly.kv-store.get',
|
|
29
32
|
'kv.put': 'fastly.kv-store.put',
|
|
33
|
+
'kv.list': 'fastly.native.kv-store.list',
|
|
34
|
+
'kv.delete': 'fastly.native.kv-store.delete',
|
|
35
|
+
'kv.getVersioned': 'fastly.native.kv-store.getVersioned',
|
|
36
|
+
'kv.insertIfAbsent': 'fastly.native.kv-store.insertIfAbsent',
|
|
37
|
+
'kv.compareAndSwap': 'fastly.native.kv-store.compareAndSwap',
|
|
30
38
|
'assets.lookup': 'fastly.kv-store.assets.lookup',
|
|
31
39
|
'grip.channel': 'fastly.fanout.channel',
|
|
32
40
|
'grip.hold': 'fastly.fanout.hold',
|
|
33
41
|
'grip.publish': 'fastly.fanout.publish',
|
|
34
42
|
'grip.broadcast': 'fastly.grip.publish-control',
|
|
35
43
|
'jwt.verify': 'fastly.native.jwt.verify',
|
|
44
|
+
'jwt.sign': 'fastly.native.jwt.sign',
|
|
45
|
+
's3.head': 'fastly.native.s3.head',
|
|
46
|
+
's3.getText': 'fastly.native.s3.getText',
|
|
47
|
+
's3.getBody': 'fastly.native.s3.getBody',
|
|
48
|
+
's3.putText': 'fastly.native.s3.putText',
|
|
36
49
|
'opaque.pass-through': 'fastly.response-body.stream'
|
|
37
50
|
}
|
|
38
51
|
});
|
|
@@ -52,9 +65,12 @@ function validateStaticBackendBindings(metadata = {}, bindings = {}) {
|
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
function createFastlyLoweringPlan(metadata, bindings = {}) {
|
|
68
|
+
function createFastlyLoweringPlan(metadata, bindings = {}, options = {}) {
|
|
69
|
+
const forwarding = require('./toolchain/body-forwarding.js').normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs);
|
|
70
|
+
const descriptor = forwarding ? FASTLY_PROVIDER_DESCRIPTOR : {...FASTLY_PROVIDER_DESCRIPTOR, capabilities: FASTLY_PROVIDER_DESCRIPTOR.capabilities.filter(id => id !== 'request.body.forward')};
|
|
71
|
+
require('./toolchain/s3.js').resolveFastlyS3(metadata.providerOperations || [], bindings.s3);
|
|
56
72
|
validateStaticBackendBindings(metadata, bindings);
|
|
57
|
-
return canonical.createProviderLoweringPlan(metadata,
|
|
73
|
+
return canonical.createProviderLoweringPlan(metadata, descriptor, {
|
|
58
74
|
...bindings,
|
|
59
75
|
grip: bindings.grip || 'fastly-fanout'
|
|
60
76
|
});
|