@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,564 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+ const fs = require('node:fs');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+ const { spawnSync } = require('node:child_process');
8
+
9
+ function loadBuildSupport() {
10
+ try { return require('@pulse-compute/wasm-build-support/assemblyscript-compile'); }
11
+ catch (error) {
12
+ if (error && ['MODULE_NOT_FOUND', 'ERR_PACKAGE_PATH_NOT_EXPORTED'].includes(error.code)) {
13
+ return require('../../../../wasm/packages/build-support/src/assemblyscript-compile.js');
14
+ }
15
+ throw error;
16
+ }
17
+ }
18
+
19
+ const { resolveAsc } = loadBuildSupport();
20
+
21
+ const FASTLY_ENTITIES_NATIVE_REALIZATION_VERSION = 'pulse.fastly-entities-native-realization.i9.v1';
22
+ const FASTLY_ENTITIES_NATIVE_SOURCE_VERSION = 'pulse.fastly-entities-native-source.i9.v1';
23
+ const ENTITIES_NATIVE_SOURCE_VERSION = 'pulse.entities-native-source.v1';
24
+ const MAX_WASM_BYTES = 1024 * 1024;
25
+ const BUFFER_BYTES = 65_536;
26
+
27
+ const ALLOWED_IMPORTS = new Set([
28
+ 'fastly_abi:init',
29
+ 'fastly_config_store:get',
30
+ 'fastly_config_store:open',
31
+ 'fastly_http_body:new',
32
+ 'fastly_http_body:read',
33
+ 'fastly_http_body:write',
34
+ 'fastly_http_req:body_downstream_get',
35
+ 'fastly_http_req:method_set',
36
+ 'fastly_http_req:new',
37
+ 'fastly_http_req:pending_req_wait',
38
+ 'fastly_http_req:send_async',
39
+ 'fastly_http_req:uri_set',
40
+ 'fastly_http_resp:header_append',
41
+ 'fastly_http_resp:new',
42
+ 'fastly_http_resp:send_downstream',
43
+ 'fastly_http_resp:status_set',
44
+ 'fastly_log:endpoint_get',
45
+ 'fastly_log:write',
46
+ 'fastly_secret_store:get',
47
+ 'fastly_secret_store:open',
48
+ 'fastly_secret_store:plaintext'
49
+ ]);
50
+
51
+ class FastlyEntitiesNativeError extends Error {
52
+ constructor(code, message, detail = {}) {
53
+ super(message);
54
+ this.name = 'FastlyEntitiesNativeError';
55
+ this.code = code;
56
+ this.detail = Object.freeze({ ...detail });
57
+ }
58
+ }
59
+
60
+ function fail(code, message, detail) {
61
+ throw new FastlyEntitiesNativeError(code, message, detail);
62
+ }
63
+
64
+ function sha256(value) {
65
+ return crypto.createHash('sha256').update(value).digest('hex');
66
+ }
67
+
68
+ function quote(value) {
69
+ return JSON.stringify(String(value));
70
+ }
71
+
72
+ function normalizedBindings(artifact, options) {
73
+ const input = Array.isArray(options.effectBindings) ? options.effectBindings : [];
74
+ if (input.length !== artifact.summary.effects) {
75
+ fail(
76
+ 'PULSE_FASTLY_ENTITIES_EFFECT_BINDINGS_INVALID',
77
+ 'Fastly Entities Native realization requires one exact binding record per generated effect.',
78
+ { expected: artifact.summary.effects, actual: input.length }
79
+ );
80
+ }
81
+ const seen = new Set();
82
+ const bindings = input.map((entry) => {
83
+ if (!entry || !Number.isInteger(entry.index) || entry.index < 0 || entry.index >= input.length || seen.has(entry.index)) {
84
+ fail('PULSE_FASTLY_ENTITIES_EFFECT_BINDINGS_INVALID', 'Fastly Entities effect indexes must be unique and contiguous.', { entry });
85
+ }
86
+ seen.add(entry.index);
87
+ const kind = String(entry.kind || '');
88
+ if (!['config.get', 'fetch', 'secret.get'].includes(kind)) {
89
+ fail('PULSE_FASTLY_ENTITIES_EFFECT_UNSUPPORTED', `Fastly Entities Native does not realize effect ${JSON.stringify(kind)}.`, {
90
+ index: entry.index,
91
+ kind,
92
+ automaticFallback: false
93
+ });
94
+ }
95
+ const backend = entry.backend == null ? '' : String(entry.backend).trim();
96
+ const origin = entry.origin == null ? '' : String(entry.origin).trim();
97
+ if (kind === 'fetch' && (!backend || !origin)) {
98
+ fail('PULSE_FASTLY_ENTITIES_BACKEND_BINDING_REQUIRED', 'Fastly Entities fetch effects require one static backend and origin.', { index: entry.index });
99
+ }
100
+ return Object.freeze({ index: entry.index, kind, backend, origin });
101
+ }).sort((left, right) => left.index - right.index);
102
+ return Object.freeze(bindings);
103
+ }
104
+
105
+ function validateArtifact(artifact) {
106
+ if (!artifact || artifact.version !== ENTITIES_NATIVE_SOURCE_VERSION || artifact.kind !== 'package-generated-source') {
107
+ fail('PULSE_FASTLY_ENTITIES_SOURCE_INVALID', `Fastly Entities Native requires ${ENTITIES_NATIVE_SOURCE_VERSION}.`);
108
+ }
109
+ if (
110
+ !artifact.policy
111
+ || artifact.policy.javascriptRuntime !== false
112
+ || artifact.policy.javascriptHandlerImports !== false
113
+ || artifact.policy.automaticFallback !== false
114
+ || artifact.policy.requestBodyReads !== 1
115
+ ) {
116
+ fail('PULSE_FASTLY_ENTITIES_SOURCE_POLICY_INVALID', 'Fastly Entities Native requires package-owned source with no JavaScript runtime or fallback.', {
117
+ policy: artifact.policy
118
+ });
119
+ }
120
+ if (!Array.isArray(artifact.imports) || artifact.imports.some((entry) => entry.module !== 'pulse_entities_host')) {
121
+ fail('PULSE_FASTLY_ENTITIES_SOURCE_IMPORT_INVALID', 'Entities package source declares an unexpected provider import.', { imports: artifact.imports });
122
+ }
123
+ if (typeof artifact.source !== 'string' || sha256(artifact.source) !== artifact.sourceSha256) {
124
+ fail('PULSE_FASTLY_ENTITIES_SOURCE_HASH_MISMATCH', 'Entities package source hash does not match its bytes.');
125
+ }
126
+ return artifact;
127
+ }
128
+
129
+ function stripPackageHostImports(source) {
130
+ return String(source)
131
+ .split('\n')
132
+ .filter((line) => !line.startsWith('@external("pulse_entities_host",'))
133
+ .join('\n');
134
+ }
135
+
136
+ function effectSwitch(bindings, field, fallback = '""') {
137
+ const lines = [' switch (effectIndex) {'];
138
+ for (const binding of bindings) lines.push(` case ${binding.index}: return ${quote(binding[field])}`);
139
+ lines.push(` default: return ${fallback}`, ' }');
140
+ return lines.join('\n');
141
+ }
142
+
143
+ function effectKindSwitch(bindings) {
144
+ const kindIds = Object.freeze({ fetch: 1, 'config.get': 2, 'secret.get': 3 });
145
+ const lines = [' switch (effectIndex) {'];
146
+ for (const binding of bindings) lines.push(` case ${binding.index}: return ${kindIds[binding.kind]}`);
147
+ lines.push(' default: return 0', ' }');
148
+ return lines.join('\n');
149
+ }
150
+
151
+ function providerRuntimeSource(bindings, options) {
152
+ const configStore = String(options.configStore || '').trim();
153
+ const secretStore = String(options.secretStore || '').trim();
154
+ if (bindings.some((entry) => entry.kind === 'config.get') && !configStore) {
155
+ fail('PULSE_FASTLY_ENTITIES_CONFIG_STORE_REQUIRED', 'Fastly Entities config effects require configStore.');
156
+ }
157
+ if (bindings.some((entry) => entry.kind === 'secret.get') && !secretStore) {
158
+ fail('PULSE_FASTLY_ENTITIES_SECRET_STORE_REQUIRED', 'Fastly Entities secret effects require secretStore.');
159
+ }
160
+
161
+ return String.raw`
162
+ @external("fastly_abi", "init") declare function fastly_abi_init(version: i64): i32
163
+ @external("fastly_http_req", "body_downstream_get") declare function fastly_http_req_body_downstream_get(requestOut: usize, bodyOut: usize): i32
164
+ @external("fastly_http_req", "new") declare function fastly_http_req_new(handleOut: usize): i32
165
+ @external("fastly_http_req", "method_set") declare function fastly_http_req_method_set(handle: i32, method: usize, methodLength: i32): i32
166
+ @external("fastly_http_req", "uri_set") declare function fastly_http_req_uri_set(handle: i32, uri: usize, uriLength: i32): i32
167
+ @external("fastly_http_req", "send_async") declare function fastly_http_req_send_async(handle: i32, body: i32, backend: usize, backendLength: i32, pendingOut: usize): i32
168
+ @external("fastly_http_req", "pending_req_wait") declare function fastly_http_req_pending_req_wait(handle: i32, responseOut: usize, bodyOut: usize): i32
169
+ @external("fastly_http_resp", "new") declare function fastly_http_resp_new(handleOut: usize): i32
170
+ @external("fastly_http_resp", "header_append") declare function fastly_http_resp_header_append(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32): i32
171
+ @external("fastly_http_resp", "status_set") declare function fastly_http_resp_status_set(handle: i32, status: i32): i32
172
+ @external("fastly_http_resp", "send_downstream") declare function fastly_http_resp_send_downstream(handle: i32, body: i32, streaming: i32): i32
173
+ @external("fastly_http_body", "new") declare function fastly_http_body_new(handleOut: usize): i32
174
+ @external("fastly_http_body", "read") declare function fastly_http_body_read(handle: i32, buffer: usize, bufferLength: i32, readOut: usize): i32
175
+ @external("fastly_http_body", "write") declare function fastly_http_body_write(handle: i32, buffer: usize, bufferLength: i32, end: i32, writtenOut: usize): i32
176
+ @external("fastly_config_store", "open") declare function fastly_config_store_open(name: usize, nameLength: i32, handleOut: usize): i32
177
+ @external("fastly_config_store", "get") declare function fastly_config_store_get(handle: i32, key: usize, keyLength: i32, value: usize, valueLength: i32, writtenOut: usize): i32
178
+ @external("fastly_secret_store", "open") declare function fastly_secret_store_open(name: usize, nameLength: i32, handleOut: usize): i32
179
+ @external("fastly_secret_store", "get") declare function fastly_secret_store_get(handle: i32, key: usize, keyLength: i32, secretOut: usize): i32
180
+ @external("fastly_secret_store", "plaintext") declare function fastly_secret_store_plaintext(secret: i32, value: usize, valueLength: i32, writtenOut: usize): i32
181
+ @external("fastly_log", "endpoint_get") declare function fastly_log_endpoint_get(name: usize, nameLength: i32, handleOut: usize): i32
182
+ @external("fastly_log", "write") declare function fastly_log_write(handle: i32, message: usize, messageLength: i32, writtenOut: usize): i32
183
+
184
+ const __PULSE_FASTLY_ENTITIES_ABI: i64 = 1
185
+ const __PULSE_FASTLY_ENTITIES_BUFFER: i32 = ${BUFFER_BYTES}
186
+ const __PULSE_FASTLY_ENTITIES_EFFECTS: i32 = ${bindings.length}
187
+ const __PULSE_FASTLY_OK: i32 = 0
188
+ const __PULSE_FASTLY_NONE: i32 = 10
189
+ let __pulse_fastly_entities_error: i32 = 0
190
+ let __pulse_fastly_entities_results = new Array<string>()
191
+ let __pulse_fastly_entities_ready = new Array<i32>()
192
+ let __pulse_fastly_entities_ok = new Array<i32>()
193
+ let __pulse_fastly_entities_redactions = new Array<string>()
194
+ let __pulse_fastly_entities_log_handle: i32 = -1
195
+
196
+ function __pulse_fastly_entities_out(): StaticArray<i32> { return new StaticArray<i32>(1) }
197
+ function __pulse_fastly_entities_out_value(value: StaticArray<i32>): i32 { return load<i32>(changetype<usize>(value)) }
198
+ function __pulse_fastly_entities_utf8(value: string): ArrayBuffer { return String.UTF8.encode(value, false) }
199
+ function __pulse_fastly_entities_decode(buffer: Uint8Array, length: i32): string { return String.UTF8.decodeUnsafe(buffer.dataStart, length, false) }
200
+ function __pulse_fastly_entities_fail(stage: i32): void { if (__pulse_fastly_entities_error == 0) __pulse_fastly_entities_error = stage }
201
+ function __pulse_fastly_entities_kind(effectIndex: i32): i32 {
202
+ ${effectKindSwitch(bindings)}
203
+ }
204
+ function __pulse_fastly_entities_backend(effectIndex: i32): string {
205
+ ${effectSwitch(bindings, 'backend')}
206
+ }
207
+ function __pulse_fastly_entities_origin(effectIndex: i32): string {
208
+ ${effectSwitch(bindings, 'origin')}
209
+ }
210
+ function __pulse_fastly_entities_input(payload: string, name: string): string {
211
+ const outerValue = JSON.parse<JSON.Value>(payload)
212
+ if (outerValue.type != JSON.Types.Object) { __pulse_fastly_entities_fail(20); return "" }
213
+ const outer = outerValue.get<JSON.Obj>()
214
+ const inputsValue = outer.get("inputs")
215
+ if (inputsValue === null || inputsValue!.type != JSON.Types.Object) { __pulse_fastly_entities_fail(21); return "" }
216
+ const value = inputsValue!.get<JSON.Obj>().get(name)
217
+ if (value === null || value!.type != JSON.Types.String) { __pulse_fastly_entities_fail(22); return "" }
218
+ return value!.get<string>()
219
+ }
220
+ function __pulse_fastly_entities_open_config(): i32 {
221
+ const name = __pulse_fastly_entities_utf8(${quote(configStore)})
222
+ const out = __pulse_fastly_entities_out()
223
+ if (fastly_config_store_open(changetype<usize>(name), name.byteLength, changetype<usize>(out)) != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(30); return 0 }
224
+ return __pulse_fastly_entities_out_value(out)
225
+ }
226
+ function __pulse_fastly_entities_open_secret(): i32 {
227
+ const name = __pulse_fastly_entities_utf8(${quote(secretStore)})
228
+ const out = __pulse_fastly_entities_out()
229
+ if (fastly_secret_store_open(changetype<usize>(name), name.byteLength, changetype<usize>(out)) != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(31); return 0 }
230
+ return __pulse_fastly_entities_out_value(out)
231
+ }
232
+ function __pulse_fastly_entities_config(key: string): string {
233
+ const store = __pulse_fastly_entities_open_config()
234
+ if (__pulse_fastly_entities_error != 0) return "null"
235
+ const keyBytes = __pulse_fastly_entities_utf8(key)
236
+ const buffer = new Uint8Array(__PULSE_FASTLY_ENTITIES_BUFFER)
237
+ const written = __pulse_fastly_entities_out()
238
+ const status = fastly_config_store_get(store, changetype<usize>(keyBytes), keyBytes.byteLength, buffer.dataStart, __PULSE_FASTLY_ENTITIES_BUFFER, changetype<usize>(written))
239
+ if (status == __PULSE_FASTLY_NONE) return "null"
240
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(32); return "null" }
241
+ return JSON.stringify<string>(__pulse_fastly_entities_decode(buffer, __pulse_fastly_entities_out_value(written)))
242
+ }
243
+ function __pulse_fastly_entities_secret(key: string): string {
244
+ const store = __pulse_fastly_entities_open_secret()
245
+ if (__pulse_fastly_entities_error != 0) return "null"
246
+ const keyBytes = __pulse_fastly_entities_utf8(key)
247
+ const secretOut = __pulse_fastly_entities_out()
248
+ let status = fastly_secret_store_get(store, changetype<usize>(keyBytes), keyBytes.byteLength, changetype<usize>(secretOut))
249
+ if (status == __PULSE_FASTLY_NONE) return "null"
250
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(33); return "null" }
251
+ const buffer = new Uint8Array(__PULSE_FASTLY_ENTITIES_BUFFER)
252
+ const written = __pulse_fastly_entities_out()
253
+ status = fastly_secret_store_plaintext(__pulse_fastly_entities_out_value(secretOut), buffer.dataStart, __PULSE_FASTLY_ENTITIES_BUFFER, changetype<usize>(written))
254
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(34); return "null" }
255
+ const value = __pulse_fastly_entities_decode(buffer, __pulse_fastly_entities_out_value(written))
256
+ if (value.length > 0) __pulse_fastly_entities_redactions.push(value)
257
+ return JSON.stringify<string>(value)
258
+ }
259
+ function __pulse_fastly_entities_read_body(handle: i32): string {
260
+ let output = ""
261
+ let total = 0
262
+ while (true) {
263
+ const buffer = new Uint8Array(__PULSE_FASTLY_ENTITIES_BUFFER)
264
+ const read = __pulse_fastly_entities_out()
265
+ if (fastly_http_body_read(handle, buffer.dataStart, __PULSE_FASTLY_ENTITIES_BUFFER, changetype<usize>(read)) != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(40); return "" }
266
+ const count = __pulse_fastly_entities_out_value(read)
267
+ if (count <= 0) return output
268
+ total += count
269
+ if (total > __PULSE_FASTLY_ENTITIES_BUFFER) { __pulse_fastly_entities_fail(41); return "" }
270
+ output += __pulse_fastly_entities_decode(buffer, count)
271
+ }
272
+ }
273
+ function __pulse_fastly_entities_fetch(effectIndex: i32, url: string): string {
274
+ const origin = __pulse_fastly_entities_origin(effectIndex)
275
+ if (origin.length == 0 || !url.startsWith(origin + "/")) { __pulse_fastly_entities_fail(50); return "null" }
276
+ const requestOut = __pulse_fastly_entities_out()
277
+ let status = fastly_http_req_new(changetype<usize>(requestOut))
278
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(51); return "null" }
279
+ const request = __pulse_fastly_entities_out_value(requestOut)
280
+ const bodyOut = __pulse_fastly_entities_out()
281
+ status = fastly_http_body_new(changetype<usize>(bodyOut))
282
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(52); return "null" }
283
+ const method = __pulse_fastly_entities_utf8("GET")
284
+ status = fastly_http_req_method_set(request, changetype<usize>(method), method.byteLength)
285
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(53); return "null" }
286
+ const uri = __pulse_fastly_entities_utf8(url)
287
+ status = fastly_http_req_uri_set(request, changetype<usize>(uri), uri.byteLength)
288
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(54); return "null" }
289
+ const backend = __pulse_fastly_entities_utf8(__pulse_fastly_entities_backend(effectIndex))
290
+ const pendingOut = __pulse_fastly_entities_out()
291
+ status = fastly_http_req_send_async(request, __pulse_fastly_entities_out_value(bodyOut), changetype<usize>(backend), backend.byteLength, changetype<usize>(pendingOut))
292
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(55); return "null" }
293
+ const responseOut = __pulse_fastly_entities_out()
294
+ const responseBodyOut = __pulse_fastly_entities_out()
295
+ status = fastly_http_req_pending_req_wait(__pulse_fastly_entities_out_value(pendingOut), changetype<usize>(responseOut), changetype<usize>(responseBodyOut))
296
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(56); return "null" }
297
+ return JSON.stringify<string>(__pulse_fastly_entities_read_body(__pulse_fastly_entities_out_value(responseBodyOut)))
298
+ }
299
+ function __pulse_fastly_entities_replace(input: string, secret: string): string {
300
+ if (secret.length == 0) return input
301
+ let source = input
302
+ let output = ""
303
+ while (true) {
304
+ const index = source.indexOf(secret)
305
+ if (index < 0) return output + source
306
+ output += source.substring(0, index) + "<redacted>"
307
+ source = source.substring(index + secret.length)
308
+ }
309
+ }
310
+ function __pulse_fastly_entities_redact(input: string): string {
311
+ let output = input
312
+ for (let index = 0; index < __pulse_fastly_entities_redactions.length; index += 1) output = __pulse_fastly_entities_replace(output, unchecked(__pulse_fastly_entities_redactions[index]))
313
+ return output
314
+ }
315
+ function pulse_entities_host_effect_begin(effectIndex: i32, payloadPointer: i32, payloadLength: i32): void {
316
+ if (effectIndex < 0 || effectIndex >= __PULSE_FASTLY_ENTITIES_EFFECTS || payloadPointer <= 0 || payloadLength < 0) { __pulse_fastly_entities_fail(60); return }
317
+ const payload = changetype<string>(payloadPointer)
318
+ const kind = __pulse_fastly_entities_kind(effectIndex)
319
+ let result = "null"
320
+ if (kind == 1) result = __pulse_fastly_entities_fetch(effectIndex, __pulse_fastly_entities_input(payload, "url"))
321
+ else if (kind == 2) result = __pulse_fastly_entities_config(__pulse_fastly_entities_input(payload, "name"))
322
+ else if (kind == 3) result = __pulse_fastly_entities_secret(__pulse_fastly_entities_input(payload, "name"))
323
+ else __pulse_fastly_entities_fail(61)
324
+ unchecked(__pulse_fastly_entities_results[effectIndex] = result)
325
+ unchecked(__pulse_fastly_entities_ok[effectIndex] = __pulse_fastly_entities_error == 0 ? 1 : 0)
326
+ unchecked(__pulse_fastly_entities_ready[effectIndex] = 1)
327
+ }
328
+ function pulse_entities_host_log(level: i32, payloadPointer: i32, payloadLength: i32): void {
329
+ if (payloadPointer <= 0 || payloadLength < 0) return
330
+ if (__pulse_fastly_entities_log_handle < 0) {
331
+ const endpoint = __pulse_fastly_entities_utf8("stdout")
332
+ const handleOut = __pulse_fastly_entities_out()
333
+ if (fastly_log_endpoint_get(changetype<usize>(endpoint), endpoint.byteLength, changetype<usize>(handleOut)) != __PULSE_FASTLY_OK) return
334
+ __pulse_fastly_entities_log_handle = __pulse_fastly_entities_out_value(handleOut)
335
+ }
336
+ const message = __pulse_fastly_entities_utf8("[pulse:entities:" + level.toString() + "] " + __pulse_fastly_entities_redact(changetype<string>(payloadPointer)) + "\n")
337
+ const written = __pulse_fastly_entities_out()
338
+ fastly_log_write(__pulse_fastly_entities_log_handle, changetype<usize>(message), message.byteLength, changetype<usize>(written))
339
+ }
340
+ function __pulse_fastly_entities_write_body(handle: i32, value: string): i32 {
341
+ const bytes = __pulse_fastly_entities_utf8(value)
342
+ let offset = 0
343
+ while (offset < bytes.byteLength) {
344
+ const written = __pulse_fastly_entities_out()
345
+ const status = fastly_http_body_write(handle, changetype<usize>(bytes) + offset, bytes.byteLength - offset, 0, changetype<usize>(written))
346
+ if (status != __PULSE_FASTLY_OK) return status
347
+ const count = __pulse_fastly_entities_out_value(written)
348
+ if (count <= 0) return 1
349
+ offset += count
350
+ }
351
+ return __PULSE_FASTLY_OK
352
+ }
353
+ function __pulse_fastly_entities_send(statusCode: i32, text: string): void {
354
+ const responseOut = __pulse_fastly_entities_out()
355
+ let status = fastly_http_resp_new(changetype<usize>(responseOut))
356
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(70); return }
357
+ const response = __pulse_fastly_entities_out_value(responseOut)
358
+ const bodyOut = __pulse_fastly_entities_out()
359
+ status = fastly_http_body_new(changetype<usize>(bodyOut))
360
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(71); return }
361
+ const body = __pulse_fastly_entities_out_value(bodyOut)
362
+ status = fastly_http_resp_status_set(response, statusCode)
363
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(72); return }
364
+ if (text.length > 0) {
365
+ const name = __pulse_fastly_entities_utf8("content-type")
366
+ const value = __pulse_fastly_entities_utf8("application/json; charset=utf-8")
367
+ status = fastly_http_resp_header_append(response, changetype<usize>(name), name.byteLength, changetype<usize>(value), value.byteLength)
368
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(73); return }
369
+ status = __pulse_fastly_entities_write_body(body, text)
370
+ if (status != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(74); return }
371
+ }
372
+ if (fastly_http_resp_send_downstream(response, body, 0) != __PULSE_FASTLY_OK) __pulse_fastly_entities_fail(75)
373
+ }
374
+ function __pulse_fastly_entities_provider_failure(): void {
375
+ __pulse_fastly_entities_send(500, "{\"error\":\"Internal Server Error\"}")
376
+ }
377
+ export function __pulse_fastly_abort(message: string | null, fileName: string | null = null, line: u32 = 0, column: u32 = 0): void { unreachable() }
378
+ export function __pulse_fastly_seed(): f64 { return 0.5 }
379
+ export function pulse_fastly_entities_last_error(): i32 { return __pulse_fastly_entities_error }
380
+ export function _start(): void {
381
+ __pulse_fastly_entities_error = fastly_abi_init(__PULSE_FASTLY_ENTITIES_ABI)
382
+ if (__pulse_fastly_entities_error != __PULSE_FASTLY_OK) { __pulse_fastly_entities_provider_failure(); return }
383
+ __pulse_fastly_entities_results = new Array<string>()
384
+ __pulse_fastly_entities_ready = new Array<i32>()
385
+ __pulse_fastly_entities_ok = new Array<i32>()
386
+ for (let index = 0; index < __PULSE_FASTLY_ENTITIES_EFFECTS; index += 1) {
387
+ __pulse_fastly_entities_results.push("null")
388
+ __pulse_fastly_entities_ready.push(0)
389
+ __pulse_fastly_entities_ok.push(0)
390
+ }
391
+ const handles = new StaticArray<i32>(2)
392
+ if (fastly_http_req_body_downstream_get(changetype<usize>(handles), changetype<usize>(handles) + 4) != __PULSE_FASTLY_OK) { __pulse_fastly_entities_fail(80); __pulse_fastly_entities_provider_failure(); return }
393
+ const requestBody = __pulse_fastly_entities_read_body(load<i32>(changetype<usize>(handles) + 4))
394
+ if (__pulse_fastly_entities_error != 0) { __pulse_fastly_entities_provider_failure(); return }
395
+ if (pulse_entities_set_request(changetype<i32>(requestBody)) != 1) { __pulse_fastly_entities_fail(81); __pulse_fastly_entities_provider_failure(); return }
396
+ let runStatus = pulse_entities_start()
397
+ while (runStatus == pulse_entities_run_suspended() && __pulse_fastly_entities_error == 0) {
398
+ for (let effectIndex = 0; effectIndex < __PULSE_FASTLY_ENTITIES_EFFECTS; effectIndex += 1) {
399
+ if (unchecked(__pulse_fastly_entities_ready[effectIndex]) == 0) continue
400
+ const result = unchecked(__pulse_fastly_entities_results[effectIndex])
401
+ if (pulse_entities_set_effect_result(effectIndex, unchecked(__pulse_fastly_entities_ok[effectIndex]), changetype<i32>(result)) != 1) { __pulse_fastly_entities_fail(82); break }
402
+ unchecked(__pulse_fastly_entities_ready[effectIndex] = 0)
403
+ }
404
+ if (__pulse_fastly_entities_error == 0) runStatus = pulse_entities_resume()
405
+ }
406
+ if (__pulse_fastly_entities_error != 0 || runStatus != pulse_entities_run_complete()) { __pulse_fastly_entities_provider_failure(); return }
407
+ const response = changetype<string>(pulse_entities_response_ptr())
408
+ __pulse_fastly_entities_send(pulse_entities_response_status(), response)
409
+ }
410
+ `;
411
+ }
412
+
413
+ function generateFastlyEntitiesNativeSource(sourceArtifact, options = {}) {
414
+ const artifact = validateArtifact(sourceArtifact);
415
+ const bindings = normalizedBindings(artifact, options);
416
+ const packageSource = stripPackageHostImports(artifact.source);
417
+ const runtime = providerRuntimeSource(bindings, options);
418
+ const source = [
419
+ '/* Generated Fastly provider adapter for package-owned Entities Native source. */',
420
+ packageSource,
421
+ runtime,
422
+ ''
423
+ ].join('\n');
424
+ return Object.freeze({
425
+ version: FASTLY_ENTITIES_NATIVE_SOURCE_VERSION,
426
+ source,
427
+ sourceBytes: Buffer.byteLength(source),
428
+ sourceSha256: sha256(source),
429
+ packageSourceSha256: artifact.sourceSha256,
430
+ planHash: artifact.planHash,
431
+ effectBindings: bindings,
432
+ policy: Object.freeze({
433
+ semanticOwner: '@pulse-compute/entities',
434
+ providerAuthorities: Object.freeze(['request', 'response-transport', 'effect-execution', 'logging']),
435
+ javascriptRuntime: false,
436
+ automaticFallback: false
437
+ })
438
+ });
439
+ }
440
+
441
+ function inspectWasm(bytes) {
442
+ const module = new WebAssembly.Module(bytes);
443
+ const imports = WebAssembly.Module.imports(module).map((entry) => Object.freeze({
444
+ module: entry.module,
445
+ name: entry.name,
446
+ kind: entry.kind
447
+ }));
448
+ const unexpected = imports.filter((entry) => !ALLOWED_IMPORTS.has(`${entry.module}:${entry.name}`));
449
+ if (unexpected.length > 0) {
450
+ fail('PULSE_FASTLY_ENTITIES_IMPORT_POLICY_FAILED', 'Fastly Entities Native emitted an unauthorized import.', { imports, unexpected });
451
+ }
452
+ const exports = WebAssembly.Module.exports(module).map((entry) => Object.freeze({ name: entry.name, kind: entry.kind }));
453
+ const required = ['_start', 'memory', 'pulse_entities_start', 'pulse_entities_resume', 'pulse_entities_set_effect_result', 'pulse_fastly_entities_last_error'];
454
+ const missing = required.filter((name) => !exports.some((entry) => entry.name === name));
455
+ if (missing.length > 0) fail('PULSE_FASTLY_ENTITIES_EXPORT_POLICY_FAILED', 'Fastly Entities Native is missing a required export.', { missing, exports });
456
+ return Object.freeze({ imports: Object.freeze(imports), exports: Object.freeze(exports) });
457
+ }
458
+
459
+ function compileFastlyEntitiesNative(sourceArtifact, options = {}) {
460
+ const generated = generateFastlyEntitiesNativeSource(sourceArtifact, options);
461
+ const cwd = path.resolve(options.cwd || process.cwd());
462
+ const compilerFallback = path.resolve(__dirname, '..', '..', '..', '..', 'wasm', 'packages', 'compiler');
463
+ const asc = resolveAsc(cwd) || resolveAsc(compilerFallback);
464
+ if (!asc) fail('PULSE_FASTLY_ENTITIES_ASC_MISSING', 'AssemblyScript compiler dependency was not found.', { cwd, compilerFallback });
465
+ let transform;
466
+ try { transform = require.resolve('json-as', { paths: [compilerFallback, cwd] }); }
467
+ catch (error) {
468
+ fail('PULSE_FASTLY_ENTITIES_JSON_AS_MISSING', 'Fastly Entities Native requires lockfile-pinned json-as 1.5.0.', { cause: error && error.message });
469
+ }
470
+ const jsonAsRoot = path.resolve(transform, '..', '..', '..');
471
+ const jsonAsManifest = JSON.parse(fs.readFileSync(path.join(jsonAsRoot, 'package.json'), 'utf8'));
472
+ if (jsonAsManifest.version !== '1.5.0') {
473
+ fail('PULSE_FASTLY_ENTITIES_JSON_AS_VERSION_INVALID', 'Fastly Entities Native requires json-as 1.5.0 exactly.', {
474
+ expected: '1.5.0',
475
+ actual: jsonAsManifest.version
476
+ });
477
+ }
478
+ const staging = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-fastly-entities-native-'));
479
+ const sourceFile = path.join(staging, 'entities-native-fastly.as.ts');
480
+ const wasmFile = path.join(staging, 'entities-native-fastly.wasm');
481
+ const watFile = path.join(staging, 'entities-native-fastly.wat');
482
+ try {
483
+ fs.writeFileSync(sourceFile, generated.source, 'utf8');
484
+ const args = [
485
+ asc.script,
486
+ path.basename(sourceFile),
487
+ '--outFile', wasmFile,
488
+ '--textFile', watFile,
489
+ '--runtime', 'incremental',
490
+ '--exportRuntime',
491
+ '--noAssert',
492
+ '--optimize',
493
+ '--use', 'abort=entities-native-fastly.as/__pulse_fastly_abort',
494
+ '--use', 'seed=entities-native-fastly.as/__pulse_fastly_seed',
495
+ '--transform', transform,
496
+ '--path', path.join(compilerFallback, 'node_modules'),
497
+ '--path', path.dirname(jsonAsRoot)
498
+ ];
499
+ const startedAt = Date.now();
500
+ const result = spawnSync(asc.executable, args, {
501
+ cwd: staging,
502
+ encoding: 'utf8',
503
+ maxBuffer: 64 * 1024 * 1024,
504
+ timeout: Number(options.timeoutMs || 180_000),
505
+ env: { ...process.env, JSON_STRICT: 'true', JSON_USE_FAST_PATH: '0' }
506
+ });
507
+ if (result.error || result.status !== 0 || !fs.existsSync(wasmFile)) {
508
+ fail('PULSE_FASTLY_ENTITIES_ASC_FAILED', 'AssemblyScript failed to compile the Fastly Entities Native realization.', {
509
+ status: result.status,
510
+ signal: result.signal,
511
+ error: result.error && result.error.message,
512
+ stdout: String(result.stdout || '').slice(-24_000),
513
+ stderr: String(result.stderr || '').slice(-24_000)
514
+ });
515
+ }
516
+ const wasm = fs.readFileSync(wasmFile);
517
+ const maxBytes = Number(options.maxWasmBytes || MAX_WASM_BYTES);
518
+ if (wasm.byteLength > maxBytes) {
519
+ fail('PULSE_FASTLY_ENTITIES_WASM_TOO_LARGE', 'Fastly Entities Native exceeds its explicit Wasm ceiling.', {
520
+ bytes: wasm.byteLength,
521
+ maxBytes
522
+ });
523
+ }
524
+ const inspection = inspectWasm(wasm);
525
+ return Object.freeze({
526
+ version: FASTLY_ENTITIES_NATIVE_REALIZATION_VERSION,
527
+ status: 'realized',
528
+ provider: 'fastly',
529
+ target: 'native',
530
+ targetId: 'fastly-compute-native',
531
+ wasm,
532
+ wat: fs.readFileSync(watFile, 'utf8'),
533
+ source: generated.source,
534
+ artifact: Object.freeze({
535
+ bytes: wasm.byteLength,
536
+ sha256: sha256(wasm),
537
+ sourceBytes: generated.sourceBytes,
538
+ sourceSha256: generated.sourceSha256,
539
+ packageSourceSha256: generated.packageSourceSha256,
540
+ planHash: generated.planHash
541
+ }),
542
+ imports: inspection.imports,
543
+ exports: inspection.exports,
544
+ effectBindings: generated.effectBindings,
545
+ compiler: Object.freeze({
546
+ package: 'assemblyscript',
547
+ version: JSON.parse(fs.readFileSync(path.join(asc.packageRoot, 'package.json'), 'utf8')).version,
548
+ jsonAs: jsonAsManifest.version,
549
+ durationMs: Date.now() - startedAt
550
+ }),
551
+ policy: generated.policy
552
+ });
553
+ } finally {
554
+ fs.rmSync(staging, { recursive: true, force: true });
555
+ }
556
+ }
557
+
558
+ module.exports = Object.freeze({
559
+ FASTLY_ENTITIES_NATIVE_REALIZATION_VERSION,
560
+ FASTLY_ENTITIES_NATIVE_SOURCE_VERSION,
561
+ FastlyEntitiesNativeError,
562
+ generateFastlyEntitiesNativeSource,
563
+ compileFastlyEntitiesNative
564
+ });