@salesforce/storefront-next-dev 1.4.0 → 1.5.0-alpha.0

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 (140) hide show
  1. package/README.md +13 -2
  2. package/dist/assets.js +25 -0
  3. package/dist/audit/catalog/categories/best-practices/CATEGORY.md +21 -0
  4. package/dist/audit/catalog/categories/best-practices/rules/commerce-api-client.md +20 -0
  5. package/dist/audit/catalog/categories/best-practices/rules/deferred-ui.js +575 -0
  6. package/dist/audit/catalog/categories/best-practices/rules/deferred-ui.md +20 -0
  7. package/dist/audit/catalog/categories/best-practices/rules/error-boundaries.md +19 -0
  8. package/dist/audit/catalog/categories/best-practices/rules/production-integrations.md +20 -0
  9. package/dist/audit/catalog/categories/best-practices/rules/route-revalidation.md +19 -0
  10. package/dist/audit/catalog/categories/best-practices/rules/server-data-loading.md +19 -0
  11. package/dist/audit/catalog/categories/performance/CATEGORY.md +18 -0
  12. package/dist/audit/catalog/categories/performance/rules/app-config-size.js +15 -0
  13. package/dist/audit/catalog/categories/performance/rules/app-config-size.md +22 -0
  14. package/dist/audit/catalog/categories/performance/rules/cache-control.js +59 -0
  15. package/dist/audit/catalog/categories/performance/rules/cache-control.md +20 -0
  16. package/dist/audit/catalog/categories/performance/rules/cls-budget.js +13 -0
  17. package/dist/audit/catalog/categories/performance/rules/cls-budget.md +20 -0
  18. package/dist/audit/catalog/categories/performance/rules/content-encoding.js +35 -0
  19. package/dist/audit/catalog/categories/performance/rules/content-encoding.md +20 -0
  20. package/dist/audit/catalog/categories/performance/rules/dom-size.js +18 -0
  21. package/dist/audit/catalog/categories/performance/rules/dom-size.md +22 -0
  22. package/dist/audit/catalog/categories/performance/rules/font-display.js +248 -0
  23. package/dist/audit/catalog/categories/performance/rules/font-display.md +21 -0
  24. package/dist/audit/catalog/categories/performance/rules/html-document-size.js +15 -0
  25. package/dist/audit/catalog/categories/performance/rules/html-document-size.md +20 -0
  26. package/dist/audit/catalog/categories/performance/rules/image-dimensions.js +141 -0
  27. package/dist/audit/catalog/categories/performance/rules/image-dimensions.md +22 -0
  28. package/dist/audit/catalog/categories/performance/rules/image-transformation.js +117 -0
  29. package/dist/audit/catalog/categories/performance/rules/image-transformation.md +22 -0
  30. package/dist/audit/catalog/categories/performance/rules/javascript-size.js +126 -0
  31. package/dist/audit/catalog/categories/performance/rules/javascript-size.md +20 -0
  32. package/dist/audit/catalog/categories/performance/rules/large-images.js +187 -0
  33. package/dist/audit/catalog/categories/performance/rules/large-images.md +22 -0
  34. package/dist/audit/catalog/categories/performance/rules/lcp-budget.js +13 -0
  35. package/dist/audit/catalog/categories/performance/rules/lcp-budget.md +20 -0
  36. package/dist/audit/catalog/categories/performance/rules/lcp-image-priority.js +227 -0
  37. package/dist/audit/catalog/categories/performance/rules/lcp-image-priority.md +27 -0
  38. package/dist/audit/catalog/categories/performance/rules/page-data-size.js +15 -0
  39. package/dist/audit/catalog/categories/performance/rules/page-data-size.md +22 -0
  40. package/dist/audit/catalog/categories/performance/rules/reduce-javascript-on-load.js +97 -0
  41. package/dist/audit/catalog/categories/performance/rules/reduce-javascript-on-load.md +73 -0
  42. package/dist/audit/catalog/categories/performance/rules/render-blocking-font-size.js +201 -0
  43. package/dist/audit/catalog/categories/performance/rules/render-blocking-font-size.md +20 -0
  44. package/dist/audit/catalog/categories/performance/rules/server-request-waterfall.md +85 -0
  45. package/dist/audit/catalog/categories/performance/rules/tbt-budget.js +13 -0
  46. package/dist/audit/catalog/categories/performance/rules/tbt-budget.md +20 -0
  47. package/dist/audit/catalog/categories/performance/rules/third-party-fonts.js +265 -0
  48. package/dist/audit/catalog/categories/performance/rules/third-party-fonts.md +20 -0
  49. package/dist/audit/catalog/categories/performance/rules/ttfb-budget.js +17 -0
  50. package/dist/audit/catalog/categories/performance/rules/ttfb-budget.md +20 -0
  51. package/dist/audit/catalog/categories/security/CATEGORY.md +17 -0
  52. package/dist/audit/catalog/categories/security/rules/browser-hardening-headers.js +37 -0
  53. package/dist/audit/catalog/categories/security/rules/browser-hardening-headers.md +20 -0
  54. package/dist/audit/catalog/categories/security/rules/content-security-policy.js +40 -0
  55. package/dist/audit/catalog/categories/security/rules/content-security-policy.md +20 -0
  56. package/dist/audit/catalog/categories/security/rules/csp-runtime-compatibility.md +24 -0
  57. package/dist/audit/catalog/categories/security/rules/dependency-vulnerabilities.md +19 -0
  58. package/dist/audit/catalog/categories/security/rules/dynamic-code-execution.md +19 -0
  59. package/dist/audit/catalog/categories/security/rules/hardcoded-credentials.md +19 -0
  60. package/dist/audit/catalog/categories/security/rules/html-config-secrets.js +46 -0
  61. package/dist/audit/catalog/categories/security/rules/html-config-secrets.md +22 -0
  62. package/dist/audit/catalog/categories/security/rules/protected-route-authorization.md +19 -0
  63. package/dist/audit/catalog/categories/security/rules/public-client-secrets.md +20 -0
  64. package/dist/audit/catalog/categories/security/rules/raw-html-sinks.md +20 -0
  65. package/dist/audit/catalog/categories/security/rules/strict-transport-security.js +183 -0
  66. package/dist/audit/catalog/categories/security/rules/strict-transport-security.md +24 -0
  67. package/dist/audit/catalog/categories/seo/CATEGORY.md +17 -0
  68. package/dist/audit/catalog/categories/seo/rules/canonical-url.md +20 -0
  69. package/dist/audit/catalog/categories/seo/rules/locale-alternates.md +19 -0
  70. package/dist/audit/catalog/categories/seo/rules/meta-description.md +19 -0
  71. package/dist/audit/catalog/categories/seo/rules/page-title.md +19 -0
  72. package/dist/audit/catalog/categories/seo/rules/robots-indexing.md +20 -0
  73. package/dist/audit/catalog/categories/seo/rules/structured-data.md +19 -0
  74. package/dist/audit/instructions/evaluate-rule.md +42 -0
  75. package/dist/audit/schemas/audit-result.schema.json +1237 -0
  76. package/dist/audit/ui/assets/banner-brand-default-CDRB3_S0.png +0 -0
  77. package/dist/audit/ui/assets/banner-group-public-default-BGRJzVY8.png +0 -0
  78. package/dist/audit/ui/assets/banner-user-default-BW81Ba3z.png +0 -0
  79. package/dist/audit/ui/assets/bg-info@2x-CzpP0vaE.png +0 -0
  80. package/dist/audit/ui/assets/einstein-figure-B1xFOo8O.svg +1 -0
  81. package/dist/audit/ui/assets/group_avatar_160-CrxVHlYA.png +0 -0
  82. package/dist/audit/ui/assets/group_avatar_200-CSLS8I0r.png +0 -0
  83. package/dist/audit/ui/assets/index-CtE_JUcs.css +1 -0
  84. package/dist/audit/ui/assets/index-aSdbHIeq.js +44 -0
  85. package/dist/audit/ui/assets/profile_avatar_160-CtQTUBo0.png +0 -0
  86. package/dist/audit/ui/assets/profile_avatar_200-DAztxshF.png +0 -0
  87. package/dist/audit/ui/assets/symbols-BRmpfDbO.svg +1 -0
  88. package/dist/audit/ui/assets/symbols-D0Ne6690.svg +1 -0
  89. package/dist/audit/ui/einstein.png +0 -0
  90. package/dist/audit/ui/index.html +13 -0
  91. package/dist/audit.js +1898 -0
  92. package/dist/cartridge-services/index.d.ts +5 -0
  93. package/dist/cartridge-services/index.d.ts.map +1 -1
  94. package/dist/cartridge-services/index.js +94 -7
  95. package/dist/cartridge-services/index.js.map +1 -1
  96. package/dist/catalog.js +89 -0
  97. package/dist/commands/audit/catalog.js +27 -0
  98. package/dist/commands/audit/finalize.js +42 -0
  99. package/dist/commands/audit/prepare.js +84 -0
  100. package/dist/commands/audit/progress.js +51 -0
  101. package/dist/commands/audit/serve.js +63 -0
  102. package/dist/commands/config/aggregate-extensions.js +1 -0
  103. package/dist/commands/create-bundle.js +2 -1
  104. package/dist/commands/create-instructions.js +1 -0
  105. package/dist/commands/create-storefront.js +1 -0
  106. package/dist/commands/deploy-cartridge.js +35 -11
  107. package/dist/commands/dev.js +2 -0
  108. package/dist/commands/extensions/create.js +1 -0
  109. package/dist/commands/extensions/install.js +1 -0
  110. package/dist/commands/extensions/list.js +1 -0
  111. package/dist/commands/extensions/remove.js +1 -0
  112. package/dist/commands/generate-cartridge.js +49 -5
  113. package/dist/commands/locales/aggregate-extensions.js +1 -0
  114. package/dist/commands/prepare-local.js +1 -0
  115. package/dist/commands/preview.js +2 -0
  116. package/dist/commands/push.js +37 -7
  117. package/dist/commands/validate-cartridge.js +13 -4
  118. package/dist/config.js +13 -2
  119. package/dist/document-size.js +231 -0
  120. package/dist/events.js +688 -0
  121. package/dist/flags2.js +34 -0
  122. package/dist/generate-cartridge.js +41 -6
  123. package/dist/har.js +205 -0
  124. package/dist/images.js +146 -0
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/live.js +277 -0
  127. package/dist/logger.js +1 -4
  128. package/dist/measured-run.js +91 -0
  129. package/dist/metric-budget.js +125 -0
  130. package/dist/mrt/ssr.mjs +50 -50
  131. package/dist/mrt/ssr.mjs.map +1 -1
  132. package/dist/mrt/streamingHandler.mjs +56 -56
  133. package/dist/mrt/streamingHandler.mjs.map +1 -1
  134. package/dist/package.js +5 -0
  135. package/dist/sanitize.js +185 -0
  136. package/dist/schema.js +121 -0
  137. package/dist/server.js +8 -60
  138. package/dist/server2.js +185 -0
  139. package/dist/ts-import.js +57 -0
  140. package/package.json +24 -6
