@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
@@ -0,0 +1,287 @@
1
+ # Router lowering
2
+
3
+ Uses the static `Router` authoring surface to define terminal middleware, route
4
+ fallthrough, error handling, mounts, parameters, wildcards, and method-specific
5
+ routes. Pulse lowers the flat Router execution graph into the same canonical
6
+ native plan used by single-handler applications.
7
+
8
+ ## Workflow
9
+
10
+ ```bash
11
+ pulse doctor
12
+ pulse inspect
13
+ pulse test
14
+ pulse dev
15
+ pulse compile
16
+ pulse build
17
+ ```
18
+
19
+ The inspect result exposes Router execution entries and route/middleware
20
+ ownership for effects and continuations. `return next()` terminates the current
21
+ handler and advances dispatch; it never resumes.
22
+
23
+ <!-- pulse-doc-run {"project":"examples/09-router-lowering","args":["inspect","--json"]} -->
24
+ ```bash
25
+ pulse inspect --json
26
+ ```
27
+ ```json
28
+ {
29
+ "status": "ok",
30
+ "provider": {
31
+ "id": "node"
32
+ },
33
+ "compiler": {
34
+ "authoring": {
35
+ "kind": "router"
36
+ },
37
+ "routing": {
38
+ "version": "pulse.router-authoring.v2",
39
+ "routes": [
40
+ {
41
+ "method": "GET",
42
+ "path": "/api/health"
43
+ }
44
+ ]
45
+ },
46
+ "effectCount": 1,
47
+ "continuationCount": 1
48
+ }
49
+ }
50
+ ```
51
+
52
+ The documented test result below is also executed by the documentation gate.
53
+
54
+ <!-- pulse-doc-run {"project":"examples/09-router-lowering","args":["test","--json"]} -->
55
+ ```bash
56
+ pulse test --json
57
+ ```
58
+ ```json
59
+ {
60
+ "status": "passed",
61
+ "provider": "node",
62
+ "summary": {
63
+ "total": 9,
64
+ "passed": 9,
65
+ "failed": 0
66
+ },
67
+ "cases": [
68
+ {
69
+ "name": "mounted-health",
70
+ "status": "passed",
71
+ "response": {
72
+ "status": 200
73
+ }
74
+ }
75
+ ]
76
+ }
77
+ ```
78
+
79
+ ## Wasm size
80
+
81
+ | Artifact | Default build | `--experimental-native-size` | Reduction |
82
+ |---|---:|---:|---:|
83
+ | `canonical-native.wasm` | 6.1 KiB (6,271 bytes) | 5.4 KiB (5,496 bytes) | 12.4% |
84
+
85
+ The executable documentation gate rebuilds and measures both variants on
86
+ `1.0.0-beta.1`. These are uncompressed on-disk sizes, not transfer sizes or
87
+ platform limits.
88
+
89
+ ## Router application
90
+
91
+ <!-- pulse-doc-source: examples/09-router-lowering/src/index.ts -->
92
+ ```ts
93
+ import { Router } from '@pulse-compute/runtime'
94
+
95
+ interface User {
96
+ id: number
97
+ name: string
98
+ }
99
+
100
+ const users = new Router()
101
+ const api = new Router()
102
+ const app = new Router()
103
+
104
+ app.use(async (ctx, next) => {
105
+ if (ctx.req.header('x-pulse-deny') === '1') {
106
+ return ctx.json(
107
+ { error: 'denied by middleware' },
108
+ { status: 401 },
109
+ )
110
+ }
111
+ return next()
112
+ })
113
+
114
+ api.use(async (ctx, next) => {
115
+ if (ctx.req.header('x-api-state') === 'closed') {
116
+ return ctx.text('api closed', { status: 503 })
117
+ }
118
+ return next()
119
+ })
120
+
121
+ users.get('/:id', async (ctx) => {
122
+ const id = ctx.param('id')
123
+ const user = await ctx
124
+ .fetch('https://users.example.test/users/' + id)
125
+ .json<User>()
126
+ return ctx.json({ route: 'user', id, user })
127
+ })
128
+
129
+ api.get('/health', async (ctx, next) => {
130
+ if (ctx.req.header('x-health-handler') === 'fallback') {
131
+ return next()
132
+ }
133
+ return ctx.json({ ok: true, handler: 'primary' })
134
+ })
135
+ api.get('/health', async (ctx) => ctx.json({ ok: true, handler: 'fallback' }))
136
+ api.head('/health', async (ctx) => ctx.text('', {
137
+ status: 204,
138
+ headers: [['x-pulse-route', 'health']],
139
+ }))
140
+ api.post('/users', async (ctx) => ctx.json(
141
+ { route: 'create', method: ctx.req.method },
142
+ { status: 201 },
143
+ ))
144
+ api.get('/files/*', async (ctx) => ctx.text(ctx.req.path))
145
+ api.get('/failure', async (ctx, next) => next('documented-failure'))
146
+
147
+ api.mount('/users', users)
148
+ app.mount('/api', api)
149
+
150
+ app.error(async (error, ctx, next) => {
151
+ if (error === 'documented-failure') {
152
+ return ctx.json({ error: 'handled' }, { status: 418 })
153
+ }
154
+ return next(error)
155
+ })
156
+
157
+ export default app
158
+ ```
159
+ <!-- /pulse-doc-source -->
160
+
161
+ ## Project config
162
+
163
+ <!-- pulse-doc-source: examples/09-router-lowering/.pulse/config.ts -->
164
+ ```ts
165
+ import { defineConfig } from '@pulse-compute/pulse'
166
+
167
+ export default defineConfig((_scope) => ({
168
+ pulse: {
169
+ entry: 'src/index.ts',
170
+ tests: 'tests/pulse.harness.ts',
171
+ defaultProfile: 'local',
172
+ strict: true,
173
+ },
174
+ local: {
175
+ host: 'node',
176
+ target: 'native',
177
+ outDir: 'dist',
178
+ dev: {
179
+ fetches: {
180
+ 'https://users.example.test/users/7': {
181
+ value: { id: 7, name: 'Ada' },
182
+ },
183
+ },
184
+ },
185
+ },
186
+ }))
187
+ ```
188
+ <!-- /pulse-doc-source -->
189
+
190
+
191
+ ## Test harness
192
+
193
+ <!-- pulse-doc-source: examples/09-router-lowering/tests/pulse.harness.ts -->
194
+ ```ts
195
+ export default {
196
+ cases: [
197
+ {
198
+ name: 'mounted-health',
199
+ request: { method: 'GET', path: '/api/health' },
200
+ expect: {
201
+ status: 200,
202
+ json: { ok: true, handler: 'primary' },
203
+ },
204
+ },
205
+ {
206
+ name: 'middleware-short-circuit',
207
+ request: {
208
+ method: 'GET',
209
+ path: '/api/health',
210
+ headers: { 'x-pulse-deny': '1' },
211
+ },
212
+ expect: {
213
+ status: 401,
214
+ json: { error: 'denied by middleware' },
215
+ },
216
+ },
217
+ {
218
+ name: 'scoped-middleware-short-circuit',
219
+ request: {
220
+ method: 'GET',
221
+ path: '/api/health',
222
+ headers: { 'x-api-state': 'closed' },
223
+ },
224
+ expect: { status: 503, text: 'api closed' },
225
+ },
226
+ {
227
+ name: 'route-fallthrough',
228
+ request: {
229
+ method: 'GET',
230
+ path: '/api/health',
231
+ headers: { 'x-health-handler': 'fallback' },
232
+ },
233
+ expect: {
234
+ status: 200,
235
+ json: { ok: true, handler: 'fallback' },
236
+ },
237
+ },
238
+ {
239
+ name: 'error-middleware',
240
+ request: { method: 'GET', path: '/api/failure' },
241
+ expect: { status: 418, json: { error: 'handled' } },
242
+ },
243
+ {
244
+ name: 'parameter-and-fetch',
245
+ request: { method: 'GET', path: '/api/users/7' },
246
+ fetches: {
247
+ 'https://users.example.test/users/7': {
248
+ value: { id: 7, name: 'Ada' },
249
+ },
250
+ },
251
+ expect: {
252
+ status: 200,
253
+ json: {
254
+ route: 'user',
255
+ id: '7',
256
+ user: { id: 7, name: 'Ada' },
257
+ },
258
+ },
259
+ },
260
+ {
261
+ name: 'method-specific-post',
262
+ request: { method: 'POST', path: '/api/users' },
263
+ expect: {
264
+ status: 201,
265
+ json: { route: 'create', method: 'POST' },
266
+ },
267
+ },
268
+ {
269
+ name: 'trailing-wildcard',
270
+ request: {
271
+ method: 'GET',
272
+ path: '/api/files/guides/start',
273
+ },
274
+ expect: {
275
+ status: 200,
276
+ text: '/api/files/guides/start',
277
+ },
278
+ },
279
+ {
280
+ name: 'default-not-found',
281
+ request: { method: 'GET', path: '/missing' },
282
+ expect: { status: 404, text: 'Not Found' },
283
+ },
284
+ ],
285
+ }
286
+ ```
287
+ <!-- /pulse-doc-source -->
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@pulse-examples/09-router-lowering",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "doctor": "pulse doctor",
8
+ "inspect": "pulse inspect",
9
+ "test": "pulse test",
10
+ "dev": "pulse dev",
11
+ "compile": "pulse compile",
12
+ "build": "pulse build"
13
+ },
14
+ "dependencies": {
15
+ "@pulse-compute/runtime": "1.0.0-beta.1",
16
+ "@pulse-compute/pulse": "1.0.0-beta.1"
17
+ },
18
+ "devDependencies": {
19
+ "@pulse-compute/cli": "1.0.0-beta.1",
20
+ "typescript": "5.9.3"
21
+ }
22
+ }
@@ -0,0 +1,65 @@
1
+ import { Router } from '@pulse-compute/runtime'
2
+
3
+ interface User {
4
+ id: number
5
+ name: string
6
+ }
7
+
8
+ const users = new Router()
9
+ const api = new Router()
10
+ const app = new Router()
11
+
12
+ app.use(async (ctx, next) => {
13
+ if (ctx.req.header('x-pulse-deny') === '1') {
14
+ return ctx.json(
15
+ { error: 'denied by middleware' },
16
+ { status: 401 },
17
+ )
18
+ }
19
+ return next()
20
+ })
21
+
22
+ api.use(async (ctx, next) => {
23
+ if (ctx.req.header('x-api-state') === 'closed') {
24
+ return ctx.text('api closed', { status: 503 })
25
+ }
26
+ return next()
27
+ })
28
+
29
+ users.get('/:id', async (ctx) => {
30
+ const id = ctx.param('id')
31
+ const user = await ctx
32
+ .fetch('https://users.example.test/users/' + id)
33
+ .json<User>()
34
+ return ctx.json({ route: 'user', id, user })
35
+ })
36
+
37
+ api.get('/health', async (ctx, next) => {
38
+ if (ctx.req.header('x-health-handler') === 'fallback') {
39
+ return next()
40
+ }
41
+ return ctx.json({ ok: true, handler: 'primary' })
42
+ })
43
+ api.get('/health', async (ctx) => ctx.json({ ok: true, handler: 'fallback' }))
44
+ api.head('/health', async (ctx) => ctx.text('', {
45
+ status: 204,
46
+ headers: [['x-pulse-route', 'health']],
47
+ }))
48
+ api.post('/users', async (ctx) => ctx.json(
49
+ { route: 'create', method: ctx.req.method },
50
+ { status: 201 },
51
+ ))
52
+ api.get('/files/*', async (ctx) => ctx.text(ctx.req.path))
53
+ api.get('/failure', async (ctx, next) => next('documented-failure'))
54
+
55
+ api.mount('/users', users)
56
+ app.mount('/api', api)
57
+
58
+ app.error(async (error, ctx, next) => {
59
+ if (error === 'documented-failure') {
60
+ return ctx.json({ error: 'handled' }, { status: 418 })
61
+ }
62
+ return next(error)
63
+ })
64
+
65
+ export default app
@@ -0,0 +1,91 @@
1
+ export default {
2
+ cases: [
3
+ {
4
+ name: 'mounted-health',
5
+ request: { method: 'GET', path: '/api/health' },
6
+ expect: {
7
+ status: 200,
8
+ json: { ok: true, handler: 'primary' },
9
+ },
10
+ },
11
+ {
12
+ name: 'middleware-short-circuit',
13
+ request: {
14
+ method: 'GET',
15
+ path: '/api/health',
16
+ headers: { 'x-pulse-deny': '1' },
17
+ },
18
+ expect: {
19
+ status: 401,
20
+ json: { error: 'denied by middleware' },
21
+ },
22
+ },
23
+ {
24
+ name: 'scoped-middleware-short-circuit',
25
+ request: {
26
+ method: 'GET',
27
+ path: '/api/health',
28
+ headers: { 'x-api-state': 'closed' },
29
+ },
30
+ expect: { status: 503, text: 'api closed' },
31
+ },
32
+ {
33
+ name: 'route-fallthrough',
34
+ request: {
35
+ method: 'GET',
36
+ path: '/api/health',
37
+ headers: { 'x-health-handler': 'fallback' },
38
+ },
39
+ expect: {
40
+ status: 200,
41
+ json: { ok: true, handler: 'fallback' },
42
+ },
43
+ },
44
+ {
45
+ name: 'error-middleware',
46
+ request: { method: 'GET', path: '/api/failure' },
47
+ expect: { status: 418, json: { error: 'handled' } },
48
+ },
49
+ {
50
+ name: 'parameter-and-fetch',
51
+ request: { method: 'GET', path: '/api/users/7' },
52
+ fetches: {
53
+ 'https://users.example.test/users/7': {
54
+ value: { id: 7, name: 'Ada' },
55
+ },
56
+ },
57
+ expect: {
58
+ status: 200,
59
+ json: {
60
+ route: 'user',
61
+ id: '7',
62
+ user: { id: 7, name: 'Ada' },
63
+ },
64
+ },
65
+ },
66
+ {
67
+ name: 'method-specific-post',
68
+ request: { method: 'POST', path: '/api/users' },
69
+ expect: {
70
+ status: 201,
71
+ json: { route: 'create', method: 'POST' },
72
+ },
73
+ },
74
+ {
75
+ name: 'trailing-wildcard',
76
+ request: {
77
+ method: 'GET',
78
+ path: '/api/files/guides/start',
79
+ },
80
+ expect: {
81
+ status: 200,
82
+ text: '/api/files/guides/start',
83
+ },
84
+ },
85
+ {
86
+ name: 'default-not-found',
87
+ request: { method: 'GET', path: '/missing' },
88
+ expect: { status: 404, text: 'Not Found' },
89
+ },
90
+ ],
91
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "noEmit": true
8
+ },
9
+ "include": [
10
+ "src/**/*.ts",
11
+ ".pulse/**/*.ts",
12
+ "tests/**/*.ts"
13
+ ]
14
+ }
@@ -0,0 +1,4 @@
1
+ *
2
+ !.gitignore
3
+ !config.ts
4
+
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from '@pulse-compute/pulse'
2
+
3
+ export default defineConfig((_scope) => ({
4
+ pulse: {
5
+ entry: 'src/index.ts',
6
+ schema: 'src/schemas.ts',
7
+ tests: 'tests/pulse.harness.ts',
8
+ defaultProfile: 'node-javascript',
9
+ strict: true,
10
+ },
11
+ 'node-javascript': {
12
+ host: 'node',
13
+ target: 'javascript',
14
+ outDir: 'dist-node-javascript',
15
+ dev: { networkFetch: false },
16
+ },
17
+ 'node-native': {
18
+ host: 'node',
19
+ target: 'native',
20
+ outDir: 'dist-node-native',
21
+ dev: { networkFetch: false },
22
+ },
23
+ }))
@@ -0,0 +1,165 @@
1
+ # Entities tools facade
2
+
3
+ This experimental example declares two schema-bound operations with
4
+ `@pulse-compute/entities`, exposes them through the first-party JSON-RPC
5
+ adapter, and projects their emitted static catalog into a dependency-free tools
6
+ facade. The facade invokes the governed request boundary; it does not call
7
+ application handlers directly.
8
+
9
+ The application contributes:
10
+
11
+ - `system.status`, which accepts no input and completes with `null`;
12
+ - `customer.lookup`, which validates named input and output and performs one
13
+ governed `ctx.fetch` effect.
14
+
15
+ ## Candidate workflow
16
+
17
+ ```bash
18
+ pulse inspect
19
+ node ../../wasm/scripts/run-wasm-tests.cjs --task entities-orchestration-demo --no-report
20
+ ```
21
+
22
+ `pulse inspect` reports the static entity catalog, declared schemas, handler
23
+ effects, and target evidence. The focused orchestration gate builds and consumes
24
+ the catalog, invokes both operations through the Node JavaScript request
25
+ boundary, validates the governed fetch, and compiles/inspects the package-owned
26
+ Native artifact.
27
+
28
+ The ordinary JavaScript `test`/`dev` application loader currently requests the
29
+ unexported physical package entry, and ordinary Native builds do not yet adopt
30
+ the Entities intrinsic. I11 records those product-integration blockers instead
31
+ of widening package exports or compiler/provider authority. A JavaScript build
32
+ can still emit the source package, catalog, and inspection artifacts; no command
33
+ may fall back to another target.
34
+
35
+ ## Wasm size
36
+
37
+ The default `node-javascript` profile emits **no application Wasm artifact**.
38
+ The ordinary `node-native` build currently fails closed at the unadopted
39
+ Entities intrinsic. The focused gate's package-owned Native artifact exercises
40
+ a different boundary and is not comparable to an emitted application guest.
41
+ Consequently, `--experimental-native-size` has no application artifact to
42
+ optimize for this example.
43
+
44
+ ## Entity application
45
+
46
+ <!-- pulse-doc-source: examples/10-entities-tools/src/index.ts -->
47
+ ```ts
48
+ import { EntityRouter, jsonRpc } from '@pulse-compute/entities'
49
+ import { lookupCustomer, systemStatus } from './handlers.js'
50
+
51
+ const rpc = new EntityRouter({
52
+ adapter: jsonRpc({ namedParamsOnly: true, acceptEmptyObjectForNoInput: true }),
53
+ })
54
+
55
+ rpc.on('system.status', {
56
+ input: null,
57
+ output: null,
58
+ metadata: {
59
+ title: 'Check system status',
60
+ description: 'Checks that the governed entity request boundary is available.',
61
+ mcp: { readOnlyHint: true },
62
+ },
63
+ }, systemStatus)
64
+
65
+ rpc.on('customer.lookup', {
66
+ input: 'tools.CustomerLookupInput',
67
+ output: 'tools.CustomerLookupOutput',
68
+ metadata: {
69
+ title: 'Look up customer',
70
+ description: 'Looks up one customer through the configured directory backend.',
71
+ mcp: { readOnlyHint: true },
72
+ },
73
+ }, lookupCustomer)
74
+
75
+ export default async function handler(ctx: unknown) {
76
+ return rpc.handle(ctx as never)
77
+ }
78
+ ```
79
+ <!-- /pulse-doc-source -->
80
+
81
+ ## Project config
82
+
83
+ <!-- pulse-doc-source: examples/10-entities-tools/.pulse/config.ts -->
84
+ ```ts
85
+ import { defineConfig } from '@pulse-compute/pulse'
86
+
87
+ export default defineConfig((_scope) => ({
88
+ pulse: {
89
+ entry: 'src/index.ts',
90
+ schema: 'src/schemas.ts',
91
+ tests: 'tests/pulse.harness.ts',
92
+ defaultProfile: 'node-javascript',
93
+ strict: true,
94
+ },
95
+ 'node-javascript': {
96
+ host: 'node',
97
+ target: 'javascript',
98
+ outDir: 'dist-node-javascript',
99
+ dev: { networkFetch: false },
100
+ },
101
+ 'node-native': {
102
+ host: 'node',
103
+ target: 'native',
104
+ outDir: 'dist-node-native',
105
+ dev: { networkFetch: false },
106
+ },
107
+ }))
108
+ ```
109
+ <!-- /pulse-doc-source -->
110
+
111
+ ## Test harness
112
+
113
+ <!-- pulse-doc-source: examples/10-entities-tools/tests/pulse.harness.ts -->
114
+ ```ts
115
+ export default { cases: [
116
+ {
117
+ name: 'system-status',
118
+ request: {
119
+ method: 'POST',
120
+ path: '/',
121
+ headers: { 'content-type': 'application/json' },
122
+ body: JSON.stringify({ jsonrpc: '2.0', method: 'system.status', id: 'status-doc' }),
123
+ },
124
+ expect: {
125
+ status: 200,
126
+ json: { jsonrpc: '2.0', result: null, id: 'status-doc' },
127
+ },
128
+ },
129
+ {
130
+ name: 'customer-lookup',
131
+ request: {
132
+ method: 'POST',
133
+ path: '/',
134
+ headers: { 'content-type': 'application/json' },
135
+ body: JSON.stringify({
136
+ jsonrpc: '2.0',
137
+ method: 'customer.lookup',
138
+ params: { email: 'ada@example.test' },
139
+ id: 'lookup-doc',
140
+ }),
141
+ },
142
+ fetches: {
143
+ 'https://directory.example.test/customers/ada@example.test': { value: 'Ada Lovelace' },
144
+ },
145
+ expect: {
146
+ status: 200,
147
+ json: {
148
+ jsonrpc: '2.0',
149
+ result: { email: 'ada@example.test', displayName: 'Ada Lovelace' },
150
+ id: 'lookup-doc',
151
+ },
152
+ },
153
+ },
154
+ ] }
155
+ ```
156
+ <!-- /pulse-doc-source -->
157
+
158
+ ## Facade boundary
159
+
160
+ `tools-facade.cjs` is an orchestration example, not a complete MCP server. It
161
+ does not add listeners, SSE, sessions, tasks, resources, prompts, sampling,
162
+ authorization, transport negotiation, or protocol lifecycle state to Pulse
163
+ runtime core. A future MCP or tools facade remains an adapter outside the
164
+ entity engine, just as a future Worker/event adapter would bind a different
165
+ request or event protocol without changing entity handler semantics.
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@pulse-examples/10-entities-tools",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "doctor": "pulse doctor",
8
+ "inspect": "pulse inspect",
9
+ "test": "pulse test",
10
+ "dev": "pulse dev",
11
+ "build": "pulse build"
12
+ },
13
+ "dependencies": {
14
+ "@pulse-compute/entities": "1.0.0-beta.1",
15
+ "@pulse-compute/pulse": "1.0.0-beta.1"
16
+ },
17
+ "devDependencies": {
18
+ "@pulse-compute/cli": "1.0.0-beta.1",
19
+ "typescript": "5.9.3"
20
+ }
21
+ }