@pulse-compute/cli 0.0.0 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +572 -0
- package/CHANGELOG.md +71 -0
- package/README.md +71 -1
- package/bin/pulse.js +15 -0
- package/bin/pulsewasm-extract.js +22 -0
- package/cli-spec.json +575 -0
- package/completions/_pulse +106 -0
- package/completions/pulse.bash +31 -0
- package/completions/pulse.fish +76 -0
- package/docs/README.md +77 -0
- package/docs/architecture/current-contracts.md +470 -0
- package/docs/architecture/overview.md +93 -0
- package/docs/architecture/vision.md +515 -0
- package/docs/concepts/bodies.md +186 -0
- package/docs/concepts/compilation-and-lowering.md +496 -0
- package/docs/concepts/contracts-and-providers.md +158 -0
- package/docs/concepts/effects-and-continuations.md +300 -0
- package/docs/concepts/entities-and-adapters.md +71 -0
- package/docs/concepts/package-owned-lowering.md +175 -0
- package/docs/concepts/targets-and-hosts.md +70 -0
- package/docs/contributing/README.md +38 -0
- package/docs/contributing/adding-core-provider.md +315 -0
- package/docs/contributing/adding-first-party-lowerer.md +327 -0
- package/docs/contributing/entities-lowering.md +91 -0
- package/docs/contributing/package-lowerer-contract.md +383 -0
- package/docs/contributing/pulse-aware-packages.md +149 -0
- package/docs/examples.md +112 -0
- package/docs/fixtures/inspect-fetch-composition.selected.json +181 -0
- package/docs/getting-started.md +128 -0
- package/docs/guides/compatibility-imports.md +60 -0
- package/docs/guides/deploying-fastly.md +188 -0
- package/docs/guides/deploying-node.md +143 -0
- package/docs/guides/events.md +233 -0
- package/docs/guides/fastly-capabilities.md +153 -0
- package/docs/guides/fetching-and-composition.md +101 -0
- package/docs/guides/grip.md +94 -0
- package/docs/guides/json-schemas.md +233 -0
- package/docs/guides/migrating-from-express.md +192 -0
- package/docs/guides/project-lifecycle.md +143 -0
- package/docs/guides/routing.md +202 -0
- package/docs/guides/troubleshooting.md +47 -0
- package/docs/maintainers/README.md +60 -0
- package/docs/maintainers/codex-maintainer.md +71 -0
- package/docs/maintainers/documentation-deployment.md +147 -0
- package/docs/maintainers/documentation-system.md +58 -0
- package/docs/maintainers/documentation-versioning.md +65 -0
- package/docs/maintainers/maintainer-charter.md +100 -0
- package/docs/maintainers/maintenance-policy.json +809 -0
- package/docs/maintainers/maintenance-policy.md +124 -0
- package/docs/maintainers/npm-publishing.md +154 -0
- package/docs/maintainers/plugin-readiness.json +19 -0
- package/docs/maintainers/plugin-readiness.md +31 -0
- package/docs/maintainers/public-site.md +158 -0
- package/docs/maintainers/release-acceptance.md +293 -0
- package/docs/maintainers/release-manifest.md +89 -0
- package/docs/maintainers/repository-setup.md +145 -0
- package/docs/maintainers/scope-policy.md +105 -0
- package/docs/maintainers/support-and-triage.md +74 -0
- package/docs/maintainers/testing.md +334 -0
- package/docs/packages/README.md +60 -0
- package/docs/packages/assets.md +85 -0
- package/docs/packages/cli.md +190 -0
- package/docs/packages/crypto.md +78 -0
- package/docs/packages/entities.md +129 -0
- package/docs/packages/grip.md +82 -0
- package/docs/packages/implementation-packages.md +122 -0
- package/docs/packages/jwt.md +55 -0
- package/docs/packages/provider-fastly.md +243 -0
- package/docs/packages/pulse.md +159 -0
- package/docs/packages/runtime.md +265 -0
- package/docs/packages/s3.md +101 -0
- package/docs/preview-scope.md +142 -0
- package/docs/reference/README.md +33 -0
- package/docs/reference/cli-spec.json +575 -0
- package/docs/reference/cli.md +457 -0
- package/docs/reference/compatibility-matrix.md +169 -0
- package/docs/reference/diagnostics.md +1872 -0
- package/docs/reference/documentation-versions.json +22 -0
- package/docs/reference/environment.md +347 -0
- package/docs/reference/handler-authoring.md +264 -0
- package/docs/reference/project-config.md +582 -0
- package/docs/reference/project-config.schema.json +981 -0
- package/docs/reference/release-manifest.json +441 -0
- package/docs/reference/shell-completion.md +23 -0
- package/documentation-site.json +668 -0
- package/documentation-versions.json +22 -0
- package/examples/01-hello-json/.pulse/.gitignore +3 -0
- package/examples/01-hello-json/.pulse/config.ts +16 -0
- package/examples/01-hello-json/README.md +119 -0
- package/examples/01-hello-json/package.json +20 -0
- package/examples/01-hello-json/src/index.ts +9 -0
- package/examples/01-hello-json/tests/pulse.harness.ts +22 -0
- package/examples/01-hello-json/tsconfig.json +14 -0
- package/examples/02-request-schema/.pulse/.gitignore +3 -0
- package/examples/02-request-schema/.pulse/config.ts +17 -0
- package/examples/02-request-schema/README.md +145 -0
- package/examples/02-request-schema/package.json +20 -0
- package/examples/02-request-schema/src/index.ts +18 -0
- package/examples/02-request-schema/src/schemas.ts +20 -0
- package/examples/02-request-schema/tests/pulse.harness.ts +37 -0
- package/examples/02-request-schema/tsconfig.json +14 -0
- package/examples/03-fetch-composition/.pulse/.gitignore +3 -0
- package/examples/03-fetch-composition/.pulse/config.ts +31 -0
- package/examples/03-fetch-composition/README.md +210 -0
- package/examples/03-fetch-composition/package.json +20 -0
- package/examples/03-fetch-composition/src/index.ts +60 -0
- package/examples/03-fetch-composition/tests/pulse.harness.ts +47 -0
- package/examples/03-fetch-composition/tsconfig.json +14 -0
- package/examples/05-fastly-capabilities/.pulse/.gitignore +3 -0
- package/examples/05-fastly-capabilities/.pulse/config.ts +57 -0
- package/examples/05-fastly-capabilities/README.md +257 -0
- package/examples/05-fastly-capabilities/package.json +22 -0
- package/examples/05-fastly-capabilities/src/index.ts +55 -0
- package/examples/05-fastly-capabilities/tests/pulse.harness.ts +66 -0
- package/examples/05-fastly-capabilities/tsconfig.json +14 -0
- package/examples/07-opaque-proxy/.pulse/.gitignore +3 -0
- package/examples/07-opaque-proxy/.pulse/config.ts +38 -0
- package/examples/07-opaque-proxy/README.md +150 -0
- package/examples/07-opaque-proxy/package.json +21 -0
- package/examples/07-opaque-proxy/src/index.ts +9 -0
- package/examples/07-opaque-proxy/tests/pulse.harness.ts +25 -0
- package/examples/07-opaque-proxy/tsconfig.json +14 -0
- package/examples/09-router-lowering/.pulse/.gitignore +3 -0
- package/examples/09-router-lowering/.pulse/config.ts +22 -0
- package/examples/09-router-lowering/README.md +287 -0
- package/examples/09-router-lowering/package.json +22 -0
- package/examples/09-router-lowering/src/index.ts +65 -0
- package/examples/09-router-lowering/tests/pulse.harness.ts +91 -0
- package/examples/09-router-lowering/tsconfig.json +14 -0
- package/examples/10-entities-tools/.pulse/.gitignore +4 -0
- package/examples/10-entities-tools/.pulse/config.ts +23 -0
- package/examples/10-entities-tools/README.md +165 -0
- package/examples/10-entities-tools/package.json +21 -0
- package/examples/10-entities-tools/src/handlers.ts +15 -0
- package/examples/10-entities-tools/src/index.ts +31 -0
- package/examples/10-entities-tools/src/schemas.ts +18 -0
- package/examples/10-entities-tools/tests/pulse.harness.ts +40 -0
- package/examples/10-entities-tools/tools-facade.cjs +158 -0
- package/examples/10-entities-tools/tsconfig.json +14 -0
- package/examples/11-events/.pulse/.gitignore +4 -0
- package/examples/11-events/.pulse/config.ts +24 -0
- package/examples/11-events/README.md +194 -0
- package/examples/11-events/package.json +20 -0
- package/examples/11-events/src/index.ts +27 -0
- package/examples/11-events/src/schemas.ts +19 -0
- package/examples/11-events/tests/pulse.harness.ts +34 -0
- package/examples/11-events/tsconfig.json +15 -0
- package/examples/12-mcp-proxy/.pulse/.gitignore +3 -0
- package/examples/12-mcp-proxy/.pulse/config.ts +25 -0
- package/examples/12-mcp-proxy/README.md +149 -0
- package/examples/12-mcp-proxy/package.json +20 -0
- package/examples/12-mcp-proxy/src/index.ts +17 -0
- package/examples/12-mcp-proxy/tests/pulse.harness.ts +29 -0
- package/examples/12-mcp-proxy/tsconfig.json +14 -0
- package/examples/13-jwt-es256/.pulse/config.ts +19 -0
- package/examples/13-jwt-es256/README.md +195 -0
- package/examples/13-jwt-es256/package.json +21 -0
- package/examples/13-jwt-es256/src/index.ts +21 -0
- package/examples/13-jwt-es256/tests/pulse.harness.ts +54 -0
- package/examples/13-jwt-es256/tsconfig.json +14 -0
- package/examples/README.md +36 -0
- package/package.json +66 -6
- package/project-config.schema.json +981 -0
- package/release-manifest.json +441 -0
- package/src/command-spec.js +279 -0
- package/src/completion.js +113 -0
- package/src/diagnostics.js +350 -0
- package/src/documentation.js +45 -0
- package/src/index.d.ts +133 -0
- package/src/index.js +15 -0
- package/src/internal/command-executor.d.ts +29 -0
- package/src/internal/command-executor.js +143 -0
- package/src/internal/command-plan.d.ts +9 -0
- package/src/internal/command-plan.js +73 -0
- package/src/internal/command-reporter.d.ts +15 -0
- package/src/internal/command-reporter.js +133 -0
- package/src/internal/command-request.d.ts +35 -0
- package/src/internal/command-request.js +154 -0
- package/src/internal/node-http.js +54 -0
- package/src/internal/project-context.d.ts +66 -0
- package/src/internal/project-context.js +175 -0
- package/src/project-config-reference.js +4 -0
- package/src/project-config-schema.d.ts +23 -0
- package/src/project-config-schema.js +271 -0
- package/src/project-config.js +724 -0
- package/src/project-execution.js +2946 -0
- package/src/provider-drivers.js +3 -0
- package/src/target-support.js +3 -0
- package/src/typescript-module-loader.js +269 -0
- package/src/workflow.js +78 -0
- package/src/workspace.js +82 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
# Compilation and lowering
|
|
2
|
+
|
|
3
|
+
Pulse handlers are async-shaped TypeScript, but native targets do not execute them with a JavaScript Promise runtime. The `pulse` CLI compiles the whole project into a provider-neutral program, records every trusted host operation the program requires, and then asks the selected provider to realize those operations.
|
|
4
|
+
|
|
5
|
+
This page explains that process. Start with [Getting started](../getting-started.md) when you only need to run an application.
|
|
6
|
+
|
|
7
|
+
## The compilation path
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
async Pulse or Router application + .pulse/config.ts profile
|
|
11
|
+
│
|
|
12
|
+
▼
|
|
13
|
+
project normalization and schema loading
|
|
14
|
+
│
|
|
15
|
+
▼
|
|
16
|
+
canonical authoring lowering
|
|
17
|
+
routes • values • branches • effects • continuations
|
|
18
|
+
│
|
|
19
|
+
▼
|
|
20
|
+
provider-neutral native execution plan
|
|
21
|
+
│
|
|
22
|
+
├── pulse compile → compact Pulse-owned Wasm
|
|
23
|
+
│
|
|
24
|
+
▼
|
|
25
|
+
capability requirements and provider operations
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
provider lowering plan
|
|
29
|
+
│
|
|
30
|
+
├── Node execution/build output
|
|
31
|
+
└── Fastly Compute source + bin/main.wasm
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Lowering** means translating a higher-level source operation into a smaller, explicit representation that later stages can validate and execute. It is not minification and it is not merely TypeScript-to-JavaScript transpilation.
|
|
35
|
+
|
|
36
|
+
The compiler must be able to answer four questions before provider execution begins:
|
|
37
|
+
|
|
38
|
+
1. Which values and branches are part of the portable program?
|
|
39
|
+
2. Which operations need host authority, such as fetch, secrets, KV, or GRIP?
|
|
40
|
+
3. Where does execution continue after each host operation completes?
|
|
41
|
+
4. Can the chosen provider satisfy every required capability and binding?
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Router topology and middleware use the same downstream plan
|
|
45
|
+
|
|
46
|
+
Static Router authoring is normalized before canonical handler analysis:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
Router registrations, middleware, error handlers, and mounts
|
|
50
|
+
→ retained topology extraction and path normalization
|
|
51
|
+
→ flat ordered execution-entry graph
|
|
52
|
+
→ terminal normal/error cursor transfers
|
|
53
|
+
→ canonical route and middleware branches
|
|
54
|
+
→ the same effects, continuations, native plan, and providers
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`return next()` does not call another JavaScript function and later unwind. It ends the current state and advances the normal Router cursor. `return next(error)` does the same for the error cursor. Middleware effects before that transfer use the same suspend/resume states as effects inside ordinary handlers; only the Router cursor determines where execution continues afterward.
|
|
58
|
+
|
|
59
|
+
Router dispatch is not a separate runtime or compiler. `pulse inspect` reports `compiler.routing.entries`, `compiler.routing.routes`, and terminal semantics. Host operations carry the stable identity of the route or middleware entry that owns them. See the [Router lowering example](../../examples/09-router-lowering/) and [routing guide](../guides/routing.md).
|
|
60
|
+
|
|
61
|
+
## From source to an effect
|
|
62
|
+
|
|
63
|
+
The single-fetch example uses the portable async authoring shape:
|
|
64
|
+
|
|
65
|
+
<!-- pulse-doc-source: examples/03-fetch-composition/src/index.ts -->
|
|
66
|
+
```ts
|
|
67
|
+
import { Pulse } from '@pulse-compute/pulse'
|
|
68
|
+
|
|
69
|
+
interface User {
|
|
70
|
+
id: number
|
|
71
|
+
name: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface Stats {
|
|
75
|
+
score: number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface Flags {
|
|
79
|
+
enabled: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const app = new Pulse({ auto: true })
|
|
83
|
+
|
|
84
|
+
// One structured origin.
|
|
85
|
+
app.get('/user', async (ctx) => {
|
|
86
|
+
const user = await ctx
|
|
87
|
+
.fetch('https://users.example.test/users/123')
|
|
88
|
+
.json<User>()
|
|
89
|
+
return ctx.json({ found: true, user })
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Multiple origins with explicit sequential awaits.
|
|
93
|
+
app.get('/user-summary', async (ctx) => {
|
|
94
|
+
const user = await ctx
|
|
95
|
+
.fetch('https://users.example.test/users/123')
|
|
96
|
+
.json<User>()
|
|
97
|
+
const stats = await ctx
|
|
98
|
+
.fetch('https://stats.example.test/users/123')
|
|
99
|
+
.json<Stats>()
|
|
100
|
+
const flags = await ctx
|
|
101
|
+
.fetch('https://flags.example.test/users/123')
|
|
102
|
+
.json<Flags>()
|
|
103
|
+
return ctx.json({
|
|
104
|
+
id: user.id,
|
|
105
|
+
name: user.name,
|
|
106
|
+
score: stats.score,
|
|
107
|
+
enabled: flags.enabled,
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Multiple origins with explicit portable concurrency.
|
|
112
|
+
app.get('/user-summary-parallel', async (ctx) => {
|
|
113
|
+
const { user, stats, flags } = await ctx.parallel({
|
|
114
|
+
user: ctx.fetch('https://users.example.test/users/123').json<User>(),
|
|
115
|
+
stats: ctx.fetch('https://stats.example.test/users/123').json<Stats>(),
|
|
116
|
+
flags: ctx.fetch('https://flags.example.test/users/123').json<Flags>(),
|
|
117
|
+
})
|
|
118
|
+
return ctx.json({
|
|
119
|
+
id: user.id,
|
|
120
|
+
name: user.name,
|
|
121
|
+
score: stats.score,
|
|
122
|
+
enabled: flags.enabled,
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
export default app
|
|
127
|
+
```
|
|
128
|
+
<!-- /pulse-doc-source -->
|
|
129
|
+
|
|
130
|
+
Run the compiler inspection without building a target:
|
|
131
|
+
|
|
132
|
+
<!-- pulse-doc-run {"args":["inspect","examples/03-fetch-composition","--json"],"display":"pulse inspect examples/03-fetch-composition --json"} -->
|
|
133
|
+
```bash
|
|
134
|
+
pulse inspect examples/03-fetch-composition --json
|
|
135
|
+
```
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"status": "ok",
|
|
139
|
+
"provider": {
|
|
140
|
+
"id": "node"
|
|
141
|
+
},
|
|
142
|
+
"compiler": {
|
|
143
|
+
"effectCount": 7,
|
|
144
|
+
"continuationCount": 3,
|
|
145
|
+
"groupedContinuationCount": 2,
|
|
146
|
+
"opaqueReturnCount": 0
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The selected fields below are copied from that inspection result. Paths, hashes, source offsets, and unrelated schema detail are intentionally omitted so the example remains stable and readable.
|
|
152
|
+
|
|
153
|
+
<!-- pulse-doc-source: docs/fixtures/inspect-fetch-composition.selected.json -->
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"status": "ok",
|
|
157
|
+
"project": {
|
|
158
|
+
"provider": "node",
|
|
159
|
+
"entry": "src/index.ts"
|
|
160
|
+
},
|
|
161
|
+
"compiler": {
|
|
162
|
+
"version": "pulse.canonical-api-compiler.v7",
|
|
163
|
+
"capabilities": [
|
|
164
|
+
"fetch",
|
|
165
|
+
"response.json",
|
|
166
|
+
"response.text"
|
|
167
|
+
],
|
|
168
|
+
"effects": [
|
|
169
|
+
{
|
|
170
|
+
"id": "fetch-1",
|
|
171
|
+
"kind": "fetch",
|
|
172
|
+
"operation": "dispatch",
|
|
173
|
+
"capability": "fetch",
|
|
174
|
+
"resource": {
|
|
175
|
+
"kind": "literal",
|
|
176
|
+
"value": "https://users.example.test/users/123",
|
|
177
|
+
"origin": "https://users.example.test"
|
|
178
|
+
},
|
|
179
|
+
"grouped": false
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "fetch-2",
|
|
183
|
+
"kind": "fetch",
|
|
184
|
+
"operation": "dispatch",
|
|
185
|
+
"capability": "fetch",
|
|
186
|
+
"resource": {
|
|
187
|
+
"kind": "literal",
|
|
188
|
+
"value": "https://users.example.test/users/123",
|
|
189
|
+
"origin": "https://users.example.test"
|
|
190
|
+
},
|
|
191
|
+
"grouped": true
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"id": "fetch-3",
|
|
195
|
+
"kind": "fetch",
|
|
196
|
+
"operation": "dispatch",
|
|
197
|
+
"capability": "fetch",
|
|
198
|
+
"resource": {
|
|
199
|
+
"kind": "literal",
|
|
200
|
+
"value": "https://stats.example.test/users/123",
|
|
201
|
+
"origin": "https://stats.example.test"
|
|
202
|
+
},
|
|
203
|
+
"grouped": true
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"id": "fetch-4",
|
|
207
|
+
"kind": "fetch",
|
|
208
|
+
"operation": "dispatch",
|
|
209
|
+
"capability": "fetch",
|
|
210
|
+
"resource": {
|
|
211
|
+
"kind": "literal",
|
|
212
|
+
"value": "https://flags.example.test/users/123",
|
|
213
|
+
"origin": "https://flags.example.test"
|
|
214
|
+
},
|
|
215
|
+
"grouped": true
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "fetch-5",
|
|
219
|
+
"kind": "fetch",
|
|
220
|
+
"operation": "dispatch",
|
|
221
|
+
"capability": "fetch",
|
|
222
|
+
"resource": {
|
|
223
|
+
"kind": "literal",
|
|
224
|
+
"value": "https://users.example.test/users/123",
|
|
225
|
+
"origin": "https://users.example.test"
|
|
226
|
+
},
|
|
227
|
+
"grouped": true,
|
|
228
|
+
"groupKey": "user"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"id": "fetch-6",
|
|
232
|
+
"kind": "fetch",
|
|
233
|
+
"operation": "dispatch",
|
|
234
|
+
"capability": "fetch",
|
|
235
|
+
"resource": {
|
|
236
|
+
"kind": "literal",
|
|
237
|
+
"value": "https://stats.example.test/users/123",
|
|
238
|
+
"origin": "https://stats.example.test"
|
|
239
|
+
},
|
|
240
|
+
"grouped": true,
|
|
241
|
+
"groupKey": "stats"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"id": "fetch-7",
|
|
245
|
+
"kind": "fetch",
|
|
246
|
+
"operation": "dispatch",
|
|
247
|
+
"capability": "fetch",
|
|
248
|
+
"resource": {
|
|
249
|
+
"kind": "literal",
|
|
250
|
+
"value": "https://flags.example.test/users/123",
|
|
251
|
+
"origin": "https://flags.example.test"
|
|
252
|
+
},
|
|
253
|
+
"grouped": true,
|
|
254
|
+
"groupKey": "flags"
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
"continuations": [
|
|
258
|
+
{
|
|
259
|
+
"id": "continuation-1",
|
|
260
|
+
"kind": "single-fetch",
|
|
261
|
+
"effectIds": [
|
|
262
|
+
"fetch-1"
|
|
263
|
+
]
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"id": "continuation-2",
|
|
267
|
+
"kind": "fetch-group",
|
|
268
|
+
"effectIds": [
|
|
269
|
+
"fetch-2",
|
|
270
|
+
"fetch-3",
|
|
271
|
+
"fetch-4"
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"id": "continuation-3",
|
|
276
|
+
"kind": "parallel-group",
|
|
277
|
+
"effectIds": [
|
|
278
|
+
"fetch-5",
|
|
279
|
+
"fetch-6",
|
|
280
|
+
"fetch-7"
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
],
|
|
284
|
+
"providerLowering": {
|
|
285
|
+
"version": "pulse.canonical-provider-plan.v1",
|
|
286
|
+
"contractVersion": "pulse.canonical-provider-contract.v1",
|
|
287
|
+
"provider": "node",
|
|
288
|
+
"requirements": [
|
|
289
|
+
"fetch",
|
|
290
|
+
"response.json",
|
|
291
|
+
"response.text"
|
|
292
|
+
],
|
|
293
|
+
"operations": [
|
|
294
|
+
{
|
|
295
|
+
"id": "fetch-1",
|
|
296
|
+
"lowering": "node.fetch.dispatch",
|
|
297
|
+
"binding": "https://users.example.test"
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"id": "fetch-2",
|
|
301
|
+
"lowering": "node.fetch.dispatch",
|
|
302
|
+
"binding": "https://users.example.test"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"id": "fetch-3",
|
|
306
|
+
"lowering": "node.fetch.dispatch",
|
|
307
|
+
"binding": "https://stats.example.test"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"id": "fetch-4",
|
|
311
|
+
"lowering": "node.fetch.dispatch",
|
|
312
|
+
"binding": "https://flags.example.test"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"id": "fetch-5",
|
|
316
|
+
"lowering": "node.fetch.dispatch",
|
|
317
|
+
"binding": "https://users.example.test"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"id": "fetch-6",
|
|
321
|
+
"lowering": "node.fetch.dispatch",
|
|
322
|
+
"binding": "https://stats.example.test"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"id": "fetch-7",
|
|
326
|
+
"lowering": "node.fetch.dispatch",
|
|
327
|
+
"binding": "https://flags.example.test"
|
|
328
|
+
}
|
|
329
|
+
],
|
|
330
|
+
"providerSpecificUserland": false,
|
|
331
|
+
"providerSdkUserland": false,
|
|
332
|
+
"capabilityDiscoveryFromUserland": false
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
<!-- /pulse-doc-source -->
|
|
338
|
+
|
|
339
|
+
The source call becomes a canonical `fetch-1` effect. The code after the call becomes `continuation-1`. The provider plan then maps that effect to `node.fetch.dispatch`. Application source does not import Node APIs, a Fastly SDK, or provider objects.
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
## Keyed parallel lowering
|
|
343
|
+
|
|
344
|
+
`ctx.parallel({ ... })` is the explicit portable concurrency form. The compiler
|
|
345
|
+
requires one nonempty static object literal with fixed non-index string keys and
|
|
346
|
+
recognized Pulse effects as values. It rejects dynamic records, spreads, computed
|
|
347
|
+
keys, methods, accessors, arbitrary promises, reused effect roots, and nested
|
|
348
|
+
parallel groups with stable diagnostics.
|
|
349
|
+
|
|
350
|
+
Native lowering erases the call, emits its members into one canonical effect group,
|
|
351
|
+
records the source key on each effect site, suspends once, and reconstructs an
|
|
352
|
+
ordinary keyed object on resume. Property order remains authoritative even when
|
|
353
|
+
provider completion order differs. The JavaScript target executes the same source
|
|
354
|
+
through the request-owned shared effect adapter and implements the same all-settle,
|
|
355
|
+
keyed result and failure contract.
|
|
356
|
+
|
|
357
|
+
This does not remove Native implicit grouping. Adjacent independently lowerable
|
|
358
|
+
effects may still be grouped as a performance optimization, while separate awaits
|
|
359
|
+
executed directly as JavaScript retain ordinary sequential semantics. Authors use
|
|
360
|
+
`ctx.parallel` when concurrency itself must be portable.
|
|
361
|
+
|
|
362
|
+
## Synchronous logging lowering
|
|
363
|
+
|
|
364
|
+
`ctx.log.error`, `warn`, `info`, and `debug` share one numeric level contract
|
|
365
|
+
across all four provider/target modes. Logging is synchronous and is not added to
|
|
366
|
+
the effect or continuation graph.
|
|
367
|
+
|
|
368
|
+
For Native targets, the resolved flat profile `reporting` threshold participates
|
|
369
|
+
in the build identity. Disabled statements are erased during lowering and enabled
|
|
370
|
+
statements call the compact `pulse_log(level, ptr, len)` ABI. JavaScript targets
|
|
371
|
+
retain ordinary expression evaluation and filter through the same threshold at
|
|
372
|
+
runtime. Provider formatting and destination may differ; level identity,
|
|
373
|
+
enabled/disabled decisions, redaction, and request-contained sink failure are the
|
|
374
|
+
portable conformance surface.
|
|
375
|
+
|
|
376
|
+
## What the compiler owns
|
|
377
|
+
|
|
378
|
+
The whole-project compiler owns:
|
|
379
|
+
|
|
380
|
+
- discovering the authoritative workspace, loading `.pulse/config.ts`, and selecting one flat profile;
|
|
381
|
+
- parsing the canonical handler and its explicitly declared JSON schema sources;
|
|
382
|
+
- rejecting unsupported ambient authority, asynchronous syntax, and dynamic forms that cannot be represented safely;
|
|
383
|
+
- assigning stable effect and continuation identities;
|
|
384
|
+
- deriving capability requirements from the program;
|
|
385
|
+
- invoking trusted package-owned lowerers for supported facades;
|
|
386
|
+
- asking the provider contract to validate and map required operations;
|
|
387
|
+
- emitting canonical metadata used by `inspect`, `test`, `dev`, `compile`, and `build`;
|
|
388
|
+
- lowering the canonical program into the versioned provider-neutral native plan used by `pulse compile`.
|
|
389
|
+
|
|
390
|
+
The compiler does **not** make network requests, read deployment secrets, or invent provider bindings while analyzing source.
|
|
391
|
+
|
|
392
|
+
## Canonical lowering and package-owned lowering
|
|
393
|
+
|
|
394
|
+
Pulse has two related lowering paths.
|
|
395
|
+
|
|
396
|
+
### Canonical handler lowering
|
|
397
|
+
|
|
398
|
+
Calls on `PulseContext`, such as `ctx.fetch`, `ctx.config.get`, `ctx.secret.get`, `ctx.kv.get`, and response constructors, are part of the canonical API contract. The central compiler recognizes them and emits canonical operations.
|
|
399
|
+
|
|
400
|
+
### Package-owned lowering
|
|
401
|
+
|
|
402
|
+
A supported package can own a normal application root, manifest, contract
|
|
403
|
+
mapping, and compiler builder. Assets is the converged example:
|
|
404
|
+
`@pulse-compute/assets` executes as the real JavaScript package and the same
|
|
405
|
+
supported `assets.lookup(ctx, ...)` shape lowers into `pulse.assets` operations
|
|
406
|
+
on Native targets. The package keeps its domain-specific validation rules; the
|
|
407
|
+
generic loader handles reachable-graph discovery and trusted invocation. Older
|
|
408
|
+
`/pulsewasm` imports are isolated in the
|
|
409
|
+
[compatibility migration guide](../guides/compatibility-imports.md).
|
|
410
|
+
|
|
411
|
+
In Pulse `1.0.0-beta.2`, package-owned builders must declare `compiler.trust: 'first-party'` and ship in the synchronized release set. This is an internal contributor mechanism, not a general third-party plugin API. See [Add a first-party package-owned lowerer](../contributing/adding-first-party-lowerer.md).
|
|
412
|
+
|
|
413
|
+
## Provider lowering is a second contract
|
|
414
|
+
|
|
415
|
+
Canonical effects describe **what** the program needs. A provider lowering plan describes **how** a selected provider realizes those needs.
|
|
416
|
+
|
|
417
|
+
For example:
|
|
418
|
+
|
|
419
|
+
```text
|
|
420
|
+
canonical capability: fetch
|
|
421
|
+
Node lowering: node.fetch.dispatch
|
|
422
|
+
Fastly lowering: fastly.fetch.dispatch
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Provider validation is fail-closed. A build or execution command stops with a stable diagnostic when a required capability or binding is missing rather than silently substituting provider-specific behavior. See [Contracts and providers](./contracts-and-providers.md).
|
|
426
|
+
|
|
427
|
+
## Why source restrictions exist
|
|
428
|
+
|
|
429
|
+
The compiler accepts a deliberately bounded TypeScript subset because every accepted construct needs deterministic lowering and equivalent provider behavior. In particular:
|
|
430
|
+
|
|
431
|
+
- managed handlers are async-shaped; only trusted Pulse awaits lower natively, while arbitrary Promise construction and arbitrary library awaits remain outside native eligibility;
|
|
432
|
+
- ambient `process.env`, global `fetch`, timers, and randomness do not become hidden authority;
|
|
433
|
+
- package facade calls use statically recognizable imports and supported argument shapes;
|
|
434
|
+
- opaque bodies can cross the boundary but cannot be inspected or transformed in userland;
|
|
435
|
+
- provider SDK objects never enter handler scope.
|
|
436
|
+
|
|
437
|
+
These restrictions are compatibility guarantees, not temporary parser
|
|
438
|
+
accidents. The complete boundary is in the
|
|
439
|
+
[Beta scope](../preview-scope.md).
|
|
440
|
+
|
|
441
|
+
## Reading `pulse inspect`
|
|
442
|
+
|
|
443
|
+
Use `pulse inspect` before target compilation when you need to answer:
|
|
444
|
+
|
|
445
|
+
- Is the selected target's core execution lane ready, and is the target generally available under its declared support policy?
|
|
446
|
+
- Is this project eligible for the selected target's implemented commands? If not, which stable capability or package reasons and owners apply?
|
|
447
|
+
- Which target descriptor, command matrix, application graph, loader observation, and parity evidence produced that decision?
|
|
448
|
+
- Which capabilities did this source require?
|
|
449
|
+
- Which source calls became effects?
|
|
450
|
+
- Which effects are grouped?
|
|
451
|
+
- Which continuations resume after them?
|
|
452
|
+
- What native plan hash and portable Wasm import/export surface will `pulse compile` produce?
|
|
453
|
+
- Which provider lowering and deployment binding was selected?
|
|
454
|
+
- Did package-owned lowering run?
|
|
455
|
+
- Is any provider-specific userland entering the program?
|
|
456
|
+
|
|
457
|
+
For a Node or Fastly JavaScript profile, `provider.targetSupport` is separate from
|
|
458
|
+
the provider-neutral compiler plan. General availability is defined as full
|
|
459
|
+
target support: both current declarations report `coreExecutionReady: true`,
|
|
460
|
+
`fullTargetSupportReady: true`, and `generalAvailable: true` because every
|
|
461
|
+
declared gate is satisfied. The project-level status (`eligible`, `pending`, or
|
|
462
|
+
`blocked`) is still derived from the JavaScript application plan, reachable
|
|
463
|
+
capability, provider-requirement, and package evidence. Loader state is a
|
|
464
|
+
separate observation: it changes the `observationHash`, not the static
|
|
465
|
+
`evidenceHash` or project eligibility. Native eligibility independently controls
|
|
466
|
+
whether the provider-neutral `compile` command can lower the project.
|
|
467
|
+
`automaticFallback` remains false; a Native compiler observation in the same
|
|
468
|
+
inspection does not change the selected JavaScript target.
|
|
469
|
+
|
|
470
|
+
A JavaScript-configured `pulse compile` still emits provider-neutral Pulse-owned
|
|
471
|
+
Wasm. Its manifest records the configured target and plan-only eligibility
|
|
472
|
+
evidence. `pulse build` is separate: Node JavaScript emits a deterministic
|
|
473
|
+
executable CommonJS package, while Fastly JavaScript emits a deterministic ESM
|
|
474
|
+
source/deployment closure with exact `esbuild` and `@fastly/js-compute` pins. The
|
|
475
|
+
offline release gate compiles that Fastly closure to a runtime Wasm and binds the
|
|
476
|
+
artifact by SHA-256; it does not deploy or publish it. Neither JavaScript build
|
|
477
|
+
emits a Pulse Native artifact or falls back to Native.
|
|
478
|
+
|
|
479
|
+
A healthy canonical plan reports all three provider isolation flags as `false`:
|
|
480
|
+
|
|
481
|
+
```json
|
|
482
|
+
{
|
|
483
|
+
"providerSpecificUserland": false,
|
|
484
|
+
"providerSdkUserland": false,
|
|
485
|
+
"capabilityDiscoveryFromUserland": false
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## Related documentation
|
|
490
|
+
|
|
491
|
+
- [Effects and continuations](./effects-and-continuations.md)
|
|
492
|
+
- [Structured and opaque bodies](./bodies.md)
|
|
493
|
+
- [Contracts and providers](./contracts-and-providers.md)
|
|
494
|
+
- [CLI reference](../reference/cli.md#pulse-inspect)
|
|
495
|
+
- [Diagnostics](../reference/diagnostics.md)
|
|
496
|
+
- [Architecture overview](../architecture/overview.md)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Contracts and providers
|
|
2
|
+
|
|
3
|
+
Pulse separates application semantics from provider realization. The compiler emits canonical contracts; a provider declares supported capabilities and maps canonical operations to its runtime and build target.
|
|
4
|
+
|
|
5
|
+
This separation is why the same handler can run through the Node conformance provider and compile to a Fastly Compute target without importing either provider’s SDK.
|
|
6
|
+
|
|
7
|
+
## Contract layers
|
|
8
|
+
|
|
9
|
+
| Layer | Owner | Purpose |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Canonical handler API | `@pulse-compute/runtime` | Defines portable request, response, fetch, config, secret, and KV semantics. |
|
|
12
|
+
| Canonical compiler program | Pulse compiler | Records values, branches, effects, continuations, schemas, and capability requirements. |
|
|
13
|
+
| Package effect contract | Owning package + shared contracts | Defines narrow package-owned operations such as `pulse.grip`. |
|
|
14
|
+
| Provider descriptor | Provider package | Declares capabilities, operation lowering names, runtime identity, and build target. |
|
|
15
|
+
| Provider lowering plan | Shared provider contract | Verifies requirements and binds every canonical operation to provider behavior. |
|
|
16
|
+
| Provider toolchain boundary | Shared provider contract + selected provider package | Validates the exact driver/target surface and projects versioned planning, target invocation, and result envelopes. |
|
|
17
|
+
| Local conformance runtime | Provider package | Executes canonical programs for `test` and `dev`. |
|
|
18
|
+
| Deployable target builder | Provider package | Emits provider source and compiled target artifacts for `build`. |
|
|
19
|
+
|
|
20
|
+
No layer may infer capability support from arbitrary application imports or provider-specific objects.
|
|
21
|
+
|
|
22
|
+
## A package-owned contract in source
|
|
23
|
+
|
|
24
|
+
Packages expose ordinary application APIs while retaining package-owned effect identities and provider requirements. Assets is the converged example:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { assets } from '@pulse-compute/assets'
|
|
28
|
+
|
|
29
|
+
const found = await assets.lookup(ctx, 'public', '/app.js')
|
|
30
|
+
return assets.respond(found)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
JavaScript executes the package implementation through the request-bound package bridge. Native compilation recognizes the same root call and emits the `pulse.assets` package operation. The package owns payload validation and result semantics; the selected provider owns lookup realization and opaque response delivery.
|
|
34
|
+
|
|
35
|
+
GRIP uses the same split. `isWebSocket`, `subscribe`, and `handoff` are pure HTTP helpers; only `grip.broadcast(ctx, message)` is a provider effect. Bounded broadcast realization is provider-owned and does not imply Pulse-owned WebSocket lifecycle.
|
|
36
|
+
|
|
37
|
+
`pulse inspect --json` reports the reachable package contract, selected target support, emitted package effects, provider requirements, and any pending realization reason separately.
|
|
38
|
+
|
|
39
|
+
## Provider selection
|
|
40
|
+
|
|
41
|
+
Pulse `1.0.0-beta.2` has three provider driver identifiers:
|
|
42
|
+
|
|
43
|
+
| Provider | Local execution | Deployable build | Purpose |
|
|
44
|
+
|---|---:|---:|---|
|
|
45
|
+
| `node` | Yes | Yes, as Node output | Default local and portable conformance path. |
|
|
46
|
+
| `fastly` | Yes, through local conformance | Yes, Fastly Compute | Typed bindings plus source and `bin/main.wasm` generation. |
|
|
47
|
+
| `none` | No | Canonical compile output only | Inspect or compile without an executable provider. |
|
|
48
|
+
|
|
49
|
+
Provider selection is normalized from the active `.pulse/config.ts` profile. Public provider overrides are not supported, and `none` cannot drive `pulse test` or `pulse dev`.
|
|
50
|
+
|
|
51
|
+
## Capability validation
|
|
52
|
+
|
|
53
|
+
The compiler derives requirements from source. The provider descriptor lists what it implements. The shared contract rejects the plan when any requirement is absent.
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
source operation
|
|
57
|
+
→ canonical capability
|
|
58
|
+
→ provider descriptor lookup
|
|
59
|
+
→ provider lowering + binding
|
|
60
|
+
→ execution/build
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This catches unsupported behavior before a request reaches a provider runtime. Relevant diagnostics include:
|
|
64
|
+
|
|
65
|
+
- [`PULSE_PROVIDER_CAPABILITY_MISSING`](../reference/diagnostics.md#pulse-provider-capability-missing);
|
|
66
|
+
- [`PULSE_PROVIDER_CAPABILITY_UNSUPPORTED`](../reference/diagnostics.md#pulse-provider-capability-unsupported);
|
|
67
|
+
- [`PULSE_FASTLY_BACKEND_REQUIRED`](../reference/diagnostics.md#pulse-fastly-backend-required);
|
|
68
|
+
- [`PULSE_FASTLY_KV_BINDINGS_INVALID`](../reference/diagnostics.md#pulse-fastly-kv-bindings-invalid).
|
|
69
|
+
|
|
70
|
+
## Bindings are configuration, not userland APIs
|
|
71
|
+
|
|
72
|
+
A Fastly provider configuration maps logical canonical resources to deployment bindings:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { defineConfig } from '@pulse-compute/cli'
|
|
76
|
+
import { fastly } from '@pulse-compute/provider-fastly'
|
|
77
|
+
|
|
78
|
+
export default defineConfig({
|
|
79
|
+
provider: fastly({
|
|
80
|
+
configStore: 'pulse_config',
|
|
81
|
+
secretStore: 'pulse_secrets',
|
|
82
|
+
kv: { sessions: 'sessions_store' },
|
|
83
|
+
backends: { 'https://api.example.com': 'api_backend' },
|
|
84
|
+
dynamicBackends: false,
|
|
85
|
+
}),
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Handler source still calls `ctx.config.get`, `ctx.secret.get`, `ctx.kv.get`, or `ctx.fetch`. It never imports a Fastly store, backend, request, response, or SDK type.
|
|
90
|
+
|
|
91
|
+
## Local conformance versus deployment reality
|
|
92
|
+
|
|
93
|
+
`pulse test` and `pulse dev` execute through provider-owned local conformance runtimes using configured fixtures and values. They prove the canonical program and provider mapping, not the health of an external deployment.
|
|
94
|
+
|
|
95
|
+
For Fastly, `pulse build` additionally emits a real Compute target. The explicit environment-dependent reality gate executes it through an inspected Fastly CLI or direct Viceroy launcher and real HTTP requests; remote deployment remains separate. See [Fastly deployment candidates](../guides/deploying-fastly.md), [Fastly package guide](../packages/provider-fastly.md), and [Release acceptance](../maintainers/release-acceptance.md).
|
|
96
|
+
|
|
97
|
+
## Current extension boundary
|
|
98
|
+
|
|
99
|
+
There is no automatic provider plugin registry in `1.0.0-beta.2`. Configuration
|
|
100
|
+
selects a bare host ID, internal `none`, or an exact scoped project-installed
|
|
101
|
+
package name. Bare IDs resolve by the `@pulse-compute/provider-<id>` convention.
|
|
102
|
+
A package is loadable only when selected and when it exports the versioned
|
|
103
|
+
`./toolchain` contract; Pulse does not scan, self-register, or fall back to
|
|
104
|
+
another provider. Adding a host to the built-in support catalog requires a
|
|
105
|
+
coordinated source, release, documentation, and acceptance change.
|
|
106
|
+
|
|
107
|
+
Likewise, package-owned lowerer manifests are executable only when they are trusted first-party packages. External npm packages cannot self-register compiler code merely by shipping a manifest.
|
|
108
|
+
|
|
109
|
+
Contributor workflows:
|
|
110
|
+
|
|
111
|
+
- [Pulse-aware package authoring](../contributing/pulse-aware-packages.md)
|
|
112
|
+
- [Add a first-party package-owned lowerer](../contributing/adding-first-party-lowerer.md)
|
|
113
|
+
- [Add a core provider](../contributing/adding-core-provider.md)
|
|
114
|
+
|
|
115
|
+
The provider toolchain page defines only its narrow, explicit bootstrap
|
|
116
|
+
contract. It does not define general plugin discovery or widen the
|
|
117
|
+
first-party-only lowerer compatibility promise.
|
|
118
|
+
|
|
119
|
+
## Toolchain and target boundary
|
|
120
|
+
|
|
121
|
+
Loading `./toolchain` is only the bootstrap. The export, driver, selected target,
|
|
122
|
+
planning input, target invocation, Native artifact, JavaScript package result,
|
|
123
|
+
and provider result each carry an exact contract identity. Driver creation
|
|
124
|
+
receives no compiler callback or option bag. Shared validation rejects unknown
|
|
125
|
+
versions and fields, missing required methods, malformed target descriptors,
|
|
126
|
+
and supported Native targets without a final-Wasm policy before provider code
|
|
127
|
+
can generate output.
|
|
128
|
+
|
|
129
|
+
The compiler composition root projects only:
|
|
130
|
+
|
|
131
|
+
- the canonical Native or JavaScript application plan;
|
|
132
|
+
- the canonical provider lowering plan and explicit capability, binding, and
|
|
133
|
+
package requirements;
|
|
134
|
+
- the exact selected target and normalized provider configuration;
|
|
135
|
+
- explicit project root, output root, profile, synchronized package versions,
|
|
136
|
+
and optimization posture;
|
|
137
|
+
- for Native, copied Wasm bytes plus the narrowed artifact manifest,
|
|
138
|
+
realization contributions, and final guest-audit authorization.
|
|
139
|
+
|
|
140
|
+
Compiler ASTs and services, builders, raw lowerer output, mutable manifests,
|
|
141
|
+
diagnostic collectors, caches, and unrelated environment/profile records do not
|
|
142
|
+
cross the seam. Target results are normalized into data-only versioned
|
|
143
|
+
envelopes before command orchestration reads them. Guest-linked packaging is
|
|
144
|
+
accepted only when the packaged artifact hash equals the final authorized audit
|
|
145
|
+
hash. Failure remains attached to the selected provider and target; Pulse does
|
|
146
|
+
not attempt another target or provider.
|
|
147
|
+
|
|
148
|
+
## Related documentation
|
|
149
|
+
|
|
150
|
+
- [Compilation and lowering](./compilation-and-lowering.md)
|
|
151
|
+
- [Effects and continuations](./effects-and-continuations.md)
|
|
152
|
+
- [Package support policy](../packages/README.md)
|
|
153
|
+
- [Managed handler TypeScript and JavaScript](../reference/handler-authoring.md)
|
|
154
|
+
- [Provider and target compatibility](../reference/compatibility-matrix.md)
|
|
155
|
+
- [Node build and execution](../guides/deploying-node.md)
|
|
156
|
+
- [Fastly deployment candidates](../guides/deploying-fastly.md)
|
|
157
|
+
- [Project configuration](../reference/project-config.md)
|
|
158
|
+
- [Fastly capabilities](../guides/fastly-capabilities.md)
|