@pulse-compute/cli 0.0.0 → 1.0.0-beta.1
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 +539 -0
- package/CHANGELOG.md +47 -0
- package/README.md +70 -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 +381 -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 +243 -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 +62 -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 +184 -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 +84 -0
- package/docs/maintainers/maintenance-policy.json +778 -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 +250 -0
- package/docs/maintainers/release-manifest.md +88 -0
- package/docs/maintainers/repository-setup.md +145 -0
- package/docs/maintainers/scope-policy.md +93 -0
- package/docs/maintainers/support-and-triage.md +74 -0
- package/docs/maintainers/testing.md +288 -0
- package/docs/packages/README.md +59 -0
- package/docs/packages/assets.md +85 -0
- package/docs/packages/cli.md +190 -0
- package/docs/packages/crypto.md +71 -0
- package/docs/packages/entities.md +129 -0
- package/docs/packages/grip.md +82 -0
- package/docs/packages/implementation-packages.md +113 -0
- package/docs/packages/jwt.md +55 -0
- package/docs/packages/provider-fastly.md +217 -0
- package/docs/packages/pulse.md +159 -0
- package/docs/packages/runtime.md +256 -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 +14 -0
- package/docs/reference/environment.md +347 -0
- package/docs/reference/handler-authoring.md +264 -0
- package/docs/reference/project-config.md +554 -0
- package/docs/reference/project-config.schema.json +918 -0
- package/docs/reference/release-manifest.json +427 -0
- package/docs/reference/shell-completion.md +23 -0
- package/documentation-site.json +661 -0
- package/documentation-versions.json +14 -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 +918 -0
- package/release-manifest.json +427 -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 +2930 -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,243 @@
|
|
|
1
|
+
# Effects and continuations
|
|
2
|
+
|
|
3
|
+
A Pulse handler is written as async-shaped TypeScript. Host operations such as fetch, KV, secrets, and GRIP are awaited in source, but Native targets do not link a JavaScript Promise runtime. The compiler erases the managed async wrapper and trusted Pulse awaits into **effects** and the code that follows into **continuations**. JavaScript targets execute the same source through the live runtime and provider-owned effect adapter.
|
|
4
|
+
|
|
5
|
+
Application authors do not create or resume continuation objects. They write canonical TypeScript; the compiler and provider runtime own the lifecycle.
|
|
6
|
+
|
|
7
|
+
## Effect
|
|
8
|
+
|
|
9
|
+
An effect is an explicit request for host authority. Each effect has an identity, capability, operation, resource, source position, and result shape. Common examples include:
|
|
10
|
+
|
|
11
|
+
- dispatching an outbound fetch;
|
|
12
|
+
- reading config or a secret;
|
|
13
|
+
- getting or putting a KV value;
|
|
14
|
+
- accepting one outbound event through `ctx.emit`;
|
|
15
|
+
- declaring a GRIP channel;
|
|
16
|
+
- holding or publishing through GRIP.
|
|
17
|
+
|
|
18
|
+
Pure value construction, branching, property reads, and arithmetic are not effects.
|
|
19
|
+
|
|
20
|
+
## Continuation
|
|
21
|
+
|
|
22
|
+
A continuation identifies the compiled work that can proceed after one effect—or one effect group—settles. It carries no application-accessible provider object. The runtime resumes it with a normalized result or failure.
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
compiled values
|
|
26
|
+
│
|
|
27
|
+
├── effect request ──► provider/runtime
|
|
28
|
+
│ │
|
|
29
|
+
└── continuation ◄────────┘ normalized result or failure
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
A continuation is single-use and time-bounded. Expired or duplicate resume attempts fail with stable diagnostics such as [`PULSE_CONTINUATION_EXPIRED`](../reference/diagnostics.md#pulse-continuation-expired) and [`PULSE_CONTINUATION_DOUBLE_RESUME`](../reference/diagnostics.md#pulse-continuation-double-resume).
|
|
33
|
+
|
|
34
|
+
## Independent effects form a group
|
|
35
|
+
|
|
36
|
+
The multi-fetch example declares three independent fetch values before consuming any of them:
|
|
37
|
+
|
|
38
|
+
<!-- pulse-doc-source: examples/03-fetch-composition/src/index.ts -->
|
|
39
|
+
```ts
|
|
40
|
+
import { Pulse } from '@pulse-compute/pulse'
|
|
41
|
+
|
|
42
|
+
interface User {
|
|
43
|
+
id: number
|
|
44
|
+
name: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface Stats {
|
|
48
|
+
score: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface Flags {
|
|
52
|
+
enabled: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const app = new Pulse({ auto: true })
|
|
56
|
+
|
|
57
|
+
// One structured origin.
|
|
58
|
+
app.get('/user', async (ctx) => {
|
|
59
|
+
const user = await ctx
|
|
60
|
+
.fetch('https://users.example.test/users/123')
|
|
61
|
+
.json<User>()
|
|
62
|
+
return ctx.json({ found: true, user })
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// Multiple origins with explicit sequential awaits.
|
|
66
|
+
app.get('/user-summary', async (ctx) => {
|
|
67
|
+
const user = await ctx
|
|
68
|
+
.fetch('https://users.example.test/users/123')
|
|
69
|
+
.json<User>()
|
|
70
|
+
const stats = await ctx
|
|
71
|
+
.fetch('https://stats.example.test/users/123')
|
|
72
|
+
.json<Stats>()
|
|
73
|
+
const flags = await ctx
|
|
74
|
+
.fetch('https://flags.example.test/users/123')
|
|
75
|
+
.json<Flags>()
|
|
76
|
+
return ctx.json({
|
|
77
|
+
id: user.id,
|
|
78
|
+
name: user.name,
|
|
79
|
+
score: stats.score,
|
|
80
|
+
enabled: flags.enabled,
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Multiple origins with explicit portable concurrency.
|
|
85
|
+
app.get('/user-summary-parallel', async (ctx) => {
|
|
86
|
+
const { user, stats, flags } = await ctx.parallel({
|
|
87
|
+
user: ctx.fetch('https://users.example.test/users/123').json<User>(),
|
|
88
|
+
stats: ctx.fetch('https://stats.example.test/users/123').json<Stats>(),
|
|
89
|
+
flags: ctx.fetch('https://flags.example.test/users/123').json<Flags>(),
|
|
90
|
+
})
|
|
91
|
+
return ctx.json({
|
|
92
|
+
id: user.id,
|
|
93
|
+
name: user.name,
|
|
94
|
+
score: stats.score,
|
|
95
|
+
enabled: flags.enabled,
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
export default app
|
|
100
|
+
```
|
|
101
|
+
<!-- /pulse-doc-source -->
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pulse inspect examples/03-fetch-composition --json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The compiler can issue those fetches as one effect group because none depends on another result. The continuation becomes eligible only after the group settles.
|
|
108
|
+
|
|
109
|
+
Grouping provides concurrency without linking a general Promise runtime into Native artifacts. Source declaration order remains deterministic for identities and result binding, but providers may perform independent operations concurrently.
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## Explicit portable groups with `ctx.parallel`
|
|
113
|
+
|
|
114
|
+
Native Pulse may discover independent adjacent effects and place them in one group
|
|
115
|
+
for performance. Direct JavaScript execution does not rewrite ordinary `await`
|
|
116
|
+
semantics, so separate awaits remain sequential there. Use `ctx.parallel({ ... })`
|
|
117
|
+
when concurrency is required application behavior across targets:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const { profile, flags } = await ctx.parallel({
|
|
121
|
+
profile: ctx.fetch(profileUrl).json<Profile>(),
|
|
122
|
+
flags: ctx.fetch(flagsUrl).json<Flags>(),
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The first contract accepts a nonempty inline object literal with fixed,
|
|
127
|
+
non-index string keys and Pulse effect expressions as values. Property order owns
|
|
128
|
+
dispatch identity, trace order, deterministic primary-failure selection, and keyed
|
|
129
|
+
result reconstruction. Providers may complete members in any order, but every
|
|
130
|
+
member settles before the continuation resumes.
|
|
131
|
+
|
|
132
|
+
On JavaScript, all members execute through one execution-owned shared effect adapter.
|
|
133
|
+
On Native, lowering erases `ctx.parallel`, emits the member operations into one
|
|
134
|
+
canonical effect group, and reconstructs the ordinary keyed result object after
|
|
135
|
+
one continuation. Arrays, spreads, computed keys, dynamic records, arbitrary
|
|
136
|
+
promises, reused roots, and nested groups are outside the initial portable shape.
|
|
137
|
+
|
|
138
|
+
## One-way outbound events
|
|
139
|
+
|
|
140
|
+
HTTP and event handlers share the direct JavaScript `ctx.emit` effect:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
await ctx.emit('device.led.set', {
|
|
144
|
+
schema: 'events.DeviceLedSet',
|
|
145
|
+
payload: { enabled: true },
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
await ctx.emit('system.tick', { schema: null })
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The event type and schema are static compiler inputs. A non-null schema requires
|
|
152
|
+
`payload`; `schema: null` forbids it. The runtime schema-validates and detaches
|
|
153
|
+
the canonical frame before dispatch, and the effect resolves only when the
|
|
154
|
+
configured host adapter accepts that frame. It returns `undefined`: there is no
|
|
155
|
+
delivery receipt, correlation ID, handler result, retry guarantee, or local
|
|
156
|
+
loopback.
|
|
157
|
+
|
|
158
|
+
`ctx.emit` may be awaited directly or used as a fresh member of an awaited
|
|
159
|
+
`ctx.parallel` group. It consumes the same execution effect budget, cancellation,
|
|
160
|
+
redaction, and disposal boundary as fetch, config, secret, and KV effects. The
|
|
161
|
+
JavaScript dispatch uses the execution-owned effect adapter. Native lowering
|
|
162
|
+
emits the same canonical `event.emit` descriptor, suspends on the ordinary
|
|
163
|
+
continuation protocol, and resumes after host acceptance; it adds no JavaScript,
|
|
164
|
+
Promise, or Asyncify runtime. The Node provider has a bounded, invocation-scoped
|
|
165
|
+
FIFO reference adapter for direct JavaScript/Native ingress and exact accepted
|
|
166
|
+
frame evidence. Its outbound ledger never loops back into ingress, and its
|
|
167
|
+
success still promises no delivery, persistence, retry, receipt, or public
|
|
168
|
+
event-bus behavior. No other provider realization or automatic fallback is
|
|
169
|
+
claimed.
|
|
170
|
+
|
|
171
|
+
One-way acceptance is also the recursion boundary. Pulse exposes no
|
|
172
|
+
`ctx.call`, generic request/reply effect, automatic local dispatch, or reserved
|
|
173
|
+
call opcode. A future reflexive mechanism cannot be inferred from `ctx.emit`;
|
|
174
|
+
it requires a separately specified host and lifecycle. See [Static events and
|
|
175
|
+
outbound emission](../guides/events.md).
|
|
176
|
+
|
|
177
|
+
## Dependencies split continuation stages
|
|
178
|
+
|
|
179
|
+
When later control flow or a later host operation depends on an earlier result, the compiler creates another continuation stage. For example, inspecting a first response and choosing a second URL is dependent work; it cannot be moved into the first independent group.
|
|
180
|
+
|
|
181
|
+
The rule is semantic rather than stylistic:
|
|
182
|
+
|
|
183
|
+
- independent operations may share one group;
|
|
184
|
+
- operations that need prior values start in a later continuation;
|
|
185
|
+
- pure computation between effects stays in the continuation that owns it;
|
|
186
|
+
- returning a final response completes the request lifecycle.
|
|
187
|
+
|
|
188
|
+
Use `pulse inspect --json` to see the actual grouping rather than inferring it from line spacing.
|
|
189
|
+
|
|
190
|
+
## Failure behavior
|
|
191
|
+
|
|
192
|
+
An effect failure is normalized into a stable public error. Examples include:
|
|
193
|
+
|
|
194
|
+
- [`PULSE_FETCH_TIMEOUT`](../reference/diagnostics.md#pulse-fetch-timeout);
|
|
195
|
+
- [`PULSE_FETCH_NETWORK`](../reference/diagnostics.md#pulse-fetch-network);
|
|
196
|
+
- [`PULSE_PROVIDER_CAPABILITY_UNSUPPORTED`](../reference/diagnostics.md#pulse-provider-capability-unsupported);
|
|
197
|
+
- [`PULSE_FASTLY_BACKEND_REQUIRED`](../reference/diagnostics.md#pulse-fastly-backend-required).
|
|
198
|
+
|
|
199
|
+
For an independent group, the runtime settles the group and reports the normalized failures through the owning continuation boundary. Userland does not receive partially live provider handles or background tasks that can outlive the request.
|
|
200
|
+
|
|
201
|
+
## Timeouts have two scopes
|
|
202
|
+
|
|
203
|
+
A fetch can have an operation timeout through `PulseFetchInit.timeoutMs`. The request runtime also owns a continuation lifetime configured by the execution environment or test case. These are different controls:
|
|
204
|
+
|
|
205
|
+
- the operation timeout bounds one host operation;
|
|
206
|
+
- the continuation TTL bounds how long compiled execution may remain suspended before resumption is rejected.
|
|
207
|
+
|
|
208
|
+
Project tests can set `continuationTtlMs` in a case inside the dedicated `tests/pulse.harness.ts` module for deterministic failure coverage.
|
|
209
|
+
|
|
210
|
+
## Managed async, not arbitrary async
|
|
211
|
+
|
|
212
|
+
Write managed handlers with `async` and await trusted Pulse effects. On Native targets, the compiler erases that notation into effects and continuations. On JavaScript targets, the live runtime executes the same async-shaped handler normally.
|
|
213
|
+
|
|
214
|
+
Arbitrary Promise construction, ambient asynchronous APIs, and unrecognized library awaits remain outside Native eligibility. They must not be mistaken for Pulse effects or silently trigger target fallback.
|
|
215
|
+
|
|
216
|
+
This design keeps:
|
|
217
|
+
|
|
218
|
+
- effects visible to the compiler;
|
|
219
|
+
- provider capability checks complete before execution;
|
|
220
|
+
- failures and timeouts normalized;
|
|
221
|
+
- request completion bounded;
|
|
222
|
+
- Node and Fastly behavior comparable.
|
|
223
|
+
|
|
224
|
+
## Inspect and test the lifecycle
|
|
225
|
+
|
|
226
|
+
A useful workflow is:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pulse inspect ./my-app --json
|
|
230
|
+
pulse test ./my-app --json
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`inspect` proves compilation, effects, grouping, continuations, and provider lowering without executing application test cases. `test` executes configured cases through the selected provider’s local conformance runtime.
|
|
234
|
+
|
|
235
|
+
## Related documentation
|
|
236
|
+
|
|
237
|
+
- [Managed handler TypeScript and JavaScript](../reference/handler-authoring.md)
|
|
238
|
+
- [Provider and target compatibility](../reference/compatibility-matrix.md)
|
|
239
|
+
- [Compilation and lowering](./compilation-and-lowering.md)
|
|
240
|
+
- [Structured and opaque bodies](./bodies.md)
|
|
241
|
+
- [Fetching and composing data](../guides/fetching-and-composition.md)
|
|
242
|
+
- [Project configuration](../reference/project-config.md)
|
|
243
|
+
- [Troubleshooting](../guides/troubleshooting.md)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Entity engine, adapters, and facades
|
|
2
|
+
|
|
3
|
+
Pulse Entities separates governed operation semantics from the protocol that
|
|
4
|
+
selects an operation and from any facade that presents those operations to an
|
|
5
|
+
external ecosystem.
|
|
6
|
+
|
|
7
|
+
## The four layers
|
|
8
|
+
|
|
9
|
+
| Layer | Owns | Does not own |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Entity engine | Static operation names, schema declarations, handler identity, governed execution, deterministic catalog | HTTP/JSON-RPC syntax, MCP lifecycle, provider SDKs |
|
|
12
|
+
| JSON-RPC adapter | Bounded envelope parsing, request/notification selection, named params, stable JSON-RPC result/error framing | Handler semantics, schema registry authority, direct provider access |
|
|
13
|
+
| Tools or MCP facade | Catalog projection, external tool naming/annotations, transport-facing invocation policy | Direct handler calls, runtime registry mutation, hidden Pulse authority |
|
|
14
|
+
| Worker/event adapter (future) | A separately specified event/request binding and response/acknowledgement mapping | Changes to entity declarations or handler effect authority |
|
|
15
|
+
|
|
16
|
+
The entity engine is therefore not a server. `EntityRouter` records a static
|
|
17
|
+
registry and binds it to one governed request. `jsonRpc()` is the only current
|
|
18
|
+
adapter. A facade may read the emitted `entities-catalog.json` and construct a
|
|
19
|
+
request for that adapter, but it cannot enumerate live handler functions or
|
|
20
|
+
call them outside a Pulse request context.
|
|
21
|
+
|
|
22
|
+
## Why the catalog is protocol-neutral
|
|
23
|
+
|
|
24
|
+
The catalog contains operation names, schema IDs, static metadata, and target
|
|
25
|
+
eligibility. It excludes handlers, raw requests, request IDs, provider objects,
|
|
26
|
+
secrets, and runtime values. That makes it suitable for build-time discovery
|
|
27
|
+
without making discovery a new ambient runtime authority.
|
|
28
|
+
|
|
29
|
+
The inspection artifact is richer and maintainer-facing: it adds redacted
|
|
30
|
+
handler-effect summaries and measured target evidence. Neither artifact is a
|
|
31
|
+
mutable service registry.
|
|
32
|
+
|
|
33
|
+
## Authority flow
|
|
34
|
+
|
|
35
|
+
1. Project configuration owns declared schemas and provider selection.
|
|
36
|
+
2. The trusted first-party lowerer extracts entity declarations without
|
|
37
|
+
evaluating application code.
|
|
38
|
+
3. The selected adapter owns bounded protocol decoding and operation selection.
|
|
39
|
+
4. The selected schema codecs decode only the chosen input and encode only the
|
|
40
|
+
chosen output.
|
|
41
|
+
5. The handler receives the normal request-owned `PulseContext`; every fetch,
|
|
42
|
+
config, secret, KV, log, or package effect remains governed by that context.
|
|
43
|
+
6. The adapter frames completion. A facade sees only the framed boundary.
|
|
44
|
+
|
|
45
|
+
No layer may silently choose another target. Native ineligibility fails closed;
|
|
46
|
+
it never authorizes JavaScript fallback.
|
|
47
|
+
|
|
48
|
+
## MCP/tools boundary
|
|
49
|
+
|
|
50
|
+
The [Entities tools example](../../examples/10-entities-tools/) demonstrates a
|
|
51
|
+
small facade that projects static metadata and invokes JSON-RPC. It is
|
|
52
|
+
intentionally not a complete MCP implementation. Pulse runtime core does not
|
|
53
|
+
gain SSE, sessions, tasks, resources, prompts, sampling, authorization,
|
|
54
|
+
transport negotiation, or MCP lifecycle state from this candidate.
|
|
55
|
+
|
|
56
|
+
A complete MCP server could be built outside the entity engine if it preserves
|
|
57
|
+
these rules: static discovery comes from an emitted catalog, invocation crosses
|
|
58
|
+
a governed adapter boundary, external protocol state stays outside runtime
|
|
59
|
+
core, and MCP behavior never becomes a source of provider or schema authority.
|
|
60
|
+
|
|
61
|
+
## Future adapters
|
|
62
|
+
|
|
63
|
+
A Worker, queue, scheduled-event, or other event adapter would be a new
|
|
64
|
+
first-party adapter contract. It would define bounded selection and completion
|
|
65
|
+
for that event shape while retaining the same static declaration, schema,
|
|
66
|
+
handler, effect, catalog, and no-fallback rules. The current package does not
|
|
67
|
+
open third-party adapter registration or claim those adapters exist.
|
|
68
|
+
|
|
69
|
+
See the [Entities package guide](../packages/entities.md) for current behavior
|
|
70
|
+
and [Entities lowering](../contributing/entities-lowering.md) for the trusted
|
|
71
|
+
maintainer boundary.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Package-owned lowering
|
|
2
|
+
|
|
3
|
+
Package-owned lowering lets a synchronized Pulse package extend the canonical compiler with narrow, statically recognized operations while keeping domain rules out of compiler core.
|
|
4
|
+
|
|
5
|
+
The synchronized `1.0.0-beta.1` release uses this pattern for Assets, GRIP,
|
|
6
|
+
JWT, and Entities.
|
|
7
|
+
It is a **trusted first-party synchronization mechanism**, not a public
|
|
8
|
+
registry that executes arbitrary npm compiler plugins.
|
|
9
|
+
|
|
10
|
+
## One package API, two execution paths
|
|
11
|
+
|
|
12
|
+
Application code imports the normal package root:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { assets } from '@pulse-compute/assets'
|
|
16
|
+
import { grip } from '@pulse-compute/grip'
|
|
17
|
+
import { jwt } from '@pulse-compute/jwt'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
On the JavaScript target, the actual package TypeScript/JavaScript implementation executes. On Native targets, the compiler recognizes supported package-root symbols and lowers them into canonical package effects. Lowering restrictions remain eligibility and diagnostic rules; they do not require target-specific application source.
|
|
21
|
+
|
|
22
|
+
Older `/pulsewasm` imports are compatibility-only and are isolated in the
|
|
23
|
+
[migration guide](../guides/compatibility-imports.md).
|
|
24
|
+
|
|
25
|
+
## Why the package owns the lowerer
|
|
26
|
+
|
|
27
|
+
A domain package knows details that compiler core should not:
|
|
28
|
+
|
|
29
|
+
- which root symbols are effects or pure result adapters;
|
|
30
|
+
- which argument shapes are statically representable;
|
|
31
|
+
- which payload versions and diagnostics belong to the domain;
|
|
32
|
+
- which sidecar symbols compiled Wasm requires;
|
|
33
|
+
- which capabilities a provider must implement;
|
|
34
|
+
- which JavaScript behavior is broader than the lowerable Native subset.
|
|
35
|
+
|
|
36
|
+
## Package anatomy
|
|
37
|
+
|
|
38
|
+
A lowerable package normally ships:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
package.json application exports and discovery pointers
|
|
42
|
+
pulse.package.json product, target, ownership, and conformance metadata
|
|
43
|
+
src/ actual JavaScript implementation and portable API
|
|
44
|
+
pulsewasm.manifest.cjs trusted lowerer and compatibility declaration
|
|
45
|
+
pulsewasm.compiler.cjs package-specific validation and effect construction
|
|
46
|
+
as/index.as.ts optional compiled-Wasm sidecar ABI
|
|
47
|
+
README.md current product and compatibility boundary
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`pulse.package.json` is static metadata. It cannot register or execute compiler code. `pulsewasm.manifest.cjs` identifies the trusted first-party compiler builder. Product metadata and executable compiler authority remain separate.
|
|
51
|
+
|
|
52
|
+
## Discovery and trust
|
|
53
|
+
|
|
54
|
+
The canonical project compiler:
|
|
55
|
+
|
|
56
|
+
1. builds the reachable module graph;
|
|
57
|
+
2. attributes package imports from that graph rather than source substrings;
|
|
58
|
+
3. loads synchronized package product contracts;
|
|
59
|
+
4. selects a trusted package compiler only for a reachable supported Native operation;
|
|
60
|
+
5. invokes it through the exact immutable shared builder envelope;
|
|
61
|
+
6. normalizes its artifact and contributions before compiler orchestration;
|
|
62
|
+
7. combines canonical package effects with core effects and continuations;
|
|
63
|
+
8. validates the complete provider capability envelope.
|
|
64
|
+
|
|
65
|
+
A package manifest must declare first-party trust and ownership matching the
|
|
66
|
+
package. Dynamic third-party compiler registration is not part of the Beta
|
|
67
|
+
contract.
|
|
68
|
+
|
|
69
|
+
Caller option bags do not cross this boundary. In particular, provider target
|
|
70
|
+
descriptors, drivers, runtime objects, raw CLI configuration, compiler caches,
|
|
71
|
+
resolved secrets, and arbitrary compiler services are not builder inputs.
|
|
72
|
+
Package builders emit canonical requirements; later compiler/provider
|
|
73
|
+
eligibility checks decide whether the selected target can satisfy them.
|
|
74
|
+
|
|
75
|
+
## Request-bound package effects
|
|
76
|
+
|
|
77
|
+
A package effect is created through an explicit `PulseContext`:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
await grip.broadcast(ctx, {
|
|
81
|
+
channel: 'events:demo',
|
|
82
|
+
data: { type: 'message', value: 'hello' },
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The shared package bridge returns the same request-owned effect value used by core operations. It therefore supports:
|
|
87
|
+
|
|
88
|
+
- direct `await`;
|
|
89
|
+
- keyed `ctx.parallel({ ... })`;
|
|
90
|
+
- request cancellation and completion containment;
|
|
91
|
+
- deterministic grouped failure selection;
|
|
92
|
+
- known-secret redaction;
|
|
93
|
+
- generic provider dispatch.
|
|
94
|
+
|
|
95
|
+
The bridge has a fixed first-party identity catalog. It does not use ambient current-request state, `AsyncLocalStorage`, or mutable global registration.
|
|
96
|
+
|
|
97
|
+
## Pure package operations
|
|
98
|
+
|
|
99
|
+
Not every package function is an effect. Pure request classification and response decoration should remain ordinary JavaScript:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
if (grip.isWebSocket(ctx.req)) {
|
|
103
|
+
return grip.handoff({ channel: 'events:demo' })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return grip.subscribe(new Response(null), {
|
|
107
|
+
channel: 'events:demo',
|
|
108
|
+
mode: 'stream',
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Similarly, `assets.respond()` is a pure result adapter. Native lowering may erase that helper around an `assets.lookup()` result without turning response decoration into another provider effect.
|
|
113
|
+
|
|
114
|
+
## Static call shapes
|
|
115
|
+
|
|
116
|
+
Native package lowering intentionally accepts less than arbitrary TypeScript. A package may require:
|
|
117
|
+
|
|
118
|
+
- a known package-root import and symbol;
|
|
119
|
+
- supported direct-await, variable, return, or `ctx.parallel()` placement;
|
|
120
|
+
- fixed object-literal options;
|
|
121
|
+
- statically representable resources or operation selectors;
|
|
122
|
+
- no escaping internal handles;
|
|
123
|
+
- no provider SDK values.
|
|
124
|
+
|
|
125
|
+
Unsupported shapes make the Native project ineligible with a source-located diagnostic. The same package may still support broader direct JavaScript behavior when the selected JavaScript target and provider contract allow it.
|
|
126
|
+
|
|
127
|
+
## Provider completion
|
|
128
|
+
|
|
129
|
+
A package is not complete merely because its compiler emits an effect. Every provider advertised for that operation must:
|
|
130
|
+
|
|
131
|
+
- declare the capability;
|
|
132
|
+
- map it to a stable operation;
|
|
133
|
+
- validate required bindings;
|
|
134
|
+
- execute it in local conformance;
|
|
135
|
+
- realize it in deployable output when claimed;
|
|
136
|
+
- preserve structured or opaque result ownership.
|
|
137
|
+
|
|
138
|
+
Assets demonstrates package-root JavaScript execution plus Native package lowering and opaque response ownership. GRIP provides stateless JavaScript framing, bounded broadcast provider realization, and Native/root conformance.
|
|
139
|
+
|
|
140
|
+
JWT demonstrates a package-owned verification effect whose Native lowering
|
|
141
|
+
composes package-owned JWT logic with crypto-owned realizations. HS256 Native
|
|
142
|
+
compiles the crypto guest source into the primary module; ES256 Native
|
|
143
|
+
contributes one exact prebuilt guest unit through the guest-link boundary.
|
|
144
|
+
JavaScript uses the same package contract with the selected runtime builtin for
|
|
145
|
+
HS256 and ES256. Provider eligibility begins only after the package emits its
|
|
146
|
+
provider-neutral capability and crypto requirements.
|
|
147
|
+
|
|
148
|
+
A package-selected prebuilt guest is a separate canonical contribution, not a
|
|
149
|
+
filesystem or compiler object. It carries only its version, stable ID,
|
|
150
|
+
package-relative manifest, exact owner/package version, and
|
|
151
|
+
`package-prebuilt` origin. The compiler resolves the synchronized package root
|
|
152
|
+
as a separate selection fact. Guest-link then receives that exact contribution
|
|
153
|
+
through its own invocation contract; `packageRoot`, target descriptors,
|
|
154
|
+
compiler realizations, and option bags are never fields of the contribution.
|
|
155
|
+
|
|
156
|
+
## Release expectations
|
|
157
|
+
|
|
158
|
+
A supported package-owned lowerer needs:
|
|
159
|
+
|
|
160
|
+
- product and trusted-manifest validation;
|
|
161
|
+
- positive and negative static lowering tests;
|
|
162
|
+
- JavaScript runtime tests for the real package implementation;
|
|
163
|
+
- direct-await and keyed-parallel package-effect tests;
|
|
164
|
+
- provider capability and binding tests;
|
|
165
|
+
- packed-package discovery and clean installation;
|
|
166
|
+
- documentation distinguishing canonical roots from compatibility subpaths;
|
|
167
|
+
- explicit evidence classifying every unsupported shape.
|
|
168
|
+
|
|
169
|
+
See [Pulse-aware package
|
|
170
|
+
authoring](../contributing/pulse-aware-packages.md), [Add a first-party
|
|
171
|
+
package-owned lowerer](../contributing/adding-first-party-lowerer.md), [Managed
|
|
172
|
+
handler TypeScript and JavaScript](../reference/handler-authoring.md),
|
|
173
|
+
[Provider and target compatibility](../reference/compatibility-matrix.md),
|
|
174
|
+
[Contracts and providers](./contracts-and-providers.md), and [Compilation and
|
|
175
|
+
lowering](./compilation-and-lowering.md).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Targets and host work
|
|
2
|
+
|
|
3
|
+
Pulse is centered on one application contract. A host is the explicit
|
|
4
|
+
realization layer for that contract, not an object application code imports or
|
|
5
|
+
branches on. The release distinguishes current execution proof from forward
|
|
6
|
+
host work so architectural direction does not become an accidental support
|
|
7
|
+
claim.
|
|
8
|
+
|
|
9
|
+
## Current Beta targets
|
|
10
|
+
|
|
11
|
+
`1.0.0-beta.1` exercises four explicit modes through one conformance corpus:
|
|
12
|
+
|
|
13
|
+
| Host | Target | Current release status | Build shape |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| Node | Native | Beta candidate | Provider-neutral application Wasm with the Node host runtime. |
|
|
16
|
+
| Node | JavaScript | Beta candidate | Explicit Node JavaScript source package. |
|
|
17
|
+
| Fastly | Native | Beta candidate | Provider-neutral application Wasm plus deployable `bin/main.wasm`. |
|
|
18
|
+
| Fastly | JavaScript | Beta candidate | Explicit Fastly JavaScript package. |
|
|
19
|
+
|
|
20
|
+
Target selection is part of the active project profile. Pulse does not switch
|
|
21
|
+
targets automatically when source crosses a Native boundary. The
|
|
22
|
+
[compatibility matrix](../reference/compatibility-matrix.md) owns exact
|
|
23
|
+
capability differences, and [Beta scope](../preview-scope.md) owns the release
|
|
24
|
+
claim.
|
|
25
|
+
|
|
26
|
+
The `none` selector is compile-only: it can inspect and emit canonical Native
|
|
27
|
+
artifacts without claiming an executable host.
|
|
28
|
+
|
|
29
|
+
## Forward boundary witnesses
|
|
30
|
+
|
|
31
|
+
Forward hosts are useful because they pressure the contract in different ways.
|
|
32
|
+
They remain outside the Beta execution matrix until they have their own
|
|
33
|
+
adapter, lifecycle, conformance evidence, and package claim.
|
|
34
|
+
|
|
35
|
+
### ESP32
|
|
36
|
+
|
|
37
|
+
The [ESP32 reference host](https://github.com/pulsecompute/pulse-esp32-host)
|
|
38
|
+
tests whether the event/effect/continuation model remains useful without a
|
|
39
|
+
JavaScript fallback, abundant memory, or an HTTP-centered lifecycle. It is a
|
|
40
|
+
boundary witness, not a `1.0.0-beta.1` provider or compatibility promise. The
|
|
41
|
+
[architecture vision](../architecture/vision.md#esp32-as-a-boundary-witness)
|
|
42
|
+
describes the intended host-owned interrupt queue and non-reentrant Wasm entry.
|
|
43
|
+
|
|
44
|
+
### Browser event conformance
|
|
45
|
+
|
|
46
|
+
The browser is a forward event-conformance target: a future browser host should
|
|
47
|
+
accept the same canonical event frames, keep HTTP and event entries separate,
|
|
48
|
+
preserve invocation-local state, and report outbound frame acceptance through
|
|
49
|
+
the same bounded contract. Pulse does not currently claim a browser runtime,
|
|
50
|
+
provider package, listener, delivery service, or event transport.
|
|
51
|
+
|
|
52
|
+
This is deliberately narrower than saying “Pulse runs in the browser.” The
|
|
53
|
+
[events guide](../guides/events.md) defines the current Node reference boundary
|
|
54
|
+
and the evidence a new host must supply.
|
|
55
|
+
|
|
56
|
+
## Where provider details belong
|
|
57
|
+
|
|
58
|
+
Application docs lead with `ctx`, Router, schemas, and capabilities. Provider
|
|
59
|
+
details belong in project profiles, deployment guides, compatibility tables,
|
|
60
|
+
and package pages where build or runtime behavior actually differs. That keeps
|
|
61
|
+
the common surface readable while leaving each host’s authority and limits
|
|
62
|
+
explicit.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!-- pulse-doc-meta:start
|
|
2
|
+
owner: docs-platform
|
|
3
|
+
status: active
|
|
4
|
+
last-reviewed: 2026-07-25
|
|
5
|
+
review-by: 2027-01-25
|
|
6
|
+
pulse-doc-meta:end -->
|
|
7
|
+
|
|
8
|
+
# Contributing documentation
|
|
9
|
+
|
|
10
|
+
These pages describe changes to the synchronized Pulse repository. They are not application-author extension APIs.
|
|
11
|
+
|
|
12
|
+
## Choose the extension boundary
|
|
13
|
+
|
|
14
|
+
- [Pulse-aware package authoring](./pulse-aware-packages.md) — ordinary JavaScript packages, bounded package-root Native lowering, trusted builders, and the separate scoped-provider boundary.
|
|
15
|
+
|
|
16
|
+
## Package-owned lowering
|
|
17
|
+
|
|
18
|
+
- [Package lowerer contract reference](./package-lowerer-contract.md) — manifest fields, builder protocol, effect shape, trust rules, and release invariants.
|
|
19
|
+
- [Add a first-party package-owned lowerer](./adding-first-party-lowerer.md) — end-to-end implementation tutorial using the GRIP/assets pattern.
|
|
20
|
+
- [Entities lowering maintainer reference](./entities-lowering.md) — static extraction, schema authority, Native realization, catalog, and seal invariants for the Entities package.
|
|
21
|
+
|
|
22
|
+
## Providers
|
|
23
|
+
|
|
24
|
+
- [Add a provider toolchain](./adding-core-provider.md) — descriptor, bindings, runtime, target, namespace bootstrap, diagnostics, docs, and tests.
|
|
25
|
+
|
|
26
|
+
## Repository context
|
|
27
|
+
|
|
28
|
+
- [Package support policy](../packages/README.md)
|
|
29
|
+
- [Architecture overview](../architecture/overview.md)
|
|
30
|
+
- [Current architecture contracts](../architecture/current-contracts.md)
|
|
31
|
+
- [Reference overview](../reference/README.md)
|
|
32
|
+
|
|
33
|
+
The current release executes only trusted first-party lowerer builders. Built-in
|
|
34
|
+
provider shorthand is release-owned, while an exact scoped project provider may
|
|
35
|
+
export the versioned `./toolchain` contract without self-registration or
|
|
36
|
+
dependency scanning. Changes that widen either executable trust boundary
|
|
37
|
+
require an explicit security and compatibility design, not only another
|
|
38
|
+
manifest or package export.
|