@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.
Files changed (190) hide show
  1. package/API.md +539 -0
  2. package/CHANGELOG.md +47 -0
  3. package/README.md +70 -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 +381 -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 +243 -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 +62 -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 +184 -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 +84 -0
  48. package/docs/maintainers/maintenance-policy.json +778 -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 +250 -0
  55. package/docs/maintainers/release-manifest.md +88 -0
  56. package/docs/maintainers/repository-setup.md +145 -0
  57. package/docs/maintainers/scope-policy.md +93 -0
  58. package/docs/maintainers/support-and-triage.md +74 -0
  59. package/docs/maintainers/testing.md +288 -0
  60. package/docs/packages/README.md +59 -0
  61. package/docs/packages/assets.md +85 -0
  62. package/docs/packages/cli.md +190 -0
  63. package/docs/packages/crypto.md +71 -0
  64. package/docs/packages/entities.md +129 -0
  65. package/docs/packages/grip.md +82 -0
  66. package/docs/packages/implementation-packages.md +113 -0
  67. package/docs/packages/jwt.md +55 -0
  68. package/docs/packages/provider-fastly.md +217 -0
  69. package/docs/packages/pulse.md +159 -0
  70. package/docs/packages/runtime.md +256 -0
  71. package/docs/preview-scope.md +142 -0
  72. package/docs/reference/README.md +33 -0
  73. package/docs/reference/cli-spec.json +575 -0
  74. package/docs/reference/cli.md +457 -0
  75. package/docs/reference/compatibility-matrix.md +169 -0
  76. package/docs/reference/diagnostics.md +1872 -0
  77. package/docs/reference/documentation-versions.json +14 -0
  78. package/docs/reference/environment.md +347 -0
  79. package/docs/reference/handler-authoring.md +264 -0
  80. package/docs/reference/project-config.md +554 -0
  81. package/docs/reference/project-config.schema.json +918 -0
  82. package/docs/reference/release-manifest.json +427 -0
  83. package/docs/reference/shell-completion.md +23 -0
  84. package/documentation-site.json +661 -0
  85. package/documentation-versions.json +14 -0
  86. package/examples/01-hello-json/.pulse/.gitignore +3 -0
  87. package/examples/01-hello-json/.pulse/config.ts +16 -0
  88. package/examples/01-hello-json/README.md +119 -0
  89. package/examples/01-hello-json/package.json +20 -0
  90. package/examples/01-hello-json/src/index.ts +9 -0
  91. package/examples/01-hello-json/tests/pulse.harness.ts +22 -0
  92. package/examples/01-hello-json/tsconfig.json +14 -0
  93. package/examples/02-request-schema/.pulse/.gitignore +3 -0
  94. package/examples/02-request-schema/.pulse/config.ts +17 -0
  95. package/examples/02-request-schema/README.md +145 -0
  96. package/examples/02-request-schema/package.json +20 -0
  97. package/examples/02-request-schema/src/index.ts +18 -0
  98. package/examples/02-request-schema/src/schemas.ts +20 -0
  99. package/examples/02-request-schema/tests/pulse.harness.ts +37 -0
  100. package/examples/02-request-schema/tsconfig.json +14 -0
  101. package/examples/03-fetch-composition/.pulse/.gitignore +3 -0
  102. package/examples/03-fetch-composition/.pulse/config.ts +31 -0
  103. package/examples/03-fetch-composition/README.md +210 -0
  104. package/examples/03-fetch-composition/package.json +20 -0
  105. package/examples/03-fetch-composition/src/index.ts +60 -0
  106. package/examples/03-fetch-composition/tests/pulse.harness.ts +47 -0
  107. package/examples/03-fetch-composition/tsconfig.json +14 -0
  108. package/examples/05-fastly-capabilities/.pulse/.gitignore +3 -0
  109. package/examples/05-fastly-capabilities/.pulse/config.ts +57 -0
  110. package/examples/05-fastly-capabilities/README.md +257 -0
  111. package/examples/05-fastly-capabilities/package.json +22 -0
  112. package/examples/05-fastly-capabilities/src/index.ts +55 -0
  113. package/examples/05-fastly-capabilities/tests/pulse.harness.ts +66 -0
  114. package/examples/05-fastly-capabilities/tsconfig.json +14 -0
  115. package/examples/07-opaque-proxy/.pulse/.gitignore +3 -0
  116. package/examples/07-opaque-proxy/.pulse/config.ts +38 -0
  117. package/examples/07-opaque-proxy/README.md +150 -0
  118. package/examples/07-opaque-proxy/package.json +21 -0
  119. package/examples/07-opaque-proxy/src/index.ts +9 -0
  120. package/examples/07-opaque-proxy/tests/pulse.harness.ts +25 -0
  121. package/examples/07-opaque-proxy/tsconfig.json +14 -0
  122. package/examples/09-router-lowering/.pulse/.gitignore +3 -0
  123. package/examples/09-router-lowering/.pulse/config.ts +22 -0
  124. package/examples/09-router-lowering/README.md +287 -0
  125. package/examples/09-router-lowering/package.json +22 -0
  126. package/examples/09-router-lowering/src/index.ts +65 -0
  127. package/examples/09-router-lowering/tests/pulse.harness.ts +91 -0
  128. package/examples/09-router-lowering/tsconfig.json +14 -0
  129. package/examples/10-entities-tools/.pulse/.gitignore +4 -0
  130. package/examples/10-entities-tools/.pulse/config.ts +23 -0
  131. package/examples/10-entities-tools/README.md +165 -0
  132. package/examples/10-entities-tools/package.json +21 -0
  133. package/examples/10-entities-tools/src/handlers.ts +15 -0
  134. package/examples/10-entities-tools/src/index.ts +31 -0
  135. package/examples/10-entities-tools/src/schemas.ts +18 -0
  136. package/examples/10-entities-tools/tests/pulse.harness.ts +40 -0
  137. package/examples/10-entities-tools/tools-facade.cjs +158 -0
  138. package/examples/10-entities-tools/tsconfig.json +14 -0
  139. package/examples/11-events/.pulse/.gitignore +4 -0
  140. package/examples/11-events/.pulse/config.ts +24 -0
  141. package/examples/11-events/README.md +194 -0
  142. package/examples/11-events/package.json +20 -0
  143. package/examples/11-events/src/index.ts +27 -0
  144. package/examples/11-events/src/schemas.ts +19 -0
  145. package/examples/11-events/tests/pulse.harness.ts +34 -0
  146. package/examples/11-events/tsconfig.json +15 -0
  147. package/examples/12-mcp-proxy/.pulse/.gitignore +3 -0
  148. package/examples/12-mcp-proxy/.pulse/config.ts +25 -0
  149. package/examples/12-mcp-proxy/README.md +149 -0
  150. package/examples/12-mcp-proxy/package.json +20 -0
  151. package/examples/12-mcp-proxy/src/index.ts +17 -0
  152. package/examples/12-mcp-proxy/tests/pulse.harness.ts +29 -0
  153. package/examples/12-mcp-proxy/tsconfig.json +14 -0
  154. package/examples/13-jwt-es256/.pulse/config.ts +19 -0
  155. package/examples/13-jwt-es256/README.md +195 -0
  156. package/examples/13-jwt-es256/package.json +21 -0
  157. package/examples/13-jwt-es256/src/index.ts +21 -0
  158. package/examples/13-jwt-es256/tests/pulse.harness.ts +54 -0
  159. package/examples/13-jwt-es256/tsconfig.json +14 -0
  160. package/examples/README.md +36 -0
  161. package/package.json +66 -6
  162. package/project-config.schema.json +918 -0
  163. package/release-manifest.json +427 -0
  164. package/src/command-spec.js +279 -0
  165. package/src/completion.js +113 -0
  166. package/src/diagnostics.js +350 -0
  167. package/src/documentation.js +45 -0
  168. package/src/index.d.ts +133 -0
  169. package/src/index.js +15 -0
  170. package/src/internal/command-executor.d.ts +29 -0
  171. package/src/internal/command-executor.js +143 -0
  172. package/src/internal/command-plan.d.ts +9 -0
  173. package/src/internal/command-plan.js +73 -0
  174. package/src/internal/command-reporter.d.ts +15 -0
  175. package/src/internal/command-reporter.js +133 -0
  176. package/src/internal/command-request.d.ts +35 -0
  177. package/src/internal/command-request.js +154 -0
  178. package/src/internal/node-http.js +54 -0
  179. package/src/internal/project-context.d.ts +66 -0
  180. package/src/internal/project-context.js +175 -0
  181. package/src/project-config-reference.js +4 -0
  182. package/src/project-config-schema.d.ts +23 -0
  183. package/src/project-config-schema.js +271 -0
  184. package/src/project-config.js +724 -0
  185. package/src/project-execution.js +2930 -0
  186. package/src/provider-drivers.js +3 -0
  187. package/src/target-support.js +3 -0
  188. package/src/typescript-module-loader.js +269 -0
  189. package/src/workflow.js +78 -0
  190. package/src/workspace.js +82 -0
