Setting the file. One moment.
Path Template · Browser To API · browserbase/skills · Skills Docs
ContentsBack to the top of the page scripts/lib/path-template.mjs
scripts/lib/ path-template.mjs
JavaScript · 86 lines · 3 KB
9 const HEX_RE = / ^ [0-9a-f] {8,}$ / i ;
10 const B62_RE = / ^ [A-Za-z0-9] {8,}$ / ;
11 const INT_RE = / ^ \d +$ / ;
12
13 // Static-looking segments we never template even if they're numeric/hex
14 // (e.g. version markers like "v1", "v2", short slugs that are real path parts).
15 const STATIC_HINTS = / ^ (v \d +| api | graphql | rest | public | private | me | self) $ / i ;
16
17 export function classifySegment ( seg ) {
18 if ( ! seg) return { kind: 'static' };
19 if ( STATIC_HINTS . test (seg)) return { kind: 'static' };
20 if ( UUID_RE . test (seg)) return { kind: 'param' , name: 'id' , schema: { type: 'string' , format: 'uuid' } };
21 if ( INT_RE . test (seg)) return { kind: 'param' , name: 'id' , schema: { type: 'integer' } };
22 if ( HEX_RE . test (seg)) return { kind: 'param' , name: 'id' , schema: { type: 'string' } };
23 if ( B62_RE . test (seg) && / [A-Z] / . test (seg) && / [a-z] / . test (seg) && / \d / . test (seg)) {
24 return { kind: 'param' , name: 'id' , schema: { type: 'string' } };
25 }
26 return { kind: 'static' };
27 }
28
29 // Single-pass templating used during the first sweep — segments are evaluated
30 // independently. Returns { template, params: [{name, schema, position}] }.
31 export function templatize ( rawPath ) {
32 const segs = rawPath. split ( '/' );
33 const params = [];
34 let counter = 0 ;
35 const out = segs. map (( seg , i ) => {
36 if ( ! seg && i > 0 ) return seg;
37 const c = classifySegment (seg);
38 if (c.kind === 'static' ) return seg;
39 counter ++ ;
40 const name = counter === 1 ? c.name : `${ c . name }${ counter }` ;
41 params. push ({ name, schema: c.schema, position: i });
42 return `{${ name }}` ;
43 });
44 return { template: out. join ( '/' ), params };
45 }
46
47 // Second pass: given a set of paths that share the same number of segments
48 // and the same statics in the obvious positions, detect "slug" segments —
49 // positions that are alpha and *vary* across samples but didn't trip the
50 // numeric/UUID/hex classifiers in pass 1. Returns the same shape as templatize.
51 export function templatizeWithSlugs ( paths ) {
52 if (paths. length < 2 ) return templatize (paths[ 0 ]);
53 const split = paths. map ( p => p. split ( '/' ));
54 const len = split[ 0 ]. length ;
55 if ( ! split. every ( s => s. length === len)) return templatize (paths[ 0 ]);
56
57 const params = [];
58 let counter = 0 ;
59 const tpl = [];
60 for ( let i = 0 ; i < len; i ++ ) {
61 const colSamples = split. map ( s => s[i]);
62 const first = colSamples[ 0 ];
63 if ( ! first && i > 0 ) { tpl. push ( '' ); continue ; }
64
65 const c0 = classifySegment (first);
66 if (c0.kind === 'param' ) {
67 counter ++ ;
68 const name = counter === 1 ? c0.name : `${ c0 . name }${ counter }` ;
69 params. push ({ name, schema: c0.schema, position: i });
70 tpl. push ( `{${ name }}` );
71 continue ;
72 }
73
74 const distinct = new Set (colSamples);
75 if (distinct.size > 1 && colSamples. every ( s => / ^ [A-Za-z0-9_-] +$ / . test (s))) {
76 counter ++ ;
77 const name = counter === 1 ? 'slug' : `slug${ counter }` ;
78 params. push ({ name, schema: { type: 'string' }, position: i });
79 tpl. push ( `{${ name }}` );
80 continue ;
81 }
82
83 tpl. push (first);
84 }
85 return { template: tpl. join ( '/' ), params };
86 }