@pulse-compute/provider-fastly 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 (69) hide show
  1. package/README.md +53 -1
  2. package/dist/config-schema.json +77 -0
  3. package/dist/index.d.ts +61 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +59 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +88 -5
  8. package/src/build/canonical-target.js +436 -0
  9. package/src/build/entities-native.js +564 -0
  10. package/src/build/fixed-memory-runtime.as.ts +148 -0
  11. package/src/build/kv-native.as.ts +229 -0
  12. package/src/build/kv-native.js +20 -0
  13. package/src/build/native-http-effects.js +871 -0
  14. package/src/build/native-http-shell.js +703 -0
  15. package/src/build/native-platform-capabilities.js +2196 -0
  16. package/src/build/s3-native.as.ts +215 -0
  17. package/src/build/s3-native.js +30 -0
  18. package/src/compiler/assets-package-out-parity-proof.js +338 -0
  19. package/src/compiler/assets-provider-proof.js +499 -0
  20. package/src/compiler/config-provider-resolution.js +573 -0
  21. package/src/compiler/fastly-adapter.js +564 -0
  22. package/src/compiler/fastly-command-entry.js +390 -0
  23. package/src/compiler/fastly-config-secret-serve.js +323 -0
  24. package/src/compiler/fastly-hostcall-binding.js +598 -0
  25. package/src/compiler/fastly-readiness.js +477 -0
  26. package/src/compiler/index.js +40 -0
  27. package/src/compiler/lifecycle-parity-contract.js +70 -0
  28. package/src/compiler/lifecycle-parity-proof.js +260 -0
  29. package/src/compiler/route-handler-effect-proof.js +255 -0
  30. package/src/compiler/runtime-provider-kv.js +237 -0
  31. package/src/compiler/runtime-provider-shape.js +109 -0
  32. package/src/config-api.js +63 -0
  33. package/src/config-schema.d.ts +13 -0
  34. package/src/config-schema.js +27 -0
  35. package/src/config-schema.json +77 -0
  36. package/src/index.js +3 -0
  37. package/src/index.ts +118 -0
  38. package/src/javascript/assets-adapter.js +122 -0
  39. package/src/javascript/bindings-adapter.js +237 -0
  40. package/src/javascript/errors.js +26 -0
  41. package/src/javascript/fetch-adapter.js +93 -0
  42. package/src/javascript/grip-broadcast.js +261 -0
  43. package/src/javascript/jwt-verifier.js +137 -0
  44. package/src/javascript/lifecycle.js +55 -0
  45. package/src/javascript/local-loader.js +138 -0
  46. package/src/javascript/logging.js +26 -0
  47. package/src/javascript/package-effects.js +68 -0
  48. package/src/javascript/runtime-host.js +108 -0
  49. package/src/javascript/sha256.js +128 -0
  50. package/src/javascript/source-package.js +776 -0
  51. package/src/javascript/support.js +142 -0
  52. package/src/javascript/target-support-policy.js +264 -0
  53. package/src/javascript/target.js +73 -0
  54. package/src/javascript/test-runtime.js +407 -0
  55. package/src/provider-contract.js +79 -0
  56. package/src/runtime/assets-provider.js +464 -0
  57. package/src/runtime/canonical-api-runtime.js +601 -0
  58. package/src/runtime/index.js +28 -0
  59. package/src/runtime/provider-primitives-contract.js +272 -0
  60. package/src/runtime/provider-primitives.js +82 -0
  61. package/src/runtime/route-handler-effects.js +507 -0
  62. package/src/testing/conditional-kv-host.js +109 -0
  63. package/src/testing/fastly-cli.js +538 -0
  64. package/src/testing/native-http-effects-host.js +390 -0
  65. package/src/testing/native-http-shell-host.js +190 -0
  66. package/src/testing/native-platform-capabilities-host.js +676 -0
  67. package/src/toolchain/config.js +131 -0
  68. package/src/toolchain/index.js +345 -0
  69. package/src/toolchain/s3.js +26 -0
@@ -0,0 +1,573 @@
1
+ 'use strict';
2
+
3
+ const { PACKAGE_VERSION, normalizeArtifact, normalizeDiagnostic } = loadContractsDiagnostics();
4
+ const {
5
+ CONFIG_PROVIDER_RESOLUTION_PHASE: PHASE,
6
+ CONFIG_PROVIDER_CONTRACT_VERSION: CONTRACT_VERSION,
7
+ RUNTIME_PROVIDER_RESOLUTION_VERSION: RESOLUTION_VERSION,
8
+ FASTLY_PROVIDER_MAP_VERSION,
9
+ CONFIG_PROVIDER_SMOKE_VERSION: SMOKE_VERSION,
10
+ CONFIG_PROVIDER_SURFACE,
11
+ CONFIG_PROVIDER_POLICY,
12
+ plainObject,
13
+ hasOwn,
14
+ platformFastly,
15
+ collectRefs,
16
+ isSymbolicRef,
17
+ refKind,
18
+ refKey
19
+ } = loadContractsConfigProviderResolution();
20
+
21
+
22
+ function loadContractsDiagnostics() {
23
+ try {
24
+ return require('@pulse-compute/wasm-contracts/diagnostics');
25
+ } catch (error) {
26
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
27
+ return require('../../../../wasm/packages/contracts/src/diagnostics.js');
28
+ }
29
+ throw error;
30
+ }
31
+ }
32
+
33
+ function loadContractsConfigProviderResolution() {
34
+ try {
35
+ return require('@pulse-compute/wasm-contracts/config/provider-resolution');
36
+ } catch (error) {
37
+ if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
38
+ return require('../../../../wasm/packages/contracts/src/config/provider-resolution.js');
39
+ }
40
+ throw error;
41
+ }
42
+ }
43
+
44
+ function normalizeResolvedConfig(input = {}) {
45
+ return input && input.artifact ? input.artifact : input;
46
+ }
47
+
48
+ function makeDiag(code, message, hint, details) {
49
+ return normalizeDiagnostic({
50
+ code,
51
+ message,
52
+ hint,
53
+ severity: 'error',
54
+ phase: 'config-provider-resolution',
55
+ loc: { file: '<config>' },
56
+ details
57
+ });
58
+ }
59
+
60
+ function validateRefs(refs, runtime) {
61
+ const diagnostics = [];
62
+ const seen = new Set();
63
+ const fastly = platformFastly(runtime);
64
+
65
+ for (const ref of refs) {
66
+ const pathText = ref.path.join('.');
67
+ if (ref.kind === 'mixed') {
68
+ diagnostics.push(makeDiag(
69
+ 'PULSEWASM_CONFIG_PROVIDER_MIXED_REF',
70
+ `Config ref at ${pathText || '<root>'} declares both $config and $secret.`,
71
+ 'Use exactly one of { $config: "KEY" } or { $secret: "KEY" }.',
72
+ { path: ref.path }
73
+ ));
74
+ continue;
75
+ }
76
+ if (typeof ref.key !== 'string' || ref.key.trim() === '') {
77
+ diagnostics.push(makeDiag(
78
+ 'PULSEWASM_CONFIG_PROVIDER_KEY_INVALID',
79
+ `Config ref at ${pathText || '<root>'} must use a non-empty string key.`,
80
+ 'Use a literal key such as { $config: "USERS_API_BASE_URL" } or { $secret: "USERS_API_TOKEN" }.',
81
+ { path: ref.path }
82
+ ));
83
+ continue;
84
+ }
85
+ const extraKeys = Object.keys(ref.raw || {}).filter((key) => !['$config', '$secret'].includes(key));
86
+ if (extraKeys.length > 0) {
87
+ diagnostics.push(makeDiag(
88
+ 'PULSEWASM_CONFIG_PROVIDER_EXTRA_KEYS',
89
+ `Config ref at ${pathText || '<root>'} contains unsupported properties: ${extraKeys.join(', ')}.`,
90
+ 'Symbolic refs stay intentionally tiny. Put metadata next to the ref, not inside it.',
91
+ { path: ref.path, extraKeys }
92
+ ));
93
+ }
94
+ const dupKey = `${ref.kind}:${ref.key}:${pathText}`;
95
+ if (seen.has(dupKey)) continue;
96
+ seen.add(dupKey);
97
+
98
+ if (ref.kind === 'config' && !fastly.configStore) {
99
+ diagnostics.push(makeDiag(
100
+ 'PULSEWASM_CONFIG_PROVIDER_FASTLY_CONFIG_STORE_MISSING',
101
+ `One or more $config refs are present but runtime.platform.fastly.configStore is not configured.`,
102
+ 'Set profiles.<name>.runtime.platform.fastly.configStore to the Fastly config store name.',
103
+ { key: ref.key, path: ref.path }
104
+ ));
105
+ }
106
+ if (ref.kind === 'secret' && !fastly.secretStore) {
107
+ diagnostics.push(makeDiag(
108
+ 'PULSEWASM_CONFIG_PROVIDER_FASTLY_SECRET_STORE_MISSING',
109
+ `One or more $secret refs are present but runtime.platform.fastly.secretStore is not configured.`,
110
+ 'Set profiles.<name>.runtime.platform.fastly.secretStore to the Fastly secret store name.',
111
+ { key: ref.key, path: ref.path }
112
+ ));
113
+ }
114
+ }
115
+
116
+ return diagnostics;
117
+ }
118
+
119
+ function createLookup(kind, source) {
120
+ if (source && typeof source.get === 'function') {
121
+ return (key) => source.get(key);
122
+ }
123
+ const table = plainObject(source);
124
+ return (key) => hasOwn(table, key) ? table[key] : undefined;
125
+ }
126
+
127
+ function createPulseRuntimeConfigProvider(options = {}) {
128
+ const resolvedConfig = normalizeResolvedConfig(options.resolvedConfig || {});
129
+ const runtime = plainObject(options.runtime || resolvedConfig.runtime);
130
+ const profile = options.profile || resolvedConfig.profile || null;
131
+ const fastly = platformFastly(runtime);
132
+ const refs = collectRefs(runtime);
133
+ const diagnostics = validateRefs(refs, runtime);
134
+
135
+ const configLookup = createLookup('config', options.configValues || options.configProvider || {});
136
+ const secretLookup = createLookup('secret', options.secretValues || options.secretProvider || {});
137
+
138
+ function getConfig(key) {
139
+ const value = configLookup(key);
140
+ if (value === undefined || value === null) {
141
+ const err = new Error(`Missing config value for ${key}`);
142
+ err.code = 'PULSEWASM_CONFIG_PROVIDER_MISSING_CONFIG';
143
+ err.kind = 'config';
144
+ err.key = key;
145
+ throw err;
146
+ }
147
+ return String(value);
148
+ }
149
+
150
+ function getSecret(key) {
151
+ const value = secretLookup(key);
152
+ if (value === undefined || value === null) {
153
+ const err = new Error(`Missing secret value for ${key}`);
154
+ err.code = 'PULSEWASM_CONFIG_PROVIDER_MISSING_SECRET';
155
+ err.kind = 'secret';
156
+ err.key = key;
157
+ throw err;
158
+ }
159
+ return String(value);
160
+ }
161
+
162
+ function resolveNode(value, pathSegments = []) {
163
+ if (isSymbolicRef(value)) {
164
+ const kind = refKind(value);
165
+ const key = refKey(value);
166
+ if (kind === 'config') return getConfig(key);
167
+ if (kind === 'secret') return getSecret(key);
168
+ return value;
169
+ }
170
+ if (Array.isArray(value)) return value.map((item, index) => resolveNode(item, pathSegments.concat(String(index))));
171
+ if (value && typeof value === 'object') {
172
+ const out = {};
173
+ for (const [key, child] of Object.entries(value)) {
174
+ out[key] = resolveNode(child, pathSegments.concat(key));
175
+ }
176
+ return out;
177
+ }
178
+ return value;
179
+ }
180
+
181
+ function previewNode(value, pathSegments = []) {
182
+ if (isSymbolicRef(value)) {
183
+ const kind = refKind(value);
184
+ const key = refKey(value);
185
+ if (kind === 'config') {
186
+ return {
187
+ kind: 'config',
188
+ key,
189
+ resolved: (() => { try { return getConfig(key); } catch { return null; } })(),
190
+ store: fastly.configStore || null,
191
+ compiledIntoBinary: false
192
+ };
193
+ }
194
+ return {
195
+ kind: 'secret',
196
+ key,
197
+ resolved: (() => { try { getSecret(key); return true; } catch { return false; } })(),
198
+ redacted: true,
199
+ store: fastly.secretStore || null,
200
+ compiledIntoBinary: false
201
+ };
202
+ }
203
+ if (Array.isArray(value)) return value.map((item, index) => previewNode(item, pathSegments.concat(String(index))));
204
+ if (value && typeof value === 'object') {
205
+ const out = {};
206
+ for (const [key, child] of Object.entries(value)) {
207
+ out[key] = previewNode(child, pathSegments.concat(key));
208
+ }
209
+ return out;
210
+ }
211
+ return value;
212
+ }
213
+
214
+ return {
215
+ profile,
216
+ runtime,
217
+ refs,
218
+ fastly,
219
+ diagnostics,
220
+ getConfig,
221
+ getSecret,
222
+ resolveRuntime() {
223
+ return resolveNode(runtime);
224
+ },
225
+ previewRuntime() {
226
+ return previewNode(runtime);
227
+ },
228
+ providerSummary() {
229
+ return {
230
+ profile,
231
+ configRefs: refs.filter((ref) => ref.kind === 'config').length,
232
+ secretRefs: refs.filter((ref) => ref.kind === 'secret').length,
233
+ fastlyConfigStore: fastly.configStore || null,
234
+ fastlySecretStore: fastly.secretStore || null
235
+ };
236
+ }
237
+ };
238
+ }
239
+
240
+ function writeGeneratedProviderModule() {
241
+ return `'use strict';
242
+
243
+ function plainObject(value) {
244
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
245
+ }
246
+
247
+ function hasOwn(obj, key) {
248
+ return Object.prototype.hasOwnProperty.call(obj, key);
249
+ }
250
+
251
+ function isSymbolicRef(value) {
252
+ return value && typeof value === 'object' && !Array.isArray(value) &&
253
+ (hasOwn(value, '$config') || hasOwn(value, '$secret'));
254
+ }
255
+
256
+ function refKind(value) {
257
+ if (!isSymbolicRef(value)) return undefined;
258
+ if (hasOwn(value, '$config') && hasOwn(value, '$secret')) return 'mixed';
259
+ if (hasOwn(value, '$secret')) return 'secret';
260
+ return 'config';
261
+ }
262
+
263
+ function refKey(value) {
264
+ const kind = refKind(value);
265
+ if (kind === 'config') return value.$config;
266
+ if (kind === 'secret') return value.$secret;
267
+ return undefined;
268
+ }
269
+
270
+ function collectRefs(value, pathSegments = [], out = []) {
271
+ if (isSymbolicRef(value)) {
272
+ out.push({ kind: refKind(value), key: refKey(value), path: pathSegments.slice(), raw: value });
273
+ return out;
274
+ }
275
+ if (Array.isArray(value)) {
276
+ value.forEach((item, index) => collectRefs(item, pathSegments.concat(String(index)), out));
277
+ return out;
278
+ }
279
+ if (value && typeof value === 'object') {
280
+ for (const [key, child] of Object.entries(value)) {
281
+ collectRefs(child, pathSegments.concat(key), out);
282
+ }
283
+ }
284
+ return out;
285
+ }
286
+
287
+ function platformFastly(runtime) {
288
+ return plainObject(plainObject(runtime.platform).fastly || runtime.fastly);
289
+ }
290
+
291
+ function createLookup(source) {
292
+ if (source && typeof source.get === 'function') return (key) => source.get(key);
293
+ const table = plainObject(source);
294
+ return (key) => hasOwn(table, key) ? table[key] : undefined;
295
+ }
296
+
297
+ function createPulseRuntimeConfigProvider(options = {}) {
298
+ const resolvedConfig = options.resolvedConfig && options.resolvedConfig.artifact ? options.resolvedConfig.artifact : (options.resolvedConfig || {});
299
+ const runtime = plainObject(options.runtime || resolvedConfig.runtime);
300
+ const profile = options.profile || resolvedConfig.profile || null;
301
+ const fastly = platformFastly(runtime);
302
+ const refs = collectRefs(runtime);
303
+ const configLookup = createLookup(options.configValues || options.configProvider || {});
304
+ const secretLookup = createLookup(options.secretValues || options.secretProvider || {});
305
+
306
+ function getConfig(key) {
307
+ const value = configLookup(key);
308
+ if (value === undefined || value === null) {
309
+ const err = new Error('Missing config value for ' + key);
310
+ err.code = 'PULSEWASM_CONFIG_PROVIDER_MISSING_CONFIG';
311
+ err.kind = 'config';
312
+ err.key = key;
313
+ throw err;
314
+ }
315
+ return String(value);
316
+ }
317
+
318
+ function getSecret(key) {
319
+ const value = secretLookup(key);
320
+ if (value === undefined || value === null) {
321
+ const err = new Error('Missing secret value for ' + key);
322
+ err.code = 'PULSEWASM_CONFIG_PROVIDER_MISSING_SECRET';
323
+ err.kind = 'secret';
324
+ err.key = key;
325
+ throw err;
326
+ }
327
+ return String(value);
328
+ }
329
+
330
+ function resolveNode(value) {
331
+ if (isSymbolicRef(value)) {
332
+ const kind = refKind(value);
333
+ const key = refKey(value);
334
+ if (kind === 'config') return getConfig(key);
335
+ if (kind === 'secret') return getSecret(key);
336
+ return value;
337
+ }
338
+ if (Array.isArray(value)) return value.map(resolveNode);
339
+ if (value && typeof value === 'object') {
340
+ const out = {};
341
+ for (const [key, child] of Object.entries(value)) out[key] = resolveNode(child);
342
+ return out;
343
+ }
344
+ return value;
345
+ }
346
+
347
+ function previewNode(value) {
348
+ if (isSymbolicRef(value)) {
349
+ const kind = refKind(value);
350
+ const key = refKey(value);
351
+ if (kind === 'config') {
352
+ return { kind: 'config', key, resolved: (() => { try { return getConfig(key); } catch { return null; } })(), store: fastly.configStore || null, compiledIntoBinary: false };
353
+ }
354
+ return { kind: 'secret', key, resolved: (() => { try { getSecret(key); return true; } catch { return false; } })(), redacted: true, store: fastly.secretStore || null, compiledIntoBinary: false };
355
+ }
356
+ if (Array.isArray(value)) return value.map(previewNode);
357
+ if (value && typeof value === 'object') {
358
+ const out = {};
359
+ for (const [key, child] of Object.entries(value)) out[key] = previewNode(child);
360
+ return out;
361
+ }
362
+ return value;
363
+ }
364
+
365
+ return {
366
+ profile,
367
+ refs,
368
+ fastly,
369
+ getConfig,
370
+ getSecret,
371
+ resolveRuntime() { return resolveNode(runtime); },
372
+ previewRuntime() { return previewNode(runtime); },
373
+ providerSummary() {
374
+ return {
375
+ profile,
376
+ configRefs: refs.filter((ref) => ref.kind === 'config').length,
377
+ secretRefs: refs.filter((ref) => ref.kind === 'secret').length,
378
+ fastlyConfigStore: fastly.configStore || null,
379
+ fastlySecretStore: fastly.secretStore || null
380
+ };
381
+ }
382
+ };
383
+ }
384
+
385
+ module.exports = { createPulseRuntimeConfigProvider, collectRefs, isSymbolicRef };
386
+ `;
387
+ }
388
+
389
+ function buildMarkdown(contract, resolution, providerMap, smoke) {
390
+ return [
391
+ '# PulseWasm Config / Secret Provider Resolution v1',
392
+ '',
393
+ `Generated by: \`${contract.generatedBy}\``,
394
+ '',
395
+ '## Locked posture',
396
+ '',
397
+ '- `$config` and `$secret` are symbolic refs in profile runtime config.',
398
+ '- Runtime/provider resolution happens outside the Wasm binary.',
399
+ '- Secrets are never compiled into the Wasm binary.',
400
+ '- Fastly config/secret stores are providers, not authoring shapes.',
401
+ '- Local/mock providers are valid for testing and development.',
402
+ '',
403
+ '## Current profile',
404
+ '',
405
+ `- profile: \`${resolution.profile || '<none>'}\``,
406
+ `- config refs: ${resolution.summary.configRefs}`,
407
+ `- secret refs: ${resolution.summary.secretRefs}`,
408
+ '',
409
+ '## Fastly provider mapping',
410
+ '',
411
+ `- config store: ${providerMap.fastly.configStore ? `\`${providerMap.fastly.configStore}\`` : '`<unmapped>`'}`,
412
+ `- secret store: ${providerMap.fastly.secretStore ? `\`${providerMap.fastly.secretStore}\`` : '`<unmapped>`'}`,
413
+ '',
414
+ '## Smoke summary',
415
+ '',
416
+ `- executed: ${smoke.executed}`,
417
+ `- failed checks: ${smoke.failedChecks}`,
418
+ `- missing config caught: ${smoke.missingConfigCaught}`,
419
+ `- missing secret caught: ${smoke.missingSecretCaught}`,
420
+ ''
421
+ ].join('\n');
422
+ }
423
+
424
+ function buildConfigProviderResolution(options = {}) {
425
+ const generatedBy = options.generatedBy || PACKAGE_VERSION;
426
+ const resolvedConfig = normalizeResolvedConfig(options.resolvedConfig || {});
427
+ const runtime = plainObject(resolvedConfig.runtime);
428
+ const provider = createPulseRuntimeConfigProvider({
429
+ resolvedConfig,
430
+ configValues: options.configValues || {},
431
+ secretValues: options.secretValues || {}
432
+ });
433
+
434
+ const refs = provider.refs.map((ref) => ({
435
+ kind: ref.kind,
436
+ key: ref.key,
437
+ path: ref.path,
438
+ store: ref.kind === 'config' ? provider.fastly.configStore || null : provider.fastly.secretStore || null,
439
+ provider: ref.kind === 'config' ? (provider.fastly.configStore ? 'fastly.configStore' : 'unmapped.configStore') : (provider.fastly.secretStore ? 'fastly.secretStore' : 'unmapped.secretStore'),
440
+ compiledIntoBinary: false,
441
+ secret: ref.kind === 'secret'
442
+ }));
443
+
444
+ const contract = {
445
+ version: CONTRACT_VERSION,
446
+ generatedBy,
447
+ phase: PHASE,
448
+ status: 'locked',
449
+ profile: resolvedConfig.profile || null,
450
+ providerSurface: CONFIG_PROVIDER_SURFACE,
451
+ policy: CONFIG_PROVIDER_POLICY,
452
+ summary: {
453
+ refs: refs.length,
454
+ configRefs: refs.filter((ref) => ref.kind === 'config').length,
455
+ secretRefs: refs.filter((ref) => ref.kind === 'secret').length,
456
+ diagnostics: provider.diagnostics.length
457
+ }
458
+ };
459
+
460
+ const preview = provider.previewRuntime();
461
+ const resolution = {
462
+ version: RESOLUTION_VERSION,
463
+ generatedBy,
464
+ phase: PHASE,
465
+ profile: resolvedConfig.profile || null,
466
+ refs,
467
+ previewRuntime: preview,
468
+ summary: {
469
+ refs: refs.length,
470
+ configRefs: contract.summary.configRefs,
471
+ secretRefs: contract.summary.secretRefs,
472
+ fastlyConfigStoreReady: Boolean(provider.fastly.configStore) || contract.summary.configRefs === 0,
473
+ fastlySecretStoreReady: Boolean(provider.fastly.secretStore) || contract.summary.secretRefs === 0,
474
+ diagnostics: provider.diagnostics.length
475
+ }
476
+ };
477
+
478
+ const providerMap = {
479
+ version: FASTLY_PROVIDER_MAP_VERSION,
480
+ generatedBy,
481
+ phase: PHASE,
482
+ profile: resolvedConfig.profile || null,
483
+ fastly: {
484
+ configStore: provider.fastly.configStore || null,
485
+ secretStore: provider.fastly.secretStore || null,
486
+ refs
487
+ },
488
+ summary: {
489
+ fastlyConfigStoreReady: resolution.summary.fastlyConfigStoreReady,
490
+ fastlySecretStoreReady: resolution.summary.fastlySecretStoreReady,
491
+ refs: refs.length
492
+ }
493
+ };
494
+
495
+ const smoke = {
496
+ version: SMOKE_VERSION,
497
+ generatedBy,
498
+ phase: PHASE,
499
+ executed: false,
500
+ checks: 0,
501
+ failedChecks: 0,
502
+ missingConfigCaught: false,
503
+ missingSecretCaught: false,
504
+ resolvedConfigKeys: [],
505
+ resolvedSecretKeys: []
506
+ };
507
+
508
+ let smokeErrors = 0;
509
+ try {
510
+ const resolved = provider.resolveRuntime();
511
+ smoke.executed = true;
512
+ smoke.checks += 1;
513
+ const backend = resolved.capabilities && resolved.capabilities.backends && resolved.capabilities.backends.usersApi;
514
+ if (backend && typeof backend.baseUrl === 'string') smoke.resolvedConfigKeys.push('USERS_API_BASE_URL'); else smokeErrors += 1;
515
+ smoke.checks += 1;
516
+ if (backend && backend.headers && typeof backend.headers.Authorization === 'string') smoke.resolvedSecretKeys.push('USERS_API_TOKEN'); else smokeErrors += 1;
517
+ smoke.checks += 1;
518
+ } catch (error) {
519
+ smokeErrors += 1;
520
+ smoke.error = { code: error.code || 'PULSEWASM_CONFIG_PROVIDER_SMOKE_ERROR', message: error.message };
521
+ }
522
+
523
+ try {
524
+ createPulseRuntimeConfigProvider({ resolvedConfig, configValues: {}, secretValues: options.secretValues || {} }).resolveRuntime();
525
+ smokeErrors += 1;
526
+ } catch (error) {
527
+ smoke.checks += 1;
528
+ smoke.missingConfigCaught = error.code === 'PULSEWASM_CONFIG_PROVIDER_MISSING_CONFIG';
529
+ if (!smoke.missingConfigCaught) smokeErrors += 1;
530
+ }
531
+
532
+ try {
533
+ createPulseRuntimeConfigProvider({ resolvedConfig, configValues: options.configValues || {}, secretValues: {} }).resolveRuntime();
534
+ smokeErrors += 1;
535
+ } catch (error) {
536
+ smoke.checks += 1;
537
+ smoke.missingSecretCaught = error.code === 'PULSEWASM_CONFIG_PROVIDER_MISSING_SECRET';
538
+ if (!smoke.missingSecretCaught) smokeErrors += 1;
539
+ }
540
+
541
+ smoke.failedChecks = smokeErrors;
542
+
543
+ const files = [
544
+ {
545
+ file: 'generated/host/config-provider.cjs',
546
+ text: writeGeneratedProviderModule()
547
+ },
548
+ {
549
+ file: 'generated/host/config-provider-resolution.md',
550
+ text: buildMarkdown(contract, resolution, providerMap, smoke)
551
+ }
552
+ ];
553
+
554
+ return {
555
+ artifact: normalizeArtifact(contract, options.cwd),
556
+ resolution: normalizeArtifact(resolution, options.cwd),
557
+ providerMap: normalizeArtifact(providerMap, options.cwd),
558
+ smoke: normalizeArtifact(smoke, options.cwd),
559
+ diagnostics: provider.diagnostics,
560
+ files
561
+ };
562
+ }
563
+
564
+ module.exports = {
565
+ CONTRACT_VERSION,
566
+ RESOLUTION_VERSION,
567
+ FASTLY_PROVIDER_MAP_VERSION,
568
+ SMOKE_VERSION,
569
+ buildConfigProviderResolution,
570
+ createPulseRuntimeConfigProvider,
571
+ collectRefs,
572
+ isSymbolicRef
573
+ };