Skip to content

feat: w3c field - #201

Open
maxday wants to merge 5 commits into
nodejs24.xfrom
maxday/w3c
Open

maxday wants to merge 5 commits into
nodejs24.xfrom
maxday/w3c

Conversation

@maxday

@maxday maxday commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Adds context.w3c() which exposes allowlisted W3C trace-context fields (traceparent, tracestate, baggage) carried on clientContext.w3c, and strips w3c from clientContext so callers can only read trace fields through the helper. Covered by unit tests plus a new dockerized end-to-end workflow against public.ecr.aws/lambda/nodejs:24.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Exposes W3C trace-context fields (traceparent, tracestate, baggage) carried on clientContext.w3c via a new context.w3c() helper. Fields are allowlisted; the source clientContext.w3c is removed after construction so callers only see the helper.
@maxday maxday changed the title Maxday/w3c feat: w3c field Oct 1, 2026
--build-arg BASE_IMAGE=public.ecr.aws/lambda/nodejs:24

- name: Run dockerized suites
uses: aws/containerized-test-runner-for-aws-lambda@76eacfb110903739d9c5f7fcdaafede6324a1473 # maxday/client-context

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will change to change when aws/containerized-test-runner-for-aws-lambda#32 will be reviewed and merged

@maxday
maxday marked this pull request as ready for review October 1, 2026 12:18
},
w3c: function (): Record<string, string> {
return { ...w3cFields };
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the intention behind returning a copy from a function here? I can think of two:

    1. Keeping w3c out of logs. Functions are skipped by JSON.stringify(context), so that works, but it's an implicit side effect. If this is the goal, I'd make it explicit with a non-enumerable property, which hides it from JSON.stringify and console.log while keeping it readable:
 Object.defineProperty(context, "w3c", {
   value: w3cFields,
   enumerable: false,
   writable: false,
 });
    1. Preventing handlers from mutating the fields. { ...w3cFields } creates a new object on every call, while identity and clientContext on the same object stay mutable, so the protection is inconsistent. I think the direction should be making the whole context immutable rather than protectingthe full context is a breaking change (handlers and middleware write to it today), sothat's probably a separate discussion. For this PR I'd return a frozen object instead of a copy, so w3c is consistent with where we want to go:
// extractAndStripW3c
 return Object.freeze(fields);

//  With the object frozen, the function isn't needed anymore either; a plain property is srantees:

 readonly w3c: Readonly<Record<string, string>>;

(non-enumerable as above if we want it out of logs). Since this changes context.w3c() to settle before this ships.

private static extractAndStripW3c(
clientContext: Record<string, unknown> | undefined,
): Record<string, string> {
if (!clientContext || typeof clientContext !== "object") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe in another PR but this check should be guaranteed by parseJsonHeader.

if (!clientContext || typeof clientContext !== "object") {
return {};
}
if (!("w3c" in clientContext)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: merge the condition with above.

Comment on lines +85 to +93
const source = rawW3c as Record<string, unknown>;
const fields: Record<string, string> = {};
for (const key of W3C_ALLOWED_FIELDS) {
const value = source[key];
if (typeof value === "string") {
fields[key] = value;
}
}
return fields;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The W3C standard tells you that every implementor should validate those fields before sharing it.

This include existent validation. For example if TraceParent is not present you should not TraceState.

They also should be validated through a RegEXP.

I did it in my solution. I can show you how it is done.

Comment thread Dockerfile.js

# Copy bare config
COPY package.json tsconfig.json eslint.config.js vitest.config.js vitest.setup.ts /app/
COPY package.json package-lock.json tsconfig.json eslint.config.js vitest.config.js vitest.setup.ts /app/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice find! Very important 🚀

Comment thread Dockerfile.js
WORKDIR /app

RUN npm install --ignore-scripts
RUN npm ci --ignore-scripts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: we should move to pnpm.

getRemainingTimeInMillis: function () {
return deadline - Date.now();
},
w3c: function (): Record<string, string> {

@darklight3it darklight3it Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another important thing is that. traceparent and tracestate belong to the same group and they are dependant on each other. While baggage is completely indipendent. We should not group them under the the same object. They can exist separately.

@darklight3it darklight3it Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the best shape would be to typer both. I have examples also for that that i can show.

Comment thread src/context/constants.ts
"baggage",
] as const;

export type W3CFieldName = (typeof W3C_ALLOWED_FIELDS)[number];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used?

@@ -0,0 +1,149 @@
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: lots of cases here. Are we sure to create such a long test for an integration test that is already covered by unit test?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants