@kungfu-tech/buildchain 3.0.4-alpha.0 → 3.0.4-alpha.10

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 (74) hide show
  1. package/AGENTS.md +14 -5
  2. package/README.md +18 -0
  3. package/bin/buildchain.mjs +136 -73
  4. package/bin/internal/trust-release-cli.mjs +15 -537
  5. package/bin/internal/trust-release-command-handlers.mjs +14 -0
  6. package/bin/internal/trust-release-inspection-handlers.mjs +175 -0
  7. package/bin/internal/trust-release-release-handlers.mjs +317 -0
  8. package/bin/internal/trust-release-verification-handlers.mjs +306 -0
  9. package/dist/site/buildchain-contract.json +36 -25
  10. package/dist/site/buildchain-site.json +1474 -61
  11. package/dist/site/capability-registry.json +8 -5
  12. package/dist/site/cli-registry.json +1869 -0
  13. package/dist/site/controller-registry.json +16 -3
  14. package/dist/site/kfd-claims.json +84 -10
  15. package/dist/site/kfd-upstream-aggregate.json +1 -1
  16. package/dist/site/manual-registry.json +49 -6
  17. package/dist/site/node-api-registry.json +17802 -11
  18. package/dist/site/page-registry.json +1441 -59
  19. package/dist/site/public-surface-audit.json +2863 -358
  20. package/dist/site/publication-registry.json +4 -4
  21. package/dist/site/release-model.json +7 -0
  22. package/dist/site/site-manifest.json +34 -10
  23. package/dist/site/workflow-registry.json +13 -5
  24. package/docs/MAP.md +21 -6
  25. package/docs/cli-reference.md +1936 -0
  26. package/docs/cli.md +15 -1
  27. package/docs/getting-started.md +167 -0
  28. package/docs/install.md +7 -8
  29. package/docs/node-api-reference.md +1952 -0
  30. package/docs/release-propagation.md +166 -8
  31. package/docs/site-bundle-contract.md +10 -4
  32. package/docs/versioning.md +5 -4
  33. package/package.json +8 -5
  34. package/packages/core/buildchain-agent-manuals.js +37 -0
  35. package/packages/core/buildchain-kfd-claims.js +4 -37
  36. package/packages/core/controller-evidence.js +8 -1
  37. package/packages/core/index.js +1 -13
  38. package/packages/core/paper-agent-entry.js +14 -11
  39. package/packages/core/paper-fleet.js +34 -7
  40. package/packages/core/paper-npm-bootstrap.js +492 -0
  41. package/packages/core/paper-repository.js +1 -0
  42. package/packages/core/paper.js +356 -674
  43. package/packages/core/public-surface-cli.js +12 -1
  44. package/packages/core/publication-package.js +7 -0
  45. package/packages/core/release-passport.js +67 -71
  46. package/packages/core/release-propagation-common.js +64 -0
  47. package/packages/core/release-propagation-execution-profile.js +59 -0
  48. package/packages/core/release-propagation-release.js +196 -0
  49. package/packages/core/release-propagation-stage-evidence.js +364 -0
  50. package/packages/core/release-propagation-work-capture.js +64 -0
  51. package/packages/core/release-propagation-work-constants.js +34 -0
  52. package/packages/core/release-propagation-work-control.js +203 -0
  53. package/packages/core/release-propagation-work-transitions.js +145 -0
  54. package/packages/core/release-propagation-work.js +517 -0
  55. package/packages/core/release-propagation.js +34 -158
  56. package/scripts/aggregate-build-summary.mjs +67 -4
  57. package/scripts/auditable-demo-renditions.mjs +131 -0
  58. package/scripts/auditable-demo.mjs +88 -91
  59. package/scripts/aws-windows-jit-controller-core.mjs +269 -0
  60. package/scripts/aws-windows-jit-controller.mjs +502 -0
  61. package/scripts/check-internal-architecture.mjs +178 -52
  62. package/scripts/check-inventory.mjs +2 -2
  63. package/scripts/check-maintainability.mjs +76 -17
  64. package/scripts/generate-public-reference.mjs +68 -0
  65. package/scripts/generate-site-bundle.mjs +35 -41
  66. package/scripts/maintainability-metrics.mjs +24 -4
  67. package/scripts/paper-work-fleet-cli.mjs +22 -3
  68. package/scripts/public-reference.mjs +557 -0
  69. package/scripts/release-propagation.mjs +126 -0
  70. package/scripts/resolve-artifact-transfer-mode.mjs +117 -0
  71. package/scripts/site-reference-registry.mjs +174 -0
  72. package/scripts/verify-golden-path.mjs +169 -0
  73. package/scripts/web-surface-core.mjs +46 -207
  74. package/scripts/web-surface-routing.mjs +286 -0
@@ -9,6 +9,29 @@ import { fileURLToPath } from "node:url";
9
9
  import { loadBuildchainConfig, validateBuildchainConfig } from "../packages/core/buildchain-config.js";
10
10
  import { createSurfaceTimestampPolicy } from "../packages/core/surface-manifest.js";
11
11
  import { validateInstallerPublication, verifyInstallerPublicReadback } from "./installer-publication.mjs";
12
+ import {
13
+ classifyPreviewAlias,
14
+ resolveChannelUrl,
15
+ resolvePathOnlyUrl,
16
+ resolveSurfaceUrl,
17
+ manifestPrefixFor,
18
+ objectPrefixFor,
19
+ normalizeSurfacePath,
20
+ surfaceDeployConfig,
21
+ surfaceObjectPrefixFor,
22
+ normalizeS3Key,
23
+ joinS3Key,
24
+ joinUrlPath,
25
+ urlWithPath,
26
+ s3Uri,
27
+ cdnPath,
28
+ cdnWildcardPath,
29
+ viewerWildcardPath,
30
+ surfaceArtifactPrefix,
31
+ surfaceArtifactRootFor,
32
+ syncStaticArtifactArgs,
33
+ mutableCacheControlArgs,
34
+ } from "./web-surface-routing.mjs";
12
35
 
13
36
  const moduleDir = path.dirname(fileURLToPath(import.meta.url));
14
37
 
@@ -89,231 +112,47 @@ function retentionConfig(config, channel) {
89
112
  };
90
113
  }
91
114
 
92
- function classifyPreviewAlias(alias) {
93
- if (/^pr-\d+$/.test(alias)) {
94
- return {
95
- kind: "pr",
96
- mutable: true,
97
- retentionClass: "preview-pr-ephemeral",
98
- retentionKey: "pr_days",
99
- };
100
- }
101
- if (/^sha-[0-9a-f]{6,40}$/i.test(alias)) {
102
- return {
103
- kind: "sha",
104
- mutable: false,
105
- retentionClass: "preview-sha-immutable",
106
- retentionKey: "sha_days",
107
- };
108
- }
109
- return {
110
- kind: "custom",
111
- mutable: true,
112
- retentionClass: "preview-custom-ephemeral",
113
- retentionKey: "pr_days",
114
- };
115
- }
116
115
 
117
- function resolveChannelUrl(channel, alias) {
118
- if (channel.urlPattern) {
119
- if (!alias) {
120
- throw new Error(`channel ${channel.name} requires alias for url_pattern`);
121
- }
122
- return channel.urlPattern.replaceAll("{alias}", alias);
123
- }
124
- return channel.url;
125
- }
126
116
 
127
- function resolvePathOnlyUrl(channel, sourcePath, alias) {
128
- const base = resolveChannelUrl(channel, alias);
129
- const url = new URL(base);
130
- const normalizedPath = normalizeSurfacePath(sourcePath);
131
- if (normalizedPath === "/") {
132
- return `${url.origin}/`;
133
- }
134
- return new URL(normalizedPath.replace(/^\//, ""), `${url.origin}/`).toString();
135
- }
136
117
 
137
- function resolveSurfaceUrl({ surface, channel, channelName, alias }) {
138
- if (channelName === "preview" && surface.previewUrlPattern) {
139
- if (!alias) {
140
- throw new Error(`surface ${surface.name} requires alias for preview_url_pattern`);
141
- }
142
- return surface.previewUrlPattern.replaceAll("{alias}", alias);
143
- }
144
- if (channelName === "staging" && surface.stagingUrl) {
145
- return surface.stagingUrl;
146
- }
147
- if (channelName === "production" && surface.productionUrl) {
148
- return surface.productionUrl;
149
- }
150
- if (surface.pathOnly) {
151
- return resolvePathOnlyUrl(channel, surface.path, alias);
152
- }
153
- const key = channelName === "preview"
154
- ? "preview_url_pattern"
155
- : `${channelName}_url`;
156
- throw new Error(`surfaces.${surface.name}.${key} is required for channel ${channelName}`);
157
- }
158
118
 
159
- function manifestPrefixFor(deployConfig) {
160
- return deployConfig.manifest_prefix || ".buildchain/deployments";
161
- }
162
119
 
163
- function objectPrefixFor(deployConfig, alias) {
164
- if (Object.hasOwn(deployConfig, "prefix")) {
165
- return normalizeS3Key(deployConfig.prefix);
166
- }
167
- return alias || "preview";
168
- }
169
120
 
170
- function normalizeSurfacePath(value) {
171
- const normalized = `/${String(value || "/").replace(/^\/+/, "")}`.replace(/\/{2,}/g, "/");
172
- if (normalized === "/") {
173
- return normalized;
174
- }
175
- return normalized.endsWith("/") ? normalized : `${normalized}/`;
176
- }
177
121
 
178
- function surfaceDeployConfig(deployConfig, surfaceName) {
179
- const overrides = deployConfig.surfaces?.[surfaceName];
180
- if (!overrides || typeof overrides !== "object" || Array.isArray(overrides)) {
181
- return deployConfig;
182
- }
183
- return {
184
- ...deployConfig,
185
- ...overrides,
186
- cacheControl: deployConfig.cacheControl || overrides.cacheControl
187
- ? { ...(deployConfig.cacheControl || {}), ...(overrides.cacheControl || {}) }
188
- : undefined,
189
- surfaces: deployConfig.surfaces,
190
- secretRefs: [
191
- ...new Set([
192
- ...(deployConfig.secretRefs || []),
193
- ...(Array.isArray(overrides.secretRefs) ? overrides.secretRefs : []),
194
- ]),
195
- ],
196
- artifactPath: overrides.artifactPath || deployConfig.artifactPath,
197
- };
198
- }
199
122
 
200
- function surfaceObjectPrefixFor({ deployConfig, surface, alias }) {
201
- const effective = surfaceDeployConfig(deployConfig, surface.name);
202
- if (Object.hasOwn(effective, "prefix")) {
203
- return normalizeS3Key(effective.prefix);
204
- }
205
- const root = objectPrefixFor(deployConfig, alias);
206
- const surfacePath = normalizeS3Key(surface.path);
207
- if (!surfacePath) {
208
- return root;
209
- }
210
- return joinS3Key(root, surfacePath);
211
- }
212
123
 
213
- function normalizeS3Key(value) {
214
- return String(value || "")
215
- .replace(/^\/+/, "")
216
- .replace(/\/{2,}/g, "/")
217
- .replace(/\/+$/, "");
218
- }
219
124
 
220
- function joinS3Key(...parts) {
221
- return normalizeS3Key(parts.filter(Boolean).join("/"));
222
- }
223
125
 
224
- function joinUrlPath(...parts) {
225
- const raw = parts.join("/");
226
- const normalized = normalizeS3Key(raw);
227
- if (!normalized) {
228
- return "/";
229
- }
230
- return raw.endsWith("/") ? `/${normalized}/` : `/${normalized}`;
231
- }
232
126
 
233
- function urlWithPath(baseUrl, requestPath) {
234
- const url = new URL(baseUrl);
235
- url.pathname = joinUrlPath(requestPath);
236
- url.search = "";
237
- url.hash = "";
238
- return url.toString();
239
- }
240
127
 
241
- function s3Uri(bucket, key = "") {
242
- if (!bucket) {
243
- throw new Error("aws-s3-cloudfront adapter requires a bucket or target");
244
- }
245
- const normalizedKey = normalizeS3Key(key);
246
- return normalizedKey ? `s3://${bucket}/${normalizedKey}` : `s3://${bucket}`;
247
- }
248
128
 
249
- function cdnPath(value) {
250
- const normalized = normalizeS3Key(value);
251
- return normalized ? `/${normalized}` : "/";
252
- }
253
129
 
254
- function cdnWildcardPath(value) {
255
- const normalized = normalizeS3Key(value);
256
- return normalized ? `/${normalized}/*` : "/*";
257
- }
258
130
 
259
- function viewerWildcardPath(binding) {
260
- try {
261
- return cdnWildcardPath(new URL(binding.url).pathname);
262
- } catch {
263
- return cdnWildcardPath(binding.objectPrefix);
264
- }
265
- }
266
131
 
267
- function surfaceArtifactPrefix(binding) {
268
- return normalizeS3Key(binding.sourcePath);
269
- }
270
132
 
271
- function surfaceArtifactRootFor({ artifactRoot, binding }) {
272
- const prefix = normalizeS3Key(binding.artifactPathPrefix || surfaceArtifactPrefix(binding));
273
- const root = prefix ? path.join(artifactRoot, prefix) : artifactRoot;
274
- if (fs.existsSync(root)) {
275
- return root;
276
- }
277
- return artifactRoot;
278
- }
279
-
280
- function syncStaticArtifactArgs({ artifactRoot, bucket, objectPrefix, deleteExcludes = [], cacheControl = "" }) {
281
- const args = ["s3", "sync", artifactRoot, s3Uri(bucket, objectPrefix), "--delete"];
282
- if (!objectPrefix) {
283
- args.push("--exclude", ".buildchain/*");
284
- }
285
- for (const pattern of deleteExcludes) {
286
- args.push("--exclude", pattern);
287
- }
288
- if (cacheControl) {
289
- args.push("--cache-control", cacheControl);
290
- }
291
- return args;
292
- }
293
-
294
- function mutableCacheControlArgs({ artifactRoot, bucket, objectPrefix, cacheControl = "", excludePatterns = [] }) {
295
- if (!cacheControl) return [];
296
- const args = [
297
- "s3",
298
- "cp",
299
- artifactRoot,
300
- s3Uri(bucket, objectPrefix),
301
- "--recursive",
302
- "--exclude",
303
- "*",
304
- "--include",
305
- "*.html",
306
- "--include",
307
- "*.json",
308
- "--include",
309
- "*.xml",
310
- ];
311
- for (const pattern of excludePatterns) {
312
- args.push("--exclude", pattern);
313
- }
314
- args.push("--cache-control", cacheControl);
315
- return args;
316
- }
133
+
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
317
156
 
318
157
  const PUBLICATION_ARCHIVE_POLICY_CONTRACT = "kungfu-buildchain-publication-archive-policy";
319
158
  const OBSERVED_EVIDENCE_OWNERSHIP_CONTRACT = "kungfu-buildchain-observed-evidence-ownership";
@@ -0,0 +1,286 @@
1
+ import fs from "node:fs";
2
+
3
+ import path from "node:path";
4
+
5
+ function classifyPreviewAlias(alias) {
6
+ if (/^pr-\d+$/.test(alias)) {
7
+ return {
8
+ kind: "pr",
9
+ mutable: true,
10
+ retentionClass: "preview-pr-ephemeral",
11
+ retentionKey: "pr_days",
12
+ };
13
+ }
14
+ if (/^sha-[0-9a-f]{6,40}$/i.test(alias)) {
15
+ return {
16
+ kind: "sha",
17
+ mutable: false,
18
+ retentionClass: "preview-sha-immutable",
19
+ retentionKey: "sha_days",
20
+ };
21
+ }
22
+ return {
23
+ kind: "custom",
24
+ mutable: true,
25
+ retentionClass: "preview-custom-ephemeral",
26
+ retentionKey: "pr_days",
27
+ };
28
+ }
29
+
30
+ function resolveChannelUrl(channel, alias) {
31
+ if (channel.urlPattern) {
32
+ if (!alias) {
33
+ throw new Error(`channel ${channel.name} requires alias for url_pattern`);
34
+ }
35
+ return channel.urlPattern.replaceAll("{alias}", alias);
36
+ }
37
+ return channel.url;
38
+ }
39
+
40
+ function resolvePathOnlyUrl(channel, sourcePath, alias) {
41
+ const base = resolveChannelUrl(channel, alias);
42
+ const url = new URL(base);
43
+ const normalizedPath = normalizeSurfacePath(sourcePath);
44
+ if (normalizedPath === "/") {
45
+ return `${url.origin}/`;
46
+ }
47
+ return new URL(
48
+ normalizedPath.replace(/^\//, ""),
49
+ `${url.origin}/`,
50
+ ).toString();
51
+ }
52
+
53
+ function resolveSurfaceUrl({ surface, channel, channelName, alias }) {
54
+ if (channelName === "preview" && surface.previewUrlPattern) {
55
+ if (!alias) {
56
+ throw new Error(
57
+ `surface ${surface.name} requires alias for preview_url_pattern`,
58
+ );
59
+ }
60
+ return surface.previewUrlPattern.replaceAll("{alias}", alias);
61
+ }
62
+ if (channelName === "staging" && surface.stagingUrl) {
63
+ return surface.stagingUrl;
64
+ }
65
+ if (channelName === "production" && surface.productionUrl) {
66
+ return surface.productionUrl;
67
+ }
68
+ if (surface.pathOnly) {
69
+ return resolvePathOnlyUrl(channel, surface.path, alias);
70
+ }
71
+ const key =
72
+ channelName === "preview" ? "preview_url_pattern" : `${channelName}_url`;
73
+ throw new Error(
74
+ `surfaces.${surface.name}.${key} is required for channel ${channelName}`,
75
+ );
76
+ }
77
+
78
+ function manifestPrefixFor(deployConfig) {
79
+ return deployConfig.manifest_prefix || ".buildchain/deployments";
80
+ }
81
+
82
+ function objectPrefixFor(deployConfig, alias) {
83
+ if (Object.hasOwn(deployConfig, "prefix")) {
84
+ return normalizeS3Key(deployConfig.prefix);
85
+ }
86
+ return alias || "preview";
87
+ }
88
+
89
+ function normalizeSurfacePath(value) {
90
+ const normalized = `/${String(value || "/").replace(/^\/+/, "")}`.replace(
91
+ /\/{2,}/g,
92
+ "/",
93
+ );
94
+ if (normalized === "/") {
95
+ return normalized;
96
+ }
97
+ return normalized.endsWith("/") ? normalized : `${normalized}/`;
98
+ }
99
+
100
+ function surfaceDeployConfig(deployConfig, surfaceName) {
101
+ const overrides = deployConfig.surfaces?.[surfaceName];
102
+ if (!overrides || typeof overrides !== "object" || Array.isArray(overrides)) {
103
+ return deployConfig;
104
+ }
105
+ return {
106
+ ...deployConfig,
107
+ ...overrides,
108
+ cacheControl:
109
+ deployConfig.cacheControl || overrides.cacheControl
110
+ ? {
111
+ ...(deployConfig.cacheControl || {}),
112
+ ...(overrides.cacheControl || {}),
113
+ }
114
+ : undefined,
115
+ surfaces: deployConfig.surfaces,
116
+ secretRefs: [
117
+ ...new Set([
118
+ ...(deployConfig.secretRefs || []),
119
+ ...(Array.isArray(overrides.secretRefs) ? overrides.secretRefs : []),
120
+ ]),
121
+ ],
122
+ artifactPath: overrides.artifactPath || deployConfig.artifactPath,
123
+ };
124
+ }
125
+
126
+ function surfaceObjectPrefixFor({ deployConfig, surface, alias }) {
127
+ const effective = surfaceDeployConfig(deployConfig, surface.name);
128
+ if (Object.hasOwn(effective, "prefix")) {
129
+ return normalizeS3Key(effective.prefix);
130
+ }
131
+ const root = objectPrefixFor(deployConfig, alias);
132
+ const surfacePath = normalizeS3Key(surface.path);
133
+ if (!surfacePath) {
134
+ return root;
135
+ }
136
+ return joinS3Key(root, surfacePath);
137
+ }
138
+
139
+ function normalizeS3Key(value) {
140
+ return String(value || "")
141
+ .replace(/^\/+/, "")
142
+ .replace(/\/{2,}/g, "/")
143
+ .replace(/\/+$/, "");
144
+ }
145
+
146
+ function joinS3Key(...parts) {
147
+ return normalizeS3Key(parts.filter(Boolean).join("/"));
148
+ }
149
+
150
+ function joinUrlPath(...parts) {
151
+ const raw = parts.join("/");
152
+ const normalized = normalizeS3Key(raw);
153
+ if (!normalized) {
154
+ return "/";
155
+ }
156
+ return raw.endsWith("/") ? `/${normalized}/` : `/${normalized}`;
157
+ }
158
+
159
+ function urlWithPath(baseUrl, requestPath) {
160
+ const url = new URL(baseUrl);
161
+ url.pathname = joinUrlPath(requestPath);
162
+ url.search = "";
163
+ url.hash = "";
164
+ return url.toString();
165
+ }
166
+
167
+ function s3Uri(bucket, key = "") {
168
+ if (!bucket) {
169
+ throw new Error("aws-s3-cloudfront adapter requires a bucket or target");
170
+ }
171
+ const normalizedKey = normalizeS3Key(key);
172
+ return normalizedKey ? `s3://${bucket}/${normalizedKey}` : `s3://${bucket}`;
173
+ }
174
+
175
+ function cdnPath(value) {
176
+ const normalized = normalizeS3Key(value);
177
+ return normalized ? `/${normalized}` : "/";
178
+ }
179
+
180
+ function cdnWildcardPath(value) {
181
+ const normalized = normalizeS3Key(value);
182
+ return normalized ? `/${normalized}/*` : "/*";
183
+ }
184
+
185
+ function viewerWildcardPath(binding) {
186
+ try {
187
+ return cdnWildcardPath(new URL(binding.url).pathname);
188
+ } catch {
189
+ return cdnWildcardPath(binding.objectPrefix);
190
+ }
191
+ }
192
+
193
+ function surfaceArtifactPrefix(binding) {
194
+ return normalizeS3Key(binding.sourcePath);
195
+ }
196
+
197
+ function surfaceArtifactRootFor({ artifactRoot, binding }) {
198
+ const prefix = normalizeS3Key(
199
+ binding.artifactPathPrefix || surfaceArtifactPrefix(binding),
200
+ );
201
+ const root = prefix ? path.join(artifactRoot, prefix) : artifactRoot;
202
+ if (fs.existsSync(root)) {
203
+ return root;
204
+ }
205
+ return artifactRoot;
206
+ }
207
+
208
+ function syncStaticArtifactArgs({
209
+ artifactRoot,
210
+ bucket,
211
+ objectPrefix,
212
+ deleteExcludes = [],
213
+ cacheControl = "",
214
+ }) {
215
+ const args = [
216
+ "s3",
217
+ "sync",
218
+ artifactRoot,
219
+ s3Uri(bucket, objectPrefix),
220
+ "--delete",
221
+ ];
222
+ if (!objectPrefix) {
223
+ args.push("--exclude", ".buildchain/*");
224
+ }
225
+ for (const pattern of deleteExcludes) {
226
+ args.push("--exclude", pattern);
227
+ }
228
+ if (cacheControl) {
229
+ args.push("--cache-control", cacheControl);
230
+ }
231
+ return args;
232
+ }
233
+
234
+ function mutableCacheControlArgs({
235
+ artifactRoot,
236
+ bucket,
237
+ objectPrefix,
238
+ cacheControl = "",
239
+ excludePatterns = [],
240
+ }) {
241
+ if (!cacheControl) return [];
242
+ const args = [
243
+ "s3",
244
+ "cp",
245
+ artifactRoot,
246
+ s3Uri(bucket, objectPrefix),
247
+ "--recursive",
248
+ "--exclude",
249
+ "*",
250
+ "--include",
251
+ "*.html",
252
+ "--include",
253
+ "*.json",
254
+ "--include",
255
+ "*.xml",
256
+ ];
257
+ for (const pattern of excludePatterns) {
258
+ args.push("--exclude", pattern);
259
+ }
260
+ args.push("--cache-control", cacheControl);
261
+ return args;
262
+ }
263
+
264
+ export {
265
+ classifyPreviewAlias,
266
+ resolveChannelUrl,
267
+ resolvePathOnlyUrl,
268
+ resolveSurfaceUrl,
269
+ manifestPrefixFor,
270
+ objectPrefixFor,
271
+ normalizeSurfacePath,
272
+ surfaceDeployConfig,
273
+ surfaceObjectPrefixFor,
274
+ normalizeS3Key,
275
+ joinS3Key,
276
+ joinUrlPath,
277
+ urlWithPath,
278
+ s3Uri,
279
+ cdnPath,
280
+ cdnWildcardPath,
281
+ viewerWildcardPath,
282
+ surfaceArtifactPrefix,
283
+ surfaceArtifactRootFor,
284
+ syncStaticArtifactArgs,
285
+ mutableCacheControlArgs,
286
+ };