From f9a72fb16c1737b8d4f2ffa7994ba0d8e6f27514 Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Fri, 18 Sep 2026 01:38:02 -0400 Subject: [PATCH 1/3] fix(api): resolve samlify's SamlLib under Node's ESM loader samlify is CommonJS and re-exports SamlLib through a getter that cjs-module-lexer does not detect, so `import * as samlify` leaves it undefined in the compiled build and every signed-in SSO attempt 500'd with "Cannot read properties of undefined (reading 'replaceTagsByValue')". vitest's interop exposes it as a named export, which is why the suite never saw it. Take module.exports (the `default` view) when it carries SamlLib. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ --- apps/api/src/saml/config.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/api/src/saml/config.ts b/apps/api/src/saml/config.ts index 84aee48..1019458 100644 --- a/apps/api/src/saml/config.ts +++ b/apps/api/src/saml/config.ts @@ -10,7 +10,18 @@ * NameID + attribute statement template that produces the assertion shape * required by specs/api/saml.md. */ -import * as samlify from 'samlify'; +import * as samlifyNs from 'samlify'; + +// samlify is CommonJS. Under Node's ESM loader, cjs-module-lexer does not +// detect `SamlLib` (it is re-exported through a getter), so `import * as` +// leaves it undefined and only `default` (the whole module.exports) carries +// it. vitest's interop exposes it as a named export, which is why the tests +// never saw the production 500. Resolve from whichever view has it. +type SamlifyModule = typeof samlifyNs; +const samlify: SamlifyModule = + (samlifyNs as SamlifyModule & { default?: SamlifyModule }).default?.SamlLib !== undefined + ? (samlifyNs as SamlifyModule & { default: SamlifyModule }).default + : samlifyNs; const { IdentityProvider, ServiceProvider, Constants, SamlLib, setSchemaValidator } = samlify; From 57ee3ea93b47d00ba82443f0187ae91fc4f1f706 Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Fri, 18 Sep 2026 01:38:02 -0400 Subject: [PATCH 2/3] chore(plans): add samlify-esm-interop Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ --- plans/samlify-esm-interop.md | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 plans/samlify-esm-interop.md diff --git a/plans/samlify-esm-interop.md b/plans/samlify-esm-interop.md new file mode 100644 index 0000000..2aa9f87 --- /dev/null +++ b/plans/samlify-esm-interop.md @@ -0,0 +1,45 @@ +--- +status: done +depends: [saml-login-return-path] +specs: + - specs/api/saml.md +issues: [] +pr: null +--- + +# Plan: samlify ESM/CJS interop in the production build + +## Scope + +The first signed-in Slack SSO attempt against the live site 500'd: +`Cannot read properties of undefined (reading 'replaceTagsByValue')` in +`dist/saml/config.js`. samlify is CommonJS; under Node's ESM loader, +cjs-module-lexer does not detect `SamlLib` (re-exported through a getter), +so `import * as samlify` leaves it undefined and only `default` +(module.exports) carries it. vitest's interop exposes it as a named export, +which is why all 19 SAML tests passed while production failed. + +In: resolve the module from whichever view carries `SamlLib`; verify the +compiled output under plain Node. Out: replacing samlify. + +## Implements + +- [api/saml.md](../specs/api/saml.md) — no behaviour change; the assertion + pipeline works as specified once the library resolves. + +## Approach + +`apps/api/src/saml/config.ts`: pick `samlifyNs.default` when it carries +`SamlLib`, else the namespace. Verified with +`node -e "import('./dist/saml/config.js')"` after `npm run build`. + +## Validation + +- `npm run type-check && npm run lint`; SAML suite 19/19. +- Post-deploy: Slack "Test configuration" completes for a signed-in user. + +## Follow-ups + +- Tracked as: a build-output smoke test (import the compiled API under plain + Node in CI) would have caught this before release — worth adding to + `ci.yml` after `npm run build`. From d7f4e410e74081ddc9881b004d525880a147e22a Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Fri, 18 Sep 2026 01:38:16 -0400 Subject: [PATCH 3/3] chore(plans): mark samlify-esm-interop done (PR #183) Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ --- plans/samlify-esm-interop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plans/samlify-esm-interop.md b/plans/samlify-esm-interop.md index 2aa9f87..34902dc 100644 --- a/plans/samlify-esm-interop.md +++ b/plans/samlify-esm-interop.md @@ -4,7 +4,7 @@ depends: [saml-login-return-path] specs: - specs/api/saml.md issues: [] -pr: null +pr: 183 --- # Plan: samlify ESM/CJS interop in the production build