├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Makefile ├── README.md ├── api-extractor.json ├── etc └── typed-url-params.api.md ├── index.ts ├── package-lock.json ├── package.json ├── test.ts └── tsconfig.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | pull_request: 4 | 5 | name: CI 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@master 11 | - name: Use Node.js 14.x 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: 14.x 15 | - name: install 16 | run: npm install 17 | - name: build and test 18 | run: make build 19 | - name: Publish 20 | uses: menduz/oddish-action@master 21 | with: 22 | registry-url: "https://registry.npmjs.org" 23 | env: 24 | NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | temp -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ifneq ($(CI), true) 3 | LOCAL_ARG = --local --verbose --diagnostics 4 | endif 5 | 6 | build: 7 | ./node_modules/.bin/tsc -p tsconfig.json 8 | node dist/test.js 9 | rm -rf node_modules/@microsoft/api-extractor/node_modules/typescript || true 10 | ./node_modules/.bin/api-extractor run $(LOCAL_ARG) --typescript-compiler-folder ./node_modules/typescript 11 | 12 | .PHONY: build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typed-url-params 2 | 3 | Types your URL parameters in compliance with Express.js router and https://github.com/pillarjs/path-to-regexp 4 | 5 | ## Install 6 | 7 | ``` 8 | npm install typed-url-params 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```ts 14 | type Params = ParseUrlParams<"/test"> // { } 15 | type Params = ParseUrlParams<"/:abc"> // { abc: string } 16 | type Params = ParseUrlParams<"/:abc+"> // { abc: string | string[] } 17 | type Params = ParseUrlParams<"/:abc*"> // { abc?: string | string[] } 18 | type Params = ParseUrlParams<"/users/:userId"> // { userId: string } 19 | type Params = ParseUrlParams<"/flights/:from-:to"> // { from: string, to: string } 20 | ``` 21 | 22 | ## Usage with Express 23 | 24 | This library only offers typings. You must write your own wrapper for Express. You can do it like this: 25 | 26 | ```typescript 27 | function handleGet( 28 | app: Express.Application, 29 | url: TypedUrl, 30 | handler: (req: Express.Request & { params: ParseUrlParams }, res: Express.Response, next) => void 31 | ) { 32 | // noop 33 | } 34 | ``` 35 | 36 | ## More examples 37 | 38 | ```ts 39 | 40 | import { ParseUrlParams } from "typed-url-params" 41 | 42 | function assert(r: ParseUrlParams): asserts r is ParseUrlParams {} 43 | 44 | assert<"/:asd/b">({ asd: "" }) 45 | assert<"/xxx/:asd/bbb:dsa">({ asd: "", dsa: "" }) 46 | assert<"/xxx/:asd/bbb/:dsa">({ asd: "", dsa: "" }) 47 | assert<"/xxx/:asd/bbb/:dsa">({ asd: "", dsa: "" }) 48 | assert<"/:test*-bar">({ test: [] }) 49 | assert<"/:test*-bar">({}) 50 | assert<"/:test*-bar">({ test: "asd" }) 51 | assert<"/:test*-bar">({ test: ["asd"] }) 52 | assert<"/:test+-bar">({ test: [""] }) 53 | assert<"/:test*">({ test: [] }) 54 | assert<"/:test+">({ test: "" }) 55 | assert<"/:test+">({ test: [""] }) 56 | assert<"/:test?-bar">({ test: "" }) 57 | assert<"/:test?">({ test: "" }) 58 | assert<"/:test(\\d+)">({ test: "" }) 59 | assert<"/:test(\\d+)+">({ test: "" }) 60 | assert<"/:test(\\d+)+">({ test: [""] }) 61 | assert<"/route.:ext(json|xml)?">({}) 62 | assert<"/route.:ext(json|xml)?">({ ext: "" }) 63 | assert<"/route.:ext(json|xml)">({ ext: "" }) 64 | assert<"/route.:ext(json|xml)+">({ ext: "" }) 65 | assert<"/route.:ext(json|xml)+">({ ext: [""] }) 66 | assert<"/route.:ext([a-z]+)*/:asd">({ ext: [""], asd: "" }) 67 | assert<"/route.:ext([a-z]+)*/:asd">({ asd: "" }) 68 | assert<"/route.:ext([a-z]+)*">({ ext: [""] }) 69 | assert<"/route.:ext([a-z]+)/:asd">({ ext: "", asd: "" }) 70 | assert<"/:test(.*)">({ test: "" }) 71 | assert<"/route\\(\\\\(\\d+\\\\)\\)">({}) 72 | assert<"/{apple-}?icon-:res(\\d+).png">({ res: "" }) 73 | assert<"/:foo/:bar">({ foo: "", bar: "" }) 74 | assert<"/users/:user_id/:asd+">({ user_id: "", asd: [] }) 75 | assert<"/flights/:from-:to">({ from: "", to: "" }) 76 | ``` -------------------------------------------------------------------------------- /api-extractor.json: -------------------------------------------------------------------------------- 1 | /** 2 | * Config file for API Extractor. For more info, please visit: https://api-extractor.com 3 | */ 4 | { 5 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", 6 | 7 | /** 8 | * Optionally specifies another JSON config file that this file extends from. This provides a way for 9 | * standard settings to be shared across multiple projects. 10 | * 11 | * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains 12 | * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be 13 | * resolved using NodeJS require(). 14 | * 15 | * SUPPORTED TOKENS: none 16 | * DEFAULT VALUE: "" 17 | */ 18 | // "extends": "./shared/api-extractor-base.json" 19 | // "extends": "my-package/include/api-extractor-base.json" 20 | 21 | /** 22 | * Determines the "" token that can be used with other config file settings. The project folder 23 | * typically contains the tsconfig.json and package.json config files, but the path is user-defined. 24 | * 25 | * The path is resolved relative to the folder of the config file that contains the setting. 26 | * 27 | * The default value for "projectFolder" is the token "", which means the folder is determined by traversing 28 | * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder 29 | * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error 30 | * will be reported. 31 | * 32 | * SUPPORTED TOKENS: 33 | * DEFAULT VALUE: "" 34 | */ 35 | // "projectFolder": "..", 36 | 37 | /** 38 | * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor 39 | * analyzes the symbols exported by this module. 40 | * 41 | * The file extension must be ".d.ts" and not ".ts". 42 | * 43 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 44 | * prepend a folder token such as "". 45 | * 46 | * SUPPORTED TOKENS: , , 47 | */ 48 | "mainEntryPointFilePath": "/dist/index.d.ts", 49 | 50 | /** 51 | * A list of NPM package names whose exports should be treated as part of this package. 52 | * 53 | * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", 54 | * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part 55 | * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly 56 | * imports library2. To avoid this, we can specify: 57 | * 58 | * "bundledPackages": [ "library2" ], 59 | * 60 | * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been 61 | * local files for library1. 62 | */ 63 | "bundledPackages": [], 64 | 65 | /** 66 | * Determines how the TypeScript compiler engine will be invoked by API Extractor. 67 | */ 68 | "compiler": { 69 | /** 70 | * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. 71 | * 72 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 73 | * prepend a folder token such as "". 74 | * 75 | * Note: This setting will be ignored if "overrideTsconfig" is used. 76 | * 77 | * SUPPORTED TOKENS: , , 78 | * DEFAULT VALUE: "/tsconfig.json" 79 | */ 80 | // "tsconfigFilePath": "/tsconfig.json", 81 | /** 82 | * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. 83 | * The object must conform to the TypeScript tsconfig schema: 84 | * 85 | * http://json.schemastore.org/tsconfig 86 | * 87 | * If omitted, then the tsconfig.json file will be read from the "projectFolder". 88 | * 89 | * DEFAULT VALUE: no overrideTsconfig section 90 | */ 91 | // "overrideTsconfig": { 92 | // . . . 93 | // } 94 | /** 95 | * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended 96 | * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when 97 | * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses 98 | * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. 99 | * 100 | * DEFAULT VALUE: false 101 | */ 102 | "skipLibCheck": true, 103 | }, 104 | 105 | /** 106 | * Configures how the API report file (*.api.md) will be generated. 107 | */ 108 | "apiReport": { 109 | /** 110 | * (REQUIRED) Whether to generate an API report. 111 | */ 112 | "enabled": true 113 | 114 | /** 115 | * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce 116 | * a full file path. 117 | * 118 | * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". 119 | * 120 | * SUPPORTED TOKENS: , 121 | * DEFAULT VALUE: ".api.md" 122 | */ 123 | // "reportFileName": ".api.md", 124 | 125 | /** 126 | * Specifies the folder where the API report file is written. The file name portion is determined by 127 | * the "reportFileName" setting. 128 | * 129 | * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, 130 | * e.g. for an API review. 131 | * 132 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 133 | * prepend a folder token such as "". 134 | * 135 | * SUPPORTED TOKENS: , , 136 | * DEFAULT VALUE: "/etc/" 137 | */ 138 | // "reportFolder": "/etc/", 139 | 140 | /** 141 | * Specifies the folder where the temporary report file is written. The file name portion is determined by 142 | * the "reportFileName" setting. 143 | * 144 | * After the temporary file is written to disk, it is compared with the file in the "reportFolder". 145 | * If they are different, a production build will fail. 146 | * 147 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 148 | * prepend a folder token such as "". 149 | * 150 | * SUPPORTED TOKENS: , , 151 | * DEFAULT VALUE: "/temp/" 152 | */ 153 | // "reportTempFolder": "/temp/" 154 | }, 155 | 156 | /** 157 | * Configures how the doc model file (*.api.json) will be generated. 158 | */ 159 | "docModel": { 160 | /** 161 | * (REQUIRED) Whether to generate a doc model file. 162 | */ 163 | "enabled": true, 164 | 165 | /** 166 | * The output path for the doc model file. The file extension should be ".api.json". 167 | * 168 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 169 | * prepend a folder token such as "". 170 | * 171 | * SUPPORTED TOKENS: , , 172 | * DEFAULT VALUE: "/temp/.api.json" 173 | */ 174 | "apiJsonFilePath": "/dist/.api.json" 175 | }, 176 | 177 | /** 178 | * Configures how the .d.ts rollup file will be generated. 179 | */ 180 | "dtsRollup": { 181 | /** 182 | * (REQUIRED) Whether to generate the .d.ts rollup file. 183 | */ 184 | "enabled": true 185 | 186 | /** 187 | * Specifies the output path for a .d.ts rollup file to be generated without any trimming. 188 | * This file will include all declarations that are exported by the main entry point. 189 | * 190 | * If the path is an empty string, then this file will not be written. 191 | * 192 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 193 | * prepend a folder token such as "". 194 | * 195 | * SUPPORTED TOKENS: , , 196 | * DEFAULT VALUE: "/dist/.d.ts" 197 | */ 198 | // "untrimmedFilePath": "/dist/.d.ts", 199 | 200 | /** 201 | * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. 202 | * This file will include only declarations that are marked as "@public" or "@beta". 203 | * 204 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 205 | * prepend a folder token such as "". 206 | * 207 | * SUPPORTED TOKENS: , , 208 | * DEFAULT VALUE: "" 209 | */ 210 | // "betaTrimmedFilePath": "/dist/-beta.d.ts", 211 | 212 | /** 213 | * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. 214 | * This file will include only declarations that are marked as "@public". 215 | * 216 | * If the path is an empty string, then this file will not be written. 217 | * 218 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 219 | * prepend a folder token such as "". 220 | * 221 | * SUPPORTED TOKENS: , , 222 | * DEFAULT VALUE: "" 223 | */ 224 | // "publicTrimmedFilePath": "/dist/-public.d.ts", 225 | 226 | /** 227 | * When a declaration is trimmed, by default it will be replaced by a code comment such as 228 | * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the 229 | * declaration completely. 230 | * 231 | * DEFAULT VALUE: false 232 | */ 233 | // "omitTrimmingComments": true 234 | }, 235 | 236 | /** 237 | * Configures how the tsdoc-metadata.json file will be generated. 238 | */ 239 | "tsdocMetadata": { 240 | /** 241 | * Whether to generate the tsdoc-metadata.json file. 242 | * 243 | * DEFAULT VALUE: true 244 | */ 245 | "enabled": true, 246 | /** 247 | * Specifies where the TSDoc metadata file should be written. 248 | * 249 | * The path is resolved relative to the folder of the config file that contains the setting; to change this, 250 | * prepend a folder token such as "". 251 | * 252 | * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", 253 | * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup 254 | * falls back to "tsdoc-metadata.json" in the package folder. 255 | * 256 | * SUPPORTED TOKENS: , , 257 | * DEFAULT VALUE: "" 258 | */ 259 | "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" 260 | }, 261 | 262 | /** 263 | * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files 264 | * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. 265 | * To use the OS's default newline kind, specify "os". 266 | * 267 | * DEFAULT VALUE: "crlf" 268 | */ 269 | // "newlineKind": "crlf", 270 | 271 | /** 272 | * Configures how API Extractor reports error and warning messages produced during analysis. 273 | * 274 | * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. 275 | */ 276 | "messages": { 277 | /** 278 | * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing 279 | * the input .d.ts files. 280 | * 281 | * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" 282 | * 283 | * DEFAULT VALUE: A single "default" entry with logLevel=warning. 284 | */ 285 | "compilerMessageReporting": { 286 | /** 287 | * Configures the default routing for messages that don't match an explicit rule in this table. 288 | */ 289 | "default": { 290 | /** 291 | * Specifies whether the message should be written to the the tool's output log. Note that 292 | * the "addToApiReportFile" property may supersede this option. 293 | * 294 | * Possible values: "error", "warning", "none" 295 | * 296 | * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail 297 | * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes 298 | * the "--local" option), the warning is displayed but the build will not fail. 299 | * 300 | * DEFAULT VALUE: "warning" 301 | */ 302 | "logLevel": "warning" 303 | 304 | /** 305 | * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), 306 | * then the message will be written inside that file; otherwise, the message is instead logged according to 307 | * the "logLevel" option. 308 | * 309 | * DEFAULT VALUE: false 310 | */ 311 | // "addToApiReportFile": false 312 | } 313 | 314 | // "TS2551": { 315 | // "logLevel": "warning", 316 | // "addToApiReportFile": true 317 | // }, 318 | // 319 | // . . . 320 | }, 321 | 322 | /** 323 | * Configures handling of messages reported by API Extractor during its analysis. 324 | * 325 | * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" 326 | * 327 | * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings 328 | */ 329 | "extractorMessageReporting": { 330 | "default": { 331 | "logLevel": "warning" 332 | // "addToApiReportFile": false 333 | } 334 | 335 | // "ae-extra-release-tag": { 336 | // "logLevel": "warning", 337 | // "addToApiReportFile": true 338 | // }, 339 | // 340 | // . . . 341 | }, 342 | 343 | /** 344 | * Configures handling of messages reported by the TSDoc parser when analyzing code comments. 345 | * 346 | * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" 347 | * 348 | * DEFAULT VALUE: A single "default" entry with logLevel=warning. 349 | */ 350 | "tsdocMessageReporting": { 351 | "default": { 352 | "logLevel": "warning" 353 | // "addToApiReportFile": false 354 | } 355 | 356 | // "tsdoc-link-tag-unescaped-text": { 357 | // "logLevel": "warning", 358 | // "addToApiReportFile": true 359 | // }, 360 | // 361 | // . . . 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /etc/typed-url-params.api.md: -------------------------------------------------------------------------------- 1 | ## API Report File for "typed-url-params" 2 | 3 | > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). 4 | 5 | ```ts 6 | 7 | // @public (undocumented) 8 | export namespace ParseUrlParams { 9 | // (undocumented) 10 | export type AddKeyValue, Key extends string, Value extends any> = Memo & { 11 | [K in Key]: Value; 12 | }; 13 | // (undocumented) 14 | export type AddOptionalKeyValue, Key extends string, Value extends any> = Memo & { 15 | [K in Key]?: Value; 16 | }; 17 | // (undocumented) 18 | export type AddUrlSection = {}> = string extends State ? ParserError<"AddUrlSection got generic string type"> : CleanKey extends `${infer Key}/` ? AddKeyValue : CleanKey extends `${infer Key}*${infer Rest}` ? ParseUrlParams> : CleanKey extends `${infer Key}/${infer Rest}` ? ParseUrlParams> : CleanKey extends `${infer Key}+${infer Rest}` ? ParseUrlParams> : CleanKey extends `${infer Key}?${infer Rest}` ? ParseUrlParams> : CleanKey extends `${infer Key}.${infer Rest}` ? ParseUrlParams> : CleanKey extends `${infer Key}-${infer Rest}` ? ParseUrlParams> : CleanKey extends `${infer Key}` ? AddKeyValue : ParseUrlParams<`AddUrlSection returned unexpected value for: ${State}`>; 19 | // (undocumented) 20 | export type CleanKey = string extends State ? ParserError<"CleanKey got generic string type"> : State extends `${infer Key}(${infer _})${infer Rest}` ? `${Key}${Rest}` : State; 21 | // (undocumented) 22 | export type ParserError = { 23 | error: true; 24 | } & T; 25 | } 26 | 27 | // @public 28 | export type ParseUrlParams = {}> = string extends State ? ParseUrlParams.ParserError<"ParseUrlParams got generic string type"> : State extends `${infer _}:${infer Rest}` ? ParseUrlParams.AddUrlSection : Memo; 29 | 30 | 31 | // (No @packageDocumentation comment for this package) 32 | 33 | ``` 34 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @public 3 | */ 4 | export declare namespace ParseUrlParams { 5 | export type ParserError = { error: true } & T 6 | 7 | // remove matcher groups 8 | export type CleanKey = string extends State 9 | ? ParserError<"CleanKey got generic string type"> 10 | : State extends `${infer Key}(${infer _})${infer Rest}` 11 | ? `${Key}${Rest}` 12 | : State 13 | 14 | export type AddUrlSection = {}> = string extends State 15 | ? ParserError<"AddUrlSection got generic string type"> 16 | : CleanKey extends `${infer Key}/` 17 | ? AddKeyValue 18 | : CleanKey extends `${infer Key}*${infer Rest}` 19 | ? ParseUrlParams> 20 | : CleanKey extends `${infer Key}/${infer Rest}` 21 | ? ParseUrlParams> 22 | : CleanKey extends `${infer Key}+${infer Rest}` 23 | ? ParseUrlParams> 24 | : CleanKey extends `${infer Key}?${infer Rest}` 25 | ? ParseUrlParams> 26 | : CleanKey extends `${infer Key}.${infer Rest}` 27 | ? ParseUrlParams> 28 | : CleanKey extends `${infer Key}-${infer Rest}` 29 | ? ParseUrlParams> 30 | : CleanKey extends `${infer Key}` 31 | ? AddKeyValue 32 | : ParseUrlParams<`AddUrlSection returned unexpected value for: ${State}`> 33 | 34 | export type AddKeyValue, Key extends string, Value extends any> = Memo & 35 | { [K in Key]: Value } 36 | export type AddOptionalKeyValue, Key extends string, Value extends any> = Memo & 37 | { [K in Key]?: Value } 38 | } 39 | 40 | /** 41 | * Creates object types compliant with https://github.com/pillarjs/path-to-regexp and Express.js 42 | * @public 43 | */ 44 | export type ParseUrlParams = {}> = string extends State 45 | ? ParseUrlParams.ParserError<"ParseUrlParams got generic string type"> 46 | : State extends `${infer _}:${infer Rest}` 47 | ? ParseUrlParams.AddUrlSection 48 | : Memo 49 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typed-url-params", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.12.11", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 10 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.12.11", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", 19 | "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "ansi-styles": { 34 | "version": "3.2.1", 35 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 36 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 37 | "dev": true, 38 | "requires": { 39 | "color-convert": "^1.9.0" 40 | } 41 | }, 42 | "chalk": { 43 | "version": "2.4.2", 44 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 45 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 46 | "dev": true, 47 | "requires": { 48 | "ansi-styles": "^3.2.1", 49 | "escape-string-regexp": "^1.0.5", 50 | "supports-color": "^5.3.0" 51 | } 52 | }, 53 | "color-convert": { 54 | "version": "1.9.3", 55 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 56 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 57 | "dev": true, 58 | "requires": { 59 | "color-name": "1.1.3" 60 | } 61 | }, 62 | "color-name": { 63 | "version": "1.1.3", 64 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 65 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 66 | "dev": true 67 | }, 68 | "has-flag": { 69 | "version": "3.0.0", 70 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 71 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 72 | "dev": true 73 | }, 74 | "supports-color": { 75 | "version": "5.5.0", 76 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 77 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 78 | "dev": true, 79 | "requires": { 80 | "has-flag": "^3.0.0" 81 | } 82 | } 83 | } 84 | }, 85 | "@jest/types": { 86 | "version": "26.6.2", 87 | "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", 88 | "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", 89 | "dev": true, 90 | "requires": { 91 | "@types/istanbul-lib-coverage": "^2.0.0", 92 | "@types/istanbul-reports": "^3.0.0", 93 | "@types/node": "*", 94 | "@types/yargs": "^15.0.0", 95 | "chalk": "^4.0.0" 96 | } 97 | }, 98 | "@microsoft/api-extractor": { 99 | "version": "7.12.1", 100 | "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.12.1.tgz", 101 | "integrity": "sha512-lleLrKkqiRvOQeoRMSHQY0wl/j9SxRVd9+Btyh/WWw0kHNy7nAKyzGmejvlz2XTn13H0elJWV6C3dxhaQy4mtA==", 102 | "dev": true, 103 | "requires": { 104 | "@microsoft/api-extractor-model": "7.12.1", 105 | "@microsoft/tsdoc": "0.12.24", 106 | "@rushstack/node-core-library": "3.35.2", 107 | "@rushstack/rig-package": "0.2.9", 108 | "@rushstack/ts-command-line": "4.7.8", 109 | "colors": "~1.2.1", 110 | "lodash": "~4.17.15", 111 | "resolve": "~1.17.0", 112 | "semver": "~7.3.0", 113 | "source-map": "~0.6.1", 114 | "typescript": "~4.0.5" 115 | }, 116 | "dependencies": { 117 | "typescript": { 118 | "version": "4.0.5", 119 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", 120 | "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", 121 | "dev": true 122 | } 123 | } 124 | }, 125 | "@microsoft/api-extractor-model": { 126 | "version": "7.12.1", 127 | "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.12.1.tgz", 128 | "integrity": "sha512-Hw+kYfUb1gt6xPWGFW8APtLVWeNEWz4JE6PbLkSHw/j+G1hAaStzgxhBx3GOAWM/G0SCDGVJOpd5YheVOyu/KQ==", 129 | "dev": true, 130 | "requires": { 131 | "@microsoft/tsdoc": "0.12.24", 132 | "@rushstack/node-core-library": "3.35.2" 133 | } 134 | }, 135 | "@microsoft/tsdoc": { 136 | "version": "0.12.24", 137 | "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", 138 | "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", 139 | "dev": true 140 | }, 141 | "@rushstack/node-core-library": { 142 | "version": "3.35.2", 143 | "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.35.2.tgz", 144 | "integrity": "sha512-SPd0uG7mwsf3E30np9afCUhtaM1SBpibrbxOXPz82KWV6SQiPUtXeQfhXq9mSnGxOb3WLWoSDe7AFxQNex3+kQ==", 145 | "dev": true, 146 | "requires": { 147 | "@types/node": "10.17.13", 148 | "colors": "~1.2.1", 149 | "fs-extra": "~7.0.1", 150 | "import-lazy": "~4.0.0", 151 | "jju": "~1.4.0", 152 | "resolve": "~1.17.0", 153 | "semver": "~7.3.0", 154 | "timsort": "~0.3.0", 155 | "z-schema": "~3.18.3" 156 | } 157 | }, 158 | "@rushstack/rig-package": { 159 | "version": "0.2.9", 160 | "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.9.tgz", 161 | "integrity": "sha512-4tqsZ/m+BjeNAGeAJYzPF53CT96TsAYeZ3Pq3T4tb1pGGM3d3TWfkmALZdKNhpRlAeShKUrb/o/f/0sAuK/1VQ==", 162 | "dev": true, 163 | "requires": { 164 | "@types/node": "10.17.13", 165 | "resolve": "~1.17.0", 166 | "strip-json-comments": "~3.1.1" 167 | } 168 | }, 169 | "@rushstack/ts-command-line": { 170 | "version": "4.7.8", 171 | "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", 172 | "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", 173 | "dev": true, 174 | "requires": { 175 | "@types/argparse": "1.0.38", 176 | "argparse": "~1.0.9", 177 | "colors": "~1.2.1", 178 | "string-argv": "~0.3.1" 179 | } 180 | }, 181 | "@types/argparse": { 182 | "version": "1.0.38", 183 | "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", 184 | "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", 185 | "dev": true 186 | }, 187 | "@types/istanbul-lib-coverage": { 188 | "version": "2.0.3", 189 | "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", 190 | "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", 191 | "dev": true 192 | }, 193 | "@types/istanbul-lib-report": { 194 | "version": "3.0.0", 195 | "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", 196 | "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", 197 | "dev": true, 198 | "requires": { 199 | "@types/istanbul-lib-coverage": "*" 200 | } 201 | }, 202 | "@types/istanbul-reports": { 203 | "version": "3.0.0", 204 | "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", 205 | "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", 206 | "dev": true, 207 | "requires": { 208 | "@types/istanbul-lib-report": "*" 209 | } 210 | }, 211 | "@types/node": { 212 | "version": "10.17.13", 213 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", 214 | "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", 215 | "dev": true 216 | }, 217 | "@types/stack-utils": { 218 | "version": "2.0.0", 219 | "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", 220 | "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", 221 | "dev": true 222 | }, 223 | "@types/yargs": { 224 | "version": "15.0.12", 225 | "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", 226 | "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", 227 | "dev": true, 228 | "requires": { 229 | "@types/yargs-parser": "*" 230 | } 231 | }, 232 | "@types/yargs-parser": { 233 | "version": "20.2.0", 234 | "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", 235 | "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", 236 | "dev": true 237 | }, 238 | "ansi-regex": { 239 | "version": "5.0.0", 240 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 241 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 242 | "dev": true 243 | }, 244 | "ansi-styles": { 245 | "version": "4.3.0", 246 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 247 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 248 | "dev": true, 249 | "requires": { 250 | "color-convert": "^2.0.1" 251 | } 252 | }, 253 | "argparse": { 254 | "version": "1.0.10", 255 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 256 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 257 | "dev": true, 258 | "requires": { 259 | "sprintf-js": "~1.0.2" 260 | } 261 | }, 262 | "braces": { 263 | "version": "3.0.2", 264 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 265 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 266 | "dev": true, 267 | "requires": { 268 | "fill-range": "^7.0.1" 269 | } 270 | }, 271 | "chalk": { 272 | "version": "4.1.0", 273 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 274 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 275 | "dev": true, 276 | "requires": { 277 | "ansi-styles": "^4.1.0", 278 | "supports-color": "^7.1.0" 279 | } 280 | }, 281 | "color-convert": { 282 | "version": "2.0.1", 283 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 284 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 285 | "dev": true, 286 | "requires": { 287 | "color-name": "~1.1.4" 288 | } 289 | }, 290 | "color-name": { 291 | "version": "1.1.4", 292 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 293 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 294 | "dev": true 295 | }, 296 | "colors": { 297 | "version": "1.2.5", 298 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", 299 | "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", 300 | "dev": true 301 | }, 302 | "commander": { 303 | "version": "2.20.3", 304 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 305 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 306 | "dev": true, 307 | "optional": true 308 | }, 309 | "diff-sequences": { 310 | "version": "26.6.2", 311 | "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", 312 | "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", 313 | "dev": true 314 | }, 315 | "escape-string-regexp": { 316 | "version": "1.0.5", 317 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 318 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 319 | "dev": true 320 | }, 321 | "expect": { 322 | "version": "26.6.2", 323 | "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", 324 | "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", 325 | "dev": true, 326 | "requires": { 327 | "@jest/types": "^26.6.2", 328 | "ansi-styles": "^4.0.0", 329 | "jest-get-type": "^26.3.0", 330 | "jest-matcher-utils": "^26.6.2", 331 | "jest-message-util": "^26.6.2", 332 | "jest-regex-util": "^26.0.0" 333 | } 334 | }, 335 | "fill-range": { 336 | "version": "7.0.1", 337 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 338 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 339 | "dev": true, 340 | "requires": { 341 | "to-regex-range": "^5.0.1" 342 | } 343 | }, 344 | "fs-extra": { 345 | "version": "7.0.1", 346 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", 347 | "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", 348 | "dev": true, 349 | "requires": { 350 | "graceful-fs": "^4.1.2", 351 | "jsonfile": "^4.0.0", 352 | "universalify": "^0.1.0" 353 | } 354 | }, 355 | "graceful-fs": { 356 | "version": "4.2.4", 357 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 358 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", 359 | "dev": true 360 | }, 361 | "has-flag": { 362 | "version": "4.0.0", 363 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 364 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 365 | "dev": true 366 | }, 367 | "import-lazy": { 368 | "version": "4.0.0", 369 | "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", 370 | "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", 371 | "dev": true 372 | }, 373 | "is-number": { 374 | "version": "7.0.0", 375 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 376 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 377 | "dev": true 378 | }, 379 | "jest-diff": { 380 | "version": "26.6.2", 381 | "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", 382 | "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", 383 | "dev": true, 384 | "requires": { 385 | "chalk": "^4.0.0", 386 | "diff-sequences": "^26.6.2", 387 | "jest-get-type": "^26.3.0", 388 | "pretty-format": "^26.6.2" 389 | } 390 | }, 391 | "jest-get-type": { 392 | "version": "26.3.0", 393 | "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", 394 | "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", 395 | "dev": true 396 | }, 397 | "jest-matcher-utils": { 398 | "version": "26.6.2", 399 | "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", 400 | "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", 401 | "dev": true, 402 | "requires": { 403 | "chalk": "^4.0.0", 404 | "jest-diff": "^26.6.2", 405 | "jest-get-type": "^26.3.0", 406 | "pretty-format": "^26.6.2" 407 | } 408 | }, 409 | "jest-message-util": { 410 | "version": "26.6.2", 411 | "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", 412 | "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", 413 | "dev": true, 414 | "requires": { 415 | "@babel/code-frame": "^7.0.0", 416 | "@jest/types": "^26.6.2", 417 | "@types/stack-utils": "^2.0.0", 418 | "chalk": "^4.0.0", 419 | "graceful-fs": "^4.2.4", 420 | "micromatch": "^4.0.2", 421 | "pretty-format": "^26.6.2", 422 | "slash": "^3.0.0", 423 | "stack-utils": "^2.0.2" 424 | } 425 | }, 426 | "jest-regex-util": { 427 | "version": "26.0.0", 428 | "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", 429 | "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", 430 | "dev": true 431 | }, 432 | "jju": { 433 | "version": "1.4.0", 434 | "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", 435 | "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", 436 | "dev": true 437 | }, 438 | "js-tokens": { 439 | "version": "4.0.0", 440 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 441 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 442 | "dev": true 443 | }, 444 | "jsonfile": { 445 | "version": "4.0.0", 446 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 447 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 448 | "dev": true, 449 | "requires": { 450 | "graceful-fs": "^4.1.6" 451 | } 452 | }, 453 | "lodash": { 454 | "version": "4.17.20", 455 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 456 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", 457 | "dev": true 458 | }, 459 | "lodash.get": { 460 | "version": "4.4.2", 461 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 462 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", 463 | "dev": true 464 | }, 465 | "lodash.isequal": { 466 | "version": "4.5.0", 467 | "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", 468 | "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", 469 | "dev": true 470 | }, 471 | "lru-cache": { 472 | "version": "6.0.0", 473 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 474 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 475 | "dev": true, 476 | "requires": { 477 | "yallist": "^4.0.0" 478 | } 479 | }, 480 | "micromatch": { 481 | "version": "4.0.2", 482 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", 483 | "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", 484 | "dev": true, 485 | "requires": { 486 | "braces": "^3.0.1", 487 | "picomatch": "^2.0.5" 488 | } 489 | }, 490 | "path-parse": { 491 | "version": "1.0.6", 492 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 493 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 494 | "dev": true 495 | }, 496 | "path-to-regexp": { 497 | "version": "6.2.0", 498 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz", 499 | "integrity": "sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==", 500 | "dev": true 501 | }, 502 | "picomatch": { 503 | "version": "2.2.2", 504 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 505 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 506 | "dev": true 507 | }, 508 | "pretty-format": { 509 | "version": "26.6.2", 510 | "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", 511 | "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", 512 | "dev": true, 513 | "requires": { 514 | "@jest/types": "^26.6.2", 515 | "ansi-regex": "^5.0.0", 516 | "ansi-styles": "^4.0.0", 517 | "react-is": "^17.0.1" 518 | } 519 | }, 520 | "react-is": { 521 | "version": "17.0.1", 522 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", 523 | "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", 524 | "dev": true 525 | }, 526 | "resolve": { 527 | "version": "1.17.0", 528 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 529 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 530 | "dev": true, 531 | "requires": { 532 | "path-parse": "^1.0.6" 533 | } 534 | }, 535 | "semver": { 536 | "version": "7.3.4", 537 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", 538 | "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", 539 | "dev": true, 540 | "requires": { 541 | "lru-cache": "^6.0.0" 542 | } 543 | }, 544 | "slash": { 545 | "version": "3.0.0", 546 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 547 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 548 | "dev": true 549 | }, 550 | "source-map": { 551 | "version": "0.6.1", 552 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 553 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 554 | "dev": true 555 | }, 556 | "sprintf-js": { 557 | "version": "1.0.3", 558 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 559 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 560 | "dev": true 561 | }, 562 | "stack-utils": { 563 | "version": "2.0.3", 564 | "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", 565 | "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", 566 | "dev": true, 567 | "requires": { 568 | "escape-string-regexp": "^2.0.0" 569 | }, 570 | "dependencies": { 571 | "escape-string-regexp": { 572 | "version": "2.0.0", 573 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", 574 | "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", 575 | "dev": true 576 | } 577 | } 578 | }, 579 | "string-argv": { 580 | "version": "0.3.1", 581 | "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", 582 | "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", 583 | "dev": true 584 | }, 585 | "strip-json-comments": { 586 | "version": "3.1.1", 587 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 588 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 589 | "dev": true 590 | }, 591 | "supports-color": { 592 | "version": "7.2.0", 593 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 594 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 595 | "dev": true, 596 | "requires": { 597 | "has-flag": "^4.0.0" 598 | } 599 | }, 600 | "timsort": { 601 | "version": "0.3.0", 602 | "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", 603 | "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", 604 | "dev": true 605 | }, 606 | "to-regex-range": { 607 | "version": "5.0.1", 608 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 609 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 610 | "dev": true, 611 | "requires": { 612 | "is-number": "^7.0.0" 613 | } 614 | }, 615 | "typescript": { 616 | "version": "4.1.3", 617 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", 618 | "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", 619 | "dev": true 620 | }, 621 | "universalify": { 622 | "version": "0.1.2", 623 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 624 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 625 | "dev": true 626 | }, 627 | "validator": { 628 | "version": "8.2.0", 629 | "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", 630 | "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", 631 | "dev": true 632 | }, 633 | "yallist": { 634 | "version": "4.0.0", 635 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 636 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 637 | "dev": true 638 | }, 639 | "z-schema": { 640 | "version": "3.18.4", 641 | "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", 642 | "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", 643 | "dev": true, 644 | "requires": { 645 | "commander": "^2.7.1", 646 | "lodash.get": "^4.0.0", 647 | "lodash.isequal": "^4.0.0", 648 | "validator": "^8.0.0" 649 | } 650 | } 651 | } 652 | } 653 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typed-url-params", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "typings": "dist/typed-url-params.d.ts", 7 | "scripts": { 8 | "test": "make build" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/menduz/typed-url-params.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/menduz/typed-url-params/issues" 19 | }, 20 | "homepage": "https://github.com/menduz/typed-url-params#readme", 21 | "devDependencies": { 22 | "@microsoft/api-extractor": "^7.12.1", 23 | "expect": "^26.6.2", 24 | "path-to-regexp": "^6.2.0", 25 | "typescript": "^4.1.3" 26 | }, 27 | "files": [ 28 | "dist" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /test.ts: -------------------------------------------------------------------------------- 1 | import { ParseUrlParams } from "./index" 2 | import expect from "expect" 3 | import { match } from "path-to-regexp" 4 | 5 | function assert( 6 | path: T, 7 | test: string, 8 | r: ParseUrlParams | undefined 9 | ): asserts r is ParseUrlParams { 10 | const res: any = match(path, { decode: decodeURIComponent })(test) 11 | 12 | expect(res.params).toEqual(r) 13 | } 14 | 15 | assert("/:asd/b", "/hola/b", { asd: "hola" }) 16 | assert("/xxx/:asd/bbb:dsa", "/xxx/a/bbbtest", { asd: "a", dsa: "test" }) 17 | assert("/xxx/:a/bbb/:b", "/xxx/a/bbb/test", { a: "a", b: "test" }) 18 | assert("/:test*-bar", "/hola-bar", { test: ["hola"] }) 19 | assert("/:test*-bar", "/", undefined) 20 | assert("/:test*-bar", "/asd-bar", { test: ["asd"] }) 21 | assert("/:test*-bar", "/x/y/z-bar", { test: ["x", "y", "z"] }) 22 | // assert("/:test*-bar", "/-bar", undefined) 23 | assert("/:test+-bar", "/a/b-bar", { test: ["a", "b"] }) 24 | assert("/:test+-bar", "/:test-bar", { test: [":test"] }) 25 | assert("/:test*", "/", {}) 26 | assert("/:test+", "/a", { test: ["a"] }) 27 | // assert("/:test?-bar", "/-bar", { }) 28 | assert("/:test?-bar", "/e-bar", { test: "e" }) 29 | assert("/:test?", "/", {}) 30 | assert("/:test(\\d+)", "/123", { test: "123" }) 31 | assert("/:test(\\d+)", "/aaa", undefined) 32 | assert("/:test(\\d+)+", "/123/123", { test: ["123", "123"] }) 33 | assert("/route.:ext(json|xml)?", "/route.json", { ext: "json" }) 34 | assert("/route.:ext(json|xml)?", "/route.NOOP", undefined) 35 | assert("/route.:ext(json|xml)", "/route.xml", { ext: "xml" }) 36 | assert("/route.:ext(json|xml)+", "/route.xml.json", { ext: ["xml", "json"] }) 37 | assert("/route.:ext([a-z]+)*/:asd", "/route.xml/meta", { ext: ["xml"], asd: "meta" }) 38 | assert("/route.:ext([a-z]+)*", "/route.asdaksdasdkajsdhkajhsdkj", { ext: ["asdaksdasdkajsdhkajhsdkj"] }) 39 | assert("/route.:ext([a-z]+)*", "/route.123", undefined) 40 | assert("/route.:ext([a-z]+)/:asd", "/route.asdaksdasdkajsdhkajhsdkj/meta", { 41 | ext: "asdaksdasdkajsdhkajhsdkj", 42 | asd: "meta", 43 | }) 44 | assert("/:test(.*)", "/pp", { test: "pp" }) 45 | // assert("/route\\(\\\\(\\d+\\\\)\\)", "/route(\\123\\)", {}) 46 | assert("/{apple-}?icon-:res(\\d+).png", "/apple-icon-240.png", { res: "240" }) 47 | assert("/:foo/:bar", "/x/y", { foo: "x", bar: "y" }) 48 | assert("/users/:user_id/:asd+", "/users/1/test/test2", { user_id: "1", asd: ["test", "test2"] }) 49 | assert("/flights/:from-:to", "/flights/EZE-SFO", { from: "EZE", to: "SFO" }) 50 | 51 | function handleRequest(url: T, handler: (req: { params: ParseUrlParams }) => void) { 52 | // noop 53 | } 54 | 55 | handleRequest("/users/:user_id/:asd+", (req) => { 56 | req.params.asd 57 | }) 58 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | /* Basic Options */ 5 | // "incremental": true, /* Enable incremental compilation */ 6 | "target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 7 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 8 | // "lib": [], /* Specify library files to be included in the compilation. */ 9 | // "allowJs": true, /* Allow javascript files to be compiled. */ 10 | // "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | "outDir": "./dist", /* Redirect output structure to the directory. */ 17 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 18 | // "composite": true, /* Enable project compilation */ 19 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 20 | // "removeComments": true, /* Do not emit comments to output. */ 21 | // "noEmit": true, /* Do not emit outputs. */ 22 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 23 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 24 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | "strictNullChecks": true, /* Enable strict null checks. */ 29 | "types": [], 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | /* Source Map Options */ 53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | /* Experimental Options */ 58 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 59 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 60 | /* Advanced Options */ 61 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 62 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 63 | } 64 | } --------------------------------------------------------------------------------