@pulse-compute/provider-fastly 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.
Files changed (69) hide show
  1. package/README.md +53 -1
  2. package/dist/config-schema.json +77 -0
  3. package/dist/index.d.ts +61 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +59 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +88 -5
  8. package/src/build/canonical-target.js +436 -0
  9. package/src/build/entities-native.js +564 -0
  10. package/src/build/fixed-memory-runtime.as.ts +148 -0
  11. package/src/build/kv-native.as.ts +229 -0
  12. package/src/build/kv-native.js +20 -0
  13. package/src/build/native-http-effects.js +871 -0
  14. package/src/build/native-http-shell.js +703 -0
  15. package/src/build/native-platform-capabilities.js +2196 -0
  16. package/src/build/s3-native.as.ts +215 -0
  17. package/src/build/s3-native.js +30 -0
  18. package/src/compiler/assets-package-out-parity-proof.js +338 -0
  19. package/src/compiler/assets-provider-proof.js +499 -0
  20. package/src/compiler/config-provider-resolution.js +573 -0
  21. package/src/compiler/fastly-adapter.js +564 -0
  22. package/src/compiler/fastly-command-entry.js +390 -0
  23. package/src/compiler/fastly-config-secret-serve.js +323 -0
  24. package/src/compiler/fastly-hostcall-binding.js +598 -0
  25. package/src/compiler/fastly-readiness.js +477 -0
  26. package/src/compiler/index.js +40 -0
  27. package/src/compiler/lifecycle-parity-contract.js +70 -0
  28. package/src/compiler/lifecycle-parity-proof.js +260 -0
  29. package/src/compiler/route-handler-effect-proof.js +255 -0
  30. package/src/compiler/runtime-provider-kv.js +237 -0
  31. package/src/compiler/runtime-provider-shape.js +109 -0
  32. package/src/config-api.js +63 -0
  33. package/src/config-schema.d.ts +13 -0
  34. package/src/config-schema.js +27 -0
  35. package/src/config-schema.json +77 -0
  36. package/src/index.js +3 -0
  37. package/src/index.ts +118 -0
  38. package/src/javascript/assets-adapter.js +122 -0
  39. package/src/javascript/bindings-adapter.js +237 -0
  40. package/src/javascript/errors.js +26 -0
  41. package/src/javascript/fetch-adapter.js +93 -0
  42. package/src/javascript/grip-broadcast.js +261 -0
  43. package/src/javascript/jwt-verifier.js +137 -0
  44. package/src/javascript/lifecycle.js +55 -0
  45. package/src/javascript/local-loader.js +138 -0
  46. package/src/javascript/logging.js +26 -0
  47. package/src/javascript/package-effects.js +68 -0
  48. package/src/javascript/runtime-host.js +108 -0
  49. package/src/javascript/sha256.js +128 -0
  50. package/src/javascript/source-package.js +776 -0
  51. package/src/javascript/support.js +142 -0
  52. package/src/javascript/target-support-policy.js +264 -0
  53. package/src/javascript/target.js +73 -0
  54. package/src/javascript/test-runtime.js +407 -0
  55. package/src/provider-contract.js +79 -0
  56. package/src/runtime/assets-provider.js +464 -0
  57. package/src/runtime/canonical-api-runtime.js +601 -0
  58. package/src/runtime/index.js +28 -0
  59. package/src/runtime/provider-primitives-contract.js +272 -0
  60. package/src/runtime/provider-primitives.js +82 -0
  61. package/src/runtime/route-handler-effects.js +507 -0
  62. package/src/testing/conditional-kv-host.js +109 -0
  63. package/src/testing/fastly-cli.js +538 -0
  64. package/src/testing/native-http-effects-host.js +390 -0
  65. package/src/testing/native-http-shell-host.js +190 -0
  66. package/src/testing/native-platform-capabilities-host.js +676 -0
  67. package/src/toolchain/config.js +131 -0
  68. package/src/toolchain/index.js +345 -0
  69. package/src/toolchain/s3.js +26 -0
@@ -0,0 +1,142 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ normalizeTargetSupportDeclaration
5
+ } = require('@pulse-compute/wasm-contracts/project/target-support-evidence');
6
+ const { FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR } = require('./target.js');
7
+
8
+ function evidence(artifact, version, cases) {
9
+ return Object.freeze({
10
+ artifact,
11
+ version,
12
+ sha256: null,
13
+ status: 'passed',
14
+ cases,
15
+ matched: cases,
16
+ mismatches: 0
17
+ });
18
+ }
19
+
20
+ function gate(id, status, reasonId, owner, summary, evidenceReference = null) {
21
+ return Object.freeze({ id, status, reasonId, owner, summary, evidence: evidenceReference });
22
+ }
23
+
24
+ const FASTLY_JAVASCRIPT_SOURCE_PACKAGE_EVIDENCE = evidence(
25
+ 'wasm/test/provider/assert-fastly-javascript-packaging.cjs',
26
+ 'pulse.fastly-javascript-source-package-evidence.v1',
27
+ 14
28
+ );
29
+
30
+ const FASTLY_JAVASCRIPT_RUNTIME_EVIDENCE = evidence(
31
+ 'wasm/test/provider/assert-fastly-javascript-runtime.cjs',
32
+ 'pulse.fastly-javascript-runtime-evidence.v1',
33
+ 27
34
+ );
35
+
36
+ const FASTLY_JAVASCRIPT_TOOLING_EVIDENCE = evidence(
37
+ 'wasm/test/provider/assert-fastly-javascript-tooling.cjs',
38
+ 'pulse.fastly-javascript-tooling-evidence.v1',
39
+ 6
40
+ );
41
+
42
+ const FASTLY_JAVASCRIPT_FOUR_MODE_EVIDENCE = evidence(
43
+ 'wasm/test/contracts/assert-four-mode-conformance.cjs',
44
+ 'pulse.four-mode-conformance.v1',
45
+ 24
46
+ );
47
+
48
+ const FASTLY_JAVASCRIPT_RELEASE_CANDIDATE_EVIDENCE = evidence(
49
+ 'scripts/offline-release-candidates.cjs',
50
+ 'pulse.offline-deployment-candidates.v1',
51
+ 2
52
+ );
53
+
54
+ const GATES = Object.freeze([
55
+ gate(
56
+ 'build-source-packaging',
57
+ 'satisfied',
58
+ 'fastly-javascript-source-packaging',
59
+ 'provider-fastly',
60
+ 'Fastly JavaScript builds emit deterministic source closures with exact downstream toolchain pins and no Pulse Native artifact.',
61
+ FASTLY_JAVASCRIPT_SOURCE_PACKAGE_EVIDENCE
62
+ ),
63
+ gate(
64
+ 'provider-request-runtime',
65
+ 'satisfied',
66
+ 'fastly-javascript-request-runtime',
67
+ 'provider-fastly',
68
+ 'Fastly FetchEvent requests execute through the shared Router/context runtime with request-contained provider failures and cleanup.',
69
+ FASTLY_JAVASCRIPT_RUNTIME_EVIDENCE
70
+ ),
71
+ gate(
72
+ 'provider-capabilities-packages-redaction',
73
+ 'satisfied',
74
+ 'fastly-javascript-capabilities',
75
+ 'provider-fastly',
76
+ 'Fetch, Config Store, Secret Store, KV Store, Assets, GRIP broadcast, logging, and request-owned redaction have provider adapters.',
77
+ FASTLY_JAVASCRIPT_RUNTIME_EVIDENCE
78
+ ),
79
+ gate(
80
+ 'representative-local-tooling',
81
+ 'satisfied',
82
+ 'fastly-javascript-representative-tooling',
83
+ 'provider-fastly',
84
+ 'Representative pulse test and pulse dev use explicit provider emulation, while deterministic build output forms a structurally deployable Fastly JavaScript candidate.',
85
+ FASTLY_JAVASCRIPT_TOOLING_EVIDENCE
86
+ ),
87
+ gate(
88
+ 'four-mode-conformance',
89
+ 'satisfied',
90
+ 'fastly-javascript-four-mode-conformance',
91
+ 'conformance',
92
+ 'Node Native, Node JavaScript, Fastly Native, and Fastly JavaScript agree across normalized semantic traces with target-integrity and negative-control evidence.',
93
+ FASTLY_JAVASCRIPT_FOUR_MODE_EVIDENCE
94
+ ),
95
+ gate(
96
+ 'offline-deployment-candidates',
97
+ 'satisfied',
98
+ 'fastly-javascript-offline-deployment-candidate',
99
+ 'release',
100
+ 'Two clean source-package builds agree byte-for-byte and the pinned downstream Fastly compiler emits a structurally deployable JavaScript runtime candidate without deployment or publication.',
101
+ FASTLY_JAVASCRIPT_RELEASE_CANDIDATE_EVIDENCE
102
+ )
103
+ ]);
104
+
105
+ const COMMANDS = Object.freeze([
106
+ ['build', true, 'eligible', 'build-implemented', 'provider-fastly'],
107
+ ['compile', true, 'eligible', 'compile-implemented', 'compiler'],
108
+ ['dev', true, 'eligible', 'fastly-javascript-dev-implemented', 'provider-fastly'],
109
+ ['doctor', true, 'eligible', 'doctor-implemented', 'cli'],
110
+ ['inspect', true, 'eligible', 'inspect-implemented', 'cli'],
111
+ ['test', true, 'eligible', 'fastly-javascript-test-implemented', 'provider-fastly']
112
+ ].map(([id, implemented, status, reasonId, owner]) => Object.freeze({
113
+ id,
114
+ implemented,
115
+ status,
116
+ reasonId,
117
+ owner
118
+ })));
119
+
120
+ const FASTLY_JAVASCRIPT_TARGET_SUPPORT_DECLARATION = normalizeTargetSupportDeclaration({
121
+ provider: 'fastly',
122
+ target: 'javascript',
123
+ targetId: FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR.targetId,
124
+ runtimeClass: FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR.runtimeClass,
125
+ definition: 'full-target-support',
126
+ coreExecutionReady: true,
127
+ fullTargetSupportReady: true,
128
+ generalAvailable: true,
129
+ automaticFallback: false,
130
+ parity: FASTLY_JAVASCRIPT_FOUR_MODE_EVIDENCE,
131
+ commands: COMMANDS,
132
+ gates: GATES
133
+ });
134
+
135
+ module.exports = Object.freeze({
136
+ FASTLY_JAVASCRIPT_SOURCE_PACKAGE_EVIDENCE,
137
+ FASTLY_JAVASCRIPT_RUNTIME_EVIDENCE,
138
+ FASTLY_JAVASCRIPT_TOOLING_EVIDENCE,
139
+ FASTLY_JAVASCRIPT_FOUR_MODE_EVIDENCE,
140
+ FASTLY_JAVASCRIPT_RELEASE_CANDIDATE_EVIDENCE,
141
+ FASTLY_JAVASCRIPT_TARGET_SUPPORT_DECLARATION
142
+ });
@@ -0,0 +1,264 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ isJavascriptCoreCapability
5
+ } = require('@pulse-compute/wasm-contracts/project/javascript-core-capabilities');
6
+
7
+ function decision(id, status, reasonId, owner, required = true) {
8
+ return Object.freeze({ id, status, required, reasonId, owner });
9
+ }
10
+
11
+ function blockedRestriction(id, restriction) {
12
+ return restriction
13
+ ? decision(id, 'blocked', restriction.reasonId, restriction.owner)
14
+ : null;
15
+ }
16
+
17
+ function classifyFastlyJavascriptCapability(id, restrictions = {}) {
18
+ const capability = String(id);
19
+ if (['kv.getVersioned', 'kv.insertIfAbsent', 'kv.compareAndSwap'].includes(capability)) return decision(capability, 'blocked', 'fastly-conditional-kv-incomplete', 'provider-fastly');
20
+ if (['s3.head', 's3.getText', 's3.putText'].includes(capability)) return decision(capability, 'blocked', 'fastly-javascript-s3-raw-headers-unavailable', 'provider-fastly');
21
+ if (isJavascriptCoreCapability(capability)) {
22
+ return decision(capability, 'eligible', 'fastly-request-router-runtime', 'provider-fastly');
23
+ }
24
+ if (
25
+ capability === 'request.json'
26
+ || capability === 'req.json'
27
+ || capability.startsWith('request.body')
28
+ || capability.startsWith('req.body')
29
+ || capability.startsWith('schema.')
30
+ ) {
31
+ return decision(capability, 'eligible', 'fastly-shared-schema-boundaries', 'runtime');
32
+ }
33
+ if (capability === 'fetch' || capability.startsWith('fetch.') || capability === 'backend-fetch') {
34
+ return blockedRestriction(capability, restrictions.fetch)
35
+ || decision(capability, 'eligible', 'fastly-fetch-adapter', 'provider-fastly');
36
+ }
37
+ if (capability === 'config' || capability === 'config.get') {
38
+ return blockedRestriction(capability, restrictions.config)
39
+ || decision(capability, 'eligible', 'fastly-config-store-adapter', 'provider-fastly');
40
+ }
41
+ if (capability === 'secret' || capability === 'secret.get') {
42
+ return blockedRestriction(capability, restrictions.secret)
43
+ || decision(capability, 'eligible', 'fastly-secret-store-adapter', 'provider-fastly');
44
+ }
45
+ if (capability === 'kv' || capability.startsWith('kv.')) {
46
+ return blockedRestriction(capability, restrictions.kv)
47
+ || decision(capability, 'eligible', 'fastly-kv-store-adapter', 'provider-fastly');
48
+ }
49
+ if (capability === 'assets' || capability.startsWith('assets.')) {
50
+ return blockedRestriction(capability, restrictions.assets)
51
+ || decision(capability, 'eligible', 'fastly-assets-kv-adapter', 'provider-fastly');
52
+ }
53
+ if (
54
+ capability === 'broadcaster'
55
+ || capability === 'channel'
56
+ || capability === 'grip'
57
+ || capability.startsWith('grip.')
58
+ ) {
59
+ return blockedRestriction(capability, restrictions.grip)
60
+ || decision(capability, 'eligible', 'fastly-grip-broadcast-adapter', 'provider-fastly');
61
+ }
62
+ if (
63
+ capability === 'body'
64
+ || capability === 'headers'
65
+ || capability === 'json'
66
+ || capability === 'opaque.pass-through'
67
+ || capability === 'result'
68
+ || capability.startsWith('result.')
69
+ || capability === 'parallel'
70
+ || capability.startsWith('parallel.')
71
+ || capability === 'logging'
72
+ || capability === 'log'
73
+ || capability.startsWith('log.')
74
+ ) {
75
+ return decision(capability, 'eligible', 'fastly-shared-javascript-runtime', 'runtime');
76
+ }
77
+ if (capability === 'clock' || capability === 'timer') {
78
+ return decision(capability, 'pending', 'fastly-javascript-clock-timer-pending', 'runtime');
79
+ }
80
+ if (capability === 'time.wall-clock') {
81
+ return decision(capability, 'eligible', 'fastly-javascript-provider-wall-clock-authority', 'provider-fastly');
82
+ }
83
+ if (
84
+ capability === 'jwt.verify'
85
+ || capability === 'jwt.verify.hs256'
86
+ || capability === 'jwt.verify.es256'
87
+ ) {
88
+ return blockedRestriction(capability, restrictions.jwt)
89
+ || decision(capability, 'eligible', 'fastly-javascript-jwt-crypto-runtime-builtin', 'provider-fastly');
90
+ }
91
+ if (capability.startsWith('jwt.verify.')) {
92
+ return decision(capability, 'blocked', 'jwt-crypto-realization-unavailable', 'crypto');
93
+ }
94
+ if (capability === 'event.ingress') {
95
+ return decision(capability, 'blocked', 'fastly-event-ingress-unavailable', 'provider-fastly');
96
+ }
97
+ if (capability === 'event.emit') {
98
+ return decision(capability, 'blocked', 'fastly-event-emit-unavailable', 'provider-fastly');
99
+ }
100
+ return decision(capability, 'blocked', 'unknown-fastly-javascript-capability-fail-closed', 'compiler');
101
+ }
102
+
103
+ function classifyFastlyJavascriptProviderRequirement(id, restrictions = {}) {
104
+ const requirement = String(id);
105
+ if (['kv.getVersioned', 'kv.insertIfAbsent', 'kv.compareAndSwap'].includes(requirement)) return decision(requirement, 'blocked', 'fastly-conditional-kv-incomplete', 'provider-fastly');
106
+ if (['s3.head', 's3.getText', 's3.putText'].includes(requirement)) return decision(requirement, 'blocked', 'fastly-javascript-s3-raw-headers-unavailable', 'provider-fastly');
107
+ if (requirement === 'request' || ['response.json', 'response.text', 'response.custom'].includes(requirement)) {
108
+ return decision(requirement, 'eligible', 'fastly-request-response-lifecycle', 'provider-fastly');
109
+ }
110
+ if (requirement === 'fetch' || requirement === 'opaque.pass-through' || ['body', 'headers', 'result'].includes(requirement)) {
111
+ return blockedRestriction(requirement, restrictions.fetch)
112
+ || decision(requirement, 'eligible', 'fastly-fetch-adapter', 'provider-fastly');
113
+ }
114
+ if (requirement === 'config.get') {
115
+ return blockedRestriction(requirement, restrictions.config)
116
+ || decision(requirement, 'eligible', 'fastly-config-store-adapter', 'provider-fastly');
117
+ }
118
+ if (requirement === 'secret.get') {
119
+ return blockedRestriction(requirement, restrictions.secret)
120
+ || decision(requirement, 'eligible', 'fastly-secret-store-adapter', 'provider-fastly');
121
+ }
122
+ if (requirement === 'kv' || requirement === 'kv.get' || requirement === 'kv.put') {
123
+ return blockedRestriction(requirement, restrictions.kv)
124
+ || decision(requirement, 'eligible', 'fastly-kv-store-adapter', 'provider-fastly');
125
+ }
126
+ if (requirement === 'assets' || requirement.startsWith('assets.')) {
127
+ return blockedRestriction(requirement, restrictions.assets)
128
+ || decision(requirement, 'eligible', 'fastly-assets-kv-adapter', 'provider-fastly');
129
+ }
130
+ if (
131
+ ['broadcaster', 'channel'].includes(requirement)
132
+ || requirement === 'grip'
133
+ || requirement.startsWith('grip.')
134
+ ) {
135
+ return blockedRestriction(requirement, restrictions.grip)
136
+ || decision(requirement, 'eligible', 'fastly-grip-broadcast-adapter', 'provider-fastly');
137
+ }
138
+ if (requirement.startsWith('schema.')) {
139
+ return decision(requirement, 'eligible', 'fastly-shared-schema-boundaries', 'runtime');
140
+ }
141
+ if (requirement === 'clock') {
142
+ return decision(requirement, 'pending', 'fastly-javascript-clock-timer-pending', 'runtime');
143
+ }
144
+ if (requirement === 'time.wall-clock') {
145
+ return decision(requirement, 'eligible', 'fastly-javascript-provider-wall-clock-authority', 'provider-fastly');
146
+ }
147
+ if (
148
+ requirement === 'jwt.verify'
149
+ || requirement === 'jwt.verify.hs256'
150
+ || requirement === 'jwt.verify.es256'
151
+ ) {
152
+ return blockedRestriction(requirement, restrictions.jwt)
153
+ || decision(requirement, 'eligible', 'fastly-javascript-jwt-crypto-runtime-builtin', 'provider-fastly');
154
+ }
155
+ if (requirement.startsWith('jwt.verify.')) {
156
+ return decision(requirement, 'blocked', 'jwt-crypto-realization-unavailable', 'crypto');
157
+ }
158
+ if (requirement === 'event.ingress') {
159
+ return decision(requirement, 'blocked', 'fastly-event-ingress-unavailable', 'provider-fastly');
160
+ }
161
+ if (requirement === 'event.emit') {
162
+ return decision(requirement, 'blocked', 'fastly-event-emit-unavailable', 'provider-fastly');
163
+ }
164
+ return decision(requirement, 'blocked', 'unknown-fastly-provider-requirement-fail-closed', 'compiler');
165
+ }
166
+
167
+ function restriction(reasonId, configuration) {
168
+ return Object.freeze({ reasonId, owner: 'provider-fastly', configuration });
169
+ }
170
+
171
+ function fastlyJavascriptProjectRestrictions(compiled, project) {
172
+ const bindings = project.providerConfig && project.providerConfig.bindings || {};
173
+ const backends = bindings.backends && typeof bindings.backends === 'object' ? bindings.backends : {};
174
+ const kv = bindings.kv && typeof bindings.kv === 'object' ? bindings.kv : {};
175
+ const sites = compiled.metadata.effectSites || [];
176
+ const fetchSites = sites.filter((entry) => entry.kind === 'fetch');
177
+ const kvSites = sites.filter((entry) => entry.kind === 'kv.get' || entry.kind === 'kv.put');
178
+ const assetSites = sites.filter((entry) => entry.contractId === 'pulse.assets' && entry.operation === 'lookup');
179
+ const gripSites = sites.filter((entry) => entry.contractId === 'pulse.grip' && entry.operation === 'broadcast');
180
+ const configSites = sites.filter((entry) => entry.kind === 'config.get');
181
+ const secretSites = sites.filter((entry) => entry.kind === 'secret.get');
182
+ const jwtSites = sites.filter((entry) => (
183
+ entry.contractId === 'pulse.jwt'
184
+ && (entry.operation === 'verify' || entry.kind === 'jwt.verify')
185
+ ));
186
+ const output = {};
187
+
188
+ if (configSites.length > 0 && !(typeof bindings.configStore === 'string' && bindings.configStore.trim())) {
189
+ output.config = restriction('fastly-config-store-binding-required', 'bindings.configStore');
190
+ }
191
+ const grip = bindings.grip && typeof bindings.grip === 'object' ? bindings.grip : {};
192
+ const gripSecretRequired = gripSites.length > 0 && grip.authentication && grip.authentication.secretRef;
193
+ if ((secretSites.length > 0 || gripSecretRequired) && !(typeof bindings.secretStore === 'string' && bindings.secretStore.trim())) {
194
+ output.secret = restriction('fastly-secret-store-binding-required', 'bindings.secretStore');
195
+ }
196
+ if (jwtSites.length > 0 && !(typeof bindings.secretStore === 'string' && bindings.secretStore.trim())) {
197
+ output.jwt = restriction('fastly-jwt-secret-store-binding-required', 'bindings.secretStore');
198
+ }
199
+
200
+ for (const site of fetchSites) {
201
+ const resource = site.resource || {};
202
+ if (resource.kind === 'literal') {
203
+ if (!resource.origin) {
204
+ output.fetch = restriction('fastly-fetch-url-invalid', 'ctx.fetch');
205
+ break;
206
+ }
207
+ if (!(typeof backends[resource.origin] === 'string' && backends[resource.origin].trim()) && bindings.dynamicBackends !== true) {
208
+ output.fetch = restriction('fastly-fetch-backend-binding-required', `bindings.backends.${resource.origin}`);
209
+ break;
210
+ }
211
+ } else if (resource.kind === 'dynamic' && bindings.dynamicBackends !== true && Object.keys(backends).length === 0) {
212
+ output.fetch = restriction('fastly-dynamic-fetch-backend-policy-required', 'bindings.backends');
213
+ break;
214
+ }
215
+ }
216
+
217
+ for (const site of kvSites) {
218
+ const resource = site.resource || {};
219
+ if (resource.kind !== 'literal') {
220
+ output.kv = restriction('fastly-dynamic-kv-namespace-unsupported', 'bindings.kv');
221
+ break;
222
+ }
223
+ const namespace = String(resource.value || '');
224
+ if (!(typeof kv[namespace] === 'string' && kv[namespace].trim())) {
225
+ output.kv = restriction('fastly-kv-binding-required', `bindings.kv.${namespace}`);
226
+ break;
227
+ }
228
+ }
229
+
230
+ for (const site of assetSites) {
231
+ const store = site.payload && typeof site.payload.store === 'string' ? site.payload.store : '';
232
+ if (!store || !(typeof kv[store] === 'string' && kv[store].trim())) {
233
+ output.assets = restriction('fastly-assets-binding-required', store ? `bindings.kv.${store}` : 'bindings.kv');
234
+ break;
235
+ }
236
+ }
237
+
238
+ if (gripSites.length > 0) {
239
+ const endpointValue = grip.publishEndpoint || grip.publishUrl;
240
+ let endpoint;
241
+ try { endpoint = new URL(String(endpointValue || '')); }
242
+ catch (_) {
243
+ output.grip = restriction('fastly-grip-publish-endpoint-required', 'bindings.grip.publishEndpoint');
244
+ }
245
+ if (endpoint && !['http:', 'https:'].includes(endpoint.protocol)) {
246
+ output.grip = restriction('fastly-grip-publish-endpoint-invalid', 'bindings.grip.publishEndpoint');
247
+ }
248
+ if (endpoint && !output.grip) {
249
+ const mapped = typeof backends[endpoint.origin] === 'string' && backends[endpoint.origin].trim();
250
+ const explicit = typeof grip.publishBackend === 'string' && grip.publishBackend.trim();
251
+ if (!mapped && !explicit && bindings.dynamicBackends !== true) {
252
+ output.grip = restriction('fastly-grip-publish-backend-required', 'bindings.grip.publishBackend');
253
+ }
254
+ }
255
+ }
256
+
257
+ return Object.freeze(output);
258
+ }
259
+
260
+ module.exports = Object.freeze({
261
+ classifyFastlyJavascriptCapability,
262
+ classifyFastlyJavascriptProviderRequirement,
263
+ fastlyJavascriptProjectRestrictions
264
+ });
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ const { normalizeJavascriptTargetDescriptor } = require('@pulse-compute/wasm-contracts/project/javascript-application');
4
+ const { defineCryptoTargetCapabilities } = require('@pulse-compute/wasm-contracts/crypto/contracts');
5
+
6
+ const FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR = normalizeJavascriptTargetDescriptor({
7
+ provider: 'fastly',
8
+ targetId: 'fastly-javascript',
9
+ status: 'provider-runtime-ready',
10
+ hostBridge: true,
11
+ applicationLoader: true,
12
+ requestAdapter: true,
13
+ lifecycle: true,
14
+ crypto: defineCryptoTargetCapabilities({
15
+ target: 'javascript',
16
+ algorithms: [{
17
+ algorithm: 'HS256',
18
+ realization: 'runtime-builtin',
19
+ implemented: true,
20
+ status: 'implemented-c2'
21
+ }, {
22
+ algorithm: 'ES256',
23
+ realization: 'runtime-builtin',
24
+ implemented: true,
25
+ status: 'implemented-h4'
26
+ }]
27
+ }),
28
+ capabilities: {
29
+ version: 'pulse.javascript-capability-envelope.v1',
30
+ provider: 'fastly',
31
+ status: 'request-lifecycle-ready',
32
+ core: {
33
+ request: true,
34
+ response: true,
35
+ router: true,
36
+ state: true,
37
+ middleware: true,
38
+ schemas: true,
39
+ logging: true
40
+ },
41
+ effects: {
42
+ fetch: true,
43
+ config: true,
44
+ secret: true,
45
+ kv: true,
46
+ parallel: true
47
+ },
48
+ packages: ['pulse.assets', 'pulse.grip', 'pulse.jwt'],
49
+ restrictions: {
50
+ fetch: 'origin-backend-map-or-dynamic-opt-in',
51
+ kv: 'logical-store-map-required',
52
+ assets: 'logical-store-to-kv-map-required',
53
+ grip: 'publish-endpoint-and-backend-or-dynamic-opt-in'
54
+ }
55
+ },
56
+ reporting: {
57
+ supported: true,
58
+ default: 'info',
59
+ levels: ['off', 'error', 'warn', 'info', 'debug'],
60
+ destination: 'provider-owned',
61
+ betaDefault: 'stdio-log-tail'
62
+ },
63
+ commands: {
64
+ compile: true,
65
+ inspect: true,
66
+ doctor: true,
67
+ build: true,
68
+ test: true,
69
+ dev: true
70
+ }
71
+ });
72
+
73
+ module.exports = Object.freeze({ FASTLY_JAVASCRIPT_TARGET_DESCRIPTOR });