1.0.0-beta.9 · exact release
@pulse-compute/provider-fastly
Source fda16de1b352 · Published v1.0.0-beta.9
@pulse-compute/provider-fastly supplies typed Fastly project configuration,
canonical capability lowering, direct JavaScript execution, local conformance
runtimes, Native and JavaScript Compute target generation, and an explicit
Fastly CLI boundary.
Install it only in projects that select the Fastly provider.
The Native platform-capabilities compiler defaults to a 4 MiB final-Wasm budget (4,194,304 bytes, inclusive). Configure a different positive safe integer byte count in the selected profile:
fastly: { build: { name: 'my-app', maxWasmBytes: 8388608 }, // 8 MiB}The fastly() facade accepts fastly({ maxWasmBytes: 8388608 }) and projects it
into the same build.maxWasmBytes field. Zero, negative, fractional, nonnumeric,
and unsafe integer values fail with PULSE_FASTLY_MAX_WASM_BYTES_INVALID.
This is a Pulse build guard, not a Fastly service limit or a runtime-memory
allowance. It applies to newly compiled and reused Native artifacts. Modules
above it fail with PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_WASM_TOO_LARGE
and the observed/configured byte counts; modules within it still require valid
Wasm and the expected host ABI. No unlimited setting or runtime allocation
change is implied. The option does not control JavaScript-target packaging.
Install
Section titled “Install”npm install @pulse-compute/provider-fastly@1.0.0-beta.9Configure a project
Section titled “Configure a project”import { defineConfig } from '@pulse-compute/pulse'
export default defineConfig((scope) => ({ pulse: { entry: 'src/index.ts', tests: 'tests/pulse.harness.ts', defaultProfile: 'local', strict: true, }, local: { host: 'fastly', target: 'native', outDir: 'dist', apiBase: scope.config('API_BASE'), apiToken: scope.secret('API_TOKEN'), dev: { config: { API_BASE: 'https://api.example.com' }, secrets: { API_TOKEN: 'local-example-secret', GRIP_TOKEN: 'local-grip-secret', }, kv: { sessions: { 'session:123': { userId: 123 } }, }, fetches: { 'https://api.example.com/users/7': { value: { id: 7, name: 'Ada' }, }, 'POST https://publisher.example.com/publish': { status: 202, value: { accepted: true, messageId: 'message-1' }, }, }, }, fastly: { bindings: { configStore: 'app_config', secretStore: 'app_secrets', kv: { sessions: 'app_sessions' }, backends: { 'https://api.example.com': 'api_backend', 'https://publisher.example.com': 'publisher_backend', }, dynamicBackends: false, grip: { publishEndpoint: 'https://publisher.example.com/publish', publishBackend: 'publisher_backend', authentication: { scheme: 'bearer', secretRef: 'GRIP_TOKEN', }, }, }, build: { name: 'pulse-fastly-capabilities-example' }, }, },}))The provider maps logical origins and stores used by canonical handler calls to Fastly deployment resources. Local values and fixtures do not create or populate deployed resources.
Configuration groups
Section titled “Configuration groups”The root fastly() helper supports:
- Config Store and Secret Store names;
- logical KV namespace to Fastly KV Store mappings;
- origin to backend-name mappings;
- explicit dynamic-backend policy;
- GRIP/Fanout hold and publish bindings;
- package name, description, and authors;
- local network-fetch policy.
See Project configuration for field-level defaults, precedence, and constraints.
Build outputs
Section titled “Build outputs”The active .pulse/config.ts profile selects one target explicitly:
pulse build ./my-appFastly Native
Section titled “Fastly Native”target: 'native' compiles the provider-neutral plan and emits generated
src/main.as.ts plus compact bin/main.wasm importing the required fastly_*
hostcalls directly. It contains no JavaScript runtime image or pulse_host.
Clock-dependent capabilities import WASI clock_time_get; enabled Native logging
imports fd_write for redacted, best-effort stdout. Named logging endpoints are
not used. See logging behavior.
The compiler avoids boxed arithmetic temporaries in proven numeric loop regions for schema and stub builds without linked guests, and for schema-inactive builds using the selected crypto guest. Schema-active linked builds remain excluded, even with JSON cache arena elision. This does not reclaim general scalar handles or change memory limits. Unlinked bounded read-loop builds retain the original cumulative charges for eliminated temporaries, including cache admissions and the exact exhaustion boundary. Linked budgeted plans remain boxed. See numeric loop regions.
The canonical Native platform driver caps each invocation at 1,024 cumulative
effect requests, including repeated loop visits and all requested group members.
The next request fails before dispatch (state error 1007, stage 170); this is a
fixed runtime policy, not a provider configuration field. Per-visit tickets fence
result injection, and terminal driver return invalidates pending work. The
artifact records pulse.effect-invocation.v1 in effectInvocations. Existing
monotonic request deadlines span the whole invocation. See the
PS2 lifecycle contract
for the managed-host boundary. Plans with bounded read loops also enable the
PS3 memory policy:
64 MiB of cumulative accounted retention, 1,048,576 value/edge units, and a
256 MiB ceiling on unlinked Wasm linear memory. Exceeding the accounting budget
closes the invocation and traps with memory error 1010; values remain valid for
the request lifetime. PS4 provider and application qualification remains open.
Conditional KV (getVersioned, insertIfAbsent, compareAndSwap) uses this
Native path and the existing logical KV bindings. It preserves generation tokens
without numeric narrowing, stages a bounded Pulse JSON envelope, and distinguishes
confirmed rejection from unconfirmed dispatch. Pending completion and body reads
use readiness selection with a monotonic deadline. Read bodies are closed; expired
pending operations remain owned by invocation teardown, with no rollback promise.
Fastly JavaScript remains explicitly incomplete for these operations. See
conditional KV for the
portable contract. The generated-Wasm corpus is local evidence; it does not replace
deployed cross-location acceptance. The legacy JavaScript fixture runner does
not realize Native conditional KV; it reports a configuration failure. Exercise
these operations through generated Wasm or the Compute execution lane.
The required conditional KV acceptance task is kv-conditional-acceptance.
It builds and installs exact tarballs and executes their Native artifact through
Fastly CLI/Viceroy; an unavailable engine fails the task. Viceroy 0.21.0 currently
fails the missing-key CAS contract: it creates the key instead of returning
conflict. A September 12, 2026 standalone Rust SDK probe on deployed Fastly
rejected CAS on both never-created and deleted keys, which remained absent.
This resolves the live missing-key concern for those cases and distinguishes it
from the Viceroy defect. The required local gate still fails, and full Pulse
deployed cross-location acceptance remains pending the isolated development
environment. The K4 acceptance record
retains both captures and their scope. Neither that standalone probe nor passing
injected-host or portable tests clears the full acceptance gates.
The Native ctx.req.headers snapshot is a cached, immutable array of name/value
pairs. Names are lowercased; repeated values remain separate pairs in their
per-name order, including empty values. Enumeration is lazy and bounded to
256 pairs and 64 KiB of UTF-8 name/value bytes, counting a terminator for each
name and value. An incomplete, malformed or oversized host result fails the
request before later effects or a success response; it never returns a partial
snapshot. This is a provider read limit, not a configurable HTTP ingress limit.
ctx.req.header(name) and fetched response.header(name) return the first value
using a case-insensitive name lookup. An absent header returns undefined; a
present empty value returns "". Native lookup hostcall failures or malformed
host results terminate the request with an explicit 500 response before later
effects, while preserving the original hostcall error for diagnostics.
Fastly JavaScript
Section titled “Fastly JavaScript”target: 'javascript' emits a deterministic, self-contained source closure:
src/index.jssrc/application.jsfastly.tomlpackage.jsonpulse-esbuild.config.jspulse-fastly-javascript-candidate.jsonpulse-fastly-javascript-deployment.jsonpulse-fastly-javascript-source-package.jsonThe closure pins esbuild and @fastly/js-compute, bundles the reachable Pulse
application and package implementations, and contains no Pulse Native artifact.
The generated build first produces dist/index.js, then the pinned Fastly
JavaScript compiler produces bin/main.wasm. Candidate, source-package,
deployment, and top-level manifests agree on the selected fastly-javascript
target and retain automaticFallback: false.
The release gate builds the source closure twice, compares its bytes, and successfully invokes the pinned downstream compiler. The resulting runtime Wasm is bound to the candidate by SHA-256. Wizer-owned runtime snapshot bytes are not claimed byte-reproducible; the Pulse-owned closure and deployment metadata are.
Provider selection is configuration-owned. Removed public --provider and
--source-only flags are rejected with stable diagnostics.
Use:
pulse doctor ./my-app --jsonto inspect compiler and Fastly CLI readiness before a release build.
Local conformance and external reality
Section titled “Local conformance and external reality”For Fastly Native, pulse test and pulse dev use the provider’s local
canonical runtime. For Fastly JavaScript, they use explicit provider emulation
and the same provider-owned bundled application closure. Both paths use
configured values, fixtures, and optional network fetch; neither silently runs
through the Node provider.
The native provider module is checked by the explicit reality profile through an inspected local Compute launcher. PULSE_FASTLY_BIN selects CLI-owned fastly compute serve --file; PULSE_VICEROY_BIN selects direct viceroy serve when no explicit Fastly CLI launcher is selected. Both are documented in Environment variables.
The Fastly reality gate proves Native local host-ABI compatibility through real
HTTP requests covering schemas, configuration, secrets, KV persistence,
named-backend fetch, opaque bytes, repeated headers, and GRIP hold/publish. The
JavaScript candidate gate proves downstream runtime compilation but does not
invoke fastly compute serve. Neither gate claims that remote resources or a
deployed Fastly service are healthy; remote deployment remains separately
unvalidated.
Fastly JavaScript is generally available under the full-target-support policy: all declared runtime, capability, packaging, four-mode, tooling, and offline candidate gates are satisfied. General availability is a supported-target statement, not evidence that a particular candidate was deployed.
Public entry points
Section titled “Public entry points”| Entry point | Purpose |
|---|---|
@pulse-compute/provider-fastly | Typed fastly() project configuration. |
@pulse-compute/provider-fastly/contract | Canonical descriptor and lowering-plan integration. |
@pulse-compute/provider-fastly/runtime/canonical-api-runtime | Local canonical runtime used by project execution. |
@pulse-compute/provider-fastly/build/canonical-target | Fastly source/target writer. |
@pulse-compute/provider-fastly/testing/fastly-cli | Explicit Fastly CLI discovery and serve boundary. |
Other exported compiler/runtime subpaths are compatibility or implementation surfaces and do not gain the same application-facing promise.
Handler isolation
Section titled “Handler isolation”Application source continues to import only @pulse-compute/runtime and supported package facades. The provider plan records:
{ "providerSpecificUserland": false, "providerSdkUserland": false, "capabilityDiscoveryFromUserland": false}Do not import Fastly SDK objects into canonical handlers. Bindings belong in project configuration.
Current limitations
Section titled “Current limitations”- The official
fastlyshorthand resolves this package's explicit./toolchainexport. That narrow bootstrap is not automatic plugin discovery, self-registration, or a lowerer extension API. - Local fixtures do not provision Fastly resources.
- Offline candidate validation does not deploy, activate, or publish a Fastly service.
- Dynamic backends are disabled unless explicitly enabled.
- GRIP requires the documented Fanout/publish bindings for the operations used.
- Full userland stream processing and provider SDK escape hatches remain outside the Beta.