From 4f598225b57bf950547b470797d1a96a233199d5 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sat, 20 Jun 2026 10:31:22 +1000 Subject: [PATCH] fix: reuse path parameters for repeated templates --- tests/templates.test.ts | 29 +++++++++++++++++++++++++++++ worker/lib/templates.ts | 7 ++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/tests/templates.test.ts b/tests/templates.test.ts index 85bfb72..44781ad 100644 --- a/tests/templates.test.ts +++ b/tests/templates.test.ts @@ -34,6 +34,35 @@ describe('resolveTemplateUrl', () => { ).toBe('https://en.wikipedia.org/wiki/Django'); }); + it('reuses pathParam for repeated occurrences of the first template variable', () => { + expect( + resolveTemplateUrl({ + targetUrl: 'https://example.com/{query}/{query}', + pathParam: 'foo bar', + query: params(), + }), + ).toBe('https://example.com/foo%20bar/foo%20bar'); + }); + + it('lets matching query params override pathParam for all repeated occurrences', () => { + expect( + resolveTemplateUrl({ + targetUrl: 'https://example.com/{query}/{query}', + pathParam: 'from-path', + query: params('query=from query'), + }), + ).toBe('https://example.com/from%20query/from%20query'); + }); + + it('reuses a default value for repeated occurrences of the same template variable', () => { + expect( + resolveTemplateUrl({ + targetUrl: 'https://example.com/{query,default=hello world}/{query,default=hello world}', + query: params(), + }), + ).toBe('https://example.com/hello%20world/hello%20world'); + }); + it('lets matching query params override pathParam and defaults', () => { expect( resolveTemplateUrl({ diff --git a/worker/lib/templates.ts b/worker/lib/templates.ts index 981af87..188e011 100644 --- a/worker/lib/templates.ts +++ b/worker/lib/templates.ts @@ -38,12 +38,13 @@ export function resolveTemplateUrl(input: TemplateInput): string { } let parameterIndex = 0; + const firstParameterName = parameters[0].name; return input.targetUrl.replace(TEMPLATE_PATTERN, () => { const parameter = parameters[parameterIndex]; parameterIndex += 1; - const value = resolveParameterValue(parameter, input); + const value = resolveParameterValue(parameter, input, firstParameterName); return encodeURIComponent(value); }); } @@ -82,13 +83,13 @@ function unquoteDefault(value: string): string { return value; } -function resolveParameterValue(parameter: TemplateParameter, input: TemplateInput): string { +function resolveParameterValue(parameter: TemplateParameter, input: TemplateInput, firstParameterName: string): string { const queryValue = input.query.get(parameter.name); if (queryValue !== null) { return queryValue; } - if (parameter.index === 0 && input.pathParam !== undefined) { + if (parameter.name === firstParameterName && input.pathParam !== undefined) { return input.pathParam; }