@@ -0,0 +1,231 @@
1
+ import { c as matchDocument, l as normalizedUrl, n as REPRESENTATIVE_CELL } from "./har.js";
2
+ import { z } from "zod";
3
+
4
+ //#region src/audit/rule-helpers/document-size.ts
5
+ const scriptIndexSchema = z.object({ observations: z.array(z.object({
6
+ runId: z.string(),
7
+ runKind: z.string(),
8
+ formFactor: z.string(),
9
+ cacheState: z.string(),
10
+ url: z.string(),
11
+ body: z.object({
12
+ status: z.string(),
13
+ sha256: z.string().optional()
14
+ }).passthrough()
15
+ }).passthrough()) }).passthrough();
16
+ const lhrSchema = z.object({ audits: z.object({ "dom-size-insight": z.object({ details: z.object({ items: z.array(z.record(z.string(), z.unknown())).optional() }).passthrough().optional() }).passthrough().optional() }).passthrough() }).passthrough();
17
+ /** What an inline `<script>` in the HTML holds, from its text. */
18
+ function inlinePart(text) {
19
+ if (text.includes("__reactRouterContext.streamController.enqueue(")) return "pageData";
20
+ if (text.includes("window.__APP_CONFIG__")) return "appConfig";
21
+ return "otherScripts";
22
+ }
23
+ const pageSizeSchema = z.object({
24
+ page: z.string(),
25
+ url: z.string(),
26
+ htmlBytes: z.number(),
27
+ transferBytes: z.number().nullable(),
28
+ pageDataBytes: z.number().nullable(),
29
+ appConfigBytes: z.number().nullable(),
30
+ otherScriptBytes: z.number().nullable(),
31
+ markupBytes: z.number().nullable(),
32
+ scripts: z.object({
33
+ pageData: z.array(z.string()),
34
+ appConfig: z.array(z.string()),
35
+ other: z.array(z.string())
36
+ }).strict(),
37
+ appConfigSections: z.record(z.string(), z.number()).nullable(),
38
+ elements: z.number().nullable(),
39
+ domDepth: z.number().nullable(),
40
+ harArtifact: z.string(),
41
+ harEntry: z.number(),
42
+ lighthouseArtifact: z.string().nullable()
43
+ }).strict();
44
+ const documentSizeDataSchema = z.object({
45
+ cell: z.string(),
46
+ budget: z.number(),
47
+ pages: z.array(pageSizeSchema),
48
+ skipped: z.array(z.object({
49
+ page: z.string(),
50
+ reason: z.string()
51
+ }).strict())
52
+ }).strict();
53
+ const numeric = (value) => {
54
+ const inner = value?.value;
55
+ return typeof inner === "number" && Number.isFinite(inner) ? inner : null;
56
+ };
57
+ /** Top-level sections of a `window.__APP_CONFIG__ = {...};` script by serialized size, largest first. */
58
+ function configSections(text) {
59
+ const json = text.slice(text.indexOf("=") + 1).trim().replace(/;$/, "");
60
+ try {
61
+ const config = JSON.parse(json);
62
+ if (!config || typeof config !== "object" || Array.isArray(config)) return null;
63
+ const sizes = Object.entries(config).map(([key, value]) => [key, Buffer.byteLength(JSON.stringify(value) ?? "")]);
64
+ return Object.fromEntries(sizes.slice().sort((a, b) => b[1] - a[1]));
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ /** Measures one page's HTML from its mobile cold-cache HAR, archived inline scripts and Lighthouse result. */
70
+ async function measurePage(playhouse, page) {
71
+ const match = await matchDocument(playhouse, page, REPRESENTATIVE_CELL);
72
+ if ("reason" in match) return { reason: match.reason };
73
+ const response = match.document.entry.response;
74
+ const htmlBytes = response.content?.size;
75
+ if (typeof htmlBytes !== "number" || htmlBytes <= 0) return { reason: "The HAR records no decoded size for the HTML document." };
76
+ const transfer = response.bodySize;
77
+ const runs = ((await playhouse.readJson(page, "scripts/index.json", scriptIndexSchema))?.observations ?? []).filter((item) => item.runKind === "measurement" && `${item.formFactor}-${item.cacheState}` === REPRESENTATIVE_CELL);
78
+ const runId = runs.map((item) => item.runId).slice().sort()[0];
79
+ const documentUrl = normalizedUrl(match.finalUrl);
80
+ const inline = runs.filter((item) => item.runId === runId && normalizedUrl(item.url) === documentUrl && item.body.status === "captured" && item.body.sha256);
81
+ const bytes = {
82
+ pageData: 0,
83
+ appConfig: 0,
84
+ otherScripts: 0
85
+ };
86
+ let appConfigSections = null;
87
+ const scripts = {
88
+ pageData: [],
89
+ appConfig: [],
90
+ other: []
91
+ };
92
+ for (const item of inline) {
93
+ const artifact = `scripts/${item.body.sha256}.js`;
94
+ const text = await playhouse.readText(page, artifact);
95
+ if (text === void 0) continue;
96
+ const part = inlinePart(text);
97
+ const size = Buffer.byteLength(text);
98
+ bytes[part] += size;
99
+ scripts[part === "otherScripts" ? "other" : part].push({
100
+ artifact,
101
+ size
102
+ });
103
+ if (part === "appConfig") appConfigSections ??= configSections(text);
104
+ }
105
+ const measured = inline.length > 0;
106
+ const inlineTotal = bytes.pageData + bytes.appConfig + bytes.otherScripts;
107
+ const lighthouseArtifact = runId ? `runs/${runId}/lhr.json` : null;
108
+ const lhr = lighthouseArtifact ? await playhouse.readJson(page, lighthouseArtifact, lhrSchema) : void 0;
109
+ const statistics = lhr?.audits["dom-size-insight"]?.details?.items ?? [];
110
+ const statistic = (name) => numeric(statistics.find((item) => item.statistic === name)?.value);
111
+ const largestFirst = (items) => items.slice().sort((a, b) => b.size - a.size).map((item) => item.artifact);
112
+ return {
113
+ page,
114
+ url: match.finalUrl,
115
+ htmlBytes,
116
+ transferBytes: typeof transfer === "number" && transfer > 0 ? transfer : null,
117
+ pageDataBytes: measured ? bytes.pageData : null,
118
+ appConfigBytes: measured ? bytes.appConfig : null,
119
+ otherScriptBytes: measured ? bytes.otherScripts : null,
120
+ markupBytes: measured ? Math.max(0, htmlBytes - inlineTotal) : null,
121
+ scripts: {
122
+ pageData: largestFirst(scripts.pageData),
123
+ appConfig: largestFirst(scripts.appConfig),
124
+ other: largestFirst(scripts.other)
125
+ },
126
+ appConfigSections,
127
+ elements: lhr ? statistic("Total elements") : null,
128
+ domDepth: lhr ? statistic("DOM depth") : null,
129
+ harArtifact: match.harArtifact,
130
+ harEntry: match.document.index,
131
+ lighthouseArtifact: lhr ? lighthouseArtifact : null
132
+ };
133
+ }
134
+ const kib = (bytes) => `${Math.round(bytes / 1024)} KiB`;
135
+ const count = (value) => value.toLocaleString("en-US");
136
+ /** "297 KiB page data, 15 KiB app config, 9 KiB other inline scripts, 181 KiB markup", or "" when no scripts were archived. */
137
+ function htmlBreakdown(size) {
138
+ if (size.markupBytes === null) return "";
139
+ return [
140
+ `${kib(size.pageDataBytes ?? 0)} page data`,
141
+ `${kib(size.appConfigBytes ?? 0)} app config`,
142
+ `${kib(size.otherScriptBytes ?? 0)} other inline scripts`,
143
+ `${kib(size.markupBytes)} markup`
144
+ ].join(", ");
145
+ }
146
+ /** A measured value, marked "just over" when it would otherwise read the same as the budget it exceeds. */
147
+ const shown = (measure, value) => {
148
+ const text = measure.format(value);
149
+ return value > measure.budget && text === measure.format(measure.budget) ? `just over ${text}` : text;
150
+ };
151
+ const evidenceFor = (measure, size, value) => {
152
+ const source = measure.evidence(size, value) ?? {
153
+ artifact: size.harArtifact,
154
+ pointer: `/log/entries/${size.harEntry}/response/content/size`
155
+ };
156
+ return {
157
+ type: "playhouse",
158
+ page: size.page,
159
+ ...source,
160
+ value: `${measure.label} ${shown(measure, value)} (budget ${measure.format(measure.budget)})`
161
+ };
162
+ };
163
+ /**
164
+ * A rule script that checks one measurement of each page's HTML against a budget. It passes when every measured page is
165
+ * within budget. A page over budget fails the rule, or, when `decideFailures` is false, leaves it undecided with the
166
+ * measurements as data so the agent can trace the cause.
167
+ */
168
+ function documentSizeRule(measure) {
169
+ return async ({ pages, playhouse }) => {
170
+ const data = {
171
+ cell: REPRESENTATIVE_CELL,
172
+ budget: measure.budget,
173
+ pages: [],
174
+ skipped: []
175
+ };
176
+ const measured = [];
177
+ for (const page of pages) {
178
+ const size = await measurePage(playhouse, page.label);
179
+ if ("reason" in size) {
180
+ data.skipped.push({
181
+ page: page.label,
182
+ reason: size.reason
183
+ });
184
+ continue;
185
+ }
186
+ data.pages.push(size);
187
+ const value = measure.value(size);
188
+ if (value === null) data.skipped.push({
189
+ page: page.label,
190
+ reason: `No ${measure.label.toLowerCase()} measurement was archived.`
191
+ });
192
+ else measured.push({
193
+ size,
194
+ value
195
+ });
196
+ }
197
+ if (!measured.length) return { data };
198
+ const over = measured.filter((item) => item.value > measure.budget);
199
+ const list = (items) => items.map((item) => `${item.size.page} ${shown(measure, item.value)}`).join(", ");
200
+ if (!over.length) return {
201
+ data,
202
+ decision: {
203
+ status: "pass",
204
+ summary: `${measure.label} is within ${measure.format(measure.budget)} on every measured page (${list(measured)}).`,
205
+ evidence: measured.map((item) => evidenceFor(measure, item.size, item.value))
206
+ }
207
+ };
208
+ if (!measure.decideFailures) return { data };
209
+ return {
210
+ data,
211
+ decision: {
212
+ status: "fail",
213
+ summary: `${measure.label} is over ${measure.format(measure.budget)} on ${over.map((item) => {
214
+ const breakdown = htmlBreakdown(item.size);
215
+ return `${item.size.page} (${shown(measure, item.value)}${breakdown ? `: ${breakdown}` : ""})`;
216
+ }).join("; ")}.`,
217
+ evidence: over.map((item) => {
218
+ const evidence = evidenceFor(measure, item.size, item.value);
219
+ const breakdown = htmlBreakdown(item.size);
220
+ return breakdown ? {
221
+ ...evidence,
222
+ value: `${evidence.value}: ${breakdown}`
223
+ } : evidence;
224
+ })
225
+ }
226
+ };
227
+ };
228
+ }
229
+
230
+ //#endregion
231
+ export { measurePage as a, kib as i, documentSizeDataSchema as n, documentSizeRule as r, count as t };