@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.
Files changed (191) hide show
  1. package/API.md +572 -0
  2. package/CHANGELOG.md +71 -0
  3. package/README.md +71 -1
  4. package/bin/pulse.js +15 -0
  5. package/bin/pulsewasm-extract.js +22 -0
  6. package/cli-spec.json +575 -0
  7. package/completions/_pulse +106 -0
  8. package/completions/pulse.bash +31 -0
  9. package/completions/pulse.fish +76 -0
  10. package/docs/README.md +77 -0
  11. package/docs/architecture/current-contracts.md +470 -0
  12. package/docs/architecture/overview.md +93 -0
  13. package/docs/architecture/vision.md +515 -0
  14. package/docs/concepts/bodies.md +186 -0
  15. package/docs/concepts/compilation-and-lowering.md +496 -0
  16. package/docs/concepts/contracts-and-providers.md +158 -0
  17. package/docs/concepts/effects-and-continuations.md +300 -0
  18. package/docs/concepts/entities-and-adapters.md +71 -0
  19. package/docs/concepts/package-owned-lowering.md +175 -0
  20. package/docs/concepts/targets-and-hosts.md +70 -0
  21. package/docs/contributing/README.md +38 -0
  22. package/docs/contributing/adding-core-provider.md +315 -0
  23. package/docs/contributing/adding-first-party-lowerer.md +327 -0
  24. package/docs/contributing/entities-lowering.md +91 -0
  25. package/docs/contributing/package-lowerer-contract.md +383 -0
  26. package/docs/contributing/pulse-aware-packages.md +149 -0
  27. package/docs/examples.md +112 -0
  28. package/docs/fixtures/inspect-fetch-composition.selected.json +181 -0
  29. package/docs/getting-started.md +128 -0
  30. package/docs/guides/compatibility-imports.md +60 -0
  31. package/docs/guides/deploying-fastly.md +188 -0
  32. package/docs/guides/deploying-node.md +143 -0
  33. package/docs/guides/events.md +233 -0
  34. package/docs/guides/fastly-capabilities.md +153 -0
  35. package/docs/guides/fetching-and-composition.md +101 -0
  36. package/docs/guides/grip.md +94 -0
  37. package/docs/guides/json-schemas.md +233 -0
  38. package/docs/guides/migrating-from-express.md +192 -0
  39. package/docs/guides/project-lifecycle.md +143 -0
  40. package/docs/guides/routing.md +202 -0
  41. package/docs/guides/troubleshooting.md +47 -0
  42. package/docs/maintainers/README.md +60 -0
  43. package/docs/maintainers/codex-maintainer.md +71 -0
  44. package/docs/maintainers/documentation-deployment.md +147 -0
  45. package/docs/maintainers/documentation-system.md +58 -0
  46. package/docs/maintainers/documentation-versioning.md +65 -0
  47. package/docs/maintainers/maintainer-charter.md +100 -0
  48. package/docs/maintainers/maintenance-policy.json +809 -0
  49. package/docs/maintainers/maintenance-policy.md +124 -0
  50. package/docs/maintainers/npm-publishing.md +154 -0
  51. package/docs/maintainers/plugin-readiness.json +19 -0
  52. package/docs/maintainers/plugin-readiness.md +31 -0
  53. package/docs/maintainers/public-site.md +158 -0
  54. package/docs/maintainers/release-acceptance.md +293 -0
  55. package/docs/maintainers/release-manifest.md +89 -0
  56. package/docs/maintainers/repository-setup.md +145 -0
  57. package/docs/maintainers/scope-policy.md +105 -0
  58. package/docs/maintainers/support-and-triage.md +74 -0
  59. package/docs/maintainers/testing.md +334 -0
  60. package/docs/packages/README.md +60 -0
  61. package/docs/packages/assets.md +85 -0
  62. package/docs/packages/cli.md +190 -0
  63. package/docs/packages/crypto.md +78 -0
  64. package/docs/packages/entities.md +129 -0
  65. package/docs/packages/grip.md +82 -0
  66. package/docs/packages/implementation-packages.md +122 -0
  67. package/docs/packages/jwt.md +55 -0
  68. package/docs/packages/provider-fastly.md +243 -0
  69. package/docs/packages/pulse.md +159 -0
  70. package/docs/packages/runtime.md +265 -0
  71. package/docs/packages/s3.md +101 -0
  72. package/docs/preview-scope.md +142 -0
  73. package/docs/reference/README.md +33 -0
  74. package/docs/reference/cli-spec.json +575 -0
  75. package/docs/reference/cli.md +457 -0
  76. package/docs/reference/compatibility-matrix.md +169 -0
  77. package/docs/reference/diagnostics.md +1872 -0
  78. package/docs/reference/documentation-versions.json +22 -0
  79. package/docs/reference/environment.md +347 -0
  80. package/docs/reference/handler-authoring.md +264 -0
  81. package/docs/reference/project-config.md +582 -0
  82. package/docs/reference/project-config.schema.json +981 -0
  83. package/docs/reference/release-manifest.json +441 -0
  84. package/docs/reference/shell-completion.md +23 -0
  85. package/documentation-site.json +668 -0
  86. package/documentation-versions.json +22 -0
  87. package/examples/01-hello-json/.pulse/.gitignore +3 -0
  88. package/examples/01-hello-json/.pulse/config.ts +16 -0
  89. package/examples/01-hello-json/README.md +119 -0
  90. package/examples/01-hello-json/package.json +20 -0
  91. package/examples/01-hello-json/src/index.ts +9 -0
  92. package/examples/01-hello-json/tests/pulse.harness.ts +22 -0
  93. package/examples/01-hello-json/tsconfig.json +14 -0
  94. package/examples/02-request-schema/.pulse/.gitignore +3 -0
  95. package/examples/02-request-schema/.pulse/config.ts +17 -0
  96. package/examples/02-request-schema/README.md +145 -0
  97. package/examples/02-request-schema/package.json +20 -0
  98. package/examples/02-request-schema/src/index.ts +18 -0
  99. package/examples/02-request-schema/src/schemas.ts +20 -0
  100. package/examples/02-request-schema/tests/pulse.harness.ts +37 -0
  101. package/examples/02-request-schema/tsconfig.json +14 -0
  102. package/examples/03-fetch-composition/.pulse/.gitignore +3 -0
  103. package/examples/03-fetch-composition/.pulse/config.ts +31 -0
  104. package/examples/03-fetch-composition/README.md +210 -0
  105. package/examples/03-fetch-composition/package.json +20 -0
  106. package/examples/03-fetch-composition/src/index.ts +60 -0
  107. package/examples/03-fetch-composition/tests/pulse.harness.ts +47 -0
  108. package/examples/03-fetch-composition/tsconfig.json +14 -0
  109. package/examples/05-fastly-capabilities/.pulse/.gitignore +3 -0
  110. package/examples/05-fastly-capabilities/.pulse/config.ts +57 -0
  111. package/examples/05-fastly-capabilities/README.md +257 -0
  112. package/examples/05-fastly-capabilities/package.json +22 -0
  113. package/examples/05-fastly-capabilities/src/index.ts +55 -0
  114. package/examples/05-fastly-capabilities/tests/pulse.harness.ts +66 -0
  115. package/examples/05-fastly-capabilities/tsconfig.json +14 -0
  116. package/examples/07-opaque-proxy/.pulse/.gitignore +3 -0
  117. package/examples/07-opaque-proxy/.pulse/config.ts +38 -0
  118. package/examples/07-opaque-proxy/README.md +150 -0
  119. package/examples/07-opaque-proxy/package.json +21 -0
  120. package/examples/07-opaque-proxy/src/index.ts +9 -0
  121. package/examples/07-opaque-proxy/tests/pulse.harness.ts +25 -0
  122. package/examples/07-opaque-proxy/tsconfig.json +14 -0
  123. package/examples/09-router-lowering/.pulse/.gitignore +3 -0
  124. package/examples/09-router-lowering/.pulse/config.ts +22 -0
  125. package/examples/09-router-lowering/README.md +287 -0
  126. package/examples/09-router-lowering/package.json +22 -0
  127. package/examples/09-router-lowering/src/index.ts +65 -0
  128. package/examples/09-router-lowering/tests/pulse.harness.ts +91 -0
  129. package/examples/09-router-lowering/tsconfig.json +14 -0
  130. package/examples/10-entities-tools/.pulse/.gitignore +4 -0
  131. package/examples/10-entities-tools/.pulse/config.ts +23 -0
  132. package/examples/10-entities-tools/README.md +165 -0
  133. package/examples/10-entities-tools/package.json +21 -0
  134. package/examples/10-entities-tools/src/handlers.ts +15 -0
  135. package/examples/10-entities-tools/src/index.ts +31 -0
  136. package/examples/10-entities-tools/src/schemas.ts +18 -0
  137. package/examples/10-entities-tools/tests/pulse.harness.ts +40 -0
  138. package/examples/10-entities-tools/tools-facade.cjs +158 -0
  139. package/examples/10-entities-tools/tsconfig.json +14 -0
  140. package/examples/11-events/.pulse/.gitignore +4 -0
  141. package/examples/11-events/.pulse/config.ts +24 -0
  142. package/examples/11-events/README.md +194 -0
  143. package/examples/11-events/package.json +20 -0
  144. package/examples/11-events/src/index.ts +27 -0
  145. package/examples/11-events/src/schemas.ts +19 -0
  146. package/examples/11-events/tests/pulse.harness.ts +34 -0
  147. package/examples/11-events/tsconfig.json +15 -0
  148. package/examples/12-mcp-proxy/.pulse/.gitignore +3 -0
  149. package/examples/12-mcp-proxy/.pulse/config.ts +25 -0
  150. package/examples/12-mcp-proxy/README.md +149 -0
  151. package/examples/12-mcp-proxy/package.json +20 -0
  152. package/examples/12-mcp-proxy/src/index.ts +17 -0
  153. package/examples/12-mcp-proxy/tests/pulse.harness.ts +29 -0
  154. package/examples/12-mcp-proxy/tsconfig.json +14 -0
  155. package/examples/13-jwt-es256/.pulse/config.ts +19 -0
  156. package/examples/13-jwt-es256/README.md +195 -0
  157. package/examples/13-jwt-es256/package.json +21 -0
  158. package/examples/13-jwt-es256/src/index.ts +21 -0
  159. package/examples/13-jwt-es256/tests/pulse.harness.ts +54 -0
  160. package/examples/13-jwt-es256/tsconfig.json +14 -0
  161. package/examples/README.md +36 -0
  162. package/package.json +66 -6
  163. package/project-config.schema.json +981 -0
  164. package/release-manifest.json +441 -0
  165. package/src/command-spec.js +279 -0
  166. package/src/completion.js +113 -0
  167. package/src/diagnostics.js +350 -0
  168. package/src/documentation.js +45 -0
  169. package/src/index.d.ts +133 -0
  170. package/src/index.js +15 -0
  171. package/src/internal/command-executor.d.ts +29 -0
  172. package/src/internal/command-executor.js +143 -0
  173. package/src/internal/command-plan.d.ts +9 -0
  174. package/src/internal/command-plan.js +73 -0
  175. package/src/internal/command-reporter.d.ts +15 -0
  176. package/src/internal/command-reporter.js +133 -0
  177. package/src/internal/command-request.d.ts +35 -0
  178. package/src/internal/command-request.js +154 -0
  179. package/src/internal/node-http.js +54 -0
  180. package/src/internal/project-context.d.ts +66 -0
  181. package/src/internal/project-context.js +175 -0
  182. package/src/project-config-reference.js +4 -0
  183. package/src/project-config-schema.d.ts +23 -0
  184. package/src/project-config-schema.js +271 -0
  185. package/src/project-config.js +724 -0
  186. package/src/project-execution.js +2946 -0
  187. package/src/provider-drivers.js +3 -0
  188. package/src/target-support.js +3 -0
  189. package/src/typescript-module-loader.js +269 -0
  190. package/src/workflow.js +78 -0
  191. package/src/workspace.js +82 -0
@@ -0,0 +1,243 @@
1
+ # `@pulse-compute/provider-fastly`
2
+
3
+ `@pulse-compute/provider-fastly` supplies typed Fastly project configuration,
4
+ canonical capability lowering, direct JavaScript execution, local conformance
5
+ runtimes, Native and JavaScript Compute target generation, and an explicit
6
+ Fastly CLI boundary.
7
+
8
+ Install it only in projects that select the Fastly provider.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @pulse-compute/provider-fastly@1.0.0-beta.2
14
+ ```
15
+
16
+ ## Configure a project
17
+
18
+ <!-- pulse-doc-source: examples/05-fastly-capabilities/.pulse/config.ts -->
19
+ ```ts
20
+ import { defineConfig } from '@pulse-compute/pulse'
21
+
22
+ export default defineConfig((scope) => ({
23
+ pulse: {
24
+ entry: 'src/index.ts',
25
+ tests: 'tests/pulse.harness.ts',
26
+ defaultProfile: 'local',
27
+ strict: true,
28
+ },
29
+ local: {
30
+ host: 'fastly',
31
+ target: 'native',
32
+ outDir: 'dist',
33
+ apiBase: scope.config('API_BASE'),
34
+ apiToken: scope.secret('API_TOKEN'),
35
+ dev: {
36
+ config: { API_BASE: 'https://api.example.com' },
37
+ secrets: {
38
+ API_TOKEN: 'local-example-secret',
39
+ GRIP_TOKEN: 'local-grip-secret',
40
+ },
41
+ kv: {
42
+ sessions: { 'session:123': { userId: 123 } },
43
+ },
44
+ fetches: {
45
+ 'https://api.example.com/users/7': {
46
+ value: { id: 7, name: 'Ada' },
47
+ },
48
+ 'POST https://publisher.example.com/publish': {
49
+ status: 202,
50
+ value: { accepted: true, messageId: 'message-1' },
51
+ },
52
+ },
53
+ },
54
+ fastly: {
55
+ bindings: {
56
+ configStore: 'app_config',
57
+ secretStore: 'app_secrets',
58
+ kv: { sessions: 'app_sessions' },
59
+ backends: {
60
+ 'https://api.example.com': 'api_backend',
61
+ 'https://publisher.example.com': 'publisher_backend',
62
+ },
63
+ dynamicBackends: false,
64
+ grip: {
65
+ publishEndpoint: 'https://publisher.example.com/publish',
66
+ publishBackend: 'publisher_backend',
67
+ authentication: {
68
+ scheme: 'bearer',
69
+ secretRef: 'GRIP_TOKEN',
70
+ },
71
+ },
72
+ },
73
+ build: { name: 'pulse-fastly-capabilities-example' },
74
+ },
75
+ },
76
+ }))
77
+ ```
78
+ <!-- /pulse-doc-source -->
79
+
80
+ 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.
81
+
82
+ ## Configuration groups
83
+
84
+ The root `fastly()` helper supports:
85
+
86
+ - Config Store and Secret Store names;
87
+ - logical KV namespace to Fastly KV Store mappings;
88
+ - origin to backend-name mappings;
89
+ - explicit dynamic-backend policy;
90
+ - GRIP/Fanout hold and publish bindings;
91
+ - package name, description, and authors;
92
+ - local network-fetch policy.
93
+
94
+ See [Project configuration](../reference/project-config.md#fastly-provider-options) for field-level defaults, precedence, and constraints.
95
+
96
+ ## Build outputs
97
+
98
+ The active `.pulse/config.ts` profile selects one target explicitly:
99
+
100
+ ```bash
101
+ pulse build ./my-app
102
+ ```
103
+
104
+ ### Fastly Native
105
+
106
+ `target: 'native'` compiles the provider-neutral plan and emits generated
107
+ `src/main.as.ts` plus compact `bin/main.wasm` importing the required `fastly_*`
108
+ hostcalls directly. It contains no JavaScript runtime image or `pulse_host`.
109
+ Clock-dependent capabilities import only WASI `clock_time_get`.
110
+
111
+ Conditional KV (`getVersioned`, `insertIfAbsent`, `compareAndSwap`) uses this
112
+ Native path and the existing logical KV bindings. It preserves generation tokens
113
+ without numeric narrowing, stages a bounded Pulse JSON envelope, and distinguishes
114
+ confirmed rejection from unconfirmed dispatch. Pending completion and body reads
115
+ use readiness selection with a monotonic deadline. Read bodies are closed; expired
116
+ pending operations remain owned by invocation teardown, with no rollback promise.
117
+ Fastly JavaScript remains explicitly incomplete for these operations. See
118
+ [conditional KV](../concepts/effects-and-continuations.md#conditional-kv) for the
119
+ portable contract. The generated-Wasm corpus is local evidence; it does not replace
120
+ deployed cross-location acceptance. The legacy JavaScript fixture runner does
121
+ not realize Native conditional KV; it reports a configuration failure. Exercise
122
+ these operations through generated Wasm or the Compute execution lane.
123
+
124
+ The required conditional KV acceptance task is `kv-conditional-acceptance`.
125
+ It builds and installs exact tarballs and executes their Native artifact through
126
+ Fastly CLI/Viceroy; an unavailable engine fails the task. Viceroy 0.21.0 currently
127
+ fails the missing-key CAS contract: it creates the key instead of returning
128
+ `conflict`. A September 12, 2026 standalone Rust SDK probe on deployed Fastly
129
+ rejected CAS on both never-created and deleted keys, which remained absent.
130
+ This resolves the live missing-key concern for those cases and distinguishes it
131
+ from the Viceroy defect. The required local gate still fails, and full Pulse
132
+ deployed cross-location acceptance remains pending the isolated development
133
+ environment. The [K4 acceptance record](https://github.com/pulse-compute/pulse/blob/latest/wasm/test/kv/K4.md)
134
+ retains both captures and their scope. Neither that standalone probe nor passing
135
+ injected-host or portable tests clears the full acceptance gates.
136
+
137
+ ### Fastly JavaScript
138
+
139
+ `target: 'javascript'` emits a deterministic, self-contained source closure:
140
+
141
+ ```text
142
+ src/index.js
143
+ src/application.js
144
+ fastly.toml
145
+ package.json
146
+ pulse-esbuild.config.js
147
+ pulse-fastly-javascript-candidate.json
148
+ pulse-fastly-javascript-deployment.json
149
+ pulse-fastly-javascript-source-package.json
150
+ ```
151
+
152
+ The closure pins `esbuild` and `@fastly/js-compute`, bundles the reachable Pulse
153
+ application and package implementations, and contains no Pulse Native artifact.
154
+ The generated build first produces `dist/index.js`, then the pinned Fastly
155
+ JavaScript compiler produces `bin/main.wasm`. Candidate, source-package,
156
+ deployment, and top-level manifests agree on the selected `fastly-javascript`
157
+ target and retain `automaticFallback: false`.
158
+
159
+ The release gate builds the source closure twice, compares its bytes, and
160
+ successfully invokes the pinned downstream compiler. The resulting runtime Wasm
161
+ is bound to the candidate by SHA-256. Wizer-owned runtime snapshot bytes are not
162
+ claimed byte-reproducible; the Pulse-owned closure and deployment metadata are.
163
+
164
+ Provider selection is configuration-owned. Removed public `--provider` and
165
+ `--source-only` flags are rejected with stable diagnostics.
166
+
167
+ Use:
168
+
169
+ ```bash
170
+ pulse doctor ./my-app --json
171
+ ```
172
+
173
+ to inspect compiler and Fastly CLI readiness before a release build.
174
+
175
+ ## Local conformance and external reality
176
+
177
+ For Fastly Native, `pulse test` and `pulse dev` use the provider’s local
178
+ canonical runtime. For Fastly JavaScript, they use explicit provider emulation
179
+ and the same provider-owned bundled application closure. Both paths use
180
+ configured values, fixtures, and optional network fetch; neither silently runs
181
+ through the Node provider.
182
+
183
+ 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](../reference/environment.md#pulse-fastly-bin).
184
+
185
+ The Fastly reality gate proves Native local host-ABI compatibility through real
186
+ HTTP requests covering schemas, configuration, secrets, KV persistence,
187
+ named-backend fetch, opaque bytes, repeated headers, and GRIP hold/publish. The
188
+ JavaScript candidate gate proves downstream runtime compilation but does not
189
+ invoke `fastly compute serve`. Neither gate claims that remote resources or a
190
+ deployed Fastly service are healthy; remote deployment remains separately
191
+ unvalidated.
192
+
193
+ Fastly JavaScript is generally available under the full-target-support policy:
194
+ all declared runtime, capability, packaging, four-mode, tooling, and offline
195
+ candidate gates are satisfied. General availability is a supported-target
196
+ statement, not evidence that a particular candidate was deployed.
197
+
198
+ ## Public entry points
199
+
200
+ | Entry point | Purpose |
201
+ |---|---|
202
+ | `@pulse-compute/provider-fastly` | Typed `fastly()` project configuration. |
203
+ | `@pulse-compute/provider-fastly/contract` | Canonical descriptor and lowering-plan integration. |
204
+ | `@pulse-compute/provider-fastly/runtime/canonical-api-runtime` | Local canonical runtime used by project execution. |
205
+ | `@pulse-compute/provider-fastly/build/canonical-target` | Fastly source/target writer. |
206
+ | `@pulse-compute/provider-fastly/testing/fastly-cli` | Explicit Fastly CLI discovery and serve boundary. |
207
+
208
+ Other exported compiler/runtime subpaths are compatibility or implementation surfaces and do not gain the same application-facing promise.
209
+
210
+ ## Handler isolation
211
+
212
+ Application source continues to import only `@pulse-compute/runtime` and supported package facades. The provider plan records:
213
+
214
+ ```json
215
+ {
216
+ "providerSpecificUserland": false,
217
+ "providerSdkUserland": false,
218
+ "capabilityDiscoveryFromUserland": false
219
+ }
220
+ ```
221
+
222
+ Do not import Fastly SDK objects into canonical handlers. Bindings belong in project configuration.
223
+
224
+ ## Current limitations
225
+
226
+ - The official `fastly` shorthand resolves this package's explicit
227
+ `./toolchain` export. That narrow bootstrap is not automatic plugin
228
+ discovery, self-registration, or a lowerer extension API.
229
+ - Local fixtures do not provision Fastly resources.
230
+ - Offline candidate validation does not deploy, activate, or publish a Fastly service.
231
+ - Dynamic backends are disabled unless explicitly enabled.
232
+ - GRIP requires the documented Fanout/publish bindings for the operations used.
233
+ - Full userland stream processing and provider SDK escape hatches remain
234
+ outside the Beta.
235
+
236
+ ## Related documentation
237
+
238
+ - [Fastly deployment candidates](../guides/deploying-fastly.md)
239
+ - [Fastly config, secrets, and KV](../guides/fastly-capabilities.md)
240
+ - [GRIP and Fanout](../guides/grip.md)
241
+ - [Contracts and providers](../concepts/contracts-and-providers.md)
242
+ - [Add a core provider](../contributing/adding-core-provider.md)
243
+ - [Fastly diagnostics](../reference/diagnostics.md#toolchain-diagnostics)
@@ -0,0 +1,159 @@
1
+ # `@pulse-compute/pulse`
2
+
3
+ `@pulse-compute/pulse` is the conventional application-authoring package for a
4
+ Pulse project. It owns the project-aware `Pulse` application root, deferred
5
+ configuration factory, and static schema declarations while reusing the
6
+ provider-neutral runtime contract.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @pulse-compute/pulse@1.0.0-beta.2
12
+ ```
13
+
14
+ The package exposes two supported entry points:
15
+
16
+ - `@pulse-compute/pulse` for `Pulse`, `defineConfig`, and runtime-owned
17
+ authoring types;
18
+ - `@pulse-compute/pulse/schema` for `schema`, `response`, and
19
+ `defineSchemaRegistry`.
20
+
21
+ ## Application root
22
+
23
+ ```ts
24
+ import { Pulse } from '@pulse-compute/pulse'
25
+
26
+ const app = new Pulse({ auto: true })
27
+
28
+ app.get('/health', async (ctx) => {
29
+ return ctx.json({ ok: true })
30
+ })
31
+
32
+ export default app
33
+ ```
34
+
35
+ `Pulse` extends the same live `Router` implementation exposed by
36
+ `@pulse-compute/runtime`; it does not introduce a second routing or execution
37
+ system. Compiler analysis normalizes the application into the canonical Router
38
+ IR before target selection and provider realization.
39
+
40
+ `Pulse` also owns root-only static event registration:
41
+
42
+ ```ts
43
+ app.on('device.reading', { schema: 'events.DeviceReading' }, async (ctx) => {
44
+ const reading = ctx.event.payload
45
+ await ctx.config.get('MODE')
46
+ void reading
47
+ })
48
+
49
+ app.on('system.tick', { schema: null }, async (ctx) => {
50
+ void ctx.event.type
51
+ })
52
+ ```
53
+
54
+ Event types and schema IDs are literal compiler inputs, every type has one
55
+ owner, and non-null schemas must resolve through the project registry. Event
56
+ handlers receive a non-HTTP context and complete with `void`. The compiler
57
+ places HTTP and event handlers in separate planes of one application-entry
58
+ table. Eligible event handlers can lower through the conditional
59
+ provider-neutral Native event ABI. Node has a bounded invocation-scoped
60
+ reference ingress and outbound-acceptance adapter for direct JavaScript/Native
61
+ parity, but no public event bus, deployment listener, delivery guarantee, or
62
+ other provider target support is implied.
63
+
64
+ The complete authoring, frame, queue, target, diagnostic, and no-call boundary
65
+ is documented in [Static events and outbound emission](../guides/events.md).
66
+
67
+ The statically analyzable root forms are:
68
+
69
+ ```ts
70
+ new Pulse({ auto: true })
71
+ new Pulse(configFactory)
72
+ ```
73
+
74
+ Aliases, subclasses, factories returning `Pulse`, mounted `Pulse` instances, and
75
+ re-exported application roots remain outside the initial single-entry grammar.
76
+ Use `Router` for mounted and child applications.
77
+
78
+ ## Project configuration
79
+
80
+ Canonical configuration is one synchronous deferred factory:
81
+
82
+ ```ts
83
+ import { defineConfig } from '@pulse-compute/pulse'
84
+
85
+ export default defineConfig((scope) => ({
86
+ pulse: {
87
+ entry: 'src/index.ts',
88
+ tests: 'tests/pulse.harness.ts',
89
+ defaultProfile: 'local',
90
+ strict: true,
91
+ reporting: 'info',
92
+ },
93
+ local: {
94
+ host: 'node',
95
+ target: 'native',
96
+ apiBase: scope.config('API_BASE'),
97
+ token: scope.secret('API_TOKEN'),
98
+ },
99
+ }))
100
+ ```
101
+
102
+ The scope produces symbolic configuration and secret references only. It does
103
+ not expose selected-profile state, resolved values, commands, or ambient
104
+ environment authority.
105
+
106
+ ## Schema declarations
107
+
108
+ ```ts
109
+ import {
110
+ defineSchemaRegistry,
111
+ response,
112
+ schema,
113
+ } from '@pulse-compute/pulse/schema'
114
+
115
+ interface User {
116
+ id: number
117
+ name: string
118
+ }
119
+
120
+ export default defineSchemaRegistry({
121
+ schemas: {
122
+ 'app.User': schema<User>(),
123
+ },
124
+ responses: {
125
+ 'app.UserResponse': response(200, 'app.User'),
126
+ },
127
+ })
128
+ ```
129
+
130
+ Schema calls are static declarations consumed by the compiler. They do not add
131
+ runtime reflection or generic JSON parsing to the guest module.
132
+
133
+ ## Async-shaped handlers and execution state
134
+
135
+ Conventional projects require async-shaped handlers. Native lowering erases the
136
+ wrapper and lowers only recognized Pulse effects; it does not add Promise or
137
+ Asyncify runtimes.
138
+
139
+ See [Managed handler TypeScript and
140
+ JavaScript](../reference/handler-authoring.md) for the canonical static source
141
+ rules and JavaScript-only boundary, and the [compatibility
142
+ matrix](../reference/compatibility-matrix.md) for four-mode support.
143
+
144
+ `ctx.state` is an execution-scoped string map. HTTP executions share it across
145
+ the forward Router cursor, mounted routers, error recovery, and Native
146
+ continuation resumption; event executions share it across their continuation
147
+ resumption. It is reset between HTTP requests and event invocations and does
148
+ not expose enumeration, object values, or persistence.
149
+
150
+ ## Ownership boundary
151
+
152
+ This package owns application ergonomics and declarations. It does not own
153
+ Handler IR, effects, continuations, Native lowering, provider lifecycle, or
154
+ provider-specific bootstraps. Fastly and other host realization remains in the
155
+ corresponding provider package.
156
+
157
+ For the lower-level static Router and complete context contract, see
158
+ [`@pulse-compute/runtime`](./runtime.md). For project orchestration, see
159
+ [`@pulse-compute/cli`](./cli.md).
@@ -0,0 +1,265 @@
1
+ # `@pulse-compute/runtime`
2
+
3
+ `@pulse-compute/runtime` is the canonical, provider-neutral TypeScript authoring surface for Pulse applications. Install it in every canonical project and import handler types or the static `Router` marker from the package root.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @pulse-compute/runtime@1.0.0-beta.2
9
+ ```
10
+
11
+ A project created by `pulse init` depends on the public
12
+ `@pulse-compute/pulse` conventional application package, which re-exports these
13
+ runtime types for convenience. Public package availability remains
14
+ release-manifest-owned.
15
+
16
+ Conventional `.pulse` projects require async-shaped managed handlers and report `PULSE_HANDLER_ASYNC_REQUIRED` for synchronous forms. The compiler erases the wrapper and lowers trusted awaited Pulse effects through the Promise-free effect/continuation machinery.
17
+
18
+ ## Minimal handler
19
+
20
+ <!-- pulse-doc-source: examples/01-hello-json/src/index.ts -->
21
+ ```ts
22
+ import { Pulse } from '@pulse-compute/pulse'
23
+
24
+ const app = new Pulse({ auto: true })
25
+
26
+ app.get('/health', async (ctx) => ctx.json({ ok: true }))
27
+ app.get('/hello', async (ctx) => ctx.json({ message: 'hello from Pulse' }))
28
+ app.get('/*', async (ctx) => ctx.text('not found', { status: 404 }))
29
+
30
+ export default app
31
+ ```
32
+ <!-- /pulse-doc-source -->
33
+
34
+ The source-bound example is the conventional authoring shape. Managed handlers are async-shaped, while `ctx` remains the handler's only authority.
35
+
36
+ ## Public contract
37
+
38
+ The package exports:
39
+
40
+ - `PulseRequest` for method, URL, path, headers, bounded text, and bounded JSON reads;
41
+ - `PulseFetchInit` and `PulseFetchResponse` for canonical outbound fetches;
42
+ - structured and opaque response types;
43
+ - `PulseExecutionContext`, shared by HTTP and event handlers, with state, logging,
44
+ fetch, config, secret, KV, keyed parallel, and one-way event emission;
45
+ - `PulseContext` with the shared authority plus HTTP request and response construction;
46
+ - `PulseEvent`, `PulseEventContext`, and `PulseEventHandler` for exact type,
47
+ immutable schema payload, non-HTTP authority, and void completion;
48
+ - `PulseResult`, `Handler`, `PulseRouteContext`, `RouteHandler`, `RouterMiddleware`, `RouterErrorHandler`, and `RouterNext`;
49
+ - `Router`, the compile-time marker for static route topology;
50
+ - `RUNTIME_API_VERSION`, currently `pulse.runtime-authoring.v4`, and `ROUTER_API_VERSION`, currently `pulse.router-authoring.v2`.
51
+
52
+ The complete type-by-type reference is in [Canonical API](../../API.md).
53
+
54
+
55
+ ## Static Router applications
56
+
57
+ ```ts
58
+ import { Router } from '@pulse-compute/runtime'
59
+
60
+ const app = new Router()
61
+ app.use(async (ctx, next) => {
62
+ if (!ctx.req.header('authorization')) return ctx.text('Unauthorized', { status: 401 })
63
+ return next()
64
+ })
65
+ app.get('/health', async (ctx) => ctx.json({ ok: true }))
66
+ app.get('/users/:id', async (ctx) => ctx.json({ id: ctx.param('id') }))
67
+ export default app
68
+ ```
69
+
70
+ The compiler extracts and flattens static routes, middleware, mounts, and error entries before normal canonical lowering. `return next()` and `return next(error)` are terminal control transfers; the current handler never resumes. The marker does not dispatch through a JavaScript runtime. See [Static Router authoring](../guides/routing.md).
71
+
72
+ ## Request access
73
+
74
+ ```ts
75
+ const method = ctx.req.method
76
+ const path = ctx.req.path
77
+ const requestId = ctx.req.header('x-request-id')
78
+ const text = await ctx.req.text()
79
+ const input = await ctx.req.json<{ name: string }>('app.CreateUserInput')
80
+ ```
81
+
82
+ Schema-backed JSON reads use an exact ID declared by the selected `.pulse/config.ts` profile:
83
+
84
+ ```ts
85
+ const input = await ctx.req.json<CreateUserInput>('app.CreateUserInput')
86
+ ```
87
+
88
+ See [Explicit JSON schemas](../guides/json-schemas.md).
89
+
90
+
91
+ ## Async and execution state
92
+
93
+ Native targets treat `async` and trusted `await` as authoring notation. The compiler erases the async wrapper, lowers awaited Pulse effects into the existing continuation state machine, warns when `await` wraps a proven synchronous `ctx` value, and rejects arbitrary library awaits for native selection. No Promise runtime or Asyncify transform is linked.
94
+
95
+ The exact accepted source shapes, `ctx.parallel` record restrictions, and
96
+ JavaScript-only forms live in [Managed handler TypeScript and
97
+ JavaScript](../reference/handler-authoring.md). The tested four-mode claims live
98
+ in the [compatibility matrix](../reference/compatibility-matrix.md).
99
+
100
+ `ctx.state` is a synchronous execution-local string map:
101
+
102
+ ```ts
103
+ ctx.state.set('request-id', 'r1')
104
+ const requestId = ctx.state.get('request-id') // string | undefined
105
+ ```
106
+
107
+ For HTTP, state is visible across forward middleware, mounted Routers, error
108
+ recovery, and effect continuation resume. Event handlers use the same state
109
+ surface across continuation resume. State is isolated between HTTP requests and
110
+ between event invocations.
111
+
112
+ With project `pulse.strict: true` (the default), schema-less request JSON is rejected. Explicit `pulse.strict: false` enables the byte-bounded `host-generic-json` capability for reachable schema-less calls; the native plan and manifest report that dynamic-host choice. Schema-bound JSON remains specialized in either mode.
113
+
114
+ ## Event context and Native ingress
115
+
116
+ Static event handlers receive `PulseEventContext<Payload>`:
117
+
118
+ ```ts
119
+ app.on('device.reading', { schema: 'events.DeviceReading' }, async (ctx) => {
120
+ const reading = ctx.event.payload
121
+ const mode = await ctx.config.get('MODE')
122
+ ctx.state.set('last-mode', mode)
123
+ void reading
124
+ })
125
+ ```
126
+
127
+ `ctx.event.type` is the exact registered event type. The payload is validated
128
+ against the registration schema and detached before entry; a `schema: null`
129
+ registration receives `null`. Event contexts expose shared execution authority
130
+ but no request, route, response, middleware, or Router-transfer surface, and
131
+ handlers complete with `void`.
132
+
133
+ Eligible event handlers lower to the provider-neutral Native plan and execute
134
+ through the conditional `pulse.native-event-abi.v1` entry. Event-only and mixed
135
+ artifacts are supported; HTTP-only Native bytes remain unchanged. Node wraps
136
+ this entry with an invocation-scoped bounded FIFO reference adapter for direct
137
+ JavaScript/Native parity. That adapter is provider-maintainer infrastructure,
138
+ not a public listener, deployment transport, or process-global bus.
139
+
140
+ ## Outbound events
141
+
142
+ HTTP and event handlers can create a one-way, schema-bound effect:
143
+
144
+ ```ts
145
+ await ctx.emit('device.led.set', {
146
+ schema: 'events.DeviceLedSet',
147
+ payload: { enabled: true },
148
+ })
149
+
150
+ await ctx.emit('system.tick', { schema: null })
151
+ ```
152
+
153
+ The call must be awaited directly or through an awaited `ctx.parallel` group.
154
+ Event type and schema IDs must be literal at compilation; non-null schemas must
155
+ resolve in the project registry and require a payload, while `schema: null`
156
+ forbids one. JavaScript and Native execution validate and detach the same frame
157
+ before host acceptance and return `undefined`. Native suspends and resumes
158
+ through the ordinary effect/continuation protocol. The Node reference adapter
159
+ records exact accepted frames independently from its FIFO ingress queue, so it
160
+ never performs automatic loopback. It does not deliver a receipt, persistence,
161
+ retry, or public provider transport.
162
+
163
+ `ctx.emit` is not a call operation. The runtime exposes no request/reply
164
+ correlation, automatic local dispatch, `ctx.call`, or reserved call capability.
165
+ See [Static events and outbound emission](../guides/events.md) for the frame,
166
+ queue, target, and Native-extension details.
167
+
168
+ ## Logging
169
+
170
+ `ctx.log` exposes four synchronous string methods:
171
+
172
+ ```ts
173
+ ctx.log.error('failed to publish event')
174
+ ctx.log.warn('retrying origin request')
175
+ ctx.log.info('user created')
176
+ ctx.log.debug('decoded request body')
177
+ ```
178
+
179
+ The active profile’s flat `reporting` setting resolves `off`, `error`, `warn`,
180
+ `info`, or `debug`; the default is `info`. Native lowering removes calls below
181
+ the resolved threshold and writes enabled messages through the synchronous
182
+ `pulse_log(level, ptr, len)` host ABI. JavaScript targets use the same threshold
183
+ and provider-owned destination at runtime.
184
+
185
+ Logging is not a Pulse effect and cannot suspend a handler. Sink failures are
186
+ request-contained, and known request secrets are redacted before managed output.
187
+ Messages are strings only; structured logging and application-significant side
188
+ effects inside message expressions are outside the Beta contract.
189
+
190
+ ## Responses
191
+
192
+ ```ts
193
+ return ctx.json({ ok: true }, { status: 201 })
194
+ return ctx.text('not found', { status: 404 })
195
+ return ctx.response({ status: 204 })
196
+ ```
197
+
198
+ A schema-backed JSON response names its compiled schema:
199
+
200
+ ```ts
201
+ return ctx.json(output, { schema: 'app.CreateUserOutput' })
202
+ ```
203
+
204
+ ## Host capabilities
205
+
206
+ Canonical host work is requested through `ctx`:
207
+
208
+ ```ts
209
+ const upstream = await ctx.fetch('https://api.example.test/items').json('app.ItemList')
210
+ const mode = await ctx.config.get('MODE')
211
+ const token = await ctx.secret.get('API_TOKEN')
212
+ const session = await ctx.kv<{ userId: number }>('sessions').get('current')
213
+ ```
214
+
215
+ Conditional KV extends the same namespace with `getVersioned`, `insertIfAbsent`,
216
+ and `compareAndSwap`. The Node reference realizes them through JavaScript and
217
+ compiled Native execution; Fastly Native uses direct conditional KV hostcalls. Reads pair a value with an opaque string generation;
218
+ writes distinguish `stored`, `conflict`, `not-stored`, and `unknown` (possibly
219
+ committed). Pulse snapshots candidates, preserves tokens without numeric
220
+ coercion, and performs no automatic retry. Fastly JavaScript remains incomplete;
221
+ K4 retains the deployed acceptance gate.
222
+ The detailed contract is in [Effects and continuations](../concepts/effects-and-continuations.md#conditional-kv).
223
+
224
+ Those calls are compiled into explicit effects and validated against the selected provider. See [Compilation and lowering](../concepts/compilation-and-lowering.md).
225
+
226
+ ## Structured and opaque fetch results
227
+
228
+ A response can be inspected as bounded structured data:
229
+
230
+ ```ts
231
+ const item = await ctx.fetch('https://api.example.test/item').json<{ id: number }>('app.Item')
232
+ return ctx.json(item)
233
+ ```
234
+
235
+ Or returned directly as an opaque pass-through response:
236
+
237
+ ```ts
238
+ return ctx.fetch('https://assets.example.test/archive.bin')
239
+ ```
240
+
241
+ Opaque bodies are returnable but not inspectable or iterable. See [Structured and opaque bodies](../concepts/bodies.md).
242
+
243
+ ## Deliberate exclusions
244
+
245
+ The runtime contract does not expose:
246
+
247
+ - arbitrary Promise construction or general Promise semantics under native lowering;
248
+ - ambient `process.env`, global fetch, timers, or randomness;
249
+ - provider SDK objects or provider-specific namespaces;
250
+ - arbitrary binary body inspection;
251
+ - userland stream transforms or background tasks;
252
+ - raw sockets.
253
+
254
+ The compiler rejects unsupported forms rather than treating them as
255
+ provider-dependent behavior. See the
256
+ [Beta scope](../preview-scope.md).
257
+
258
+ ## Package and installed references
259
+
260
+ The npm tarball includes:
261
+
262
+ - `docs/API.md` — the canonical API reference;
263
+ - `docs/preview-scope.md` — the supported and excluded Beta surface.
264
+
265
+ For workflow and project configuration, use [`@pulse-compute/cli`](./cli.md).