Skip to content

Commit 1e040e9

Browse files
committed
Add method to determine if a URI is a template or not
1 parent dc77d9c commit 1e040e9

File tree

2 files changed

+41
-5
lines changed

2 files changed

+41
-5
lines changed

src/shared/uriTemplate.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
import { UriTemplate } from "./uriTemplate.js";
22

33
describe("UriTemplate", () => {
4+
describe("isTemplate", () => {
5+
it("should return true for strings containing template expressions", () => {
6+
expect(UriTemplate.isTemplate("{foo}")).toBe(true);
7+
expect(UriTemplate.isTemplate("/users/{id}")).toBe(true);
8+
expect(UriTemplate.isTemplate("http://example.com/{path}/{file}")).toBe(true);
9+
expect(UriTemplate.isTemplate("/search{?q,limit}")).toBe(true);
10+
});
11+
12+
it("should return false for strings without template expressions", () => {
13+
expect(UriTemplate.isTemplate("")).toBe(false);
14+
expect(UriTemplate.isTemplate("plain string")).toBe(false);
15+
expect(UriTemplate.isTemplate("http://example.com/foo/bar")).toBe(false);
16+
expect(UriTemplate.isTemplate("{}")).toBe(false); // Empty braces don't count
17+
expect(UriTemplate.isTemplate("{ }")).toBe(false); // Just whitespace doesn't count
18+
});
19+
});
20+
421
describe("simple string expansion", () => {
522
it("should expand simple string variables", () => {
623
const template = new UriTemplate("http://example.com/users/{username}");

src/shared/uriTemplate.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
// Claude-authored implementation of RFC 6570 URI Templates
22

3-
type Variables = Record<string, string | string[]>;
3+
export type Variables = Record<string, string | string[]>;
44

55
const MAX_TEMPLATE_LENGTH = 1000000; // 1MB
66
const MAX_VARIABLE_LENGTH = 1000000; // 1MB
77
const MAX_TEMPLATE_EXPRESSIONS = 10000;
88
const MAX_REGEX_LENGTH = 1000000; // 1MB
99

1010
export class UriTemplate {
11-
private static validateLength(str: string, max: number, context: string): void {
11+
/**
12+
* Returns true if the given string contains any URI template expressions.
13+
* A template expression is a sequence of characters enclosed in curly braces,
14+
* like {foo} or {?bar}.
15+
*/
16+
static isTemplate(str: string): boolean {
17+
// Look for any sequence of characters between curly braces
18+
// that isn't just whitespace
19+
return /\{[^}\s]+\}/.test(str);
20+
}
21+
22+
private static validateLength(
23+
str: string,
24+
max: number,
25+
context: string,
26+
): void {
1227
if (str.length > max) {
1328
throw new Error(
1429
`${context} exceeds maximum length of ${max} characters (got ${str.length})`,
@@ -60,7 +75,7 @@ export class UriTemplate {
6075
const exploded = expr.includes("*");
6176
const names = this.getNames(expr);
6277
const name = names[0];
63-
78+
6479
// Validate variable name length
6580
for (const name of names) {
6681
UriTemplate.validateLength(
@@ -263,7 +278,11 @@ export class UriTemplate {
263278
}
264279

265280
pattern += "$";
266-
UriTemplate.validateLength(pattern, MAX_REGEX_LENGTH, "Generated regex pattern");
281+
UriTemplate.validateLength(
282+
pattern,
283+
MAX_REGEX_LENGTH,
284+
"Generated regex pattern",
285+
);
267286
const regex = new RegExp(pattern);
268287
const match = uri.match(regex);
269288

@@ -284,4 +303,4 @@ export class UriTemplate {
284303

285304
return result;
286305
}
287-
}
306+
}

0 commit comments

Comments
 (0)