@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,2946 @@
1
+ 'use strict';
2
+
3
+ const assert = require('node:assert/strict');
4
+ const crypto = require('node:crypto');
5
+ const fs = require('node:fs');
6
+ const http = require('node:http');
7
+ const path = require('node:path');
8
+ const { once } = require('node:events');
9
+ const {
10
+ loadCanonicalModule,
11
+ writeCanonicalBuild
12
+ } = require('@pulse-compute/wasm-compiler/canonical-api-compiler');
13
+ const {
14
+ compileCanonicalProject,
15
+ packageRealizationArtifactsForCompiled
16
+ } = require('@pulse-compute/wasm-compiler/canonical-project-compiler');
17
+ const { createCanonicalSchemaCodecs } = require('@pulse-compute/wasm-schema-json/compiler/canonical-schema-codecs');
18
+ const { buildJavascriptApplicationPlan } = require('@pulse-compute/wasm-compiler/javascript-application-plan');
19
+ const { buildCanonicalNativePlan, CanonicalNativePlanError } = require('@pulse-compute/wasm-compiler/canonical-native-plan');
20
+ const {
21
+ compileCanonicalNativePlan,
22
+ writeCanonicalNativeModule
23
+ } = require('@pulse-compute/wasm-compiler/canonical-native-compiler');
24
+ const {
25
+ executeCanonicalNativeModule
26
+ } = require('@pulse-compute/wasm-host-runtime/runtime/canonical-native-host');
27
+ const { PulseProjectError, projectJson, relative } = require('./project-config.js');
28
+ const { describeDiagnostic, decorateDiagnostic, httpStatusForDiagnostic } = require('./diagnostics.js');
29
+ const {
30
+ getProviderDriver,
31
+ getProviderTargetDescriptor,
32
+ getProviderTargetSupportDeclaration,
33
+ providerTargetAvailability,
34
+ providerIds
35
+ } = require('./provider-drivers.js');
36
+ const {
37
+ buildJavascriptTargetSupportEvidence,
38
+ buildEventTargetSupportEvidence
39
+ } = require('./target-support.js');
40
+ const eventContracts = require(path.join(
41
+ path.dirname(require.resolve('@pulse-compute/wasm-contracts')),
42
+ 'events',
43
+ 'contracts.js'
44
+ ));
45
+ const {
46
+ PROVIDER_TARGET_INVOCATION_VERSION,
47
+ PROVIDER_PLAN_INPUT_VERSION,
48
+ createProviderNativeArtifact,
49
+ createProviderPlanInput,
50
+ createProviderTargetInvocation,
51
+ normalizeProviderTargetResult,
52
+ projectProviderRequirements
53
+ } = require('@pulse-compute/wasm-contracts/provider/toolchain');
54
+ const { loadJavascriptApplication } = require('./typescript-module-loader.js');
55
+ const { writeNodeHttpResponse } = require('./internal/node-http.js');
56
+ const {
57
+ HANDLER_AUTHORING_MODES
58
+ } = require('@pulse-compute/wasm-contracts/handler/surface-contract');
59
+ const {
60
+ CANONICAL_PACKAGE_INSPECTION_VERSION
61
+ } = require('@pulse-compute/wasm-contracts/package/package-contract');
62
+
63
+ const PROJECT_EXECUTION_VERSION = 'pulse.project-execution.v10';
64
+ const EVENT_INSPECTION_VERSION = 'pulse.event-inspection.v1';
65
+ const BUILD_MANIFEST = 'pulse-build.json';
66
+ const COMPILE_MANIFEST = 'pulse-compile.json';
67
+ const cliPackageVersion = require('../package.json').version;
68
+ const releaseCatalog = require('../release-manifest.json');
69
+ const releaseNode = releaseCatalog.publication;
70
+
71
+ function nodeVersionSatisfiesEngines(version, engines) {
72
+ const actual = /^(\d+)\.(\d+)\.(\d+)$/.exec(String(version));
73
+ if (!actual) return false;
74
+ const actualParts = actual.slice(1).map((part) => Number.parseInt(part, 10));
75
+ return String(engines).split(/\s*\|\|\s*/).some((range) => {
76
+ const minimum = /^\^(\d+)\.(\d+)\.(\d+)$/.exec(range);
77
+ if (!minimum) return false;
78
+ const minimumParts = minimum.slice(1).map((part) => Number.parseInt(part, 10));
79
+ if (actualParts[0] !== minimumParts[0]) return false;
80
+ for (let index = 1; index < 3; index += 1) {
81
+ if (actualParts[index] !== minimumParts[index]) return actualParts[index] > minimumParts[index];
82
+ }
83
+ return true;
84
+ });
85
+ }
86
+
87
+ function stableJson(value) {
88
+ return `${JSON.stringify(value, null, 2)}\n`;
89
+ }
90
+
91
+ function writePackageInspectionArtifacts(compiled, outDir) {
92
+ const inspection = compiled && compiled.packageInspection;
93
+ const artifacts = inspection && Array.isArray(inspection.artifacts) ? inspection.artifacts : [];
94
+ const files = artifacts.map((artifact) => {
95
+ const relativeFile = String(artifact.file || '').replace(/\\/g, '/');
96
+ const target = path.resolve(outDir, relativeFile);
97
+ const relativeTarget = path.relative(path.resolve(outDir), target);
98
+ if (!relativeFile || relativeTarget.startsWith('..') || path.isAbsolute(relativeTarget)) {
99
+ throw new PulseProjectError(
100
+ 'PULSE_PACKAGE_INSPECTION_ARTIFACT_UNSAFE',
101
+ `Package inspection artifact ${artifact.id || '<unknown>'} must remain inside the build output.`,
102
+ { id: artifact.id, file: relativeFile }
103
+ );
104
+ }
105
+ fs.mkdirSync(path.dirname(target), { recursive: true });
106
+ const bytes = stableJson(artifact.data);
107
+ fs.writeFileSync(target, bytes);
108
+ return Object.freeze({
109
+ id: artifact.id,
110
+ contractId: artifact.contractId,
111
+ kind: artifact.kind,
112
+ mediaType: artifact.mediaType,
113
+ file: relativeFile,
114
+ path: target,
115
+ sha256: crypto.createHash('sha256').update(bytes).digest('hex')
116
+ });
117
+ });
118
+ return Object.freeze({
119
+ version: inspection && inspection.version || CANONICAL_PACKAGE_INSPECTION_VERSION,
120
+ artifacts: Object.freeze(files),
121
+ summary: inspection && inspection.summary || Object.freeze({ packages: 0, artifacts: 0, declaredHandlers: 0, handlerEffects: 0 })
122
+ });
123
+ }
124
+
125
+ function packageInspectionManifest(written) {
126
+ return Object.freeze({
127
+ version: written.version,
128
+ artifacts: Object.freeze(written.artifacts.map((artifact) => Object.freeze({
129
+ id: artifact.id,
130
+ contractId: artifact.contractId,
131
+ kind: artifact.kind,
132
+ mediaType: artifact.mediaType,
133
+ file: artifact.file,
134
+ sha256: artifact.sha256
135
+ }))),
136
+ summary: written.summary
137
+ });
138
+ }
139
+
140
+ function selectedEventTargetSupport(project, compiled, descriptor = targetDescriptor(project)) {
141
+ const driver = providerDriver(project);
142
+ return buildEventTargetSupportEvidence(compiled, {
143
+ provider: project.provider,
144
+ target: project.target || 'native',
145
+ descriptor,
146
+ eventTestTargets: driver.events && driver.events.targets || []
147
+ });
148
+ }
149
+
150
+ function sortedUnique(values) {
151
+ return Object.freeze([...new Set((values || []).filter((value) => value !== null && value !== undefined).map(String))].sort());
152
+ }
153
+
154
+ function eventInspectionProjection(project, compiled, support = selectedEventTargetSupport(project, compiled)) {
155
+ if (!support) return null;
156
+ const catalog = compiled.eventCatalog || compiled.metadata && compiled.metadata.events && compiled.metadata.events.catalog;
157
+ const outbound = compiled.eventOutboundRequirements || Object.freeze({
158
+ version: compiled.metadata.events.outboundRequirementsVersion,
159
+ callsites: Object.freeze([]),
160
+ capabilities: Object.freeze([]),
161
+ hostRequirements: Object.freeze([]),
162
+ summary: Object.freeze({ callsites: 0, schemaBound: 0, noPayload: 0, grouped: 0, handlers: 0 })
163
+ });
164
+ const registrations = catalog && catalog.events || [];
165
+ const callsites = outbound.callsites || [];
166
+ const capabilities = sortedUnique([
167
+ ...registrations.flatMap((event) => event.capabilities || []),
168
+ ...(outbound.capabilities || [])
169
+ ]);
170
+ const hostRequirements = sortedUnique([
171
+ ...registrations.flatMap((event) => event.hostRequirements || []),
172
+ ...(outbound.hostRequirements || [])
173
+ ]);
174
+ return Object.freeze({
175
+ version: EVENT_INSPECTION_VERSION,
176
+ contractId: eventContracts.EVENT_CONTRACT_ID,
177
+ catalog,
178
+ outboundRequirements: outbound,
179
+ registrations: Object.freeze(registrations),
180
+ callsites: Object.freeze(callsites),
181
+ schemas: Object.freeze({
182
+ registrations: sortedUnique(registrations.map((event) => event.schemaId)),
183
+ emissions: sortedUnique(callsites.map((callsite) => callsite.schemaId)),
184
+ all: sortedUnique([
185
+ ...registrations.map((event) => event.schemaId),
186
+ ...callsites.map((callsite) => callsite.schemaId)
187
+ ])
188
+ }),
189
+ capabilities,
190
+ hostRequirements,
191
+ targetSupport: support,
192
+ policy: Object.freeze({
193
+ providerNeutralCatalog: true,
194
+ publicInjectionCommand: false,
195
+ automaticLoopback: false,
196
+ automaticFallback: false
197
+ })
198
+ });
199
+ }
200
+
201
+ function writeEventInspectionArtifacts(project, compiled, outDir, support) {
202
+ const inspection = eventInspectionProjection(project, compiled, support);
203
+ if (!inspection) return null;
204
+ const catalogFile = path.join(outDir, 'event-catalog.json');
205
+ const inspectionFile = path.join(outDir, 'event-inspection.json');
206
+ fs.writeFileSync(catalogFile, stableJson(inspection.catalog));
207
+ fs.writeFileSync(inspectionFile, stableJson(inspection));
208
+ return Object.freeze({
209
+ inspection,
210
+ catalogFile,
211
+ inspectionFile,
212
+ files: Object.freeze({ catalog: path.basename(catalogFile), inspection: path.basename(inspectionFile) })
213
+ });
214
+ }
215
+
216
+ function eventManifestProjection(written) {
217
+ return written ? Object.freeze({ ...written.inspection, files: written.files }) : undefined;
218
+ }
219
+
220
+ function assertEventCommandEligible(project, compiled, command, descriptor = targetDescriptor(project)) {
221
+ const support = selectedEventTargetSupport(project, compiled, descriptor);
222
+ if (!support) return null;
223
+ const commandSupport = support.commands.find((entry) => entry.id === command);
224
+ if (!commandSupport || commandSupport.status !== 'eligible') {
225
+ const code = support.diagnosticCode || 'PULSE_EVENT_TARGET_UNSUPPORTED';
226
+ throw new PulseProjectError(
227
+ code,
228
+ `pulse ${command} cannot realize the event plane for ${project.provider}/${project.target || 'native'}.`,
229
+ {
230
+ provider: project.provider,
231
+ target: project.target || 'native',
232
+ command,
233
+ eventTargetSupport: support,
234
+ automaticFallback: false
235
+ }
236
+ );
237
+ }
238
+ return support;
239
+ }
240
+
241
+ function projectPlanInspection(project, compiled) {
242
+ const profile = project.selectedProfile || project.profile || null;
243
+ return Object.freeze({
244
+ mode: 'conventional',
245
+ workspace: project.workspace ? Object.freeze({
246
+ root: project.workspace.root,
247
+ configFile: project.workspace.configFile,
248
+ source: project.workspace.source
249
+ }) : null,
250
+ projectHash: project.projectHash,
251
+ planHash: project.planHash,
252
+ selectedProfile: profile ? Object.freeze({
253
+ name: profile.name,
254
+ source: profile.source,
255
+ host: profile.host,
256
+ target: profile.target,
257
+ reporting: profile.reporting,
258
+ reportingLevel: project.reportingLevel
259
+ }) : null,
260
+ strict: project.strict !== false,
261
+ bindings: Object.freeze({
262
+ config: Object.freeze([...(project.bindings && project.bindings.config || [])]),
263
+ secret: Object.freeze([...(project.bindings && project.bindings.secret || [])])
264
+ }),
265
+ fragments: Object.freeze({
266
+ keys: Object.freeze(Object.keys(project.fragments || {}).sort()),
267
+ ownership: compiled && compiled.packageProduct
268
+ ? compiled.packageProduct.fragments
269
+ : Object.freeze([]),
270
+ unclaimed: compiled && compiled.packageProduct
271
+ ? Object.freeze(compiled.packageProduct.fragments.filter((entry) => entry.present && !entry.contractId).map((entry) => entry.key))
272
+ : Object.freeze(Object.keys(project.fragments || {}).sort())
273
+ }),
274
+ crypto: Object.freeze({
275
+ declaration: project.profilePlan && project.profilePlan.crypto || null,
276
+ realizationPlan: compiled && compiled.cryptoRealizationPlan || null
277
+ }),
278
+ application: compiled && (compiled.application || compiled.metadata && compiled.metadata.application) || null
279
+ });
280
+ }
281
+
282
+ function jsonPolicyInspection(project, compiled, nativePlan) {
283
+ const policy = compiled && compiled.metadata && compiled.metadata.json;
284
+ return Object.freeze({
285
+ strict: project.strict !== false,
286
+ schemaBoundRequestCount: Number(policy && policy.schemaBoundRequestCount || 0),
287
+ genericRequestCount: Number(policy && policy.genericRequestCount || 0),
288
+ genericParserRequired: Boolean(policy && policy.genericParserRequired),
289
+ maxBytes: Number(policy && policy.maxBytes || project.schemas && project.schemas.maxBytes || 65536),
290
+ native: nativePlan && nativePlan.json || null
291
+ });
292
+ }
293
+
294
+ function compileProject(project, options = {}) {
295
+ const handlerAuthoring = options.handlerAuthoring || HANDLER_AUTHORING_MODES.ASYNC_REQUIRED;
296
+ const configuredTarget = options.target || project.target || 'native';
297
+ const packageTargetDescriptor = getProviderTargetDescriptor(providerDriver(project), configuredTarget);
298
+ return compileCanonicalProject(project.entryFile, {
299
+ target: configuredTarget,
300
+ rootDir: project.root,
301
+ // Package lowerers are discovered from the nearest package-manager workspace.
302
+ // The Pulse project root remains authoritative for config and paths, but a
303
+ // conventional project may live inside a larger source workspace.
304
+ workspaceRoot: undefined,
305
+ configFile: project.configFile,
306
+ schemas: project.schemas,
307
+ strict: project.strict !== false,
308
+ emitJsonPolicy: true,
309
+ handlerAuthoring,
310
+ requireAsync: handlerAuthoring === HANDLER_AUTHORING_MODES.ASYNC_REQUIRED,
311
+ requireEffectAwait: false,
312
+ packageTargetDescriptor,
313
+ packageTarget: configuredTarget,
314
+ applicationProjectMetadata: Object.freeze({
315
+ constructionMode: 'auto',
316
+ projectHash: project.projectHash,
317
+ configPlanHash: project.planHash,
318
+ selectedProfile: project.profile,
319
+ strict: project.strict !== false,
320
+ bindings: project.bindings,
321
+ fragments: project.fragments,
322
+ crypto: project.profilePlan && project.profilePlan.crypto,
323
+ reporting: project.reporting,
324
+ reportingLevel: project.reportingLevel,
325
+ target: configuredTarget,
326
+ host: project.provider || null
327
+ })
328
+ });
329
+ }
330
+
331
+ function compileNativeProjectInMemory(project, options = {}) {
332
+ let compiled;
333
+ try {
334
+ compiled = options.compiled && options.compiled.target !== 'javascript'
335
+ ? options.compiled
336
+ : compileProject(project, { ...options, target: 'native' });
337
+ } catch (error) {
338
+ // Native inspection is independent of a successful JavaScript compilation.
339
+ // Keep its rejection in the Native diagnostic lane used by doctor/inspect.
340
+ if ((project.target || 'native') !== 'javascript' || !Array.isArray(error.diagnostics)) throw error;
341
+ const failure = new CanonicalNativePlanError(error.message, error.diagnostics);
342
+ failure.detail = Object.freeze({ ...error.detail, causeCode: error.code, automaticFallback: false });
343
+ throw failure;
344
+ }
345
+ const plan = buildCanonicalNativePlan(compiled, { reporting: project.reporting });
346
+ const native = compileCanonicalNativePlan(plan, {
347
+ cwd: project.root,
348
+ projectRoot: project.root,
349
+ profile: project.selectedProfile && project.selectedProfile.name || project.profile && project.profile.name || 'native',
350
+ targetDescriptor: getProviderTargetDescriptor(providerDriver(project), 'native'),
351
+ synchronizedPackages: releaseCatalog.packages.map(({ name, version }) => Object.freeze({ name, version })),
352
+ timeoutMs: options.timeoutMs,
353
+ nativeOptimization: options.experimentalNativeSize === true
354
+ ? 'experimental-native-size'
355
+ : options.nativeOptimization
356
+ });
357
+ return Object.freeze({ compiled, plan, native });
358
+ }
359
+
360
+ function providerDriver(project) {
361
+ return getProviderDriver(project.providerSelector || project.provider, {
362
+ projectRoot: project.root
363
+ });
364
+ }
365
+
366
+ function targetDescriptor(project, target = project.target || 'native') {
367
+ return Object.freeze({
368
+ ...getProviderTargetDescriptor(providerDriver(project), target),
369
+ resolvedReporting: project.reportingPolicy
370
+ });
371
+ }
372
+
373
+ function selectedProviderTarget(project, target = project.target || 'native') {
374
+ return getProviderTargetDescriptor(providerDriver(project), target);
375
+ }
376
+
377
+ function assertImplementedTarget(project, command) {
378
+ const target = project.target || 'native';
379
+ const descriptor = targetDescriptor(project, target);
380
+ if (!descriptor.commands || descriptor.commands[command] !== true) {
381
+ throw new PulseProjectError(
382
+ 'PULSE_TARGET_IMPLEMENTATION_PENDING',
383
+ `pulse ${command} cannot realize the selected ${target} target yet.`,
384
+ {
385
+ target,
386
+ command,
387
+ provider: project.provider,
388
+ selectedProfile: project.profile,
389
+ supportedCommands: Object.freeze(Object.entries(descriptor.commands || {}).filter(([, supported]) => supported).map(([name]) => name)),
390
+ targetDescriptor: descriptor,
391
+ targetSupport: getProviderTargetSupportDeclaration(providerDriver(project), target),
392
+ automaticFallback: false
393
+ }
394
+ );
395
+ }
396
+ return descriptor;
397
+ }
398
+
399
+ function javascriptLoadFailure(error) {
400
+ if (!error) return null;
401
+ return Object.freeze({
402
+ name: error.name ? String(error.name) : 'Error',
403
+ code: error.code ? String(error.code) : null,
404
+ message: error.message ? String(error.message) : String(error),
405
+ detail: error.detail && typeof error.detail === 'object' ? Object.freeze({ ...error.detail }) : null,
406
+ diagnostics: Array.isArray(error.diagnostics) ? Object.freeze([...error.diagnostics]) : Object.freeze([])
407
+ });
408
+ }
409
+
410
+ function prepareJavascriptApplication(project, options = {}) {
411
+ const descriptor = targetDescriptor(project, 'javascript');
412
+ const javascript = providerDriver(project).javascript;
413
+ const plan = buildJavascriptApplicationPlan(project.entryFile, {
414
+ rootDir: project.root,
415
+ workspaceRoot: undefined,
416
+ configFile: project.configFile,
417
+ projectFragments: project.fragments,
418
+ provider: project.provider,
419
+ targetId: descriptor.targetId,
420
+ providerLifecycleDeferred: descriptor.lifecycle !== true,
421
+ capabilityEnvelope: descriptor.capabilities || Object.freeze({
422
+ version: 'pulse.javascript-capability-envelope.v1',
423
+ provider: project.provider,
424
+ status: descriptor.requestAdapter === true ? 'request-lifecycle-ready' : 'provider-adapters-deferred',
425
+ core: Object.freeze({ request: descriptor.requestAdapter === true, response: descriptor.requestAdapter === true, state: true }),
426
+ effects: Object.freeze({ fetch: false, config: false, secret: false, kv: false }),
427
+ packages: Object.freeze([])
428
+ })
429
+ });
430
+ let loaded = null;
431
+ let loadError = null;
432
+ let loadAttempted = false;
433
+ let loadSkippedReason = null;
434
+ const localEmulation = options.localEmulation === true && javascript && javascript.localEmulation === true;
435
+ if (descriptor.applicationLoader === true && javascript && (javascript.validateApplication || (localEmulation && javascript.loadApplication))) {
436
+ if (options.load === false) {
437
+ loadSkippedReason = 'plan-only-evidence';
438
+ } else if (options.captureLoadError === true && plan.loadable !== true) {
439
+ loadSkippedReason = 'application-plan-not-loadable';
440
+ } else {
441
+ loadAttempted = true;
442
+ try {
443
+ loaded = localEmulation && javascript.loadApplication
444
+ ? javascript.loadApplication(plan, {
445
+ projectRoot: project.root,
446
+ schemaBundle: options.schemaBundle
447
+ })
448
+ : loadJavascriptApplication(plan, {
449
+ workspaceRoot: project.root,
450
+ validateApplication: javascript.validateApplication,
451
+ allowedPackages: options.allowedPackages
452
+ });
453
+ } catch (error) {
454
+ if (options.captureLoadError !== true) throw error;
455
+ loadError = javascriptLoadFailure(error);
456
+ }
457
+ }
458
+ } else if (descriptor.applicationLoader === true && javascript && javascript.buildTimeLoader === true) {
459
+ loadSkippedReason = 'provider-build-time-bundle-loader';
460
+ } else {
461
+ loadSkippedReason = 'provider-application-loader-unavailable';
462
+ }
463
+ return Object.freeze({ descriptor, plan, loaded, loadError, loadAttempted, loadSkippedReason });
464
+ }
465
+
466
+ function selectedTargetSupportEvidence(project, compiled, javascriptApplication, descriptor, options = {}) {
467
+ if ((project.target || 'native') !== 'javascript') return null;
468
+ const javascript = providerDriver(project).javascript;
469
+ const policy = javascript && javascript.targetSupportPolicy;
470
+ if (!policy) return null;
471
+ return buildJavascriptTargetSupportEvidence(project, {
472
+ compiled,
473
+ javascriptApplication,
474
+ descriptor: descriptor || javascriptApplication && javascriptApplication.descriptor,
475
+ declaration: options.targetSupportDeclaration
476
+ }, policy);
477
+ }
478
+
479
+ function assertExecutableProvider(project, command) {
480
+ const driver = providerDriver(project);
481
+ if (!driver.executable) {
482
+ throw new PulseProjectError(
483
+ command === 'test' ? 'PULSE_TEST_PROVIDER_REQUIRED' : 'PULSE_DEV_PROVIDER_UNSUPPORTED',
484
+ `pulse ${command} requires an executable provider.`,
485
+ { provider: project.provider, supported: providerIds({ executable: true }) }
486
+ );
487
+ }
488
+ return driver;
489
+ }
490
+
491
+ function providerExecutionOptions(project, values = {}) {
492
+ const driver = providerDriver(project);
493
+ const resolved = { ...values, reporting: project.reporting };
494
+ return driver.executionOptions ? driver.executionOptions(project.providerConfig, resolved) : resolved;
495
+ }
496
+
497
+ function requiresExactNativeCrypto(compiled) {
498
+ return Boolean(
499
+ compiled
500
+ && compiled.cryptoRealizationPlan
501
+ && Array.isArray(compiled.cryptoRealizationPlan.algorithms)
502
+ && compiled.cryptoRealizationPlan.algorithms.some((entry) => entry.kind === 'guest-linked')
503
+ );
504
+ }
505
+
506
+ function providerLoweringPlan(project, metadata) {
507
+ const driver = providerDriver(project);
508
+ if (typeof driver.createLoweringPlan === 'function') {
509
+ const input = createProviderPlanInput({
510
+ version: PROVIDER_PLAN_INPUT_VERSION,
511
+ capabilities: metadata && metadata.capabilities || [],
512
+ providerOperations: metadata && metadata.providerOperations || [],
513
+ opaqueReturnCount: metadata && metadata.opaqueReturnCount || 0
514
+ });
515
+ return driver.createLoweringPlan(input, project.providerConfig);
516
+ }
517
+ return Object.freeze({
518
+ version: 'pulse.canonical-provider-plan.v1',
519
+ contractVersion: 'pulse.canonical-provider-contract.v1',
520
+ provider: 'none',
521
+ providerVersion: 'internal',
522
+ package: '',
523
+ runtime: '',
524
+ buildTarget: 'portable-native-wasm',
525
+ deployable: false,
526
+ localExecution: false,
527
+ requirements: Object.freeze([]),
528
+ bindings: Object.freeze({}),
529
+ operations: Object.freeze([]),
530
+ providerSpecificUserland: false,
531
+ providerSdkUserland: false,
532
+ capabilityDiscoveryFromUserland: false
533
+ });
534
+ }
535
+
536
+ function synchronizedPackageFacts() {
537
+ return Object.freeze(releaseCatalog.packages.map(
538
+ ({ name, version }) => Object.freeze({ name, version })
539
+ ));
540
+ }
541
+
542
+ function selectedProfileName(project) {
543
+ return project.selectedProfile && project.selectedProfile.name
544
+ || project.profile && project.profile.name
545
+ || 'native';
546
+ }
547
+
548
+ function providerTargetInvocation(project, action, applicationPlan, providerPlan, options = {}) {
549
+ const nativeArtifact = options.native ? createProviderNativeArtifact(options.native) : null;
550
+ return createProviderTargetInvocation({
551
+ version: PROVIDER_TARGET_INVOCATION_VERSION,
552
+ action,
553
+ provider: project.provider,
554
+ selectedTarget: selectedProviderTarget(project, action.endsWith('javascript') ? 'javascript' : 'native'),
555
+ applicationPlan,
556
+ providerPlan,
557
+ providerConfig: project.providerConfig,
558
+ requirements: projectProviderRequirements(applicationPlan, providerPlan),
559
+ nativeArtifact,
560
+ javascript: options.javascript || null,
561
+ project: Object.freeze({
562
+ root: project.root,
563
+ outDir: options.outDir || null,
564
+ profile: selectedProfileName(project)
565
+ }),
566
+ synchronizedPackages: synchronizedPackageFacts(),
567
+ optimization: options.optimization,
568
+ timeoutMs: options.timeoutMs
569
+ });
570
+ }
571
+
572
+ function canonicalFuturePath(candidate) {
573
+ const requested = path.resolve(candidate);
574
+ const suffix = [];
575
+ let existing = requested;
576
+
577
+ while (!fs.existsSync(existing)) {
578
+ const parent = path.dirname(existing);
579
+ if (parent === existing) break;
580
+ suffix.unshift(path.basename(existing));
581
+ existing = parent;
582
+ }
583
+
584
+ return Object.freeze({
585
+ requested,
586
+ existing,
587
+ target: path.resolve(fs.realpathSync(existing), ...suffix)
588
+ });
589
+ }
590
+
591
+ function resolveOutputDirectory(project, outDir) {
592
+ const projectRoot = fs.realpathSync(path.resolve(project.root));
593
+ const requestedTarget = path.isAbsolute(outDir)
594
+ ? path.resolve(outDir)
595
+ : path.resolve(projectRoot, outDir);
596
+
597
+ const physical = canonicalFuturePath(requestedTarget);
598
+ const target = physical.target;
599
+ const relativeTarget = path.relative(projectRoot, target);
600
+
601
+ if (
602
+ !relativeTarget
603
+ || relativeTarget.startsWith('..')
604
+ || path.isAbsolute(relativeTarget)
605
+ ) {
606
+ throw new PulseProjectError(
607
+ 'PULSE_BUILD_OUT_UNSAFE',
608
+ `Pulse build output must be a child directory of the project root: ${requestedTarget}`,
609
+ { outDir: requestedTarget, projectRoot }
610
+ );
611
+ }
612
+
613
+ let current = physical.existing;
614
+
615
+ for (;;) {
616
+ if (fs.lstatSync(current).isSymbolicLink()) {
617
+ throw new PulseProjectError(
618
+ 'PULSE_BUILD_OUT_UNSAFE',
619
+ `Pulse build output must not traverse a symbolic link: ${current}`,
620
+ { outDir: requestedTarget, projectRoot, symbolicLink: current }
621
+ );
622
+ }
623
+
624
+ if (fs.realpathSync(current) === projectRoot) break;
625
+
626
+ const parent = path.dirname(current);
627
+ if (parent === current) {
628
+ throw new PulseProjectError(
629
+ 'PULSE_BUILD_OUT_UNSAFE',
630
+ `Pulse build output resolves outside the project root: ${requestedTarget}`,
631
+ { outDir: requestedTarget, projectRoot }
632
+ );
633
+ }
634
+
635
+ current = parent;
636
+ }
637
+
638
+ return target;
639
+ }
640
+
641
+ function cleanOutputDirectory(project, outDir) {
642
+ const target = resolveOutputDirectory(project, outDir);
643
+ fs.rmSync(target, { recursive: true, force: true });
644
+ fs.mkdirSync(target, { recursive: true });
645
+ return target;
646
+ }
647
+
648
+ function buildProject(project, options = {}) {
649
+ const driver = providerDriver(project);
650
+ if (options.sourceOnly === true) {
651
+ throw new PulseProjectError(
652
+ 'PULSE_SOURCE_ONLY_REMOVED',
653
+ 'pulse build --source-only was removed. Native builds always emit generated source and provider Wasm together.',
654
+ { provider: project.provider, replacement: 'pulse build' }
655
+ );
656
+ }
657
+ if (options.experimentalNativeSize === true && (project.target || 'native') !== 'native') {
658
+ throw new PulseProjectError(
659
+ 'PULSE_EXPERIMENTAL_NATIVE_SIZE_UNSUPPORTED',
660
+ 'The --experimental-native-size flag is available only for Native compilation.',
661
+ {
662
+ target: project.target || 'native',
663
+ required: Object.freeze({ target: 'native' })
664
+ }
665
+ );
666
+ }
667
+ if (!driver.executable) {
668
+ throw new PulseProjectError(
669
+ 'PULSE_BUILD_PROVIDER_REQUIRED',
670
+ 'pulse build requires a deployment provider in the active .pulse/config.ts profile. Use pulse compile for provider-neutral Wasm.',
671
+ { provider: project.provider, supported: providerIds({ executable: true }), replacement: 'pulse compile' }
672
+ );
673
+ }
674
+ const descriptor = assertImplementedTarget(project, 'build');
675
+
676
+ if ((project.target || 'native') === 'javascript') {
677
+ const javascriptApplication = prepareJavascriptApplication(project, {
678
+ load: false,
679
+ captureLoadError: true,
680
+ allowedPackages: options.allowedPackages
681
+ });
682
+ if (javascriptApplication.plan.loadable !== true) {
683
+ throw new PulseProjectError(
684
+ 'PULSE_JAVASCRIPT_SOURCE_PACKAGE_PLAN_UNAVAILABLE',
685
+ 'pulse build requires a loadable JavaScript application plan.',
686
+ {
687
+ provider: project.provider,
688
+ target: 'javascript',
689
+ blockers: javascriptApplication.plan.blockers,
690
+ automaticFallback: false
691
+ }
692
+ );
693
+ }
694
+ const compiled = compileProject(project);
695
+ const eventSupport = assertEventCommandEligible(project, compiled, 'build', descriptor);
696
+ const targetSupport = selectedTargetSupportEvidence(project, compiled, javascriptApplication, descriptor, options);
697
+ if (targetSupport && targetSupport.project.status !== 'eligible') {
698
+ throw new PulseProjectError(
699
+ 'PULSE_PROVIDER_CAPABILITY_UNSUPPORTED',
700
+ 'pulse build cannot realize one or more project capabilities for the selected JavaScript target.',
701
+ {
702
+ provider: project.provider,
703
+ target: 'javascript',
704
+ status: targetSupport.project.status,
705
+ reasonIds: targetSupport.project.reasonIds,
706
+ targetSupport,
707
+ automaticFallback: false
708
+ }
709
+ );
710
+ }
711
+ const requestedOutDir = options.outDir || project.outDir;
712
+ const outDir = options.clean === false
713
+ ? resolveOutputDirectory(project, requestedOutDir)
714
+ : cleanOutputDirectory(project, requestedOutDir);
715
+ const javascript = driver.javascript;
716
+ if (!javascript || typeof javascript.writeSourcePackage !== 'function') {
717
+ throw new PulseProjectError(
718
+ 'PULSE_TARGET_IMPLEMENTATION_PENDING',
719
+ 'The selected provider does not implement JavaScript source packaging.',
720
+ { provider: project.provider, target: 'javascript', automaticFallback: false }
721
+ );
722
+ }
723
+ const providerPlan = providerLoweringPlan(project, compiled.metadata);
724
+ const invocation = providerTargetInvocation(
725
+ project,
726
+ 'write-javascript',
727
+ javascriptApplication.plan,
728
+ providerPlan,
729
+ {
730
+ outDir,
731
+ javascript: Object.freeze({
732
+ schemaBundle: compiled.schema.bundle,
733
+ reporting: project.reporting,
734
+ strict: project.strict !== false,
735
+ maxStructuredBodyBytes: project.schemas && project.schemas.maxBytes,
736
+ targetSupport
737
+ })
738
+ }
739
+ );
740
+ const sourcePackage = javascript.writeSourcePackage(invocation);
741
+ const sourcePackageDescription = javascript.describeSourcePackage(sourcePackage, {
742
+ outDir,
743
+ plan: javascriptApplication.plan
744
+ });
745
+ const packageInspection = writePackageInspectionArtifacts(compiled, outDir);
746
+ const eventInspection = writeEventInspectionArtifacts(project, compiled, outDir, eventSupport);
747
+ const manifest = Object.freeze({
748
+ version: PROJECT_EXECUTION_VERSION,
749
+ status: 'built',
750
+ buildMode: 'javascript-source-package',
751
+ provider: project.provider,
752
+ configuredTarget: 'javascript',
753
+ automaticFallback: false,
754
+ reporting: project.reportingPolicy,
755
+ cryptoRealizationPlan: compiled.cryptoRealizationPlan,
756
+ targetDescriptor: Object.freeze({ ...descriptor, resolvedReporting: project.reportingPolicy }),
757
+ providerTarget: Object.freeze({
758
+ package: driver.descriptor && driver.descriptor.package,
759
+ target: descriptor.targetId,
760
+ status: descriptor.status,
761
+ deployable: true,
762
+ deploymentCandidate: sourcePackageDescription.deploymentCandidate,
763
+ deploymentValidated: false,
764
+ providerRealityValidated: false,
765
+ sourcePackage: true,
766
+ sourceOnly: true,
767
+ nativeWasm: false,
768
+ compiledWasmPresent: false,
769
+ javascriptRuntime: true,
770
+ jsComputeRuntime: false,
771
+ downstreamJavascriptRuntimeWasm: sourcePackageDescription.downstreamJavascriptRuntimeWasm,
772
+ executionReady: descriptor.requestAdapter === true && descriptor.lifecycle === true,
773
+ applicationLoader: descriptor.applicationLoader === true,
774
+ requestAdapter: descriptor.requestAdapter === true,
775
+ lifecycle: descriptor.lifecycle === true
776
+ }),
777
+ project: Object.freeze({
778
+ root: '.',
779
+ config: project.configFile ? relative(project.configFile, project.root) : undefined,
780
+ entry: relative(project.entryFile, project.root),
781
+ outDir: '.'
782
+ }),
783
+ application: Object.freeze({
784
+ plan: path.basename(sourcePackage.paths.applicationPlan),
785
+ planHash: javascriptApplication.plan.planHash,
786
+ graphHash: javascriptApplication.plan.graph.graphHash,
787
+ loadable: true,
788
+ sourcePackage: path.basename(sourcePackage.paths.manifest),
789
+ entry: sourcePackageDescription.application.entry,
790
+ package: path.basename(sourcePackage.paths.package),
791
+ modules: sourcePackageDescription.application.modules,
792
+ dependencies: sourcePackage.manifest.package.dependencies,
793
+ ...sourcePackageDescription.application
794
+ }),
795
+ schemas: sourcePackage.manifest.schemas,
796
+ packageInspection: packageInspectionManifest(packageInspection),
797
+ ...(eventInspection ? { events: eventManifestProjection(eventInspection) } : {}),
798
+ targetSupport
799
+ });
800
+ const manifestFile = path.join(outDir, BUILD_MANIFEST);
801
+ fs.writeFileSync(manifestFile, stableJson(manifest));
802
+ return Object.freeze({
803
+ status: 'built',
804
+ version: PROJECT_EXECUTION_VERSION,
805
+ buildMode: 'javascript-source-package',
806
+ provider: project.provider,
807
+ configuredTarget: 'javascript',
808
+ automaticFallback: false,
809
+ project: projectJson(project),
810
+ outDir,
811
+ files: Object.freeze({
812
+ entry: sourcePackage.paths.entry,
813
+ package: sourcePackage.paths.package,
814
+ applicationPlan: sourcePackage.paths.applicationPlan,
815
+ schemaRegistry: sourcePackage.paths.schemaRegistry,
816
+ schemaCodecs: sourcePackage.paths.schemaCodecs,
817
+ ...sourcePackageDescription.files,
818
+ sourcePackage: sourcePackage.paths.manifest,
819
+ ...(eventInspection ? {
820
+ eventCatalog: eventInspection.catalogFile,
821
+ eventInspection: eventInspection.inspectionFile
822
+ } : {}),
823
+ manifest: manifestFile
824
+ }),
825
+ targetSupport,
826
+ packageInspection,
827
+ ...(eventInspection ? { events: eventInspection.inspection } : {}),
828
+ sourcePackage: sourcePackage.manifest,
829
+ manifest
830
+ });
831
+ }
832
+
833
+ const prepared = compileNativeProjectInMemory(project, options);
834
+ const eventSupport = assertEventCommandEligible(project, prepared.compiled, 'build', descriptor);
835
+ const requestedOutDir = options.outDir || project.outDir;
836
+ const outDir = options.clean === false
837
+ ? resolveOutputDirectory(project, requestedOutDir)
838
+ : cleanOutputDirectory(project, requestedOutDir);
839
+ fs.mkdirSync(outDir, { recursive: true });
840
+
841
+ const build = writeCanonicalBuild(prepared.compiled, outDir);
842
+ const nativeBuild = writeCanonicalNativeModule(prepared.native, outDir);
843
+ const packageInspection = writePackageInspectionArtifacts(prepared.compiled, outDir);
844
+ const eventInspection = writeEventInspectionArtifacts(project, prepared.compiled, outDir, eventSupport);
845
+ const lowering = providerLoweringPlan(project, prepared.compiled.metadata);
846
+ const providerBuild = driver.writeTarget
847
+ ? normalizeProviderTargetResult(driver.writeTarget(providerTargetInvocation(
848
+ project,
849
+ 'write-native',
850
+ prepared.plan,
851
+ lowering,
852
+ {
853
+ outDir,
854
+ native: prepared.native,
855
+ optimization: prepared.native.manifest.optimization,
856
+ timeoutMs: options.timeoutMs
857
+ }
858
+ )), { action: 'write-native', provider: project.provider, target: 'native' })
859
+ : undefined;
860
+ const providerFiles = providerBuild ? providerBuild.files : Object.freeze({});
861
+ const providerBuildMetadata = providerBuild && providerBuild.build;
862
+ const nativeManifest = prepared.native.manifest;
863
+
864
+ const manifest = Object.freeze({
865
+ version: PROJECT_EXECUTION_VERSION,
866
+ status: 'built',
867
+ buildMode: 'native-provider',
868
+ provider: project.provider,
869
+ configuredTarget: 'native',
870
+ reporting: project.reportingPolicy,
871
+ cryptoRealizationPlan: prepared.compiled.cryptoRealizationPlan,
872
+ targetDescriptor: targetDescriptor(project, 'native'),
873
+ providerTarget: Object.freeze({
874
+ package: driver.descriptor && driver.descriptor.package,
875
+ buildTarget: driver.descriptor && driver.descriptor.buildTarget,
876
+ target: providerBuildMetadata && providerBuildMetadata.target,
877
+ deployable: driver.deployable === true,
878
+ deploymentValidated: driver.deploymentValidated === true,
879
+ sourcePackage: driver.sourcePackage === true,
880
+ sourceOnly: false,
881
+ sourceOnlySupported: false,
882
+ nativeWasm: true,
883
+ providerNeutralInput: true,
884
+ compiledWasmPresent: true,
885
+ javascriptRuntime: false,
886
+ jsComputeRuntime: false,
887
+ compiler: providerBuildMetadata && providerBuildMetadata.compiler,
888
+ optimization: providerBuildMetadata && providerBuildMetadata.optimization,
889
+ wasm: providerBuildMetadata && providerBuildMetadata.wasm,
890
+ wat: providerBuildMetadata && providerBuildMetadata.wat,
891
+ importModules: providerBuildMetadata && providerBuildMetadata.importModules,
892
+ imports: providerBuildMetadata && providerBuildMetadata.imports,
893
+ exports: providerBuildMetadata && providerBuildMetadata.exports,
894
+ localConformanceRuntime: driver.localExecution === true,
895
+ lowering
896
+ }),
897
+ project: Object.freeze({
898
+ root: '.',
899
+ config: project.configFile ? relative(project.configFile, project.root) : undefined,
900
+ entry: relative(project.entryFile, project.root),
901
+ outDir: relative(outDir, project.root)
902
+ }),
903
+ program: Object.freeze({
904
+ file: path.basename(build.programFile),
905
+ handler: path.basename(build.handlerFile),
906
+ sourceHash: prepared.compiled.metadata.sourceHash,
907
+ compilerVersion: prepared.compiled.metadata.compilerVersion,
908
+ runtimeProtocolVersion: prepared.compiled.metadata.runtimeProtocolVersion,
909
+ authoring: prepared.compiled.metadata.authoring,
910
+ application: prepared.compiled.metadata.application,
911
+ routing: prepared.plan.routing,
912
+ json: prepared.compiled.metadata.json,
913
+ warnings: prepared.compiled.metadata.warnings,
914
+ warningCount: prepared.compiled.metadata.warningCount || 0,
915
+ capabilities: prepared.compiled.metadata.capabilities,
916
+ effectCount: prepared.compiled.metadata.effectCount,
917
+ continuationCount: prepared.compiled.metadata.continuationCount,
918
+ groupedContinuationCount: prepared.compiled.metadata.groupedContinuationCount,
919
+ opaqueReturnCount: prepared.compiled.metadata.opaqueReturnCount,
920
+ projectSourceHash: prepared.compiled.metadata.projectSourceHash,
921
+ schemaReferenceCount: prepared.compiled.metadata.schemaReferenceCount,
922
+ schemaSourceHash: prepared.compiled.metadata.schemaSourceHash
923
+ }),
924
+ portable: Object.freeze({
925
+ target: 'portable-native-wasm',
926
+ providerNeutral: true,
927
+ version: prepared.native.version,
928
+ compilerVersion: prepared.native.compilerVersion,
929
+ planVersion: prepared.plan.version,
930
+ planCompilerVersion: prepared.plan.compilerVersion,
931
+ planHash: prepared.plan.planHash,
932
+ ownership: prepared.plan.ownership,
933
+ summary: prepared.plan.summary,
934
+ logging: prepared.plan.logging,
935
+ ...(prepared.plan.json ? { json: prepared.plan.json } : {}),
936
+ ...(prepared.plan.requestState ? { requestState: prepared.plan.requestState } : {}),
937
+ assemblyScript: nativeManifest.assemblyScript,
938
+ optimization: nativeManifest.optimization,
939
+ source: Object.freeze({ file: path.basename(nativeBuild.sourceFile), sha256: prepared.native.sourceHash }),
940
+ wasm: Object.freeze({ file: path.basename(nativeBuild.wasmFile), ...nativeManifest.wasm }),
941
+ wat: Object.freeze({ file: path.basename(nativeBuild.watFile), ...nativeManifest.wat }),
942
+ plan: path.basename(nativeBuild.planFile),
943
+ manifest: path.basename(nativeBuild.manifestFile),
944
+ packageRealizationArtifacts: Object.freeze({
945
+ ...(nativeBuild.realizationArtifactsFile
946
+ ? { file: path.basename(nativeBuild.realizationArtifactsFile) }
947
+ : {}),
948
+ ...nativeManifest.packageRealizationArtifacts
949
+ }),
950
+ ...(nativeBuild.guestUnitPlanFile ? {
951
+ guestUnit: Object.freeze({
952
+ plan: path.basename(nativeBuild.guestUnitPlanFile),
953
+ report: path.basename(nativeBuild.guestLinkReportFile),
954
+ audit: path.basename(nativeBuild.finalWasmAuditFile),
955
+ finalArtifact: nativeManifest.guestLinkReport.finalArtifact,
956
+ providerPackaging: nativeManifest.providerPackaging
957
+ })
958
+ } : {}),
959
+ importModules: nativeManifest.importModules,
960
+ imports: nativeManifest.imports,
961
+ exports: nativeManifest.exports,
962
+ policy: nativeManifest.policy
963
+ }),
964
+ schemas: Object.freeze({
965
+ active: prepared.compiled.schema.active,
966
+ registry: build.schemaRegistryFile ? path.basename(build.schemaRegistryFile) : undefined,
967
+ codecs: build.schemaCodecsFile ? path.basename(build.schemaCodecsFile) : undefined,
968
+ ids: prepared.compiled.metadata.schemaIds,
969
+ references: prepared.compiled.metadata.schemaReferences,
970
+ sourceHash: prepared.compiled.metadata.schemaSourceHash,
971
+ policy: prepared.compiled.metadata.json
972
+ }),
973
+ packageInspection: packageInspectionManifest(packageInspection),
974
+ ...(eventInspection ? { events: eventManifestProjection(eventInspection) } : {})
975
+ });
976
+ const manifestFile = path.join(outDir, BUILD_MANIFEST);
977
+ fs.writeFileSync(manifestFile, stableJson(manifest));
978
+ return Object.freeze({
979
+ status: 'built',
980
+ version: PROJECT_EXECUTION_VERSION,
981
+ buildMode: 'native-provider',
982
+ provider: project.provider,
983
+ project: projectJson(project),
984
+ outDir,
985
+ files: Object.freeze({
986
+ handler: build.handlerFile,
987
+ program: build.programFile,
988
+ schemaRegistry: build.schemaRegistryFile,
989
+ schemaCodecs: build.schemaCodecsFile,
990
+ nativeSource: nativeBuild.sourceFile,
991
+ nativeWasm: nativeBuild.wasmFile,
992
+ nativeWat: nativeBuild.watFile,
993
+ nativePlan: nativeBuild.planFile,
994
+ nativeManifest: nativeBuild.manifestFile,
995
+ nativePackageArtifacts: nativeBuild.realizationArtifactsFile,
996
+ guestUnitPlan: nativeBuild.guestUnitPlanFile,
997
+ guestLinkReport: nativeBuild.guestLinkReportFile,
998
+ finalWasmAudit: nativeBuild.finalWasmAuditFile,
999
+ ...(eventInspection ? {
1000
+ eventCatalog: eventInspection.catalogFile,
1001
+ eventInspection: eventInspection.inspectionFile
1002
+ } : {}),
1003
+ manifest: manifestFile,
1004
+ ...providerFiles
1005
+ }),
1006
+ metadata: prepared.compiled.metadata,
1007
+ packageInspection,
1008
+ ...(eventInspection ? { events: eventInspection.inspection } : {}),
1009
+ native: Object.freeze({
1010
+ version: prepared.native.version,
1011
+ compilerVersion: prepared.native.compilerVersion,
1012
+ planVersion: prepared.plan.version,
1013
+ planHash: prepared.plan.planHash,
1014
+ summary: prepared.plan.summary,
1015
+ logging: prepared.plan.logging,
1016
+ ...(prepared.plan.json ? { json: prepared.plan.json } : {}),
1017
+ ...(prepared.plan.requestState ? { requestState: prepared.plan.requestState } : {}),
1018
+ assemblyScript: nativeManifest.assemblyScript,
1019
+ optimization: nativeManifest.optimization,
1020
+ wasm: nativeManifest.wasm,
1021
+ importModules: nativeManifest.importModules,
1022
+ exports: nativeManifest.exports
1023
+ }),
1024
+ manifest
1025
+ });
1026
+ }
1027
+
1028
+ function compileNativeProject(project, options = {}) {
1029
+ const requestedOutDir = options.outDir || project.outDir;
1030
+ const outDir = options.clean === false
1031
+ ? resolveOutputDirectory(project, requestedOutDir)
1032
+ : cleanOutputDirectory(project, requestedOutDir);
1033
+ fs.mkdirSync(outDir, { recursive: true });
1034
+
1035
+ const prepared = compileNativeProjectInMemory(project, options);
1036
+ const javascriptApplication = (project.target || 'native') === 'javascript'
1037
+ ? prepareJavascriptApplication(project, { load: false, captureLoadError: true, allowedPackages: options.allowedPackages })
1038
+ : null;
1039
+ const targetSupport = javascriptApplication
1040
+ ? selectedTargetSupportEvidence(project, prepared.compiled, javascriptApplication, javascriptApplication.descriptor, options)
1041
+ : null;
1042
+ const eventSupport = selectedEventTargetSupport(project, prepared.compiled);
1043
+ const build = writeCanonicalBuild(prepared.compiled, outDir);
1044
+ const nativeBuild = writeCanonicalNativeModule(prepared.native, outDir);
1045
+ const packageInspection = writePackageInspectionArtifacts(prepared.compiled, outDir);
1046
+ const eventInspection = writeEventInspectionArtifacts(project, prepared.compiled, outDir, eventSupport);
1047
+ const nativeManifest = prepared.native.manifest;
1048
+ const manifest = Object.freeze({
1049
+ version: PROJECT_EXECUTION_VERSION,
1050
+ status: 'compiled',
1051
+ target: 'portable-native-wasm',
1052
+ provider: null,
1053
+ providerNeutral: true,
1054
+ configuredProvider: project.provider,
1055
+ configuredTarget: project.target || 'native',
1056
+ automaticFallback: false,
1057
+ reporting: project.reportingPolicy,
1058
+ cryptoRealizationPlan: prepared.compiled.cryptoRealizationPlan,
1059
+ ...(targetSupport ? { targetSupport } : {}),
1060
+ project: Object.freeze({
1061
+ root: '.',
1062
+ config: project.configFile ? relative(project.configFile, project.root) : undefined,
1063
+ entry: relative(project.entryFile, project.root),
1064
+ outDir: relative(outDir, project.root)
1065
+ }),
1066
+ program: Object.freeze({
1067
+ file: path.basename(build.programFile),
1068
+ handler: path.basename(build.handlerFile),
1069
+ sourceHash: prepared.compiled.metadata.sourceHash,
1070
+ compilerVersion: prepared.compiled.metadata.compilerVersion,
1071
+ runtimeProtocolVersion: prepared.compiled.metadata.runtimeProtocolVersion,
1072
+ authoring: prepared.compiled.metadata.authoring,
1073
+ application: prepared.compiled.metadata.application,
1074
+ routing: prepared.plan.routing,
1075
+ json: prepared.compiled.metadata.json,
1076
+ warnings: prepared.compiled.metadata.warnings,
1077
+ warningCount: prepared.compiled.metadata.warningCount || 0,
1078
+ capabilities: prepared.compiled.metadata.capabilities,
1079
+ effectCount: prepared.compiled.metadata.effectCount,
1080
+ continuationCount: prepared.compiled.metadata.continuationCount,
1081
+ groupedContinuationCount: prepared.compiled.metadata.groupedContinuationCount,
1082
+ opaqueReturnCount: prepared.compiled.metadata.opaqueReturnCount,
1083
+ projectSourceHash: prepared.compiled.metadata.projectSourceHash,
1084
+ schemaReferenceCount: prepared.compiled.metadata.schemaReferenceCount,
1085
+ schemaSourceHash: prepared.compiled.metadata.schemaSourceHash
1086
+ }),
1087
+ native: Object.freeze({
1088
+ version: prepared.native.version,
1089
+ compilerVersion: prepared.native.compilerVersion,
1090
+ planVersion: prepared.plan.version,
1091
+ planCompilerVersion: prepared.plan.compilerVersion,
1092
+ planHash: prepared.plan.planHash,
1093
+ ownership: prepared.plan.ownership,
1094
+ summary: prepared.plan.summary,
1095
+ logging: prepared.plan.logging,
1096
+ ...(prepared.plan.json ? { json: prepared.plan.json } : {}),
1097
+ ...(prepared.plan.requestState ? { requestState: prepared.plan.requestState } : {}),
1098
+ assemblyScript: nativeManifest.assemblyScript,
1099
+ optimization: nativeManifest.optimization,
1100
+ source: Object.freeze({
1101
+ file: path.basename(nativeBuild.sourceFile),
1102
+ sha256: prepared.native.sourceHash
1103
+ }),
1104
+ wasm: Object.freeze({
1105
+ file: path.basename(nativeBuild.wasmFile),
1106
+ ...nativeManifest.wasm
1107
+ }),
1108
+ wat: Object.freeze({
1109
+ file: path.basename(nativeBuild.watFile),
1110
+ ...nativeManifest.wat
1111
+ }),
1112
+ plan: path.basename(nativeBuild.planFile),
1113
+ manifest: path.basename(nativeBuild.manifestFile),
1114
+ packageRealizationArtifacts: Object.freeze({
1115
+ ...(nativeBuild.realizationArtifactsFile
1116
+ ? { file: path.basename(nativeBuild.realizationArtifactsFile) }
1117
+ : {}),
1118
+ ...nativeManifest.packageRealizationArtifacts
1119
+ }),
1120
+ ...(nativeBuild.guestUnitPlanFile ? {
1121
+ guestUnit: Object.freeze({
1122
+ plan: path.basename(nativeBuild.guestUnitPlanFile),
1123
+ report: path.basename(nativeBuild.guestLinkReportFile),
1124
+ audit: path.basename(nativeBuild.finalWasmAuditFile),
1125
+ finalArtifact: nativeManifest.guestLinkReport.finalArtifact,
1126
+ providerPackaging: nativeManifest.providerPackaging
1127
+ })
1128
+ } : {}),
1129
+ importModules: nativeManifest.importModules,
1130
+ imports: nativeManifest.imports,
1131
+ exports: nativeManifest.exports,
1132
+ policy: nativeManifest.policy
1133
+ }),
1134
+ schemas: Object.freeze({
1135
+ active: prepared.compiled.schema.active,
1136
+ registry: build.schemaRegistryFile ? path.basename(build.schemaRegistryFile) : undefined,
1137
+ codecs: build.schemaCodecsFile ? path.basename(build.schemaCodecsFile) : undefined,
1138
+ ids: prepared.compiled.metadata.schemaIds,
1139
+ references: prepared.compiled.metadata.schemaReferences,
1140
+ sourceHash: prepared.compiled.metadata.schemaSourceHash,
1141
+ policy: prepared.compiled.metadata.json
1142
+ }),
1143
+ packageInspection: packageInspectionManifest(packageInspection),
1144
+ ...(eventInspection ? { events: eventManifestProjection(eventInspection) } : {})
1145
+ });
1146
+ const manifestFile = path.join(outDir, COMPILE_MANIFEST);
1147
+ fs.writeFileSync(manifestFile, stableJson(manifest));
1148
+
1149
+ return Object.freeze({
1150
+ status: 'compiled',
1151
+ version: PROJECT_EXECUTION_VERSION,
1152
+ target: 'portable-native-wasm',
1153
+ provider: null,
1154
+ providerNeutral: true,
1155
+ configuredProvider: project.provider,
1156
+ configuredTarget: project.target || 'native',
1157
+ automaticFallback: false,
1158
+ targetSupport,
1159
+ project: projectJson(project),
1160
+ outDir,
1161
+ files: Object.freeze({
1162
+ handler: build.handlerFile,
1163
+ program: build.programFile,
1164
+ schemaRegistry: build.schemaRegistryFile,
1165
+ schemaCodecs: build.schemaCodecsFile,
1166
+ nativeSource: nativeBuild.sourceFile,
1167
+ nativeWasm: nativeBuild.wasmFile,
1168
+ nativeWat: nativeBuild.watFile,
1169
+ nativePlan: nativeBuild.planFile,
1170
+ nativeManifest: nativeBuild.manifestFile,
1171
+ nativePackageArtifacts: nativeBuild.realizationArtifactsFile,
1172
+ guestUnitPlan: nativeBuild.guestUnitPlanFile,
1173
+ guestLinkReport: nativeBuild.guestLinkReportFile,
1174
+ finalWasmAudit: nativeBuild.finalWasmAuditFile,
1175
+ ...(eventInspection ? {
1176
+ eventCatalog: eventInspection.catalogFile,
1177
+ eventInspection: eventInspection.inspectionFile
1178
+ } : {}),
1179
+ manifest: manifestFile
1180
+ }),
1181
+ metadata: prepared.compiled.metadata,
1182
+ packageInspection,
1183
+ ...(eventInspection ? { events: eventInspection.inspection } : {}),
1184
+ native: Object.freeze({
1185
+ version: prepared.native.version,
1186
+ compilerVersion: prepared.native.compilerVersion,
1187
+ planVersion: prepared.plan.version,
1188
+ planHash: prepared.plan.planHash,
1189
+ summary: prepared.plan.summary,
1190
+ logging: prepared.plan.logging,
1191
+ ...(prepared.plan.json ? { json: prepared.plan.json } : {}),
1192
+ ...(prepared.plan.requestState ? { requestState: prepared.plan.requestState } : {}),
1193
+ assemblyScript: nativeManifest.assemblyScript,
1194
+ optimization: nativeManifest.optimization,
1195
+ wasm: nativeManifest.wasm,
1196
+ importModules: nativeManifest.importModules,
1197
+ exports: nativeManifest.exports
1198
+ }),
1199
+ manifest
1200
+ });
1201
+ }
1202
+
1203
+ function headerValue(headers, name) {
1204
+ const lower = String(name).toLowerCase();
1205
+ const values = (headers || []).filter(([key]) => String(key).toLowerCase() === lower).map(([, value]) => String(value));
1206
+ return values.length <= 1 ? values[0] : values;
1207
+ }
1208
+
1209
+ function responseJson(response) {
1210
+ if (response.bodyClass === 'opaque') throw new Error('Opaque response cannot be decoded as JSON.');
1211
+ return JSON.parse(response.body || 'null');
1212
+ }
1213
+
1214
+ function statusAllowsResponseBody(status) {
1215
+ const value = Number(status);
1216
+ return value !== 204 && value !== 205 && value !== 304 && !(value >= 100 && value < 200);
1217
+ }
1218
+
1219
+ function observableTestResponse(testCase, response) {
1220
+ const method = String(testCase.request && testCase.request.method || 'GET').toUpperCase();
1221
+ if (method !== 'HEAD' && statusAllowsResponseBody(response.status)) return response;
1222
+ return Object.freeze({ ...response, body: '' });
1223
+ }
1224
+
1225
+ function materializeTestResponse(response) {
1226
+ if (!response || response.bodyClass !== 'opaque' || response.body !== undefined) return response;
1227
+ const chunks = response.bodyStream && Array.isArray(response.bodyStream.chunks)
1228
+ ? response.bodyStream.chunks
1229
+ : undefined;
1230
+ if (!chunks) return response;
1231
+ const body = Buffer.concat(chunks.map((chunk) => Buffer.isBuffer(chunk)
1232
+ ? chunk
1233
+ : chunk instanceof Uint8Array
1234
+ ? Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength)
1235
+ : Buffer.from(String(chunk)))).toString('utf8');
1236
+ return Object.freeze({ ...response, body });
1237
+ }
1238
+
1239
+ function assertExpectation(testCase, execution) {
1240
+ const expected = testCase.expect;
1241
+ const response = observableTestResponse(testCase, execution.response);
1242
+ if (expected.status !== undefined) assert.equal(response.status, expected.status, `${testCase.name}: unexpected status`);
1243
+ if (expected.bodyClass !== undefined) assert.equal(response.bodyClass, expected.bodyClass, `${testCase.name}: unexpected body class`);
1244
+ if (Object.prototype.hasOwnProperty.call(expected, 'json') && expected.json !== undefined) assert.deepEqual(responseJson(response), expected.json, `${testCase.name}: unexpected JSON body`);
1245
+ if (expected.text !== undefined) assert.equal(String(response.body || ''), expected.text, `${testCase.name}: unexpected text body`);
1246
+ if (expected.headers) {
1247
+ for (const [name, value] of expected.headers) assert.deepEqual(headerValue(response.headers, name), value, `${testCase.name}: unexpected header ${name}`);
1248
+ }
1249
+ }
1250
+
1251
+ function secretTokens(secrets) {
1252
+ return [...new Set(Object.values(secrets || {})
1253
+ .filter((value) => typeof value === 'string' && value.length > 0))]
1254
+ .sort((left, right) => right.length - left.length);
1255
+ }
1256
+
1257
+ function redactString(value, tokens) {
1258
+ let text = String(value);
1259
+ for (const token of tokens) text = text.split(token).join('<redacted>');
1260
+ return text;
1261
+ }
1262
+
1263
+ function redactValue(value, tokens, seen = new WeakSet()) {
1264
+ if (typeof value === 'string') return redactString(value, tokens);
1265
+ if (value === null || value === undefined || typeof value !== 'object') return value;
1266
+ if (Buffer.isBuffer(value)) return '<binary>';
1267
+ if (seen.has(value)) return '<circular>';
1268
+ seen.add(value);
1269
+ if (Array.isArray(value)) return value.map((entry) => redactValue(entry, tokens, seen));
1270
+ const out = {};
1271
+ for (const [key, entry] of Object.entries(value)) out[key] = redactValue(entry, tokens, seen);
1272
+ return out;
1273
+ }
1274
+
1275
+ function errorSummary(error, secrets) {
1276
+ const tokens = secretTokens(secrets);
1277
+ const code = error && error.code ? String(error.code) : undefined;
1278
+ const descriptor = describeDiagnostic(code);
1279
+ const diagnostics = error && Array.isArray(error.diagnostics)
1280
+ ? Object.freeze(redactValue(error.diagnostics, tokens).map((entry) => decorateDiagnostic(entry)))
1281
+ : undefined;
1282
+ return Object.freeze({
1283
+ name: error && error.name ? String(error.name) : 'Error',
1284
+ code,
1285
+ title: descriptor.title,
1286
+ summary: descriptor.summary,
1287
+ category: descriptor.category,
1288
+ message: redactString(error && error.message ? error.message : String(error), tokens),
1289
+ remediation: descriptor.remediation,
1290
+ stability: descriptor.stability,
1291
+ scope: descriptor.scope,
1292
+ docs: descriptor.docs,
1293
+ detail: error && error.detail ? Object.freeze(redactValue(error.detail, tokens)) : undefined,
1294
+ diagnostics,
1295
+ execution: error && error.execution ? Object.freeze(redactValue({
1296
+ executionId: error.execution.executionId,
1297
+ resolutionOrder: error.execution.resolutionOrder,
1298
+ continuations: error.execution.continuations
1299
+ }, tokens)) : undefined
1300
+ });
1301
+ }
1302
+
1303
+ function assertExpectedError(testCase, error) {
1304
+ const expected = testCase.expect.error;
1305
+ if (!expected) throw error;
1306
+ if (expected.name !== undefined) assert.equal(error.name, expected.name, `${testCase.name}: unexpected error name`);
1307
+ if (expected.code !== undefined) assert.equal(error.code, expected.code, `${testCase.name}: unexpected error code`);
1308
+ if (expected.category !== undefined) assert.equal(error.category, expected.category, `${testCase.name}: unexpected error category`);
1309
+ if (expected.message !== undefined) assert.match(String(error.message || ''), new RegExp(String(expected.message)), `${testCase.name}: unexpected error message`);
1310
+ }
1311
+
1312
+ function assertEventExpectation(testCase, execution) {
1313
+ const expected = testCase.expect;
1314
+ const result = eventContracts.normalizeEventExecutionResult(execution.result);
1315
+ assert.equal(result.status, expected.status, `${testCase.name}: unexpected event execution status`);
1316
+ if (expected.error) {
1317
+ assert.ok(result.error, `${testCase.name}: expected an event execution error`);
1318
+ if (expected.error.name !== undefined) assert.equal(result.error.name, expected.error.name, `${testCase.name}: unexpected event error name`);
1319
+ if (expected.error.code !== undefined) assert.equal(result.error.code, expected.error.code, `${testCase.name}: unexpected event error code`);
1320
+ if (expected.error.category !== undefined) assert.equal(result.error.category, expected.error.category, `${testCase.name}: unexpected event error category`);
1321
+ if (expected.error.message !== undefined) assert.match(String(result.error.message || ''), new RegExp(String(expected.error.message)), `${testCase.name}: unexpected event error message`);
1322
+ }
1323
+ assert.deepEqual(execution.emittedFrames, expected.emitted, `${testCase.name}: unexpected emitted event frames`);
1324
+ return result;
1325
+ }
1326
+
1327
+ async function executeProviderEventTestCase(driver, target, application, testCase, options = {}) {
1328
+ if (!driver.events || !driver.events.targets.includes(target) || typeof driver.events.executeTestCase !== 'function') {
1329
+ throw new PulseProjectError(
1330
+ 'PULSE_EVENT_TARGET_UNSUPPORTED',
1331
+ `The selected ${driver.id}/${target} provider target does not implement event test execution.`,
1332
+ { provider: driver.id, target, command: 'test', automaticFallback: false }
1333
+ );
1334
+ }
1335
+ return driver.events.executeTestCase(Object.freeze({
1336
+ target,
1337
+ application,
1338
+ testCase,
1339
+ options: Object.freeze({ ...options })
1340
+ }));
1341
+ }
1342
+
1343
+ function eventTestCaseResult(testCase, execution, result, started) {
1344
+ const evidence = execution.executionEvidence || {};
1345
+ const effectCount = Number(evidence.effectCount === undefined ? evidence.ownedEffectCount || 0 : evidence.effectCount);
1346
+ return Object.freeze({
1347
+ name: testCase.name,
1348
+ kind: 'event',
1349
+ status: 'passed',
1350
+ durationMs: Number(process.hrtime.bigint() - started) / 1e6,
1351
+ event: Object.freeze({ type: testCase.event.type, schemaId: testCase.event.schemaId }),
1352
+ result,
1353
+ emittedFrames: Object.freeze(redactValue(execution.emittedFrames, secretTokens(testCase.secrets))),
1354
+ emittedFrameCount: execution.emittedFrames.length,
1355
+ effects: effectCount,
1356
+ adapter: execution.adapter
1357
+ });
1358
+ }
1359
+
1360
+ async function runJavascriptProjectTests(project, options = {}) {
1361
+ const descriptor = assertImplementedTarget(project, 'test');
1362
+ const javascript = providerDriver(project).javascript;
1363
+ if (!javascript || typeof javascript.executeTestCase !== 'function' || descriptor.applicationLoader !== true || descriptor.requestAdapter !== true) {
1364
+ throw new PulseProjectError(
1365
+ 'PULSE_TARGET_IMPLEMENTATION_PENDING',
1366
+ `pulse test cannot realize the selected javascript target for ${project.provider} yet.`,
1367
+ { target: 'javascript', command: 'test', provider: project.provider, targetDescriptor: descriptor, automaticFallback: false }
1368
+ );
1369
+ }
1370
+ const selected = options.caseName ? project.tests.filter((entry) => entry.name === options.caseName) : project.tests;
1371
+ if (selected.length === 0) {
1372
+ throw new PulseProjectError('PULSE_TEST_CASES_MISSING', options.caseName ? `No Pulse test case named ${options.caseName}.` : 'Pulse project defines no test cases.', { available: project.tests.map((entry) => entry.name) });
1373
+ }
1374
+ const localEmulation = javascript.localEmulation === true;
1375
+ const compiled = compileProject(project);
1376
+ const eventSupport = selectedEventTargetSupport(project, compiled, descriptor);
1377
+ if (eventSupport) assertEventCommandEligible(project, compiled, 'test', descriptor);
1378
+ if (selected.some((testCase) => testCase.kind === 'event') && !eventSupport) {
1379
+ throw new PulseProjectError('PULSE_TEST_EVENT_INVALID', 'The test harness contains an event case, but the application has no reachable event topology.');
1380
+ }
1381
+ const prepared = prepareJavascriptApplication(project, {
1382
+ allowedPackages: options.allowedPackages,
1383
+ localEmulation,
1384
+ schemaBundle: compiled.schema.bundle
1385
+ });
1386
+ if (!prepared.loaded) {
1387
+ throw new PulseProjectError('PULSE_JAVASCRIPT_APPLICATION_UNAVAILABLE', 'The graph-backed JavaScript application could not be loaded for project tests.', {
1388
+ planHash: prepared.plan.planHash,
1389
+ blockers: prepared.plan.blockers,
1390
+ automaticFallback: false
1391
+ });
1392
+ }
1393
+ const schemaCodecs = createCanonicalSchemaCodecs(compiled.schema.bundle.registry);
1394
+ const cases = [];
1395
+ let failed = 0;
1396
+ for (const testCase of selected) {
1397
+ const started = process.hrtime.bigint();
1398
+ const jsonTrace = [];
1399
+ try {
1400
+ if (testCase.kind === 'event') {
1401
+ const execution = await executeProviderEventTestCase(providerDriver(project), 'javascript', prepared.loaded.application, testCase, {
1402
+ application: Object.freeze({
1403
+ projectHash: project.projectHash,
1404
+ planHash: project.planHash,
1405
+ profile: project.profile,
1406
+ target: 'javascript',
1407
+ provider: project.provider
1408
+ }),
1409
+ schemaCodecs,
1410
+ reporting: project.reporting,
1411
+ networkFetch: project.dev.networkFetch
1412
+ });
1413
+ const result = assertEventExpectation(testCase, execution);
1414
+ cases.push(eventTestCaseResult(testCase, execution, result, started));
1415
+ continue;
1416
+ }
1417
+ const runtimeOptions = {
1418
+ application: Object.freeze({
1419
+ projectHash: project.projectHash,
1420
+ planHash: project.planHash,
1421
+ profile: project.profile,
1422
+ target: 'javascript',
1423
+ provider: project.provider
1424
+ }),
1425
+ maxBodyBytes: testCase.maxBodyBytes === undefined
1426
+ ? (project.schemas.active ? project.schemas.maxBytes : undefined)
1427
+ : testCase.maxBodyBytes,
1428
+ schemaCodecs,
1429
+ strict: project.strict !== false,
1430
+ provider: project.provider,
1431
+ reporting: project.reporting,
1432
+ grip: testCase.grip,
1433
+ onJsonTrace(event) { jsonTrace.push(event); }
1434
+ };
1435
+ const execution = await javascript.executeTestCase(prepared.loaded.application, testCase, {
1436
+ ...runtimeOptions,
1437
+ bindings: project.providerConfig.bindings,
1438
+ networkFetch: project.dev.networkFetch
1439
+ });
1440
+ if (testCase.expect.error) throw new assert.AssertionError({ message: `${testCase.name}: expected ${testCase.expect.error.name || 'an error'} but execution completed` });
1441
+ assertExpectation(testCase, Object.freeze({
1442
+ ...execution,
1443
+ response: materializeTestResponse(execution.response)
1444
+ }));
1445
+ cases.push(Object.freeze({
1446
+ name: testCase.name,
1447
+ status: 'passed',
1448
+ durationMs: Number(process.hrtime.bigint() - started) / 1e6,
1449
+ response: Object.freeze({ status: execution.response.status, bodyClass: execution.response.bodyClass, kind: execution.response.kind }),
1450
+ effects: execution.effectCount,
1451
+ ...(localEmulation ? { providerExecution: execution.provider } : {}),
1452
+ jsonTrace: Object.freeze(jsonTrace),
1453
+ continuations: execution.continuations,
1454
+ resolutionOrder: execution.resolutionOrder
1455
+ }));
1456
+ } catch (error) {
1457
+ try {
1458
+ if (testCase.kind === 'event') throw error;
1459
+ assertExpectedError(testCase, error);
1460
+ cases.push(Object.freeze({ name: testCase.name, status: 'passed', expectedError: errorSummary(error, testCase.secrets), durationMs: Number(process.hrtime.bigint() - started) / 1e6 }));
1461
+ } catch (assertion) {
1462
+ failed += 1;
1463
+ cases.push(Object.freeze({ name: testCase.name, status: 'failed', error: errorSummary(assertion === error ? error : assertion, testCase.secrets), actualError: assertion === error ? undefined : errorSummary(error, testCase.secrets), durationMs: Number(process.hrtime.bigint() - started) / 1e6 }));
1464
+ }
1465
+ }
1466
+ }
1467
+ return Object.freeze({
1468
+ status: failed === 0 ? 'passed' : 'failed',
1469
+ version: PROJECT_EXECUTION_VERSION,
1470
+ provider: project.provider,
1471
+ target: 'javascript',
1472
+ targetId: descriptor.targetId,
1473
+ automaticFallback: false,
1474
+ project: projectJson(project),
1475
+ applicationPlan: Object.freeze({
1476
+ version: prepared.plan.version,
1477
+ planHash: prepared.plan.planHash,
1478
+ graphHash: prepared.plan.graph.graphHash,
1479
+ nativeEligibility: prepared.plan.nativeEligibility,
1480
+ automaticFallback: false
1481
+ }),
1482
+ metadata: Object.freeze({
1483
+ runtimeClass: descriptor.runtimeClass,
1484
+ sourceApplication: true,
1485
+ schemaRegistryHash: compiled.metadata.schemaRegistryHash,
1486
+ schemaCodecTableHash: compiled.metadata.schemaCodecTableHash,
1487
+ schemaIds: compiled.metadata.schemaIds,
1488
+ responseCaseIds: compiled.metadata.responseCaseIds,
1489
+ ...(localEmulation ? {
1490
+ localExecution: Object.freeze({
1491
+ mode: javascript.localExecutionMode,
1492
+ providerReality: false,
1493
+ realityRunner: javascript.realityRunner,
1494
+ automaticFallback: false,
1495
+ loader: Object.freeze({
1496
+ version: prepared.loaded.version,
1497
+ kind: 'provider-owned-bundled-closure',
1498
+ sourceHash: prepared.loaded.closure.sourceHash,
1499
+ bundledInputs: prepared.loaded.closure.inputs.length,
1500
+ packagesLoaded: prepared.loaded.packagesLoaded
1501
+ })
1502
+ })
1503
+ } : {})
1504
+ }),
1505
+ summary: Object.freeze({ total: cases.length, passed: cases.length - failed, failed }),
1506
+ cases: Object.freeze(cases)
1507
+ });
1508
+ }
1509
+
1510
+ async function runProjectTests(project, options = {}) {
1511
+ if ((project.target || 'native') === 'javascript') return runJavascriptProjectTests(project, options);
1512
+ const selected = options.caseName ? project.tests.filter((entry) => entry.name === options.caseName) : project.tests;
1513
+ if (selected.length === 0) {
1514
+ throw new PulseProjectError('PULSE_TEST_CASES_MISSING', options.caseName ? `No Pulse test case named ${options.caseName}.` : 'Pulse project defines no test cases.', { available: project.tests.map((entry) => entry.name) });
1515
+ }
1516
+ const hasEventCase = selected.some((testCase) => testCase.kind === 'event');
1517
+ const descriptor = targetDescriptor(project, 'native');
1518
+ let compiled = hasEventCase ? compileProject(project) : null;
1519
+ const eventSupport = hasEventCase ? selectedEventTargetSupport(project, compiled, descriptor) : null;
1520
+ if (hasEventCase && eventSupport) assertEventCommandEligible(project, compiled, 'test', descriptor);
1521
+ if (hasEventCase && !eventSupport) {
1522
+ throw new PulseProjectError('PULSE_TEST_EVENT_INVALID', 'The test harness contains an event case, but the application has no reachable event topology.');
1523
+ }
1524
+ const driver = assertExecutableProvider(project, 'test');
1525
+ assertImplementedTarget(project, 'test');
1526
+ const executeCanonicalProgram = driver.execute;
1527
+ if (!compiled) compiled = compileProject(project);
1528
+ const exactNativeCrypto = requiresExactNativeCrypto(compiled);
1529
+ const program = exactNativeCrypto ? null : loadCanonicalModule(compiled);
1530
+ const native = hasEventCase || exactNativeCrypto
1531
+ ? compileNativeProjectInMemory(project, { ...options, compiled }).native
1532
+ : null;
1533
+ const cases = [];
1534
+ let failed = 0;
1535
+ for (const testCase of selected) {
1536
+ const started = process.hrtime.bigint();
1537
+ try {
1538
+ if (testCase.kind === 'event') {
1539
+ const schemaCodecs = createCanonicalSchemaCodecs(compiled.schema.bundle.registry);
1540
+ const execution = await executeProviderEventTestCase(driver, 'native', native, testCase, {
1541
+ application: Object.freeze({
1542
+ projectHash: project.projectHash,
1543
+ planHash: project.planHash,
1544
+ profile: project.profile,
1545
+ target: 'native',
1546
+ provider: project.provider
1547
+ }),
1548
+ schemaCodecs,
1549
+ reporting: project.reporting,
1550
+ networkFetch: project.dev.networkFetch
1551
+ });
1552
+ const result = assertEventExpectation(testCase, execution);
1553
+ cases.push(eventTestCaseResult(testCase, execution, result, started));
1554
+ continue;
1555
+ }
1556
+ const executionOptions = providerExecutionOptions(project, {
1557
+ packageArtifacts: packageRealizationArtifactsForCompiled(compiled),
1558
+ request: testCase.request,
1559
+ fetches: testCase.fetches,
1560
+ config: testCase.config,
1561
+ secrets: testCase.secrets,
1562
+ kv: testCase.kv,
1563
+ grip: testCase.grip,
1564
+ maxBodyBytes: testCase.maxBodyBytes === undefined
1565
+ ? (project.schemas.active ? project.schemas.maxBytes : undefined)
1566
+ : testCase.maxBodyBytes,
1567
+ continuationTtlMs: testCase.continuationTtlMs,
1568
+ executionId: `test:${testCase.name}`
1569
+ });
1570
+ const execution = exactNativeCrypto
1571
+ ? await executeCanonicalNativeModule(native, executionOptions)
1572
+ : await executeCanonicalProgram(program, executionOptions);
1573
+ if (testCase.expect.error) throw new assert.AssertionError({ message: `${testCase.name}: expected ${testCase.expect.error.name || 'an error'} but execution completed` });
1574
+ assertExpectation(testCase, Object.freeze({
1575
+ ...execution,
1576
+ response: materializeTestResponse(execution.response)
1577
+ }));
1578
+ cases.push(Object.freeze({
1579
+ name: testCase.name,
1580
+ status: 'passed',
1581
+ durationMs: Number(process.hrtime.bigint() - started) / 1e6,
1582
+ response: Object.freeze({ status: execution.response.status, bodyClass: execution.response.bodyClass, kind: execution.response.kind }),
1583
+ effects: execution.effectCount,
1584
+ continuations: execution.continuations.map((entry) => Object.freeze({ id: entry.id, state: entry.state, effectIds: entry.effectIds })),
1585
+ resolutionOrder: execution.resolutionOrder
1586
+ }));
1587
+ } catch (error) {
1588
+ try {
1589
+ if (testCase.kind === 'event') throw error;
1590
+ assertExpectedError(testCase, error);
1591
+ cases.push(Object.freeze({ name: testCase.name, status: 'passed', expectedError: errorSummary(error, testCase.secrets), durationMs: Number(process.hrtime.bigint() - started) / 1e6 }));
1592
+ } catch (assertion) {
1593
+ failed += 1;
1594
+ cases.push(Object.freeze({ name: testCase.name, status: 'failed', error: errorSummary(assertion === error ? error : assertion, testCase.secrets), actualError: assertion === error ? undefined : errorSummary(error, testCase.secrets), durationMs: Number(process.hrtime.bigint() - started) / 1e6 }));
1595
+ }
1596
+ }
1597
+ }
1598
+ return Object.freeze({
1599
+ status: failed === 0 ? 'passed' : 'failed',
1600
+ version: PROJECT_EXECUTION_VERSION,
1601
+ provider: project.provider,
1602
+ project: projectJson(project),
1603
+ metadata: compiled.metadata,
1604
+ summary: Object.freeze({ total: cases.length, passed: cases.length - failed, failed }),
1605
+ cases: Object.freeze(cases)
1606
+ });
1607
+ }
1608
+
1609
+ function inspectProject(project, options = {}) {
1610
+ const compiled = compileProject(project);
1611
+ const selectedTarget = project.target || 'native';
1612
+ let nativePlan = null;
1613
+ let native = null;
1614
+ let nativeInspectionError = null;
1615
+ try {
1616
+ const prepared = compileNativeProjectInMemory(project, { ...options, compiled });
1617
+ nativePlan = prepared.plan;
1618
+ native = prepared.native;
1619
+ } catch (error) {
1620
+ if (selectedTarget !== 'javascript' && !compiled.packageApplication) throw error;
1621
+ nativeInspectionError = errorSummary(error, project.dev && project.dev.secrets);
1622
+ }
1623
+ const driver = providerDriver(project);
1624
+ const availability = providerTargetAvailability(driver);
1625
+ const selectedDescriptor = targetDescriptor(project);
1626
+ const eventSupport = selectedEventTargetSupport(project, compiled, selectedDescriptor);
1627
+ const eventInspection = eventInspectionProjection(project, compiled, eventSupport);
1628
+ const javascriptApplication = selectedTarget === 'javascript'
1629
+ ? prepareJavascriptApplication(project, { captureLoadError: true })
1630
+ : null;
1631
+ const targetSupport = javascriptApplication
1632
+ ? selectedTargetSupportEvidence(project, compiled, javascriptApplication, selectedDescriptor, options)
1633
+ : null;
1634
+ const effectiveAvailability = targetSupport && options.targetSupportDeclaration
1635
+ ? Object.freeze({ ...availability, javascript: targetSupport.availability.generalAvailable })
1636
+ : availability;
1637
+ const referencedSchemaIds = new Set(compiled.metadata.schemaReferences.map((entry) => entry.id));
1638
+ for (const schemaId of eventInspection && eventInspection.schemas.all || []) referencedSchemaIds.add(schemaId);
1639
+ const realization = selectedTarget === 'native'
1640
+ ? (nativePlan && native && driver.inspectRealization && (!eventSupport || eventSupport.status !== 'blocked')
1641
+ ? normalizeProviderTargetResult(driver.inspectRealization(providerTargetInvocation(
1642
+ project,
1643
+ 'inspect-native',
1644
+ nativePlan,
1645
+ providerLoweringPlan(project, compiled.metadata),
1646
+ {
1647
+ native,
1648
+ optimization: native && native.manifest && native.manifest.optimization,
1649
+ timeoutMs: options.timeoutMs
1650
+ }
1651
+ )), { action: 'inspect-native', provider: project.provider, target: 'native' }).realization
1652
+ : Object.freeze({
1653
+ target: selectedDescriptor.targetId,
1654
+ provider: project.provider,
1655
+ runtimeClass: selectedDescriptor.runtimeClass,
1656
+ status: 'unavailable-for-project',
1657
+ nativeWasm: false,
1658
+ providerRealityValidated: false,
1659
+ automaticFallback: false,
1660
+ error: eventSupport && eventSupport.status === 'blocked'
1661
+ ? Object.freeze({ code: eventSupport.diagnosticCode, eventTargetSupport: eventSupport })
1662
+ : nativeInspectionError
1663
+ }))
1664
+ : (javascriptApplication ? Object.freeze({
1665
+ target: selectedDescriptor.targetId,
1666
+ provider: project.provider,
1667
+ runtimeClass: selectedDescriptor.runtimeClass,
1668
+ sourceApplication: true,
1669
+ status: eventSupport && eventSupport.status === 'blocked'
1670
+ ? 'unavailable-for-project'
1671
+ : selectedDescriptor.status,
1672
+ javascriptRuntime: true,
1673
+ nativeWasm: false,
1674
+ automaticFallback: false,
1675
+ applicationPlan: Object.freeze({
1676
+ version: javascriptApplication.plan.version,
1677
+ plannerVersion: javascriptApplication.plan.plannerVersion,
1678
+ planHash: javascriptApplication.plan.planHash,
1679
+ graphHash: javascriptApplication.plan.graph.graphHash,
1680
+ loadable: javascriptApplication.plan.loadable,
1681
+ blockers: javascriptApplication.plan.blockers,
1682
+ modules: javascriptApplication.plan.graph.modules.filter((entry) => entry.runtime && entry.kind === 'project').length,
1683
+ packages: javascriptApplication.plan.packages,
1684
+ nativeEligibility: javascriptApplication.plan.nativeEligibility,
1685
+ entrySafety: javascriptApplication.plan.entrySafety
1686
+ }),
1687
+ loader: targetSupport ? Object.freeze({
1688
+ ...targetSupport.loader,
1689
+ applicationValid: targetSupport.loader.loaded === true
1690
+ || (targetSupport.loader.skippedReason === 'provider-build-time-bundle-loader'
1691
+ && javascriptApplication.plan.loadable === true)
1692
+ }) : null,
1693
+ loadError: javascriptApplication.loadError,
1694
+ supportEvidenceHash: targetSupport && targetSupport.evidenceHash,
1695
+ coreExecutionReady: targetSupport && targetSupport.availability.coreExecutionReady,
1696
+ fullTargetSupportReady: targetSupport && targetSupport.availability.fullTargetSupportReady,
1697
+ generalAvailable: targetSupport && targetSupport.availability.generalAvailable,
1698
+ projectEligibility: targetSupport && targetSupport.project.status,
1699
+ requestAdapter: selectedDescriptor.requestAdapter,
1700
+ lifecycle: selectedDescriptor.lifecycle,
1701
+ ...(eventSupport && eventSupport.status === 'blocked' ? {
1702
+ error: Object.freeze({ code: eventSupport.diagnosticCode, eventTargetSupport: eventSupport })
1703
+ } : {}),
1704
+ ...(driver.javascript && driver.javascript.localExecutionMode ? {
1705
+ localTooling: Object.freeze({
1706
+ test: driver.javascript.localExecutionMode,
1707
+ dev: driver.javascript.localExecutionMode,
1708
+ loader: 'provider-owned-bundled-closure',
1709
+ packageClosure: 'esbuild-bundled',
1710
+ providerReality: false,
1711
+ realityRunner: driver.javascript.realityRunner,
1712
+ automaticFallback: false
1713
+ }),
1714
+ deploymentCandidate: Object.freeze({
1715
+ status: 'structurally-deployable',
1716
+ emittedBy: 'pulse build',
1717
+ providerRealityValidated: false,
1718
+ deployed: false
1719
+ })
1720
+ } : {})
1721
+ }) : undefined);
1722
+ return Object.freeze({
1723
+ status: 'ok',
1724
+ version: PROJECT_EXECUTION_VERSION,
1725
+ project: projectJson(project),
1726
+ provider: Object.freeze({
1727
+ id: project.provider,
1728
+ package: driver.descriptor && driver.descriptor.package,
1729
+ buildTarget: driver.descriptor && driver.descriptor.buildTarget,
1730
+ selectedTarget,
1731
+ targetAvailability: Object.freeze({ native: effectiveAvailability.native, javascript: effectiveAvailability.javascript }),
1732
+ targetDescriptors: effectiveAvailability.descriptors,
1733
+ selectedTargetDescriptor: selectedDescriptor,
1734
+ targetSupport,
1735
+ eventTargetSupport: eventSupport,
1736
+ defaultBuildMode: driver.defaultBuildMode,
1737
+ sourceOnlySupported: false,
1738
+ realization
1739
+ }),
1740
+ plan: projectPlanInspection(project, compiled),
1741
+ compiler: Object.freeze({
1742
+ version: compiled.version,
1743
+ file: compiled.metadata.file,
1744
+ sourceHash: compiled.metadata.sourceHash,
1745
+ authoring: compiled.metadata.authoring || Object.freeze({ kind: 'handler' }),
1746
+ ...(compiled.metadata.application ? { application: compiled.metadata.application } : {}),
1747
+ ...(compiled.metadata.json ? { json: compiled.metadata.json } : {}),
1748
+ ...((compiled.metadata.warnings && compiled.metadata.warnings.length > 0) ? { warnings: compiled.metadata.warnings } : {}),
1749
+ warningCount: compiled.metadata.warningCount || 0,
1750
+ jsonPolicy: jsonPolicyInspection(project, compiled, nativePlan),
1751
+ requestState: nativePlan && nativePlan.requestState || Object.freeze({ enabled: false, representation: 'absent', reset: 'not-required' }),
1752
+ routing: nativePlan && nativePlan.routing || null,
1753
+ capabilities: compiled.metadata.capabilities,
1754
+ effects: compiled.metadata.effectSites,
1755
+ continuations: compiled.metadata.continuationSites,
1756
+ effectCount: compiled.metadata.effectCount,
1757
+ continuationCount: compiled.metadata.continuationCount,
1758
+ groupedContinuationCount: compiled.metadata.groupedContinuationCount,
1759
+ opaqueReturnCount: compiled.metadata.opaqueReturnCount,
1760
+ providerOperations: compiled.metadata.providerOperations,
1761
+ providerLowering: eventSupport && eventSupport.status === 'blocked'
1762
+ ? Object.freeze({
1763
+ status: 'blocked',
1764
+ code: eventSupport.diagnosticCode,
1765
+ eventTargetSupport: eventSupport,
1766
+ automaticFallback: false
1767
+ })
1768
+ : providerLoweringPlan(project, compiled.metadata),
1769
+ reachableGraph: Object.freeze({
1770
+ version: compiled.reachableGraph.version,
1771
+ graphHash: compiled.reachableGraph.graphHash,
1772
+ modules: compiled.reachableGraph.modules.length,
1773
+ runtimeModules: compiled.reachableGraph.modules.filter((entry) => entry.runtime).length,
1774
+ edges: compiled.reachableGraph.edges.length,
1775
+ handlers: compiled.reachableGraph.handlers.length,
1776
+ cycles: compiled.reachableGraph.cycles.length,
1777
+ unsupportedBoundaries: compiled.reachableGraph.unsupportedBoundaries.length
1778
+ }),
1779
+ packageReachability: compiled.packageReachability,
1780
+ packageProduct: compiled.packageProduct,
1781
+ packageInspection: compiled.packageInspection,
1782
+ ...(eventInspection ? { events: eventInspection } : {}),
1783
+ ...(compiled.packageApplication ? { packageApplication: compiled.packageApplication } : {}),
1784
+ applicationEntrySafety: compiled.entrySafety,
1785
+ nativeEligibility: compiled.nativeEligibility,
1786
+ native: nativePlan && native ? Object.freeze({
1787
+ status: 'available',
1788
+ target: 'portable-native-wasm',
1789
+ providerNeutral: true,
1790
+ requiredForSelectedTarget: selectedTarget === 'native',
1791
+ selectedTarget,
1792
+ automaticFallback: false,
1793
+ planVersion: nativePlan.version,
1794
+ planCompilerVersion: nativePlan.compilerVersion,
1795
+ planHash: nativePlan.planHash,
1796
+ ownership: nativePlan.ownership,
1797
+ summary: nativePlan.summary,
1798
+ ...(nativePlan.json ? { json: nativePlan.json } : {}),
1799
+ ...(nativePlan.requestState ? { requestState: nativePlan.requestState } : {}),
1800
+ compilerVersion: native.compilerVersion,
1801
+ assemblyScript: native.manifest.assemblyScript,
1802
+ wasm: native.manifest.wasm,
1803
+ wat: native.manifest.wat,
1804
+ importModules: native.manifest.importModules,
1805
+ imports: native.manifest.imports,
1806
+ exports: native.manifest.exports,
1807
+ policy: native.manifest.policy
1808
+ }) : Object.freeze({
1809
+ status: 'unavailable-for-project',
1810
+ target: 'portable-native-wasm',
1811
+ providerNeutral: true,
1812
+ requiredForSelectedTarget: selectedTarget === 'native',
1813
+ selectedTarget,
1814
+ automaticFallback: false,
1815
+ error: nativeInspectionError
1816
+ }),
1817
+ schemas: Object.freeze({
1818
+ active: compiled.schema.active,
1819
+ compilerVersion: compiled.schema.compilerVersion,
1820
+ sourceHash: compiled.metadata.schemaSourceHash,
1821
+ registry: compiled.metadata.schemaRegistry,
1822
+ references: compiled.metadata.schemaReferences,
1823
+ unused: Object.freeze(compiled.metadata.schemaIds.filter((id) => !referencedSchemaIds.has(id))),
1824
+ diagnostics: compiled.schema.diagnostics,
1825
+ warnings: compiled.schema.warnings
1826
+ })
1827
+ }),
1828
+ ...(eventInspection ? { events: eventInspection } : {}),
1829
+ testCases: project.tests.map((entry) => entry.name)
1830
+ });
1831
+ }
1832
+
1833
+ function doctorProject(project, options = {}) {
1834
+ const checks = [];
1835
+ const selectedTarget = project.target || 'native';
1836
+ const selectedTargetDescriptor = targetDescriptor(project, selectedTarget);
1837
+ const targetSupportDeclaration = options.targetSupportDeclaration || getProviderTargetSupportDeclaration(providerDriver(project), selectedTarget);
1838
+ const providerAvailability = providerTargetAvailability(providerDriver(project));
1839
+ const targetAvailability = options.targetSupportDeclaration && targetSupportDeclaration
1840
+ ? Object.freeze({ ...providerAvailability, javascript: targetSupportDeclaration.availability.generalAvailable })
1841
+ : providerAvailability;
1842
+ checks.push(Object.freeze({
1843
+ id: 'execution-target',
1844
+ status: selectedTarget === 'javascript' && targetAvailability.javascript !== true ? 'warning' : 'passed',
1845
+ message: selectedTarget === 'javascript'
1846
+ ? (targetSupportDeclaration
1847
+ ? (targetSupportDeclaration.availability.generalAvailable
1848
+ ? `${project.provider} JavaScript full target support is sealed and generally available; the selected JavaScript target remains authoritative and no automatic fallback will occur.`
1849
+ : `Core ${project.provider} JavaScript execution is ready, but general availability remains false under the full-target-support policy while ${targetSupportDeclaration.availability.summary.pending} gate(s) remain pending; no automatic fallback will occur.`)
1850
+ : selectedTargetDescriptor.applicationLoader
1851
+ ? 'The JavaScript application loader is present, but this provider does not declare full target support; no automatic fallback will occur.'
1852
+ : selectedTargetDescriptor.status === 'source-packaging-ready'
1853
+ ? 'The selected JavaScript source-packaging boundary is ready, while provider execution remains deferred; no automatic fallback will occur.'
1854
+ : 'The selected JavaScript target is recorded but not realized for this provider; no automatic fallback will occur.')
1855
+ : 'The selected native target is available.',
1856
+ detail: Object.freeze({
1857
+ selected: selectedTarget,
1858
+ native: targetAvailability.native,
1859
+ javascript: targetAvailability.javascript,
1860
+ descriptor: selectedTargetDescriptor,
1861
+ targetSupport: targetSupportDeclaration,
1862
+ automaticFallback: false
1863
+ })
1864
+ }));
1865
+ function check(id, status, message, detail = {}, explicitCode) {
1866
+ const code = explicitCode || detail.code;
1867
+ const descriptor = code ? describeDiagnostic(code) : undefined;
1868
+ checks.push(Object.freeze({
1869
+ id,
1870
+ status,
1871
+ message,
1872
+ code,
1873
+ title: descriptor && descriptor.title,
1874
+ summary: descriptor && descriptor.summary,
1875
+ category: descriptor && descriptor.category,
1876
+ remediation: descriptor && descriptor.remediation,
1877
+ stability: descriptor && descriptor.stability,
1878
+ scope: descriptor && descriptor.scope,
1879
+ docs: descriptor && descriptor.docs,
1880
+ detail
1881
+ }));
1882
+ }
1883
+ const nodeSupported = nodeVersionSatisfiesEngines(process.versions.node, releaseNode.nodeEngines);
1884
+ check(
1885
+ 'node-version',
1886
+ nodeSupported ? 'passed' : 'failed',
1887
+ `Node ${process.versions.node}`,
1888
+ {
1889
+ engines: releaseNode.nodeEngines,
1890
+ minimumVersion: releaseNode.nodeMinimumVersion,
1891
+ sealRange: releaseNode.nodeReleaseRange,
1892
+ reproducibleToolchainVersion: releaseNode.nodeVersion
1893
+ },
1894
+ nodeSupported ? undefined : 'PULSE_NODE_VERSION_UNSUPPORTED'
1895
+ );
1896
+ check('config', project.configFile ? 'passed' : 'warning', project.configFile ? `Loaded ${relative(project.configFile, project.root)}` : 'No config file; using explicit entry defaults.', {}, project.configFile ? undefined : 'PULSE_CONFIG_IMPLICIT');
1897
+ check('entry', fs.existsSync(project.entryFile) ? 'passed' : 'failed', relative(project.entryFile, project.root), { file: relative(project.entryFile, project.root) }, fs.existsSync(project.entryFile) ? undefined : 'PULSE_ENTRY_NOT_FOUND');
1898
+ if (project.profilePlan) {
1899
+ check('project-plan', 'passed', `Selected profile ${project.profile.name} from ${project.profile.source} with plan ${String(project.planHash).slice(0, 12)}…`, projectPlanInspection(project));
1900
+ }
1901
+ let compiled;
1902
+ let nativePrepared;
1903
+ let preparedJavascript;
1904
+ let targetSupport;
1905
+ let eventSupport;
1906
+ let eventInspection;
1907
+ try {
1908
+ compiled = compileProject(project);
1909
+ check('canonical-compile', (compiled.metadata.warningCount || 0) > 0 ? 'warning' : 'passed',
1910
+ compiled.target === 'javascript'
1911
+ ? `JavaScript source inspected with ${compiled.metadata.effectCount} recognized Pulse effect(s) and ${compiled.metadata.warningCount || 0} warning(s); execution retains the original source graph.`
1912
+ : `Canonical source lowered with ${compiled.metadata.effectCount} effect(s), ${compiled.metadata.continuationCount} continuation(s), and ${compiled.metadata.warningCount || 0} warning(s).`,
1913
+ { capabilities: compiled.metadata.capabilities, warnings: compiled.metadata.warnings || [] });
1914
+ eventSupport = selectedEventTargetSupport(project, compiled, selectedTargetDescriptor);
1915
+ if (eventSupport) {
1916
+ eventInspection = eventInspectionProjection(project, compiled, eventSupport);
1917
+ check(
1918
+ 'events',
1919
+ eventSupport.status === 'eligible' ? 'passed' : (eventSupport.status === 'inspection-only' ? 'warning' : 'failed'),
1920
+ eventSupport.status === 'eligible'
1921
+ ? `The ${project.provider}/${selectedTarget} target satisfies ${eventSupport.requirements.length} reachable event requirement(s).`
1922
+ : eventSupport.status === 'inspection-only'
1923
+ ? 'The event catalog is available for compile-only inspection; no execution provider is selected.'
1924
+ : `The ${project.provider}/${selectedTarget} target cannot realize the reachable event plane and no fallback will occur.`,
1925
+ eventInspection,
1926
+ eventSupport.status === 'blocked' ? eventSupport.diagnosticCode : undefined
1927
+ );
1928
+ }
1929
+ const referencedSchemaIds = new Set(compiled.metadata.schemaReferences.map((entry) => entry.id));
1930
+ for (const schemaId of eventInspection && eventInspection.schemas.all || []) referencedSchemaIds.add(schemaId);
1931
+ const unusedSchemaIds = compiled.metadata.schemaIds.filter((id) => !referencedSchemaIds.has(id));
1932
+ check(
1933
+ 'schemas',
1934
+ unusedSchemaIds.length > 0 ? 'warning' : 'passed',
1935
+ compiled.schema.active
1936
+ ? `Compiled ${compiled.metadata.schemaIds.length} JSON schema(s) with ${compiled.metadata.schemaReferenceCount} linked HTTP handler reference(s) and ${eventInspection ? eventInspection.schemas.all.length : 0} linked event schema(s).`
1937
+ : 'No explicit JSON schemas are configured.',
1938
+ {
1939
+ ids: compiled.metadata.schemaIds,
1940
+ references: compiled.metadata.schemaReferences,
1941
+ eventReferences: eventInspection ? eventInspection.schemas.all : Object.freeze([]),
1942
+ unused: unusedSchemaIds,
1943
+ sourceHash: compiled.metadata.schemaSourceHash,
1944
+ dependencies: compiled.watchFiles.map((file) => relative(file, project.root))
1945
+ }
1946
+ );
1947
+ const jsonPolicy = jsonPolicyInspection(project, compiled);
1948
+ check(
1949
+ 'json-policy',
1950
+ 'passed',
1951
+ jsonPolicy.genericParserRequired
1952
+ ? `Non-strict schema-less JSON is enabled for ${jsonPolicy.genericRequestCount} reachable call(s); native realization uses the explicit host generic parser path.`
1953
+ : (jsonPolicy.strict ? 'Strict schema-bound JSON policy is active.' : 'Non-strict JSON policy is active, but no schema-less JSON call is reachable.'),
1954
+ jsonPolicy
1955
+ );
1956
+ nativePrepared = compileNativeProjectInMemory(project, { compiled });
1957
+ if (nativePrepared.plan.requestState && nativePrepared.plan.requestState.enabled) {
1958
+ check('request-state', 'passed', 'Request-local ctx.state is guest-owned, survives continuations, and resets at pulse_start.', nativePrepared.plan.requestState);
1959
+ }
1960
+ check(
1961
+ 'canonical-native-plan',
1962
+ 'passed',
1963
+ `Provider-neutral native plan ${nativePrepared.plan.planHash.slice(0, 12)}… contains ${nativePrepared.plan.summary.statementCount} statement(s).`,
1964
+ {
1965
+ version: nativePrepared.plan.version,
1966
+ compilerVersion: nativePrepared.plan.compilerVersion,
1967
+ planHash: nativePrepared.plan.planHash,
1968
+ ownership: nativePrepared.plan.ownership,
1969
+ summary: nativePrepared.plan.summary
1970
+ }
1971
+ );
1972
+ check(
1973
+ 'canonical-native-wasm',
1974
+ 'passed',
1975
+ `Compiled ${nativePrepared.native.manifest.wasm.bytes} bytes of provider-neutral Pulse Wasm with AssemblyScript ${nativePrepared.native.manifest.assemblyScript.version}.`,
1976
+ {
1977
+ version: nativePrepared.native.version,
1978
+ compilerVersion: nativePrepared.native.compilerVersion,
1979
+ assemblyScript: nativePrepared.native.manifest.assemblyScript,
1980
+ wasm: nativePrepared.native.manifest.wasm,
1981
+ importModules: nativePrepared.native.manifest.importModules,
1982
+ planHash: nativePrepared.plan.planHash
1983
+ }
1984
+ );
1985
+ } catch (error) {
1986
+ const nativeFailure = error && ['PULSE_CANONICAL_NATIVE_PLAN_FAILED', 'PULSE_CANONICAL_NATIVE_AS_GENERATION_FAILED', 'PULSE_CANONICAL_NATIVE_COMPILE_FAILED'].includes(error.code);
1987
+ if (nativeFailure && compiled) {
1988
+ const selectedTargetRequiresNative = selectedTarget === 'native';
1989
+ check(
1990
+ error.code === 'PULSE_CANONICAL_NATIVE_PLAN_FAILED' ? 'canonical-native-plan' : 'canonical-native-wasm',
1991
+ selectedTargetRequiresNative ? 'failed' : 'warning',
1992
+ selectedTargetRequiresNative
1993
+ ? error.message
1994
+ : `Provider-neutral native realization is unavailable for this project, but the selected JavaScript target is inspected independently and no native fallback will occur: ${error.message}`,
1995
+ {
1996
+ code: error.code,
1997
+ diagnostics: error.diagnostics,
1998
+ detail: error.detail,
1999
+ selectedTarget,
2000
+ requiredForSelectedTarget: selectedTargetRequiresNative,
2001
+ automaticFallback: false
2002
+ }
2003
+ );
2004
+ } else {
2005
+ check('canonical-compile', 'failed', error.message, { code: error.code, diagnostics: error.diagnostics });
2006
+ }
2007
+ const schemaFailure = error.code === 'PULSE_SCHEMA_COMPILE_FAILED' || (error.diagnostics || []).some((entry) => String(entry.code || '').includes('SCHEMA'));
2008
+ if (schemaFailure) check('schemas', 'failed', error.message, { code: error.code, diagnostics: error.diagnostics, detail: error.detail });
2009
+ }
2010
+ if (selectedTarget === 'javascript') {
2011
+ try {
2012
+ const javascript = providerDriver(project).javascript;
2013
+ preparedJavascript = prepareJavascriptApplication(project, { captureLoadError: true });
2014
+ targetSupport = selectedTargetSupportEvidence(project, compiled, preparedJavascript, selectedTargetDescriptor, options);
2015
+ const applicationDecision = targetSupport && targetSupport.project.application.find((entry) => entry.id === 'application.plan');
2016
+ const buildTimeLoaderReady = javascript && javascript.buildTimeLoader === true
2017
+ && preparedJavascript.plan.loadable === true
2018
+ && targetSupport
2019
+ && targetSupport.loader.skippedReason === 'provider-build-time-bundle-loader'
2020
+ && targetSupport.project.status === 'eligible';
2021
+ const applicationStatus = applicationDecision && applicationDecision.status === 'pending'
2022
+ ? 'warning'
2023
+ : preparedJavascript.loaded || buildTimeLoaderReady
2024
+ ? 'passed'
2025
+ : targetSupport && targetSupport.project.status === 'pending'
2026
+ ? 'warning'
2027
+ : 'failed';
2028
+ const applicationMessage = applicationDecision && applicationDecision.status === 'pending'
2029
+ ? `The JavaScript application graph is deterministic, but reachable package realization remains pending for ${targetSupport.project.reasonIds.join(', ')}.`
2030
+ : preparedJavascript.loaded
2031
+ ? `Graph-backed JavaScript application ${preparedJavascript.plan.planHash.slice(0, 12)}… loaded ${preparedJavascript.loaded.modulesLoaded} project module(s).`
2032
+ : buildTimeLoaderReady
2033
+ ? `${project.provider} JavaScript application ${preparedJavascript.plan.planHash.slice(0, 12)}… is loadable through the deterministic build-time bundle loader and available to explicit local provider emulation.`
2034
+ : targetSupport && targetSupport.project.status === 'pending'
2035
+ ? `The JavaScript application graph is deterministic, but reachable package/capability realization remains pending for ${targetSupport.project.reasonIds.join(', ')}.`
2036
+ : (preparedJavascript.loadError
2037
+ ? `The JavaScript application loader failed: ${preparedJavascript.loadError.message}`
2038
+ : 'The JavaScript application plan is blocked for the selected target.');
2039
+ check(
2040
+ 'javascript-application',
2041
+ applicationStatus,
2042
+ applicationMessage,
2043
+ {
2044
+ target: preparedJavascript.descriptor,
2045
+ planVersion: preparedJavascript.plan.version,
2046
+ planHash: preparedJavascript.plan.planHash,
2047
+ graphHash: preparedJavascript.plan.graph.graphHash,
2048
+ loadable: preparedJavascript.plan.loadable,
2049
+ blockers: preparedJavascript.plan.blockers,
2050
+ packages: preparedJavascript.plan.packages,
2051
+ nativeEligibility: preparedJavascript.plan.nativeEligibility,
2052
+ entrySafety: preparedJavascript.plan.entrySafety,
2053
+ loader: preparedJavascript.loaded ? {
2054
+ version: preparedJavascript.loaded.version,
2055
+ modulesLoaded: preparedJavascript.loaded.modulesLoaded,
2056
+ packagesLoaded: preparedJavascript.loaded.packagesLoaded
2057
+ } : null,
2058
+ loadError: preparedJavascript.loadError,
2059
+ targetSupportEvidenceHash: targetSupport && targetSupport.evidenceHash,
2060
+ projectEligibility: targetSupport && targetSupport.project.status,
2061
+ automaticFallback: false
2062
+ },
2063
+ applicationStatus === 'passed'
2064
+ ? undefined
2065
+ : targetSupport && targetSupport.project.packages.find((entry) => entry.required && entry.status === 'pending' && entry.reasonCode)?.reasonCode
2066
+ || preparedJavascript.plan.blockers[0]?.code
2067
+ || preparedJavascript.loadError?.code
2068
+ || 'PULSE_TARGET_IMPLEMENTATION_PENDING'
2069
+ );
2070
+ if (targetSupport) {
2071
+ const pendingProjectItems = targetSupport.project.summary.application.pending
2072
+ + targetSupport.project.summary.capabilities.pending
2073
+ + targetSupport.project.summary.providerRequirements.pending
2074
+ + targetSupport.project.summary.packages.pending;
2075
+ const blockedProjectItems = targetSupport.project.summary.application.blocked
2076
+ + targetSupport.project.summary.capabilities.blocked
2077
+ + targetSupport.project.summary.providerRequirements.blocked
2078
+ + targetSupport.project.summary.packages.blocked;
2079
+ check(
2080
+ 'target-support',
2081
+ targetSupport.project.status === 'blocked'
2082
+ ? 'failed'
2083
+ : targetSupport.project.status === 'eligible' && targetSupport.availability.generalAvailable
2084
+ ? 'passed'
2085
+ : 'warning',
2086
+ targetSupport.project.status === 'eligible'
2087
+ ? (targetSupport.availability.generalAvailable
2088
+ ? `This project is eligible for the sealed and generally available ${project.provider} JavaScript target; the selected target remains authoritative and automatic fallback is disabled.`
2089
+ : `This project is eligible for the sealed core ${project.provider} JavaScript lane, but general availability remains false until ${targetSupport.availability.summary.pending} full-support gate(s) close.`)
2090
+ : targetSupport.project.status === 'pending'
2091
+ ? (targetSupport.availability.generalAvailable
2092
+ ? `The ${project.provider} JavaScript target is generally available, but this project has ${pendingProjectItems} pending capability/package decision(s) and is not yet eligible.`
2093
+ : `This project has ${pendingProjectItems} pending JavaScript capability/package decision(s); general availability also remains false under the full-target-support policy.`)
2094
+ : `This project has ${blockedProjectItems} blocked JavaScript capability/package decision(s) and cannot execute on the selected target.`,
2095
+ targetSupport
2096
+ );
2097
+ }
2098
+ if (javascript && javascript.localExecutionMode) {
2099
+ check(
2100
+ 'javascript-local-tooling',
2101
+ 'passed',
2102
+ `pulse test and pulse dev use explicit ${project.provider} provider emulation; provider reality remains a separate ${javascript.realityRunner} step.`,
2103
+ {
2104
+ mode: javascript.localExecutionMode,
2105
+ loader: 'provider-owned-bundled-closure',
2106
+ packageClosure: 'esbuild-bundled',
2107
+ providerReality: false,
2108
+ realityRunner: javascript.realityRunner,
2109
+ deploymentCandidate: 'pulse build',
2110
+ automaticFallback: false
2111
+ }
2112
+ );
2113
+ }
2114
+ } catch (error) {
2115
+ check('javascript-application', 'failed', error.message, { code: error.code, detail: error.detail, diagnostics: error.diagnostics }, error.code);
2116
+ }
2117
+ }
2118
+ const driver = providerDriver(project);
2119
+ if (!driver.executable) check('provider', 'warning', 'Compile-only provider selected; pulse build, dev, and test are unavailable. Use pulse compile for provider-neutral Wasm.', { provider: project.provider }, 'PULSE_PROVIDER_COMPILE_ONLY');
2120
+ else {
2121
+ try {
2122
+ const lowering = compiled ? driver.createLoweringPlan(compiled.metadata, project.providerConfig) : undefined;
2123
+ check('provider', selectedTarget === 'javascript' && targetAvailability.javascript !== true ? 'warning' : 'passed', selectedTarget === 'javascript'
2124
+ ? (targetSupportDeclaration
2125
+ ? (targetSupportDeclaration.availability.generalAvailable
2126
+ ? `${project.provider} JavaScript full target support is sealed and generally available; bounded GRIP realization is included and automatic fallback remains disabled.`
2127
+ : `${project.provider} JavaScript core execution and bounded GRIP realization are ready, but one or more full-target-support gates remain pending, so general availability remains false.`)
2128
+ : `${project.provider} JavaScript application loading is not declared as a fully supported target.`)
2129
+ : `${project.provider} native provider contract and local conformance runtime are available.`, {
2130
+ package: driver.descriptor.package,
2131
+ buildTarget: driver.descriptor.buildTarget,
2132
+ deployable: driver.deployable === true,
2133
+ deploymentValidated: driver.deploymentValidated === true,
2134
+ sourcePackage: driver.sourcePackage === true,
2135
+ compiledWasm: driver.compiledWasm === true,
2136
+ selectedTarget,
2137
+ targetAvailability: Object.freeze({ native: targetAvailability.native, javascript: targetAvailability.javascript }),
2138
+ targetDescriptors: targetAvailability.descriptors,
2139
+ selectedTargetDescriptor,
2140
+ targetSupportDeclaration,
2141
+ targetSupportEvidenceHash: targetSupport && targetSupport.evidenceHash,
2142
+ defaultBuildMode: driver.defaultBuildMode,
2143
+ sourceOnlySupported: false,
2144
+ localExecution: driver.localExecution === true,
2145
+ requirements: lowering && lowering.requirements
2146
+ });
2147
+ } catch (error) {
2148
+ check('provider', 'failed', error.message, { code: error.code, detail: error.detail });
2149
+ }
2150
+ }
2151
+ if (selectedTarget === 'native' && nativePrepared && driver.executable && driver.inspectRealization && (!eventSupport || eventSupport.status !== 'blocked')) {
2152
+ try {
2153
+ const realization = normalizeProviderTargetResult(driver.inspectRealization(providerTargetInvocation(
2154
+ project,
2155
+ 'inspect-native',
2156
+ nativePrepared.plan,
2157
+ providerLoweringPlan(project, nativePrepared.compiled.metadata),
2158
+ {
2159
+ native: nativePrepared.native,
2160
+ optimization: nativePrepared.native.manifest.optimization,
2161
+ timeoutMs: options.timeoutMs
2162
+ }
2163
+ )), { action: 'inspect-native', provider: project.provider, target: 'native' }).realization;
2164
+ check(
2165
+ 'provider-native-realization',
2166
+ 'passed',
2167
+ `${project.provider} realizes the portable plan as ${realization.wasm.bytes} bytes of native provider Wasm.`,
2168
+ {
2169
+ target: realization.target,
2170
+ provider: project.provider,
2171
+ nativeWasm: realization.nativeWasm === true,
2172
+ javascriptRuntime: realization.javascriptRuntime === true,
2173
+ jsComputeRuntime: realization.jsComputeRuntime === true,
2174
+ compiler: realization.compiler,
2175
+ wasm: realization.wasm,
2176
+ importModules: realization.importModules,
2177
+ imports: realization.imports,
2178
+ exports: realization.exports,
2179
+ policy: realization.policy
2180
+ }
2181
+ );
2182
+ } catch (error) {
2183
+ check('provider-native-realization', 'failed', error.message, { code: error.code, detail: error.detail, diagnostics: error.diagnostics });
2184
+ }
2185
+ }
2186
+ try {
2187
+ const output = resolveOutputDirectory(project, project.outDir);
2188
+ check('build-output', 'passed', relative(output, project.root));
2189
+ } catch (error) {
2190
+ check('build-output', 'failed', error.message, { code: error.code, outDir: project.outDir });
2191
+ }
2192
+ if (driver.executable && project.dev.networkFetch) {
2193
+ check('network-fetch', typeof globalThis.fetch === 'function' ? 'passed' : 'failed', typeof globalThis.fetch === 'function' ? `${project.provider} local live fetch is available for pulse dev.` : 'Local live fetch is unavailable.', { enabled: true, provider: project.provider }, typeof globalThis.fetch === 'function' ? undefined : 'PULSE_FETCH_IMPLEMENTATION_UNAVAILABLE');
2194
+ } else {
2195
+ check('network-fetch', 'passed', project.dev.networkFetch ? 'Provider owns outbound fetch.' : 'Live network fetch is disabled; configured fixtures are required.', { enabled: project.dev.networkFetch });
2196
+ }
2197
+ check('tests', project.tests.length > 0 ? 'passed' : 'warning', project.tests.length > 0 ? `${project.tests.length} project test case(s) configured.` : 'No project test cases configured.', { count: project.tests.length }, project.tests.length > 0 ? undefined : 'PULSE_TEST_CASES_MISSING');
2198
+ const failed = checks.filter((entry) => entry.status === 'failed').length;
2199
+ const warnings = checks.filter((entry) => entry.status === 'warning').length;
2200
+ const ok = failed === 0 && (!options.strict || warnings === 0);
2201
+ return Object.freeze({
2202
+ status: ok ? (warnings > 0 ? 'warning' : 'passed') : 'failed',
2203
+ version: PROJECT_EXECUTION_VERSION,
2204
+ project: projectJson(project),
2205
+ summary: Object.freeze({ total: checks.length, passed: checks.filter((entry) => entry.status === 'passed').length, warnings, failed }),
2206
+ checks: Object.freeze(checks),
2207
+ targetSupport,
2208
+ ...(eventSupport ? { events: eventInspectionProjection(project, compiled, eventSupport) } : {}),
2209
+ metadata: compiled && compiled.metadata
2210
+ });
2211
+ }
2212
+
2213
+ async function readRequestBody(req, maxBytes) {
2214
+ const chunks = [];
2215
+ let bytes = 0;
2216
+ for await (const chunk of req) {
2217
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2218
+ bytes += buffer.byteLength;
2219
+ if (bytes > maxBytes) throw new PulseProjectError('PULSE_REQUEST_BODY_TOO_LARGE', `Request body exceeds ${maxBytes} bytes.`, { maxBytes, bytes });
2220
+ chunks.push(buffer);
2221
+ }
2222
+ return Buffer.concat(chunks).toString('utf8');
2223
+ }
2224
+
2225
+ function requestHeaders(req) {
2226
+ if (Array.isArray(req.rawHeaders) && req.rawHeaders.length > 0) {
2227
+ const pairs = [];
2228
+ for (let index = 0; index < req.rawHeaders.length; index += 2) pairs.push([req.rawHeaders[index], req.rawHeaders[index + 1] || '']);
2229
+ return pairs;
2230
+ }
2231
+ return Object.entries(req.headers || {}).flatMap(([name, value]) => Array.isArray(value) ? value.map((entry) => [name, entry]) : [[name, value === undefined ? '' : value]]);
2232
+ }
2233
+
2234
+ function devErrorResponse(res, error, secrets) {
2235
+ if (res.headersSent) {
2236
+ if (!res.writableEnded) res.end();
2237
+ return;
2238
+ }
2239
+ const summary = errorSummary(error, secrets);
2240
+ res.statusCode = httpStatusForDiagnostic(summary.code, 500);
2241
+ res.setHeader('content-type', 'application/json; charset=utf-8');
2242
+ res.end(stableJson({
2243
+ error: summary.name,
2244
+ code: summary.code,
2245
+ category: summary.category,
2246
+ message: summary.message,
2247
+ remediation: summary.remediation,
2248
+ docs: summary.docs
2249
+ }));
2250
+ }
2251
+
2252
+
2253
+ function javascriptProjectWatchFiles(project, prepared, compiled) {
2254
+ const files = new Set();
2255
+ if (project.configFile) files.add(path.resolve(project.configFile));
2256
+ for (const module of prepared.plan.graph.modules || []) {
2257
+ if (module.kind === 'project' && module.path) files.add(path.resolve(project.root, module.path));
2258
+ }
2259
+ for (const file of compiled && compiled.watchFiles || []) files.add(path.resolve(file));
2260
+ return Object.freeze([...files].sort());
2261
+ }
2262
+
2263
+ async function writeJavascriptWebResponse(response, res, method = 'GET') {
2264
+ res.statusCode = response.status;
2265
+ const grouped = new Map();
2266
+ for (const [name, value] of response.headers.entries()) {
2267
+ const key = String(name).toLowerCase();
2268
+ if (!grouped.has(key)) grouped.set(key, []);
2269
+ grouped.get(key).push(String(value));
2270
+ }
2271
+ if (typeof response.headers.getSetCookie === 'function') {
2272
+ const cookies = response.headers.getSetCookie();
2273
+ if (cookies.length > 0) grouped.set('set-cookie', cookies);
2274
+ }
2275
+ for (const [name, values] of grouped) res.setHeader(name, values.length === 1 ? values[0] : values);
2276
+ if (String(method).toUpperCase() === 'HEAD' || !statusAllowsResponseBody(response.status) || !response.body) {
2277
+ if (response.body && typeof response.body.cancel === 'function') {
2278
+ try { await response.body.cancel(); } catch (_) { /* Best-effort local stream cleanup. */ }
2279
+ }
2280
+ res.end();
2281
+ return;
2282
+ }
2283
+ res.end(Buffer.from(await response.arrayBuffer()));
2284
+ }
2285
+
2286
+ async function startBundledJavascriptDevServer(project, options = {}) {
2287
+ const descriptor = assertImplementedTarget(project, 'dev');
2288
+ const javascript = providerDriver(project).javascript;
2289
+ if (
2290
+ !javascript
2291
+ || typeof javascript.createLocalEnvironment !== 'function'
2292
+ || typeof javascript.createTestRequest !== 'function'
2293
+ || typeof javascript.executeLocalRequest !== 'function'
2294
+ || descriptor.requestAdapter !== true
2295
+ || descriptor.lifecycle !== true
2296
+ ) {
2297
+ throw new PulseProjectError(
2298
+ 'PULSE_TARGET_IMPLEMENTATION_PENDING',
2299
+ 'pulse dev cannot realize the selected provider-bundled JavaScript target yet.',
2300
+ { target: 'javascript', provider: project.provider, targetDescriptor: descriptor, automaticFallback: false }
2301
+ );
2302
+ }
2303
+
2304
+ let prepared;
2305
+ let application;
2306
+ let compileError;
2307
+ let schemaCodecs;
2308
+ let reloadTimer;
2309
+ const watchEnabled = project.dev.watch && options.watch !== false;
2310
+ const watched = new Map();
2311
+ const events = typeof options.onEvent === 'function' ? options.onEvent : () => {};
2312
+ const environment = javascript.createLocalEnvironment({
2313
+ bindings: project.providerConfig.bindings,
2314
+ config: project.dev.config,
2315
+ secrets: project.dev.secrets,
2316
+ kv: project.dev.kv,
2317
+ fetches: project.dev.fetches,
2318
+ networkFetch: project.dev.networkFetch,
2319
+ fetchImplementation: project.dev.networkFetch ? globalThis.fetch : undefined
2320
+ });
2321
+
2322
+ function syncWatchFiles(files) {
2323
+ if (!watchEnabled) return;
2324
+ const desired = new Set((files || []).map((file) => path.resolve(file)));
2325
+ for (const [file, listener] of watched) {
2326
+ if (desired.has(file)) continue;
2327
+ fs.unwatchFile(file, listener);
2328
+ watched.delete(file);
2329
+ }
2330
+ for (const file of desired) {
2331
+ if (watched.has(file)) continue;
2332
+ const listener = scheduleReload;
2333
+ fs.watchFile(file, { interval: 200 }, listener);
2334
+ watched.set(file, listener);
2335
+ }
2336
+ }
2337
+
2338
+ function reload(reason) {
2339
+ try {
2340
+ const nextCompiled = compileProject(project);
2341
+ const next = prepareJavascriptApplication(project, {
2342
+ allowedPackages: options.allowedPackages,
2343
+ localEmulation: true,
2344
+ schemaBundle: nextCompiled.schema.bundle
2345
+ });
2346
+ if (!next.loaded) {
2347
+ throw new PulseProjectError(
2348
+ 'PULSE_JAVASCRIPT_APPLICATION_UNAVAILABLE',
2349
+ 'The graph-backed JavaScript application could not be loaded for local provider emulation.',
2350
+ { planHash: next.plan.planHash, blockers: next.plan.blockers, automaticFallback: false }
2351
+ );
2352
+ }
2353
+ prepared = next;
2354
+ application = next.loaded.application;
2355
+ schemaCodecs = createCanonicalSchemaCodecs(nextCompiled.schema.bundle.registry);
2356
+ compileError = undefined;
2357
+ const watchFiles = javascriptProjectWatchFiles(project, next, nextCompiled);
2358
+ syncWatchFiles(watchFiles);
2359
+ events(Object.freeze({
2360
+ event: reason === 'initial' ? 'compiled' : 'reloaded',
2361
+ provider: project.provider,
2362
+ target: 'javascript',
2363
+ targetId: descriptor.targetId,
2364
+ executionMode: javascript.localExecutionMode,
2365
+ providerReality: false,
2366
+ entry: relative(project.entryFile, project.root),
2367
+ planHash: next.plan.planHash,
2368
+ graphHash: next.plan.graph.graphHash,
2369
+ localClosureHash: next.loaded.closure.sourceHash,
2370
+ bundledInputs: next.loaded.closure.inputs.length,
2371
+ packagesLoaded: next.loaded.packagesLoaded,
2372
+ watchFiles: Object.freeze(watchFiles.map((file) => relative(file, project.root)))
2373
+ }));
2374
+ } catch (error) {
2375
+ if (!application) compileError = error;
2376
+ events(Object.freeze({
2377
+ event: 'compile-error',
2378
+ provider: project.provider,
2379
+ target: 'javascript',
2380
+ executionMode: javascript.localExecutionMode,
2381
+ retainedLastGoodProgram: Boolean(application),
2382
+ error: errorSummary(error, project.dev.secrets)
2383
+ }));
2384
+ }
2385
+ }
2386
+
2387
+ function scheduleReload() {
2388
+ if (reloadTimer) clearTimeout(reloadTimer);
2389
+ reloadTimer = setTimeout(() => {
2390
+ reloadTimer = undefined;
2391
+ reload('change');
2392
+ }, 60);
2393
+ }
2394
+
2395
+ reload('initial');
2396
+ if (compileError) throw compileError;
2397
+
2398
+ let handled = 0;
2399
+ const structuredBodyLimit = project.schemas.active
2400
+ ? Math.min(project.dev.maxBodyBytes, project.schemas.maxBytes)
2401
+ : project.dev.maxBodyBytes;
2402
+ const server = http.createServer(async (req, res) => {
2403
+ if (options.once) res.once('finish', () => server.close());
2404
+ try {
2405
+ if (compileError) throw compileError;
2406
+ const body = await readRequestBody(req, structuredBodyLimit);
2407
+ const host = req.headers.host || `${project.dev.host}:${project.dev.port}`;
2408
+ const url = new URL(req.url || '/', `http://${host}`);
2409
+ const request = javascript.createTestRequest({
2410
+ method: req.method || 'GET',
2411
+ url: url.href,
2412
+ path: url.pathname,
2413
+ headers: requestHeaders(req),
2414
+ body: body || undefined
2415
+ });
2416
+ const execution = await javascript.executeLocalRequest(application, request, {
2417
+ environment,
2418
+ bindings: project.providerConfig.bindings,
2419
+ application: Object.freeze({
2420
+ projectHash: project.projectHash,
2421
+ planHash: project.planHash,
2422
+ profile: project.profile,
2423
+ target: 'javascript',
2424
+ provider: project.provider
2425
+ }),
2426
+ maxBodyBytes: structuredBodyLimit,
2427
+ maxRequestBodyBytes: structuredBodyLimit,
2428
+ maxFetchBodyBytes: structuredBodyLimit,
2429
+ schemaCodecs,
2430
+ strict: project.strict !== false,
2431
+ reporting: project.reporting
2432
+ });
2433
+ await writeJavascriptWebResponse(execution.response, res, req.method || 'GET');
2434
+ handled += 1;
2435
+ events(Object.freeze({
2436
+ event: 'request',
2437
+ provider: project.provider,
2438
+ target: 'javascript',
2439
+ executionMode: javascript.localExecutionMode,
2440
+ providerReality: false,
2441
+ requestId: `dev:${handled}`,
2442
+ method: req.method || 'GET',
2443
+ path: url.pathname,
2444
+ status: execution.response.status,
2445
+ effects: execution.effectSummary ? execution.effectSummary.ownedEffectCount : 0
2446
+ }));
2447
+ } catch (error) {
2448
+ devErrorResponse(res, error, project.dev.secrets);
2449
+ events(Object.freeze({
2450
+ event: 'request-error',
2451
+ provider: project.provider,
2452
+ target: 'javascript',
2453
+ executionMode: javascript.localExecutionMode,
2454
+ method: req.method || 'GET',
2455
+ path: req.url || '/',
2456
+ error: errorSummary(error, project.dev.secrets)
2457
+ }));
2458
+ }
2459
+ });
2460
+
2461
+ function cleanup() {
2462
+ if (reloadTimer) clearTimeout(reloadTimer);
2463
+ for (const [file, listener] of watched) fs.unwatchFile(file, listener);
2464
+ watched.clear();
2465
+ }
2466
+ server.once('close', cleanup);
2467
+ server.once('error', cleanup);
2468
+ const listenHost = options.host || project.dev.host;
2469
+ const listenPort = options.port !== undefined ? Number(options.port) : project.dev.port;
2470
+ server.listen(listenPort, listenHost);
2471
+ await once(server, 'listening');
2472
+ const address = server.address();
2473
+ const boundHost = typeof address === 'object' && address ? address.address : listenHost;
2474
+ const boundPort = typeof address === 'object' && address ? address.port : listenPort;
2475
+ const advertisedHost = listenHost === '0.0.0.0' || listenHost === '::' ? '127.0.0.1' : listenHost;
2476
+ const urlHost = advertisedHost.includes(':') ? `[${advertisedHost}]` : advertisedHost;
2477
+ const ready = Object.freeze({
2478
+ event: 'ready',
2479
+ version: PROJECT_EXECUTION_VERSION,
2480
+ provider: project.provider,
2481
+ target: 'javascript',
2482
+ targetId: descriptor.targetId,
2483
+ runtimeClass: descriptor.runtimeClass,
2484
+ executionMode: javascript.localExecutionMode,
2485
+ providerReality: false,
2486
+ realityRunner: javascript.realityRunner,
2487
+ host: boundHost,
2488
+ port: boundPort,
2489
+ url: `http://${urlHost}:${boundPort}`,
2490
+ watch: watched.size > 0,
2491
+ watchFiles: Object.freeze([...watched.keys()].map((file) => relative(file, project.root))),
2492
+ once: Boolean(options.once),
2493
+ applicationPlan: Object.freeze({ planHash: prepared.plan.planHash, graphHash: prepared.plan.graph.graphHash }),
2494
+ localLoader: Object.freeze({
2495
+ version: prepared.loaded.version,
2496
+ kind: 'provider-owned-bundled-closure',
2497
+ sourceHash: prepared.loaded.closure.sourceHash,
2498
+ bundledInputs: prepared.loaded.closure.inputs.length,
2499
+ packagesLoaded: prepared.loaded.packagesLoaded
2500
+ }),
2501
+ automaticFallback: false,
2502
+ project: projectJson(project)
2503
+ });
2504
+ events(ready);
2505
+ return Object.freeze({ server, ready, closed: once(server, 'close') });
2506
+ }
2507
+
2508
+ async function startJavascriptDevServer(project, options = {}) {
2509
+ const javascript = providerDriver(project).javascript;
2510
+ if (javascript && javascript.kind === 'provider-bundled') return startBundledJavascriptDevServer(project, options);
2511
+ const descriptor = assertImplementedTarget(project, 'dev');
2512
+ if (!javascript || typeof javascript.createServer !== 'function' || descriptor.requestAdapter !== true || descriptor.lifecycle !== true) {
2513
+ throw new PulseProjectError(
2514
+ 'PULSE_TARGET_IMPLEMENTATION_PENDING',
2515
+ `pulse dev cannot realize the selected javascript target for ${project.provider} yet.`,
2516
+ { target: 'javascript', provider: project.provider, targetDescriptor: descriptor, automaticFallback: false }
2517
+ );
2518
+ }
2519
+
2520
+ let prepared;
2521
+ let application;
2522
+ let compileError;
2523
+ let schemaCodecs;
2524
+ let reloadTimer;
2525
+ const watchEnabled = project.dev.watch && options.watch !== false;
2526
+ const watched = new Map();
2527
+ const events = typeof options.onEvent === 'function' ? options.onEvent : () => {};
2528
+
2529
+ function syncWatchFiles(files) {
2530
+ if (!watchEnabled) return;
2531
+ const desired = new Set((files || []).map((file) => path.resolve(file)));
2532
+ for (const [file, listener] of watched) {
2533
+ if (desired.has(file)) continue;
2534
+ fs.unwatchFile(file, listener);
2535
+ watched.delete(file);
2536
+ }
2537
+ for (const file of desired) {
2538
+ if (watched.has(file)) continue;
2539
+ const listener = scheduleReload;
2540
+ fs.watchFile(file, { interval: 200 }, listener);
2541
+ watched.set(file, listener);
2542
+ }
2543
+ }
2544
+
2545
+ function reload(reason) {
2546
+ try {
2547
+ const next = prepareJavascriptApplication(project, { allowedPackages: options.allowedPackages });
2548
+ if (!next.loaded) {
2549
+ throw new PulseProjectError(
2550
+ 'PULSE_JAVASCRIPT_APPLICATION_UNAVAILABLE',
2551
+ 'The graph-backed JavaScript application could not be loaded.',
2552
+ { planHash: next.plan.planHash, blockers: next.plan.blockers, automaticFallback: false }
2553
+ );
2554
+ }
2555
+ const nextCompiled = compileProject(project);
2556
+ prepared = next;
2557
+ application = next.loaded.application;
2558
+ schemaCodecs = createCanonicalSchemaCodecs(nextCompiled.schema.bundle.registry);
2559
+ compileError = undefined;
2560
+ const watchFiles = javascriptProjectWatchFiles(project, next, nextCompiled);
2561
+ syncWatchFiles(watchFiles);
2562
+ events(Object.freeze({
2563
+ event: reason === 'initial' ? 'compiled' : 'reloaded',
2564
+ target: 'javascript',
2565
+ targetId: descriptor.targetId,
2566
+ entry: relative(project.entryFile, project.root),
2567
+ planHash: next.plan.planHash,
2568
+ graphHash: next.plan.graph.graphHash,
2569
+ watchFiles: Object.freeze(watchFiles.map((file) => relative(file, project.root)))
2570
+ }));
2571
+ } catch (error) {
2572
+ if (!application) compileError = error;
2573
+ events(Object.freeze({ event: 'compile-error', target: 'javascript', retainedLastGoodProgram: Boolean(application), error: errorSummary(error, project.dev.secrets) }));
2574
+ }
2575
+ }
2576
+
2577
+ function scheduleReload() {
2578
+ if (reloadTimer) clearTimeout(reloadTimer);
2579
+ reloadTimer = setTimeout(() => { reloadTimer = undefined; reload('change'); }, 60);
2580
+ }
2581
+
2582
+ reload('initial');
2583
+ if (compileError) throw compileError;
2584
+
2585
+ let handled = 0;
2586
+ let server;
2587
+ const structuredBodyLimit = project.schemas.active
2588
+ ? Math.min(project.dev.maxBodyBytes, project.schemas.maxBytes)
2589
+ : project.dev.maxBodyBytes;
2590
+ server = javascript.createServer(null, {
2591
+ getApplication() {
2592
+ if (compileError) throw compileError;
2593
+ if (!application) throw new PulseProjectError('PULSE_JAVASCRIPT_APPLICATION_UNAVAILABLE', 'No last-good JavaScript application is available.');
2594
+ return application;
2595
+ },
2596
+ schemaCodecs() {
2597
+ if (compileError) throw compileError;
2598
+ return schemaCodecs;
2599
+ },
2600
+ strict: project.strict !== false,
2601
+ provider: project.provider,
2602
+ maxBodyBytes: structuredBodyLimit,
2603
+ application: Object.freeze({
2604
+ projectHash: project.projectHash,
2605
+ planHash: project.planHash,
2606
+ profile: project.profile,
2607
+ target: 'javascript',
2608
+ provider: project.provider
2609
+ }),
2610
+ capabilities: options.capabilities,
2611
+ config: project.dev.config,
2612
+ secrets: project.dev.secrets,
2613
+ kv: project.dev.kv,
2614
+ fetchImplementation: javascript.createFixtureFetch(
2615
+ project.dev.fetches,
2616
+ project.dev.networkFetch ? globalThis.fetch : undefined
2617
+ ),
2618
+ maxRequestBodyBytes: structuredBodyLimit,
2619
+ maxFetchBodyBytes: structuredBodyLimit,
2620
+ onRequest(event) {
2621
+ handled += 1;
2622
+ events(Object.freeze({ event: 'request', target: 'javascript', ...event, requestId: `dev:${handled}` }));
2623
+ if (options.once) server.close();
2624
+ },
2625
+ onError(error, context) {
2626
+ const request = context && context.request;
2627
+ events(Object.freeze({
2628
+ event: 'request-error',
2629
+ target: 'javascript',
2630
+ method: request && request.method || 'GET',
2631
+ path: request && request.url || '/',
2632
+ error: errorSummary(error, project.dev.secrets)
2633
+ }));
2634
+ if (options.once) server.close();
2635
+ }
2636
+ });
2637
+
2638
+ function cleanup() {
2639
+ if (reloadTimer) clearTimeout(reloadTimer);
2640
+ for (const [file, listener] of watched) fs.unwatchFile(file, listener);
2641
+ watched.clear();
2642
+ }
2643
+ server.once('close', cleanup);
2644
+ server.once('error', cleanup);
2645
+ const listenHost = options.host || project.dev.host;
2646
+ const listenPort = options.port !== undefined ? Number(options.port) : project.dev.port;
2647
+ server.listen(listenPort, listenHost);
2648
+ await once(server, 'listening');
2649
+ const address = server.address();
2650
+ const boundHost = typeof address === 'object' && address ? address.address : listenHost;
2651
+ const boundPort = typeof address === 'object' && address ? address.port : listenPort;
2652
+ const advertisedHost = listenHost === '0.0.0.0' || listenHost === '::' ? '127.0.0.1' : listenHost;
2653
+ const urlHost = advertisedHost.includes(':') ? `[${advertisedHost}]` : advertisedHost;
2654
+ const ready = Object.freeze({
2655
+ event: 'ready',
2656
+ version: PROJECT_EXECUTION_VERSION,
2657
+ provider: project.provider,
2658
+ target: 'javascript',
2659
+ targetId: descriptor.targetId,
2660
+ runtimeClass: descriptor.runtimeClass,
2661
+ host: boundHost,
2662
+ port: boundPort,
2663
+ url: `http://${urlHost}:${boundPort}`,
2664
+ watch: watched.size > 0,
2665
+ watchFiles: Object.freeze([...watched.keys()].map((file) => relative(file, project.root))),
2666
+ once: Boolean(options.once),
2667
+ applicationPlan: Object.freeze({ planHash: prepared.plan.planHash, graphHash: prepared.plan.graph.graphHash }),
2668
+ automaticFallback: false,
2669
+ project: projectJson(project)
2670
+ });
2671
+ events(ready);
2672
+ return Object.freeze({ server, ready, closed: once(server, 'close') });
2673
+ }
2674
+
2675
+ async function startDevServer(project, options = {}) {
2676
+ if ((project.target || 'native') === 'javascript') return startJavascriptDevServer(project, options);
2677
+ const driver = assertExecutableProvider(project, 'dev');
2678
+ assertImplementedTarget(project, 'dev');
2679
+ const executeCanonicalProgram = driver.execute;
2680
+ let compiled;
2681
+ let program;
2682
+ let native;
2683
+ let exactNativeCrypto = false;
2684
+ let packageArtifacts = Object.freeze([]);
2685
+ let compileError;
2686
+ let reloadTimer;
2687
+ const watchEnabled = project.dev.watch && options.watch !== false;
2688
+ const watched = new Map();
2689
+ const events = typeof options.onEvent === 'function' ? options.onEvent : () => {};
2690
+ function syncWatchFiles(files) {
2691
+ if (!watchEnabled) return;
2692
+ const desired = new Set((files || []).map((file) => path.resolve(file)));
2693
+ for (const [file, listener] of watched) {
2694
+ if (desired.has(file)) continue;
2695
+ fs.unwatchFile(file, listener);
2696
+ watched.delete(file);
2697
+ }
2698
+ for (const file of desired) {
2699
+ if (watched.has(file)) continue;
2700
+ const listener = scheduleReload;
2701
+ fs.watchFile(file, { interval: 200 }, listener);
2702
+ watched.set(file, listener);
2703
+ }
2704
+ }
2705
+ function reload(reason) {
2706
+ try {
2707
+ const nextCompiled = compileProject(project);
2708
+ const nextExactNativeCrypto = requiresExactNativeCrypto(nextCompiled);
2709
+ const nextProgram = nextExactNativeCrypto ? null : loadCanonicalModule(nextCompiled);
2710
+ const nextNative = nextExactNativeCrypto
2711
+ ? compileNativeProjectInMemory(project, { ...options, compiled: nextCompiled }).native
2712
+ : null;
2713
+ compiled = nextCompiled;
2714
+ program = nextProgram;
2715
+ native = nextNative;
2716
+ exactNativeCrypto = nextExactNativeCrypto;
2717
+ packageArtifacts = packageRealizationArtifactsForCompiled(nextCompiled);
2718
+ compileError = undefined;
2719
+ syncWatchFiles(compiled.watchFiles);
2720
+ events(Object.freeze({
2721
+ event: reason === 'initial' ? 'compiled' : 'reloaded',
2722
+ entry: relative(project.entryFile, project.root),
2723
+ sourceHash: compiled.metadata.sourceHash,
2724
+ projectSourceHash: compiled.metadata.projectSourceHash,
2725
+ schemaSourceHash: compiled.metadata.schemaSourceHash,
2726
+ watchFiles: Object.freeze(compiled.watchFiles.map((file) => relative(file, project.root)))
2727
+ }));
2728
+ } catch (error) {
2729
+ if (!program) compileError = error;
2730
+ events(Object.freeze({ event: 'compile-error', retainedLastGoodProgram: Boolean(program), error: errorSummary(error, project.dev.secrets) }));
2731
+ }
2732
+ }
2733
+ function scheduleReload() {
2734
+ if (reloadTimer) clearTimeout(reloadTimer);
2735
+ reloadTimer = setTimeout(() => { reloadTimer = undefined; reload('change'); }, 60);
2736
+ }
2737
+ reload('initial');
2738
+ if (compileError) throw compileError;
2739
+
2740
+ let handled = 0;
2741
+ const server = http.createServer(async (req, res) => {
2742
+ if (options.once) res.once('finish', () => server.close());
2743
+ try {
2744
+ if (compileError) throw compileError;
2745
+ const bodyLimit = project.schemas.active ? Math.min(project.dev.maxBodyBytes, project.schemas.maxBytes) : project.dev.maxBodyBytes;
2746
+ const body = await readRequestBody(req, bodyLimit);
2747
+ const host = req.headers.host || `${project.dev.host}:${project.dev.port}`;
2748
+ const url = new URL(req.url || '/', `http://${host}`);
2749
+ const executionOptions = providerExecutionOptions(project, {
2750
+ packageArtifacts,
2751
+ request: { method: req.method || 'GET', url: url.href, path: url.pathname, headers: requestHeaders(req), body: body || undefined },
2752
+ config: project.dev.config,
2753
+ secrets: project.dev.secrets,
2754
+ kv: project.dev.kv,
2755
+ fetches: project.dev.fetches,
2756
+ maxBodyBytes: bodyLimit,
2757
+ fetchImplementation: project.dev.networkFetch ? globalThis.fetch : undefined,
2758
+ liveFetch: project.dev.networkFetch,
2759
+ executionId: `dev:${++handled}`
2760
+ });
2761
+ const execution = exactNativeCrypto
2762
+ ? await executeCanonicalNativeModule(native, executionOptions)
2763
+ : await executeCanonicalProgram(program, executionOptions);
2764
+ writeNodeHttpResponse(res, execution.response);
2765
+ events(Object.freeze({ event: 'request', method: req.method || 'GET', path: url.pathname, status: execution.response.status, effects: execution.effectCount }));
2766
+ } catch (error) {
2767
+ devErrorResponse(res, error, project.dev.secrets);
2768
+ events(Object.freeze({ event: 'request-error', method: req.method || 'GET', path: req.url || '/', error: errorSummary(error, project.dev.secrets) }));
2769
+ }
2770
+ });
2771
+
2772
+ function cleanup() {
2773
+ if (reloadTimer) clearTimeout(reloadTimer);
2774
+ for (const [file, listener] of watched) fs.unwatchFile(file, listener);
2775
+ watched.clear();
2776
+ }
2777
+ server.once('close', cleanup);
2778
+ server.once('error', cleanup);
2779
+ const listenHost = options.host || project.dev.host;
2780
+ const listenPort = options.port !== undefined ? Number(options.port) : project.dev.port;
2781
+ server.listen(listenPort, listenHost);
2782
+ await once(server, 'listening');
2783
+ const address = server.address();
2784
+ const boundHost = typeof address === 'object' && address ? address.address : listenHost;
2785
+ const boundPort = typeof address === 'object' && address ? address.port : listenPort;
2786
+ const advertisedHost = listenHost === '0.0.0.0' || listenHost === '::' ? '127.0.0.1' : listenHost;
2787
+ const urlHost = advertisedHost.includes(':') ? `[${advertisedHost}]` : advertisedHost;
2788
+ const ready = Object.freeze({
2789
+ event: 'ready',
2790
+ version: PROJECT_EXECUTION_VERSION,
2791
+ provider: project.provider,
2792
+ host: boundHost,
2793
+ port: boundPort,
2794
+ url: `http://${urlHost}:${boundPort}`,
2795
+ watch: watched.size > 0,
2796
+ watchFiles: Object.freeze([...watched.keys()].map((file) => relative(file, project.root))),
2797
+ once: Boolean(options.once),
2798
+ project: projectJson(project)
2799
+ });
2800
+ events(ready);
2801
+ return Object.freeze({ server, ready, closed: once(server, 'close') });
2802
+ }
2803
+
2804
+ function initProject(target, options = {}) {
2805
+ const root = path.resolve(target || '.');
2806
+ if (fs.existsSync(root)) {
2807
+ const entries = fs.readdirSync(root);
2808
+ if (entries.length > 0 && !options.force) throw new PulseProjectError('PULSE_INIT_NOT_EMPTY', `Target directory is not empty: ${root}`, { root, hint: 'Use --force only when replacing the generated Pulse files is intentional.' });
2809
+ }
2810
+
2811
+ const normalizedName = String(options.name || path.basename(root) || 'pulse-app').toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
2812
+ const name = normalizedName || 'pulse-app';
2813
+ const provider = String(options.provider || 'node').toLowerCase();
2814
+ let driver;
2815
+ try {
2816
+ driver = getProviderDriver(provider);
2817
+ } catch (_) {
2818
+ throw new PulseProjectError(
2819
+ 'PULSE_INIT_PROVIDER_UNSUPPORTED',
2820
+ 'pulse init requires a registered executable provider template.',
2821
+ { provider, supported: providerIds({ executable: true }) }
2822
+ );
2823
+ }
2824
+ if (!driver.executable || typeof driver.initTemplate !== 'function') {
2825
+ throw new PulseProjectError(
2826
+ 'PULSE_INIT_PROVIDER_UNSUPPORTED',
2827
+ 'pulse init requires a registered executable provider template.',
2828
+ { provider, supported: providerIds({ executable: true }) }
2829
+ );
2830
+ }
2831
+ const template = driver.initTemplate(name);
2832
+ const profileFragments = ` outDir: 'dist',
2833
+ dev: { host: '127.0.0.1', port: 8787 },
2834
+ ${template.profileFragment || ''}`;
2835
+
2836
+ const dependencies = { '@pulse-compute/pulse': cliPackageVersion };
2837
+ Object.assign(dependencies, template.dependencies || {});
2838
+
2839
+ const files = {
2840
+ '.gitignore': 'dist\nnode_modules\n',
2841
+ '.pulse/.gitignore': '*\n!.gitignore\n!config.ts\n',
2842
+ '.pulse/config.ts': `import { defineConfig } from '@pulse-compute/pulse';
2843
+
2844
+ export default defineConfig((_scope) => ({
2845
+ pulse: {
2846
+ entry: 'src/index.ts',
2847
+ tests: 'tests/pulse.harness.ts',
2848
+ defaultProfile: 'local',
2849
+ strict: true,
2850
+ },
2851
+ local: {
2852
+ host: '${provider}',
2853
+ target: 'native',
2854
+ ${profileFragments}
2855
+ },
2856
+ }));
2857
+ `,
2858
+ 'README.md': `# ${name}\n\nInstall dependencies with \`npm install\`, then run \`npm run doctor\`.\n\nThe generated project uses the conventional \`.pulse/config.ts\` workspace, an async \`Pulse\` application root, and a dedicated test harness.\n\nUse \`npm run dev\`, \`npm test\`, \`npm run inspect\`, \`npm run compile\`, and \`npm run build\` for the canonical project workflow.\n`,
2859
+ 'package.json': stableJson({
2860
+ name,
2861
+ version: '0.0.0',
2862
+ private: true,
2863
+ scripts: {
2864
+ build: 'pulse build',
2865
+ compile: 'pulse compile',
2866
+ dev: 'pulse dev',
2867
+ doctor: 'pulse doctor',
2868
+ inspect: 'pulse inspect',
2869
+ test: 'pulse test'
2870
+ },
2871
+ dependencies,
2872
+ devDependencies: { '@pulse-compute/cli': cliPackageVersion, typescript: '5.9.3' }
2873
+ }),
2874
+ 'src/index.ts': `import { Pulse } from '@pulse-compute/pulse';
2875
+
2876
+ const app = new Pulse({ auto: true });
2877
+
2878
+ app.get('/health', async (ctx) => {
2879
+ return ctx.json({ ok: true });
2880
+ });
2881
+
2882
+ app.get('/', async (ctx) => {
2883
+ return ctx.text('Pulse is running');
2884
+ });
2885
+
2886
+ export default app;
2887
+ `,
2888
+ 'tests/pulse.harness.ts': `export default {
2889
+ cases: [
2890
+ {
2891
+ name: 'health',
2892
+ request: { method: 'GET', path: '/health' },
2893
+ expect: { status: 200, json: { ok: true } },
2894
+ },
2895
+ ],
2896
+ };
2897
+ `,
2898
+ 'tsconfig.json': stableJson({
2899
+ compilerOptions: {
2900
+ target: 'ES2022',
2901
+ module: 'NodeNext',
2902
+ moduleResolution: 'NodeNext',
2903
+ strict: true,
2904
+ noEmit: true,
2905
+ skipLibCheck: false
2906
+ },
2907
+ include: ['src/**/*.ts', '.pulse/**/*.ts', 'tests/**/*.ts']
2908
+ })
2909
+ };
2910
+
2911
+ const fileNames = Object.keys(files).sort();
2912
+ for (const name of fileNames) {
2913
+ const file = path.join(root, name);
2914
+ fs.mkdirSync(path.dirname(file), { recursive: true });
2915
+ if (fs.existsSync(file) && !options.force) throw new PulseProjectError('PULSE_INIT_FILE_EXISTS', `Refusing to overwrite ${file}.`, { file });
2916
+ fs.writeFileSync(file, files[name]);
2917
+ }
2918
+
2919
+ return Object.freeze({
2920
+ status: 'initialized',
2921
+ version: PROJECT_EXECUTION_VERSION,
2922
+ root,
2923
+ files: Object.freeze(fileNames),
2924
+ packageManager: 'npm',
2925
+ nextSteps: Object.freeze([`cd ${root}`, 'npm install', 'npm run doctor'])
2926
+ });
2927
+ }
2928
+
2929
+ module.exports = {
2930
+ PROJECT_EXECUTION_VERSION,
2931
+ BUILD_MANIFEST,
2932
+ COMPILE_MANIFEST,
2933
+ resolveOutputDirectory,
2934
+ compileProject,
2935
+ compileNativeProjectInMemory,
2936
+ prepareJavascriptApplication,
2937
+ compileNativeProject,
2938
+ buildProject,
2939
+ runProjectTests,
2940
+ inspectProject,
2941
+ doctorProject,
2942
+ startDevServer,
2943
+ initProject,
2944
+ errorSummary,
2945
+ responseJson
2946
+ };