package/API.md ADDED
@@ -0,0 +1,539 @@
1
+ # Pulse runtime contract
2
+
3
+ For source eligibility, use [Managed handler TypeScript and
4
+ JavaScript](./docs/reference/handler-authoring.md). For provider and target
5
+ differences, use the [compatibility
6
+ matrix](./docs/reference/compatibility-matrix.md). For CLI and runtime failures,
7
+ use the stable codes in the [diagnostics
8
+ reference](./docs/reference/diagnostics.md).
9
+
10
+ This document describes the provider-neutral TypeScript application contract compiled by Pulse. Low-level authoring types and the static `Router` come from `@pulse-compute/runtime`. The public `@pulse-compute/pulse` package owns the conventional `Pulse` application root, deferred project configuration, and schema declarations, while `@pulse-compute/cli` owns workspace orchestration.
11
+
12
+ `@pulse-compute/runtime` is the low-level portable application surface and
13
+ `@pulse-compute/pulse` is the conventional project surface. Both belong to the
14
+ 14-package public release catalog. Native and JavaScript execution remain
15
+ explicitly selected targets over the same canonical runtime contract.
16
+
17
+ ## Context at a glance
18
+
19
+ `ctx` is the complete application authority. There is no ambient request,
20
+ process, provider SDK, or global network surface behind it.
21
+
22
+ | Surface | Available in | Purpose |
23
+ |---|---|---|
24
+ | [`ctx.req`](#ctxreq) | HTTP handlers and middleware | Method, URL, path, ordered headers, and bounded body reads. |
25
+ | [`ctx.req.header(name)`](#request-metadata-and-headers) | HTTP handlers and middleware | Case-insensitive first-value header lookup. |
26
+ | [`ctx.param(name)`](#ctxparam) | Matched route handlers | Named parameters from the static route pattern. |
27
+ | [`ctx.state`](#ctxstate) | HTTP and event handlers | Invocation-local string state shared across one execution. |
28
+ | [`ctx.fetch`](#ctxfetch) | HTTP and event handlers | Explicit outbound HTTP effect and structured or opaque response ownership. |
29
+ | [`ctx.parallel`](#ctxparallel) | HTTP and event handlers | Statically keyed concurrent Pulse effects. |
30
+ | [`ctx.emit`](#ctxemit) | HTTP and event handlers | One-way, schema-bound event acceptance effect. |
31
+ | [`ctx.log`](#ctxlog) | HTTP and event handlers | Synchronous thresholded logging. |
32
+ | [`ctx.config`, `ctx.secret`](#config-and-secrets) | HTTP and event handlers | Explicit configured binding reads. |
33
+ | [`ctx.kv(name)`](#kv) | HTTP and event handlers | Bound `get` and `put` storage effects. |
34
+ | [`ctx.json`, `ctx.text`, `ctx.response`](#response-builders) | HTTP handlers and middleware | Synchronous response construction. |
35
+ | `ctx.event` | Event handlers only | Exact event type and immutable validated payload. |
36
+
37
+ ## Handler
38
+
39
+ ```ts
40
+ import type { Handler, PulseContext } from '@pulse-compute/runtime'
41
+
42
+ const handler: Handler = async (ctx: PulseContext) => ctx.json({ ok: true })
43
+ export default handler
44
+ ```
45
+
46
+ Every managed handler is async-shaped:
47
+
48
+ ```ts
49
+ type Handler = (ctx: PulseContext) => Promise<PulseResult | PulseFetchResponse>
50
+ ```
51
+
52
+ For native targets the compiler erases the async wrapper. Awaited Pulse effects lower into the existing explicit effect and continuation state machine; no Promise runtime or Asyncify transform is linked. Awaiting a proven synchronous `ctx` expression is redundant and may warn, while arbitrary non-Pulse awaits mark the native eligibility boundary. The canonical [handler authoring reference](./docs/reference/handler-authoring.md) defines the static language subset; the [compatibility matrix](./docs/reference/compatibility-matrix.md) owns the tested four-mode claims.
53
+
54
+ ## Static `Router`
55
+
56
+ ```ts
57
+ import { Router } from '@pulse-compute/runtime'
58
+
59
+ const api = new Router()
60
+ const app = new Router()
61
+
62
+ api.use(async (ctx, next) => {
63
+ if (ctx.req.header('authorization') === undefined) {
64
+ return ctx.text('Unauthorized', { status: 401 })
65
+ }
66
+ return next()
67
+ })
68
+
69
+ api.get('/health', async (ctx) => ctx.json({ ok: true }))
70
+ api.get('/users/:id', async (ctx, next) => {
71
+ if (ctx.param('id') === '0') return next()
72
+ return ctx.json({ id: ctx.param('id') })
73
+ })
74
+ api.get('/users/:id', async (ctx) => ctx.json({ fallback: ctx.param('id') }))
75
+
76
+ api.error(async (error, ctx, next) => {
77
+ const routedError = error as { code?: string }
78
+ if (routedError.code === 'NOT_FOUND') return ctx.text('Missing', { status: 404 })
79
+ return next(error)
80
+ })
81
+
82
+ app.mount('/api', api)
83
+ export default app
84
+ ```
85
+
86
+ `Router` is a compile-time marker. Canonical v2 supports `get`, `head`, `post`, exact paths, named parameters, a trailing wildcard, static mounts, global/path-scoped/mounted middleware, route fallthrough, and error middleware. `ctx.param(name)` returns the matched named parameter inside route handlers.
87
+
88
+ `next()` is a terminal control transfer. `return next()` advances the normal Router cursor and permanently ends the current handler scope. `return next(error)` enters or advances the error lane. There is no onion-style downstream return or post-`next()` resume. Normal and error exhaustion produce compiler-owned 404 and 500 responses respectively.
89
+
90
+ The flattened execution graph, route table, and entry-owned effects/continuations are reported by `pulse inspect`. See the [routing guide](./docs/guides/routing.md).
91
+
92
+ ## Static event handlers
93
+
94
+ `Pulse.on` declares an exact event entry beside, not inside, Router topology:
95
+
96
+ ```ts
97
+ import { Pulse } from '@pulse-compute/pulse'
98
+
99
+ const app = new Pulse({ auto: true })
100
+
101
+ app.on('device.reading', { schema: 'events.DeviceReading' }, async (ctx) => {
102
+ const mode = await ctx.config.get('MODE')
103
+ ctx.state.set('mode', mode)
104
+ void ctx.event.payload
105
+ })
106
+
107
+ app.on('system.tick', { schema: null }, async (ctx) => {
108
+ void ctx.event.type
109
+ })
110
+ ```
111
+
112
+ The type and schema are literal compiler inputs, every type has one owner, and
113
+ non-null schemas resolve through the project registry. `ctx.event.type` is the
114
+ exact registered type; `ctx.event.payload` is an immutable schema-validated
115
+ value, or `null` for a no-payload registration. Event handlers share state,
116
+ logging, fetch, config, secret, KV, and keyed-parallel authority, but expose no
117
+ request, route, response, middleware, or Router-transfer surface and complete
118
+ with `void`.
119
+
120
+ Eligible handlers lower into plane-neutral application entries and execute
121
+ through the conditional provider-neutral `pulse.native-event-abi.v1`
122
+ extension. Event-only and mixed HTTP/event artifacts are supported, while
123
+ HTTP-only Native output remains byte-identical and has no event ABI. This does
124
+ not by itself activate a provider transport. The Node provider now has an
125
+ invocation-scoped bounded FIFO reference ingress/acceptance adapter for direct
126
+ JavaScript and Native parity evidence; it is not a public event bus or a
127
+ deployment listener.
128
+
129
+ Conventional project harnesses can exercise this reference boundary with an
130
+ explicit `kind: 'event'` case, a canonical input frame, and an ordered exact
131
+ `expect.emitted` frame list. Node JavaScript and Node Native support that test
132
+ workflow. `pulse inspect` and `pulse doctor` report the event catalog,
133
+ registrations, schemas, outbound callsites, host requirements, and selected
134
+ target support; eligible build and compile output includes `event-catalog.json`
135
+ and `event-inspection.json`. Compile-only `none` reports inspection-only
136
+ eligibility. Fastly targets fail closed because no event ingress/emit adapter is
137
+ claimed. There is no public event injection command, no event-aware development
138
+ listener, no HTTP/GRIP translation, and no automatic target fallback.
139
+
140
+ The [static events guide](./docs/guides/events.md) owns the complete frame,
141
+ queue, target-eligibility, diagnostic, and Native-extension contract. The
142
+ source-bound [event example](./examples/11-events/) runs the same mixed project
143
+ on Node JavaScript and Node Native.
144
+
145
+ ## `ctx.req`
146
+
147
+ ```ts
148
+ interface PulseRequest {
149
+ readonly method: string
150
+ readonly url: string
151
+ readonly path: string
152
+ readonly headers: readonly [string, string][]
153
+ header(name: string): string | undefined
154
+ text(): PulseEffect<string>
155
+ json<T = unknown>(schemaId?: string): PulseEffect<T>
156
+ }
157
+ ```
158
+
159
+ Structured request bodies are bounded runtime-owned snapshots. Repeated `text()` and `json()` reads are memoized immutable transforms.
160
+
161
+ ### Request metadata and headers
162
+
163
+ ```ts
164
+ const requestId = ctx.req.header('x-request-id')
165
+ const authorization = ctx.req.header('Authorization')
166
+ ```
167
+
168
+ `header(name)` compares names case-insensitively, returns the first matching
169
+ value, and returns `undefined` when the header is absent. Use
170
+ `ctx.req.headers` when order or repeated fields matter: it is an immutable,
171
+ ordered array of `[name, value]` pairs and preserves repeated pairs. Pulse does
172
+ not expose an ambient `Request` or mutable `Headers` object to application
173
+ code.
174
+
175
+ `method` is normalized to uppercase, `url` is the complete request URL, and
176
+ `path` is its pathname.
177
+
178
+ When a schema ID is supplied, it must be a literal declared by the selected `.pulse/config.ts` profile:
179
+
180
+ ```ts
181
+ const input = await ctx.req.json<Input>('app.Input')
182
+ ```
183
+
184
+ ## `ctx.param`
185
+
186
+ ```ts
187
+ app.get('/users/:id', async (ctx) => {
188
+ const id = ctx.param('id')
189
+ return id === undefined
190
+ ? ctx.text('missing route parameter', { status: 500 })
191
+ : ctx.json({ id })
192
+ })
193
+ ```
194
+
195
+ `ctx.param(name)` is synchronous and is available only to a matched route
196
+ handler. It returns the decoded value owned by the static route match or
197
+ `undefined` when the named parameter is not present. Middleware and event
198
+ handlers do not receive route-parameter authority.
199
+
200
+ ## `ctx.state`
201
+
202
+ ```ts
203
+ ctx.state.set('request-id', 'r1')
204
+ const requestId = ctx.state.get('request-id') // string | undefined
205
+ ```
206
+
207
+ State is a synchronous, invocation-local string map. One HTTP execution shares
208
+ it across forward middleware, mounted Routers, error recovery, and effect
209
+ continuation resume. An event handler retains its state across continuation
210
+ resume. State is isolated between requests and event invocations; it is not
211
+ durable storage and does not cross an invocation boundary.
212
+
213
+ ## `ctx.fetch`
214
+
215
+ ```ts
216
+ interface PulseFetchInit {
217
+ readonly method?: 'GET' | 'HEAD' | 'POST'
218
+ readonly headers?: Readonly<Record<string, string>> | readonly [string, string][]
219
+ readonly body?: string
220
+ readonly json?: unknown
221
+ readonly timeoutMs?: number
222
+ }
223
+
224
+ ctx.fetch(url: string, init?: PulseFetchInit): PulseFetchOperation
225
+ ```
226
+
227
+ HTTP status is response data. Network and timeout failures are runtime failures.
228
+
229
+ ```ts
230
+ const user = await ctx.fetch('https://api.example.test/user').json<User>('app.User')
231
+ return ctx.json({ found: true, user })
232
+ ```
233
+
234
+ Consecutive independent fetches may lower into a deterministic effect group. Results are presented in declaration order, not host completion order.
235
+
236
+
237
+ ## `ctx.parallel`
238
+
239
+ Use `ctx.parallel({ ... })` to make concurrent Pulse effects an explicit,
240
+ key-preserving cross-target contract:
241
+
242
+ ```ts
243
+ const { user, permissions } = await ctx.parallel({
244
+ user: ctx.fetch('https://api.example.test/user').json<User>(),
245
+ permissions: ctx.fetch('https://api.example.test/permissions').json<Permissions>(),
246
+ })
247
+ ```
248
+
249
+ Its TypeScript result preserves every input key and the independently inferred
250
+ result type of that effect. The initial portable form accepts exactly one nonempty
251
+ inline object literal with fixed identifier or string-literal keys and directly
252
+ recognizable Pulse effects as values. Array-index keys, `__proto__`, arrays,
253
+ spreads, computed keys, shorthand properties, methods, accessors, dynamic records,
254
+ arbitrary promises, reused effect roots, and nested parallel groups are rejected.
255
+
256
+ Source property order defines effect registration, deterministic identity, result
257
+ reconstruction, trace order, and primary-failure ownership. Operations may finish
258
+ in any order, but all settle before the continuation proceeds. When multiple
259
+ members fail, the first failure in property order is primary and bounded keyed
260
+ failure evidence identifies the others.
261
+
262
+ The JavaScript runtime executes every member through one execution-owned shared
263
+ effect adapter. Native lowering erases the call into one canonical effect group
264
+ and reconstructs an ordinary keyed object after one continuation. Separate awaits
265
+ remain sequential in direct JavaScript execution; the Native compiler may still
266
+ implicitly group adjacent eligible effects as a performance optimization.
267
+
268
+ ## `ctx.emit`
269
+
270
+ HTTP and event handlers share a one-way outbound event effect:
271
+
272
+ ```ts
273
+ await ctx.emit('device.led.set', {
274
+ schema: 'events.DeviceLedSet',
275
+ payload: { enabled: true },
276
+ })
277
+
278
+ await ctx.emit('system.tick', { schema: null })
279
+ ```
280
+
281
+ The event type and schema are literal compiler inputs. A non-null schema must
282
+ resolve through the project registry and requires `payload`; `schema: null`
283
+ forbids it. JavaScript and Native runtimes validate and detach the same
284
+ canonical frame before passing it to the execution-owned adapter. Public effect
285
+ evidence redacts the payload.
286
+
287
+ The effect is valid as a fresh member of an awaited `ctx.parallel` group and
288
+ resolves to `undefined` after bounded host acceptance. It does not return a
289
+ delivery receipt, correlation ID, or handler result, and it never invokes a
290
+ matching local event handler. Native lowering uses the ordinary canonical
291
+ effect/continuation protocol and adds no JavaScript or Asyncify imports. The
292
+ invocation-scoped Node reference adapter provides bounded host acceptance and
293
+ exact-frame evidence in JavaScript and Native modes; it provides no delivery,
294
+ retry, persistence, public event bus, or automatic loopback. Other provider
295
+ realizations remain unclaimed.
296
+
297
+ There is no `ctx.call`, `app.call`, generic call effect, request/reply bus, or
298
+ reserved compiler/runtime opcode for reflexive routing. `ctx.emit` cannot
299
+ observe or invoke a local handler. A future call mechanism requires a separate
300
+ host and lifecycle contract.
301
+
302
+ ## `ctx.log`
303
+
304
+ Logging is synchronous, string-only, and provider-neutral:
305
+
306
+ ```ts
307
+ ctx.log.error('failed to publish event')
308
+ ctx.log.warn('retrying origin request')
309
+ ctx.log.info('user created')
310
+ ctx.log.debug('decoded request body')
311
+ ```
312
+
313
+ The fixed levels are `error = 1`, `warn = 2`, `info = 3`, and `debug = 4`.
314
+ `off = 0` is configuration-only. A statement emits when its level is less than
315
+ or equal to the selected profile’s resolved `reporting` level; the default is
316
+ `info`.
317
+
318
+ `ctx.log` is not an effect, continuation, or asynchronous operation. Native
319
+ lowering erases statements disabled by the resolved build threshold. JavaScript
320
+ targets filter at runtime, so a disabled JavaScript call may still evaluate its
321
+ message expression; application-significant side effects do not belong in log
322
+ expressions. Provider formatting and destination are intentionally outside the
323
+ portable contract.
324
+
325
+ Logging failures do not fail the request. Known request secrets pass through the
326
+ same redaction boundary before provider emission and evidence capture. The
327
+ initial contract accepts only strings; structured logging and dynamic level
328
+ registration are not supported.
329
+
330
+ ## Fetch responses
331
+
332
+ Structured responses expose:
333
+
334
+ ```ts
335
+ response.status
336
+ response.ok
337
+ response.headers
338
+ response.header(name)
339
+ response.text()
340
+ response.json<T>(schemaId?)
341
+ ```
342
+
343
+ `response.header(name)` uses the same case-insensitive, first-value lookup as
344
+ `ctx.req.header(name)`. `response.headers` retains immutable ordered pairs when
345
+ repeated response fields must be observed. HTTP status is always response data;
346
+ only transport and timeout failures reject the fetch effect.
347
+
348
+ Opaque responses preserve a host-owned body handle and may be returned directly:
349
+
350
+ <!-- pulse-doc-source: examples/07-opaque-proxy/src/index.ts -->
351
+ ```ts
352
+ import { Pulse } from '@pulse-compute/pulse'
353
+
354
+ const app = new Pulse({ auto: true })
355
+
356
+ app.get('/archive', async (ctx) => {
357
+ return ctx.fetch('https://assets.example.com/archive.bin')
358
+ })
359
+
360
+ export default app
361
+ ```
362
+ <!-- /pulse-doc-source -->
363
+
364
+ Opaque bodies cannot be decoded, copied into Wasm, mutated, iterated, or transformed in user scope.
365
+
366
+ ## Response builders
367
+
368
+ ```ts
369
+ ctx.json(value, options?)
370
+ ctx.text(value, options?)
371
+ ctx.response({ status?, headers?, body? })
372
+ ```
373
+
374
+ JSON responses may use an explicit output schema:
375
+
376
+ ```ts
377
+ return ctx.json(output, {
378
+ status: 201,
379
+ headers: { 'x-schema': 'app.Output' },
380
+ schema: 'app.Output',
381
+ })
382
+ ```
383
+
384
+ ## Config and secrets
385
+
386
+ ```ts
387
+ ctx.config.get(name: string): PulseEffect<string | undefined>
388
+ ctx.secret.get(name: string): PulseEffect<string | undefined>
389
+ ```
390
+
391
+ Reads use exact, provider-injected names. Missing values resolve to `undefined`; Pulse does not enumerate bindings, consult inherited properties, or fall back to `process.env`. Names and returned strings are UTF-8 byte-bounded.
392
+
393
+ Resolved secret values are registered with the execution-owned redaction boundary. Pulse removes known secret substrings and sensitive fields from its own observations, traces, diagnostics, and managed errors. This is not taint tracking: application responses are never silently rewritten, and ordinary config values are not automatically treated as secrets.
394
+
395
+ ## KV
396
+
397
+ ```ts
398
+ const sessions = ctx.kv<Session>('sessions')
399
+ const value = await sessions.get('current')
400
+ if (value !== undefined) await sessions.put('last', value)
401
+ ```
402
+
403
+ The Beta supports `get` and `put`. Store names and keys are
404
+ provider-neutral logical bindings. Values are bounded, detached, deeply frozen
405
+ JSON-compatible trees; accessors, symbols, sparse arrays, repeated references,
406
+ cycles, class instances, nonfinite numbers, and nested `undefined` are rejected.
407
+ `put` resolves to an explicit boolean acknowledgement. Durability, consistency,
408
+ and cross-request lifetime remain provider capabilities rather than properties
409
+ of the common API.
410
+
411
+ ## Explicit JSON schemas
412
+
413
+ <!-- pulse-doc-source: examples/02-request-schema/.pulse/config.ts -->
414
+ ```ts
415
+ import { defineConfig } from '@pulse-compute/pulse'
416
+
417
+ export default defineConfig((_scope) => ({
418
+ pulse: {
419
+ entry: 'src/index.ts',
420
+ schema: 'src/schemas.ts',
421
+ tests: 'tests/pulse.harness.ts',
422
+ defaultProfile: 'local',
423
+ strict: true,
424
+ },
425
+ local: {
426
+ host: 'node',
427
+ target: 'native',
428
+ outDir: 'dist',
429
+ schemas: { contentTypePolicy: 'require-json', maxBytes: 1024 },
430
+ },
431
+ }))
432
+ ```
433
+ <!-- /pulse-doc-source -->
434
+
435
+ Pulse compiles declared TypeScript interfaces and type aliases into a registry and direct codecs. It does not discover arbitrary types automatically.
436
+
437
+ ## GRIP
438
+
439
+ The canonical GRIP application surface is the package root:
440
+
441
+ ```ts
442
+ import { grip } from '@pulse-compute/grip'
443
+ ```
444
+
445
+ Pure request classification and response framing use `grip.isWebSocket`,
446
+ `grip.subscribe`, and `grip.handoff`. Configured outbound work is the
447
+ request-bound `await grip.broadcast(ctx, message)` effect. Supported Native
448
+ forms lower into canonical package operations; JavaScript targets execute the
449
+ real package implementation.
450
+
451
+ Older `/pulsewasm` imports are compatibility-only and are isolated in the
452
+ [migration guide](./docs/guides/compatibility-imports.md). See the
453
+ [GRIP package guide](./docs/packages/grip.md) for the complete current surface.
454
+
455
+ ## Entities API
456
+
457
+ `@pulse-compute/entities` is part of the synchronized `1.0.0-beta.1` package
458
+ set. The application surface has two runtime values:
459
+
460
+ ```ts
461
+ import { EntityRouter, jsonRpc } from '@pulse-compute/entities'
462
+
463
+ const rpc = new EntityRouter({ adapter: jsonRpc({ namedParamsOnly: true }) })
464
+
465
+ rpc.on('customer.lookup', {
466
+ input: 'tools.CustomerLookupInput',
467
+ output: 'tools.CustomerLookupOutput',
468
+ }, lookupCustomer)
469
+
470
+ export default function handler(ctx: unknown) {
471
+ return rpc.handle(ctx as never)
472
+ }
473
+ ```
474
+
475
+ ```ts
476
+ interface EntityRouterOptions {
477
+ readonly adapter: JsonRpcAdapter
478
+ }
479
+
480
+ interface EntityDeclaration {
481
+ readonly input: string | null
482
+ readonly output: string | null
483
+ readonly metadata?: StaticEntityMetadata
484
+ }
485
+
486
+ type EntityHandler<Input, Output> = (
487
+ ctx: PulseContext,
488
+ input: DeepReadonly<Input>,
489
+ ) => Output | Promise<Output>
490
+
491
+ class EntityRouter {
492
+ constructor(options: EntityRouterOptions)
493
+ on<Input, Output>(
494
+ discriminator: string,
495
+ declaration: EntityDeclaration,
496
+ handler: EntityHandler<Input, Output>,
497
+ ): this
498
+ handle(ctx: PulseContext): Promise<Response>
499
+ }
500
+
501
+ interface JsonRpcOptions {
502
+ readonly namedParamsOnly?: true
503
+ readonly acceptEmptyObjectForNoInput?: boolean
504
+ }
505
+
506
+ function jsonRpc(options?: JsonRpcOptions): JsonRpcAdapter
507
+ ```
508
+
509
+ `StaticJsonPrimitive`, `StaticJsonValue`, `StaticJsonObject`,
510
+ `StaticEntityMetadata`, `EntitySchemaId`, `DeepReadonly`,
511
+ `EntityHandlerResult`, `EntityAdapter`, and `JsonRpcAdapter` are also exported
512
+ as types. The package exposes no public runtime registry, schema codecs, raw
513
+ JSON access, provider objects, or third-party adapter registration.
514
+
515
+ Registrations and the terminal binding must use the supported static form. The
516
+ first-party JSON-RPC adapter accepts bounded JSON-RPC 2.0 request objects and
517
+ named params, validates declared schemas, uses stable error framing, and
518
+ acknowledges notifications with HTTP `204`. See the [package
519
+ guide](./docs/packages/entities.md), [entity/adapter
520
+ model](./docs/concepts/entities-and-adapters.md), and [executable
521
+ example](./examples/10-entities-tools/).
522
+
523
+ ## Project workflow
524
+
525
+ ```text
526
+ pulse init
527
+ pulse doctor
528
+ pulse test
529
+ pulse dev
530
+ pulse build
531
+ ```
532
+
533
+ These normal lifecycle commands resolve the same authoritative workspace,
534
+ selected profile, handler entry, schema declarations, provider bindings, and
535
+ output directory through `.pulse/config.ts`. `pulse inspect` is optional
536
+ observability, and `pulse compile` is the advanced provider-neutral Native
537
+ artifact command; neither is required before `pulse build`. See the [project
538
+ lifecycle guide](./docs/guides/project-lifecycle.md) and [CLI
539
+ reference](./docs/reference/cli.md).
package/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ Pulse follows semantic versioning for public releases. The repository begins its public history with the `1.0.0-beta.1` prerelease.
4
+
5
+ ## 1.0.0-beta.1 — Beta (2026-08-01)
6
+
7
+ The first public Beta establishes a native-first Pulse application contract
8
+ and completes the synchronized release candidate:
9
+
10
+ - promotes `@pulse-compute/pulse` into the synchronized public package set and
11
+ removes its duplicate Fastly-conditioned bootstrap so provider-specific
12
+ startup remains owned by `@pulse-compute/provider-fastly`.
13
+ - adds `--experimental-native-size` to provider-neutral compile and Native
14
+ provider builds. The Native compiler owns the experimental size profile and
15
+ the default remains unchanged.
16
+ - moves the fixed provider composition behind a neutral CLI facade. Concrete
17
+ host operations and target policy are now composed from provider-owned
18
+ modules, while Node remains the reference/smoke substrate.
19
+ - adds synchronous string logging through `ctx.log.error`, `warn`, `info`, and
20
+ `debug`, including profile thresholds, Native pruning, a compact host ABI,
21
+ provider-owned output, redaction, and four-mode conformance.
22
+ - completes direct Fastly JavaScript execution, provider capabilities, package
23
+ effects, local tooling, deterministic source/deployment packaging, and
24
+ downstream runtime compilation.
25
+ - seals full-target-support availability for Node and Fastly JavaScript without
26
+ automatic fallback.
27
+ - adds revision-bound release reports, sixteen-shard evidence aggregation,
28
+ offline Fastly Native/JavaScript candidates, and exact binary-patch replay.
29
+ - adds the bounded Crypto, JWT, and Entities packages to the synchronized
30
+ 18-package release catalog;
31
+ - synchronizes the Apache-2.0 package metadata, Beta documentation,
32
+ exact `v1.0.0-beta.1` routes, and npm `beta` policy without assigning `latest`;
33
+ - performs no deployment or publication; those operations remain separately
34
+ authorized release actions.
35
+
36
+ - `@pulse-compute/runtime` as the canonical application surface;
37
+ - static Router authoring with parameters, mounts, terminal middleware, fallthrough, and error lanes;
38
+ - whole-project analysis with explicit effects and continuations;
39
+ - schema-backed request, fetch, and response handling;
40
+ - provider-neutral portable Wasm and direct native Fastly realization;
41
+ - Node development, inspection, testing, and provider conformance;
42
+ - package-owned GRIP and asset lowering;
43
+ - generated, versioned documentation and guarded release candidates;
44
+ - explicit runtime-target support gates with no automatic fallback.
45
+
46
+ The Beta deliberately rejects unsupported Native source instead of
47
+ silently changing execution targets.
package/README.md CHANGED
@@ -1,3 +1,72 @@
1
1
  # @pulse-compute/cli
2
2
 
3
- This is an inert namespace-bootstrap package. It contains no executable code.
3
+ <!-- pulse-package-status:start -->
4
+ > **Support tier:** Canonical application surface<br>
5
+ > **Audience:** Pulse application authors, CI workflows, and tooling integrations.<br>
6
+ > **Install directly:** Yes. Install the package globally or as a project development dependency.<br>
7
+ > **Supported entry points:** `pulse binary`, `@pulse-compute/cli`, `@pulse-compute/cli/workflow`, `@pulse-compute/cli/project-config`, `@pulse-compute/cli/project-execution`, `@pulse-compute/cli/project-config-schema`, `@pulse-compute/cli/diagnostics`, `@pulse-compute/cli/project-config.schema.json`, `@pulse-compute/cli/cli-spec.json`, `@pulse-compute/cli/release-manifest.json`, `@pulse-compute/cli/documentation-versions.json`, `@pulse-compute/cli/documentation-site.json`<br>
8
+ > **Stability:** Supported Beta workflow and project-configuration contract.<br>
9
+ > **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.1/packages/cli/)
10
+ >
11
+ > This release-status block is generated from the synchronized `Pulse 1.0.0-beta.1` package policy.
12
+ <!-- pulse-package-status:end -->
13
+
14
+ The public CLI and conventional `@pulse-compute/pulse` application root are
15
+ both part of the synchronized `1.0.0-beta.1` release set. Validate the generated
16
+ workflow from the repository or the exact packed acceptance set:
17
+
18
+ ```bash
19
+ pnpm pulse -- init ./my-app
20
+ cd ./my-app
21
+ npm install
22
+ pulse doctor
23
+ pulse test
24
+ pulse inspect
25
+ pulse dev
26
+ pulse build
27
+ ```
28
+
29
+ `pulse init` writes exact catalog package versions and does not invoke a package manager. Project commands discover one authoritative `.pulse/config.ts` workspace, select a flat profile, compile the same canonical handler contract, and realize Node, Fastly, or compile-only `none`.
30
+
31
+ ## Supported programmatic entries
32
+
33
+ - package root for `defineConfig`, public types, diagnostics, and workflow integration;
34
+ - `workflow` for command parsing/execution;
35
+ - `project-config` for discovery and normalization;
36
+ - `project-execution` for canonical command orchestration;
37
+ - `diagnostics` for stable public descriptors and mappings.
38
+
39
+ Repository fixture/profile/task flags are not part of installed help or the public contract.
40
+
41
+ ## JavaScript support and eligibility
42
+
43
+ Profiles may select `target: 'javascript'` with either the Node or Fastly
44
+ provider. Both targets execute the live Router/context and package
45
+ implementations, and both satisfy their declared full-target-support gates.
46
+
47
+ `pulse inspect` and `pulse doctor` expose general target availability separately
48
+ from deterministic project eligibility and loader observations. `pulse test`
49
+ and `pulse dev` execute only when the reachable application, capabilities,
50
+ provider requirements, and packages are eligible. Fastly identifies this local
51
+ mode as provider emulation; it is not Node-provider fallback.
52
+
53
+ `pulse compile` remains provider-neutral while recording the configured
54
+ JavaScript target and static eligibility evidence in `pulse-compile.json`.
55
+ `pulse build` emits a deterministic Node or Fastly source package. Fastly output
56
+ also carries exact downstream compiler pins and deployment-candidate metadata;
57
+ the release gate compiles that closure to a runtime Wasm without deploying or
58
+ publishing it. Pending or blocked projects never switch to Native execution:
59
+ `automaticFallback` is always false.
60
+
61
+ ## Included documentation
62
+
63
+ - [Documentation index](./docs/README.md)
64
+ - [Package guide](./docs/packages/cli.md)
65
+ - [CLI reference](./docs/reference/cli.md)
66
+ - [Project configuration](./docs/reference/project-config.md)
67
+ - [Diagnostics](./docs/reference/diagnostics.md)
68
+ - [Environment variables](./docs/reference/environment.md)
69
+ - [Canonical API](./API.md)
70
+ - [Runnable examples](./examples/README.md)
71
+
72
+ These references and examples ship in the npm tarball and are checked after packing.
package/bin/pulse.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { runPulseWorkflowCli } = require('../src/workflow.js');
5
+
6
+ runPulseWorkflowCli(process.argv.slice(2), {
7
+ cwd: process.cwd(),
8
+ stdout: process.stdout,
9
+ stderr: process.stderr
10
+ }).then((result) => {
11
+ process.exitCode = result.status || 0;
12
+ }).catch((error) => {
13
+ process.stderr.write(`${error && error.stack ? error.stack : String(error)}\n`);
14
+ process.exitCode = 1;
15
+ });