Skip to content

Commit 77c59f5

Browse files
committed
pkg: Update path-to-regexp to v8
1 parent ec23566 commit 77c59f5

File tree

10 files changed

+84
-55
lines changed

10 files changed

+84
-55
lines changed

.changeset/moody-dots-drum.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@data-client/rest': minor
3+
---
4+
5+
RestEndpoint.path and Resource.path syntax updated
6+
7+
Upgrading path-to-regexp from 6 to 8.
8+
- https://github.com/pillarjs/path-to-regexp/releases/tag/v8.0.0
9+
- https://github.com/pillarjs/path-to-regexp/releases/tag/v7.0.0
10+
11+
BREAKING CHANGES:
12+
- /:optional? -> {/:optional}
13+
- /:repeating+ -> /*repeating
14+
- /:repeating* -> {/*repeating}
15+
- `(`, `)`, `[`, `]` must be escaped `"\\("`
16+
- `()[]{}*:;,!@` are all characters that need escaping
17+
- /:with-dash -> /:"with-dash"

__tests__/new.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { Temporal } from '@js-temporal/polyfill';
2+
import React, { createContext, useContext } from 'react';
3+
14
import {
25
schema,
36
Endpoint,
@@ -12,8 +15,6 @@ import {
1215
Resource,
1316
ResourceOptions,
1417
} from '@data-client/rest';
15-
import { Temporal } from '@js-temporal/polyfill';
16-
import React, { createContext, useContext } from 'react';
1718

1819
/** Represents data with primary key being from 'id' field. */
1920
export class IDEntity extends Entity {
@@ -358,14 +359,14 @@ const CoolerArticleResourceBase = createArticleResource({
358359
export const CoolerArticleResource = {
359360
...CoolerArticleResourceBase,
360361
get: CoolerArticleResourceBase.get.extend({
361-
path: '/:id?/:title?',
362+
path: '{/:id}{/:title}',
362363
}),
363364
};
364365
export const OptimisticArticleResource = createArticleResource({
365366
schema: CoolerArticle,
366367
urlRoot: 'article-cooler',
367368
optimistic: true,
368-
}).extend('get', { path: '/:id?/:title?' });
369+
}).extend('get', { path: '{/:id}{/:title}' });
369370

370371
const CoolerArticleResourceFromMixinBase = createArticleResource({
371372
schema: ArticleFromMixin,
@@ -374,7 +375,7 @@ const CoolerArticleResourceFromMixinBase = createArticleResource({
374375
export const CoolerArticleResourceFromMixin = {
375376
...CoolerArticleResourceFromMixinBase,
376377
get: CoolerArticleResourceFromMixinBase.get.extend({
377-
path: '/:id?/:title?',
378+
path: '{/:id}{/:title}',
378379
}),
379380
};
380381

packages/rest/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
"dependencies": {
133133
"@babel/runtime": "^7.17.0",
134134
"@data-client/endpoint": "^0.14.12",
135-
"path-to-regexp": "^6.3.0"
135+
"path-to-regexp": "^8.1.0"
136136
},
137137
"devDependencies": {
138138
"@anansi/browserslist-config": "^1.4.2",

packages/rest/src/RestHelpers.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,38 @@
1-
import { compile, PathFunction, parse } from 'path-to-regexp';
1+
import { compile, PathFunction, parse, Token, ParamData } from 'path-to-regexp';
22

33
import { ShortenPath } from './pathTypes.js';
44

55
const urlBaseCache: Record<string, PathFunction<object>> = Object.create(null);
6-
export function getUrlBase(path: string): PathFunction {
6+
export function getUrlBase(path: string): PathFunction<ParamData> {
77
if (!(path in urlBaseCache)) {
8-
urlBaseCache[path] = compile(path, {
9-
encode: encodeURIComponent,
10-
validate: false,
11-
});
8+
urlBaseCache[path] = compile(path);
129
}
1310
return urlBaseCache[path];
1411
}
1512

1613
const urlTokensCache: Record<string, Set<string>> = Object.create(null);
1714
export function getUrlTokens(path: string): Set<string> {
1815
if (!(path in urlTokensCache)) {
19-
urlTokensCache[path] = new Set(
20-
parse(path).map(t => (typeof t === 'string' ? t : `${t['name']}`)),
21-
);
16+
urlTokensCache[path] = tokenMap(parse(path).tokens);
2217
}
2318
return urlTokensCache[path];
2419
}
2520

21+
function tokenMap(tokens: Token[]): Set<string> {
22+
const tokenNames = new Set<string>();
23+
tokens.forEach(token => {
24+
switch (token.type) {
25+
case 'param':
26+
case 'wildcard':
27+
tokenNames.add(token.name);
28+
break;
29+
case 'group':
30+
return tokenNames.union(tokenMap(token.tokens));
31+
}
32+
});
33+
return tokenNames;
34+
}
35+
2636
const proto = Object.prototype;
2737
const gpo = Object.getPrototypeOf;
2838

packages/rest/src/__tests__/RestEndpoint.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,9 @@ describe('RestEndpoint', () => {
208208
});
209209

210210
it('only optional path means the arg is not required', () => {
211-
const ep = new RestEndpoint({ path: '/users/:id?/:group?' });
211+
const ep = new RestEndpoint({ path: '/users{/:id}{/:group}' });
212212
const epbody = new RestEndpoint({
213-
path: '/users/:id?/:group?',
213+
path: '/users{/:id}{/:group}',
214214
body: { title: '' },
215215
method: 'POST',
216216
});
@@ -1369,7 +1369,6 @@ describe('RestEndpoint.fetch()', () => {
13691369
expect(error).toBeDefined();
13701370
expect(error.status).toBe(500);
13711371

1372-
// eslint-disable-next-line require-atomic-updates
13731372
console.error = oldError;
13741373
});
13751374

packages/rest/src/pathTypes.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
type OnlyOptional<S extends string> =
2-
S extends `${infer K}}?` ? K
3-
: S extends `${infer K}?` ? K
4-
: never;
5-
type OnlyRequired<S extends string> = S extends `${string}?` ? never : S;
1+
type OnlyOptional<S extends string> = S extends `${infer K}}` ? K : never;
2+
type OnlyRequired<S extends string> = S extends `${string}}` ? never : S;
63

74
/** Parameters for a given path */
85
export type PathArgs<S extends string> =
@@ -14,18 +11,18 @@ export type PathArgs<S extends string> =
1411
/** Computes the union of keys for a path string */
1512
export type PathKeys<S extends string> =
1613
string extends S ? string
17-
: S extends `${infer A}\\${':' | '?' | '+' | '*' | '{' | '}'}${infer B}` ?
14+
: S extends `${infer A}\\${':' | '*' | '}'}${infer B}` ?
1815
PathKeys<A> | PathKeys<B>
1916
: PathSplits<S>;
2017

2118
type PathSplits<S extends string> =
2219
S extends (
23-
`${string}:${infer K}${'/' | ',' | '%' | '&' | '+' | '*' | '{'}${infer R}`
20+
`${string}${':' | '*'}${infer K}${'/' | '\\' | '%' | '&' | '*' | '{' | ';' | ',' | '!' | '@'}${infer R}`
2421
) ?
25-
PathSplits<`:${K}`> | PathSplits<R>
26-
: S extends `${string}:${infer K}:${infer R}` ?
27-
PathSplits<`:${K}`> | PathSplits<`:${R}`>
28-
: S extends `${string}:${infer K}` ? K
22+
PathSplits<`${':' | '*'}${K}`> | PathSplits<R>
23+
: S extends `${string}${':' | '*'}${infer K}${':' | '*'}${infer R}` ?
24+
PathSplits<`${':' | '*'}${K}`> | PathSplits<`${':' | '*'}${R}`>
25+
: S extends `${string}${':' | '*'}${infer K}` ? K
2926
: never;
3027

3128
export type KeysToArgs<Key extends string> = {

packages/rest/typescript-tests/types.test.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/ban-ts-comment */
21
import { Entity, schema } from '@data-client/endpoint';
32
import { useController, useSuspense } from '@data-client/react';
43
import { User } from '__tests__/new';
@@ -197,31 +196,31 @@ it('should precisely type function arguments', () => {
197196
// path: '/todos/:id?'
198197
() => {
199198
const optionalUndefSearch = new RestEndpoint({
200-
path: '/todos/:id?',
199+
path: '/todos{/:id}',
201200
searchParams: {} as
202201
| {
203202
userId?: string | number;
204203
}
205204
| undefined,
206205
});
207206
const optionalSearch = new RestEndpoint({
208-
path: '/todos/:id?',
207+
path: '/todos{/:id}',
209208
searchParams: {} as {
210209
userId?: string | number;
211210
},
212211
});
213212
const undef = new RestEndpoint({
214-
path: '/todos/:id?',
213+
path: '/todos{/:id}',
215214
searchParams: undefined,
216215
});
217216
const requiredSearch = new RestEndpoint({
218-
path: '/todos/:id?',
217+
path: '/todos{/:id}',
219218
searchParams: {} as {
220219
userId: string | number;
221220
},
222221
});
223222
const noSearch = new RestEndpoint({
224-
path: '/todos/:id?',
223+
path: '/todos{/:id}',
225224
});
226225
() => optionalUndefSearch();
227226
() => optionalUndefSearch({});
@@ -573,20 +572,19 @@ it('should handle more open ended type definitions', () => {
573572

574573
() => {
575574
const getThing = new RestEndpoint({
576-
path: '/:id*:bob',
575+
path: '{/*id}:bob',
577576
});
578577

579578
getThing({ id: 5, bob: 'hi' });
580579
// @ts-expect-error
581580
getThing({ id: 'hi' });
582-
// @ts-expect-error
583581
getThing({ bob: 'hi' });
584582
// @ts-expect-error
585583
getThing(5);
586584
};
587585
() => {
588586
const getThing = new RestEndpoint({
589-
path: '/:id+:bob',
587+
path: '/*id:bob',
590588
});
591589

592590
getThing({ id: 5, bob: 'hi' });
@@ -601,7 +599,7 @@ it('should handle more open ended type definitions', () => {
601599
};
602600
() => {
603601
const getThing = new RestEndpoint({
604-
path: '/:id\\+:bob',
602+
path: '/:id\\,:bob',
605603
});
606604

607605
getThing({ id: 5, bob: 'hi' });
@@ -616,7 +614,7 @@ it('should handle more open ended type definitions', () => {
616614
};
617615
() => {
618616
const getThing = new RestEndpoint({
619-
path: '/:id:bob+',
617+
path: '/:id/*bob',
620618
});
621619

622620
getThing({ id: 5, bob: 'hi' });
@@ -631,7 +629,7 @@ it('should handle more open ended type definitions', () => {
631629
};
632630
() => {
633631
const getThing = new RestEndpoint({
634-
path: '/:foo/(.*)',
632+
path: '/:foo/(.)',
635633
});
636634

637635
getThing({ foo: 'hi' });
@@ -644,7 +642,7 @@ it('should handle more open ended type definitions', () => {
644642
};
645643
() => {
646644
const getThing = new RestEndpoint({
647-
path: '/:attr1?{-:attr2}?{-:attr3}?',
645+
path: '{/:attr1}{-:attr2}{-:attr3}',
648646
});
649647

650648
getThing({ attr1: 'hi' });

website/src/components/Playground/editor-types/@data-client/rest.d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PathFunction } from 'path-to-regexp';
1+
import { PathFunction, ParamData } from 'path-to-regexp';
22

33
interface NetworkError$1 extends Error {
44
status: number;
@@ -1163,13 +1163,13 @@ type ExtractCollection<S extends Schema | undefined> = S extends ({
11631163
[K: string]: Schema;
11641164
} ? ExtractObject<S> : never;
11651165

1166-
type OnlyOptional<S extends string> = S extends `${infer K}}?` ? K : S extends `${infer K}?` ? K : never;
1167-
type OnlyRequired<S extends string> = S extends `${string}?` ? never : S;
1166+
type OnlyOptional<S extends string> = S extends `${infer K}}` ? K : never;
1167+
type OnlyRequired<S extends string> = S extends `${string}}` ? never : S;
11681168
/** Parameters for a given path */
11691169
type PathArgs<S extends string> = PathKeys<S> extends never ? unknown : KeysToArgs<PathKeys<S>>;
11701170
/** Computes the union of keys for a path string */
1171-
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\${':' | '?' | '+' | '*' | '{' | '}'}${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
1172-
type PathSplits<S extends string> = S extends (`${string}:${infer K}${'/' | ',' | '%' | '&' | '+' | '*' | '{'}${infer R}`) ? PathSplits<`:${K}`> | PathSplits<R> : S extends `${string}:${infer K}:${infer R}` ? PathSplits<`:${K}`> | PathSplits<`:${R}`> : S extends `${string}:${infer K}` ? K : never;
1171+
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\${':' | '*' | '}'}${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
1172+
type PathSplits<S extends string> = S extends (`${string}${':' | '*'}${infer K}${'/' | '\\' | '%' | '&' | '*' | '{' | ';' | ',' | '!' | '@'}${infer R}`) ? PathSplits<`${':' | '*'}${K}`> | PathSplits<R> : S extends `${string}${':' | '*'}${infer K}${':' | '*'}${infer R}` ? PathSplits<`${':' | '*'}${K}`> | PathSplits<`${':' | '*'}${R}`> : S extends `${string}${':' | '*'}${infer K}` ? K : never;
11731173
type KeysToArgs<Key extends string> = {
11741174
[K in Key as OnlyOptional<K>]?: string | number;
11751175
} & (OnlyRequired<Key> extends never ? unknown : {
@@ -1533,7 +1533,7 @@ type MutateEndpoint<O extends {
15331533
*/
15341534
declare let RestEndpoint: RestEndpointConstructor;
15351535

1536-
declare function getUrlBase(path: string): PathFunction;
1536+
declare function getUrlBase(path: string): PathFunction<ParamData>;
15371537
declare function getUrlTokens(path: string): Set<string>;
15381538

15391539
type ResourceExtension<R extends {

website/src/components/Playground/editor-types/globals.d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PathFunction } from 'path-to-regexp';
1+
import { PathFunction, ParamData } from 'path-to-regexp';
22
import { Manager, State, Controller, EndpointInterface as EndpointInterface$1, FetchFunction as FetchFunction$1, Schema as Schema$1, ResolveType as ResolveType$1, Denormalize as Denormalize$1, DenormalizeNullable as DenormalizeNullable$1, Queryable as Queryable$1, NI as NI$1, SchemaArgs as SchemaArgs$1, NetworkError as NetworkError$2, UnknownError as UnknownError$1, ErrorTypes as ErrorTypes$2 } from '@data-client/core';
33
export { Manager } from '@data-client/core';
44
import React, { JSX } from 'react';
@@ -1167,13 +1167,13 @@ type ExtractCollection<S extends Schema | undefined> = S extends ({
11671167
[K: string]: Schema;
11681168
} ? ExtractObject<S> : never;
11691169

1170-
type OnlyOptional<S extends string> = S extends `${infer K}}?` ? K : S extends `${infer K}?` ? K : never;
1171-
type OnlyRequired<S extends string> = S extends `${string}?` ? never : S;
1170+
type OnlyOptional<S extends string> = S extends `${infer K}}` ? K : never;
1171+
type OnlyRequired<S extends string> = S extends `${string}}` ? never : S;
11721172
/** Parameters for a given path */
11731173
type PathArgs<S extends string> = PathKeys<S> extends never ? unknown : KeysToArgs<PathKeys<S>>;
11741174
/** Computes the union of keys for a path string */
1175-
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\${':' | '?' | '+' | '*' | '{' | '}'}${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
1176-
type PathSplits<S extends string> = S extends (`${string}:${infer K}${'/' | ',' | '%' | '&' | '+' | '*' | '{'}${infer R}`) ? PathSplits<`:${K}`> | PathSplits<R> : S extends `${string}:${infer K}:${infer R}` ? PathSplits<`:${K}`> | PathSplits<`:${R}`> : S extends `${string}:${infer K}` ? K : never;
1175+
type PathKeys<S extends string> = string extends S ? string : S extends `${infer A}\\${':' | '*' | '}'}${infer B}` ? PathKeys<A> | PathKeys<B> : PathSplits<S>;
1176+
type PathSplits<S extends string> = S extends (`${string}${':' | '*'}${infer K}${'/' | '\\' | '%' | '&' | '*' | '{' | ';' | ',' | '!' | '@'}${infer R}`) ? PathSplits<`${':' | '*'}${K}`> | PathSplits<R> : S extends `${string}${':' | '*'}${infer K}${':' | '*'}${infer R}` ? PathSplits<`${':' | '*'}${K}`> | PathSplits<`${':' | '*'}${R}`> : S extends `${string}${':' | '*'}${infer K}` ? K : never;
11771177
type KeysToArgs<Key extends string> = {
11781178
[K in Key as OnlyOptional<K>]?: string | number;
11791179
} & (OnlyRequired<Key> extends never ? unknown : {
@@ -1537,7 +1537,7 @@ type MutateEndpoint<O extends {
15371537
*/
15381538
declare let RestEndpoint: RestEndpointConstructor;
15391539

1540-
declare function getUrlBase(path: string): PathFunction;
1540+
declare function getUrlBase(path: string): PathFunction<ParamData>;
15411541
declare function getUrlTokens(path: string): Set<string>;
15421542

15431543
type ResourceExtension<R extends {

yarn.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3213,7 +3213,7 @@ __metadata:
32133213
"@babel/runtime": "npm:^7.17.0"
32143214
"@data-client/endpoint": "npm:^0.14.12"
32153215
"@types/node": "npm:^22.0.0"
3216-
path-to-regexp: "npm:^6.3.0"
3216+
path-to-regexp: "npm:^8.1.0"
32173217
languageName: unknown
32183218
linkType: soft
32193219

@@ -23022,6 +23022,13 @@ __metadata:
2302223022
languageName: node
2302323023
linkType: hard
2302423024

23025+
"path-to-regexp@npm:^8.1.0":
23026+
version: 8.1.0
23027+
resolution: "path-to-regexp@npm:8.1.0"
23028+
checksum: 10c0/1c46be3806ab081bedc51eb238fcb026b61b15f19e8924b26e7dad88812dda499efe357a780665dc915dcab3be67213f145f5e2921b8fc8c6c497608d4e092ed
23029+
languageName: node
23030+
linkType: hard
23031+
2302523032
"path-type@npm:^3.0.0":
2302623033
version: 3.0.0
2302723034
resolution: "path-type@npm:3.0.0"

0 commit comments

Comments
 (0)