@salesforce/storefront-next-dev 1.0.1 → 1.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cartridge-services/index.d.ts.map +1 -1
- package/dist/cartridge-services/index.js +4 -0
- package/dist/cartridge-services/index.js.map +1 -1
- package/dist/commands/config/aggregate-extensions.js +279 -0
- package/dist/commands/create-storefront.js +37 -6
- package/dist/commands/locales/aggregate-extensions.js +3 -2
- package/dist/commands/push.js +2 -0
- package/dist/constants.js +29 -0
- package/dist/dependency-utils.js +56 -4
- package/dist/entry/server.d.ts +1 -1
- package/dist/entry/server.js +68 -23
- package/dist/entry/server.js.map +1 -1
- package/dist/generate-cartridge.js +4 -0
- package/dist/index.d.ts +58 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -123
- package/dist/index.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/manage-extensions.js +1 -1
- package/dist/mrt/ssr.mjs +53 -53
- package/dist/mrt/ssr.mjs.map +1 -1
- package/dist/mrt/streamingHandler.mjs +59 -59
- package/dist/mrt/streamingHandler.mjs.map +1 -1
- package/dist/server.js +79 -28
- package/package.json +13 -11
package/dist/server.js
CHANGED
|
@@ -12,12 +12,12 @@ import compression from "compression";
|
|
|
12
12
|
import zlib from "node:zlib";
|
|
13
13
|
import morgan from "morgan";
|
|
14
14
|
import { minimatch } from "minimatch";
|
|
15
|
-
import { SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
15
|
+
import { ROOT_CONTEXT, SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
16
|
+
import { ExportResultCode, W3CTraceContextPropagator, hrTimeToTimeStamp, parseTraceParent } from "@opentelemetry/core";
|
|
17
|
+
import { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_SERVICE_NAME, ATTR_URL_PATH } from "@opentelemetry/semantic-conventions";
|
|
16
18
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
17
|
-
import { ConsoleSpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
18
|
-
import { ExportResultCode, hrTimeToTimeStamp } from "@opentelemetry/core";
|
|
19
|
+
import { AlwaysOnSampler, ConsoleSpanExporter, ParentBasedSampler, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
19
20
|
import { Resource } from "@opentelemetry/resources";
|
|
20
|
-
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
21
21
|
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
22
22
|
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
|
|
23
23
|
|
|
@@ -403,6 +403,43 @@ var MrtConsoleSpanExporter = class extends ConsoleSpanExporter {
|
|
|
403
403
|
|
|
404
404
|
//#endregion
|
|
405
405
|
//#region src/otel/setup.ts
|
|
406
|
+
/**
|
|
407
|
+
* A privately-held W3C Trace Context propagator, used to inject `traceparent` onto
|
|
408
|
+
* outbound fetches WITHOUT going through OpenTelemetry's global propagator registry.
|
|
409
|
+
*
|
|
410
|
+
* ## Why not the global propagator?
|
|
411
|
+
*
|
|
412
|
+
* `UndiciInstrumentation` injects outbound headers by calling the *global* registered
|
|
413
|
+
* propagator (`propagation.inject()`). Registering one with `setGlobalPropagator()`
|
|
414
|
+
* works locally, but is a silent no-op on Managed Runtime (MRT):
|
|
415
|
+
*
|
|
416
|
+
* - The MRT Lambda wrapper bundles its own copy of `@opentelemetry/api` (declared
|
|
417
|
+
* `^1.9.1`, with core/sdk at 2.x) and boots first, creating the version-keyed
|
|
418
|
+
* global registry object (`globalThis[Symbol.for('opentelemetry.js.api.1')]`,
|
|
419
|
+
* stamped `version: "1.9.1"`).
|
|
420
|
+
* - Our SDK bundle ships a *different* copy of `@opentelemetry/api` (`1.9.0`). When
|
|
421
|
+
* our code calls `setGlobalPropagator()`, OTel's `registerGlobal()` compares the
|
|
422
|
+
* registry's recorded version against ours with an EXACT string match — `"1.9.1"
|
|
423
|
+
* !== "1.9.0"` — refuses the registration, and reports the error only to OTel's
|
|
424
|
+
* internal diag channel (which we do not wire up). The propagation slot stays
|
|
425
|
+
* empty, so undici's `propagation.inject()` injects nothing and no `traceparent`
|
|
426
|
+
* reaches SCAPI. Proven on soak: `propagation.fields() === []`, 0/198 outbound
|
|
427
|
+
* requests carried a header.
|
|
428
|
+
*
|
|
429
|
+
* This is the same multi-instance split that makes inbound `propagation.extract()`
|
|
430
|
+
* fail (see `express/middleware.ts`, which bypasses it with `parseTraceParent` +
|
|
431
|
+
* `trace.setSpanContext`). MRT's own data-plane tracer solves it the same way: it
|
|
432
|
+
* holds a private `new W3CTraceContextPropagator()` and calls `inject()`/`extract()`
|
|
433
|
+
* on it directly, never touching the global registry.
|
|
434
|
+
*
|
|
435
|
+
* Holding our own instance and injecting it ourselves (in the undici `requestHook`)
|
|
436
|
+
* makes outbound propagation deterministic and independent of MRT's bundle, its OTel
|
|
437
|
+
* version, and its boot order. We deliberately do NOT call `setGlobalPropagator()` —
|
|
438
|
+
* leaving the global propagator as the default no-op guarantees undici's own
|
|
439
|
+
* `propagation.inject()` (which runs right after our hook) adds nothing, so the
|
|
440
|
+
* `traceparent` is written exactly once in every environment.
|
|
441
|
+
*/
|
|
442
|
+
const outboundPropagator = new W3CTraceContextPropagator();
|
|
406
443
|
const SERVICE_NAME = "storefront-next";
|
|
407
444
|
/**
|
|
408
445
|
* Initializes OpenTelemetry and returns a Tracer from the provider directly.
|
|
@@ -425,18 +462,25 @@ const UNDICI_REGISTERED_KEY = Symbol.for("sfnext.otel.undici_registered");
|
|
|
425
462
|
function initTelemetry() {
|
|
426
463
|
if (cachedTracer) return cachedTracer;
|
|
427
464
|
try {
|
|
428
|
-
const provider = new NodeTracerProvider({
|
|
465
|
+
const provider = new NodeTracerProvider({
|
|
466
|
+
resource: new Resource({ [ATTR_SERVICE_NAME]: SERVICE_NAME }),
|
|
467
|
+
sampler: new ParentBasedSampler({ root: new AlwaysOnSampler() })
|
|
468
|
+
});
|
|
429
469
|
provider.addSpanProcessor(new SimpleSpanProcessor(new MrtConsoleSpanExporter()));
|
|
430
|
-
provider.register();
|
|
470
|
+
provider.register({ propagator: null });
|
|
431
471
|
if (!globalThis[UNDICI_REGISTERED_KEY]) {
|
|
432
472
|
globalThis[UNDICI_REGISTERED_KEY] = true;
|
|
433
473
|
registerInstrumentations({
|
|
434
474
|
tracerProvider: provider,
|
|
435
475
|
instrumentations: [new UndiciInstrumentation({ requestHook(span, request) {
|
|
436
476
|
try {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
477
|
+
span.updateName("sfnext.fetch");
|
|
478
|
+
} catch {}
|
|
479
|
+
try {
|
|
480
|
+
const ctx = trace.setSpan(context.active(), span);
|
|
481
|
+
outboundPropagator.inject(ctx, request, { set(req, key, value) {
|
|
482
|
+
req.addHeader?.(key, value);
|
|
483
|
+
} });
|
|
440
484
|
} catch {}
|
|
441
485
|
} })]
|
|
442
486
|
});
|
|
@@ -459,43 +503,50 @@ function createOtelExpressMiddleware() {
|
|
|
459
503
|
try {
|
|
460
504
|
const url = new URL(req.originalUrl || req.url, "http://localhost").pathname;
|
|
461
505
|
const method = req.method;
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
506
|
+
const traceparent = req.headers.traceparent;
|
|
507
|
+
const inboundSpanContext = typeof traceparent === "string" ? parseTraceParent(traceparent) : null;
|
|
508
|
+
const parentContext = inboundSpanContext ? trace.setSpanContext(ROOT_CONTEXT, {
|
|
509
|
+
...inboundSpanContext,
|
|
510
|
+
isRemote: true
|
|
511
|
+
}) : ROOT_CONTEXT;
|
|
512
|
+
tracer.startActiveSpan("sfnext.request", {
|
|
513
|
+
kind: SpanKind.SERVER,
|
|
514
|
+
attributes: {
|
|
515
|
+
[ATTR_HTTP_REQUEST_METHOD]: method,
|
|
516
|
+
[ATTR_URL_PATH]: url
|
|
517
|
+
}
|
|
518
|
+
}, parentContext, (serverSpan) => {
|
|
466
519
|
try {
|
|
467
520
|
const spanContext = trace.getSpan(context.active())?.spanContext();
|
|
468
521
|
if (spanContext) {
|
|
469
522
|
const flags = spanContext.traceFlags.toString(16).padStart(2, "0");
|
|
470
|
-
const
|
|
471
|
-
res.setHeader("traceparent",
|
|
523
|
+
const responseTraceparent = `00-${spanContext.traceId}-${spanContext.spanId}-${flags}`;
|
|
524
|
+
res.setHeader("traceparent", responseTraceparent);
|
|
472
525
|
}
|
|
473
526
|
} catch {}
|
|
474
527
|
const serverCtx = context.active();
|
|
475
528
|
const startTime = performance.now();
|
|
476
529
|
let streamingSpan = null;
|
|
477
|
-
let
|
|
530
|
+
let firstByteMs = 0;
|
|
478
531
|
let ended = false;
|
|
479
|
-
function
|
|
532
|
+
function startStreamingSpan() {
|
|
480
533
|
if (streamingSpan) return;
|
|
481
534
|
try {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
"url.path": url,
|
|
487
|
-
"sfnext.ttfb_ms": ttfbMs
|
|
535
|
+
firstByteMs = Math.round(performance.now() - startTime);
|
|
536
|
+
streamingSpan = tracer.startSpan("sfnext.response_streaming", { attributes: {
|
|
537
|
+
[ATTR_HTTP_REQUEST_METHOD]: method,
|
|
538
|
+
[ATTR_URL_PATH]: url
|
|
488
539
|
} }, serverCtx);
|
|
489
540
|
} catch {}
|
|
490
541
|
}
|
|
491
542
|
const origWriteHead = res.writeHead.bind(res);
|
|
492
543
|
res.writeHead = ((...args) => {
|
|
493
|
-
|
|
544
|
+
startStreamingSpan();
|
|
494
545
|
return origWriteHead(...args);
|
|
495
546
|
});
|
|
496
547
|
const origWrite = res.write.bind(res);
|
|
497
548
|
res.write = ((...args) => {
|
|
498
|
-
|
|
549
|
+
startStreamingSpan();
|
|
499
550
|
return origWrite(...args);
|
|
500
551
|
});
|
|
501
552
|
function endSpans() {
|
|
@@ -505,12 +556,12 @@ function createOtelExpressMiddleware() {
|
|
|
505
556
|
const totalMs = Math.round(performance.now() - startTime);
|
|
506
557
|
const statusCode = res.statusCode;
|
|
507
558
|
if (streamingSpan) {
|
|
508
|
-
streamingSpan.setAttribute("http.streaming_duration_ms", totalMs -
|
|
509
|
-
streamingSpan.setAttribute(
|
|
559
|
+
streamingSpan.setAttribute("http.streaming_duration_ms", totalMs - firstByteMs);
|
|
560
|
+
streamingSpan.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, statusCode);
|
|
510
561
|
if (statusCode >= 500) streamingSpan.setStatus({ code: SpanStatusCode.ERROR });
|
|
511
562
|
streamingSpan.end();
|
|
512
563
|
}
|
|
513
|
-
serverSpan.setAttribute(
|
|
564
|
+
serverSpan.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, statusCode);
|
|
514
565
|
serverSpan.setAttribute("http.total_duration_ms", totalMs);
|
|
515
566
|
if (statusCode >= 500) serverSpan.setStatus({ code: SpanStatusCode.ERROR });
|
|
516
567
|
serverSpan.end();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/storefront-next-dev",
|
|
3
|
-
"version": "1.0.1",
|
|
3
|
+
"version": "1.1.0-alpha.1",
|
|
4
4
|
"description": "Dev and build tools for SFCC Storefront Next",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -110,15 +110,16 @@
|
|
|
110
110
|
"license": "Apache-2.0",
|
|
111
111
|
"peerDependencies": {
|
|
112
112
|
"@react-router/dev": ">=7.0.0",
|
|
113
|
+
"@salesforce/storefront-next-runtime": "^1.1.0-alpha.0",
|
|
113
114
|
"react": ">=19.0.0",
|
|
114
115
|
"react-dom": ">=19.0.0",
|
|
115
116
|
"react-router": ">=7.0.0",
|
|
116
|
-
"vite": ">=7.0.0"
|
|
117
|
-
"@salesforce/storefront-next-runtime": "1.0.1"
|
|
117
|
+
"vite": ">=7.0.0"
|
|
118
118
|
},
|
|
119
119
|
"devDependencies": {
|
|
120
120
|
"@oclif/test": "^4.1.15",
|
|
121
|
-
"@react-router/dev": "7.
|
|
121
|
+
"@react-router/dev": "7.18.0",
|
|
122
|
+
"@react-router/node": "7.18.0",
|
|
122
123
|
"@serverless/event-mocks": "1.1.1",
|
|
123
124
|
"@types/archiver": "^6.0.2",
|
|
124
125
|
"@types/aws-lambda": "8.10.159",
|
|
@@ -139,19 +140,19 @@
|
|
|
139
140
|
"@types/react": "19.1.13",
|
|
140
141
|
"@types/react-dom": "19.1.9",
|
|
141
142
|
"@types/semver": "7.7.1",
|
|
142
|
-
"@vitest/coverage-v8": "4.
|
|
143
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
143
144
|
"fs-extra": "11.3.2",
|
|
144
145
|
"jest": "^29.7.0",
|
|
145
146
|
"memfs": "^4.42.0",
|
|
146
147
|
"react": "19.2.3",
|
|
147
148
|
"react-dom": "19.2.3",
|
|
148
|
-
"react-router": "7.
|
|
149
|
+
"react-router": "7.18.0",
|
|
149
150
|
"shx": "^0.4.0",
|
|
150
151
|
"ts-jest": "29.4.1",
|
|
151
152
|
"tsdown": "^0.15.4",
|
|
152
153
|
"typescript": "^5.6.0",
|
|
153
|
-
"vite": "7.
|
|
154
|
-
"vitest": "4.
|
|
154
|
+
"vite": "7.3.5",
|
|
155
|
+
"vitest": "4.1.8"
|
|
155
156
|
},
|
|
156
157
|
"dependencies": {
|
|
157
158
|
"@babel/generator": "7.28.5",
|
|
@@ -172,8 +173,8 @@
|
|
|
172
173
|
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
|
173
174
|
"@opentelemetry/sdk-trace-node": "^1.30.0",
|
|
174
175
|
"@opentelemetry/semantic-conventions": "^1.28.0",
|
|
175
|
-
"@react-router/express": "7.
|
|
176
|
-
"@salesforce/b2c-tooling-sdk": "^1.
|
|
176
|
+
"@react-router/express": "7.18.0",
|
|
177
|
+
"@salesforce/b2c-tooling-sdk": "^1.15.0",
|
|
177
178
|
"archiver": "^7.0.1",
|
|
178
179
|
"babel-dead-code-elimination": "1.0.12",
|
|
179
180
|
"chalk": "^5.3.0",
|
|
@@ -191,11 +192,12 @@
|
|
|
191
192
|
"negotiator": "1.0.0",
|
|
192
193
|
"npm-run-path": "6.0.0",
|
|
193
194
|
"openapi-typescript": "7.10.1",
|
|
195
|
+
"prettier": "3.6.2",
|
|
194
196
|
"prompts": "2.4.2",
|
|
195
197
|
"ts-morph": "^24.0.0",
|
|
196
198
|
"tsdown": "^0.15.4",
|
|
197
199
|
"tsx": "^4.20.5",
|
|
198
|
-
"undici": "
|
|
200
|
+
"undici": "7.28.0",
|
|
199
201
|
"yaml": "^2.8.2",
|
|
200
202
|
"zod": "4.1.13"
|
|
201
203
|
},
|