@vercel/config 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,46 +1,159 @@
1
- # Vercel Router SDK
1
+ # @vercel/config
2
2
 
3
- This is a prototype of what a Router SDK could look like.
3
+ TypeScript SDK for defining Vercel configuration programmatically.
4
4
 
5
- ## Usage
5
+ ## Installation
6
6
 
7
- 1. Install the package:
8
- `npm install @vercel/router`
7
+ ```bash
8
+ npm install @vercel/config
9
+ ```
9
10
 
10
- 2. Create a router configuration file (e.g., `router.config.ts`):
11
+ ## Usage
12
+
13
+ Create a `vercel.ts` file in your project root:
11
14
 
12
15
  ```typescript
13
- import { createRouter } from "@vercel/router";
16
+ import { createRouter } from '@vercel/config';
14
17
 
15
18
  const router = createRouter();
16
19
 
17
- router
18
- .rewrite("/api/(.*)", "/api/$1")
19
- .redirect("/old-docs", "/docs", { permanent: true })
20
- .cacheControl("/static/(.*)", {
21
- public: true,
22
- maxAge: "1week",
23
- immutable: true
24
- })
25
- .setCleanUrls(true);
20
+ // Basic rewrite
21
+ router.rewrite('/api/(.*)', 'https://backend.com/$1');
22
+
23
+ // Rewrite with transforms
24
+ router.rewrite('/users/:userId', 'https://api.example.com/users/$1', ({userId, env}) => ({
25
+ requestHeaders: {
26
+ 'x-user-id': userId,
27
+ 'authorization': `Bearer ${env.API_TOKEN}`
28
+ }
29
+ }));
30
+
31
+ // Redirects
32
+ router.redirect('/old-docs', '/docs', { permanent: true });
33
+
34
+ // Cache control
35
+ router.cacheControl('/static/(.*)', {
36
+ public: true,
37
+ maxAge: '1week',
38
+ immutable: true
39
+ });
40
+
41
+ // Global settings
42
+ router.cleanUrls = true;
43
+ router.trailingSlash = true;
44
+
45
+ // Bulk redirects
46
+ router.bulkRedirectsPath = './bulkRedirectsDemo.json';
47
+
48
+ // Cron jobs
49
+ router.cron('/api/cleanup', '0 0 * * *');
26
50
 
27
51
  export default router.getConfig();
28
52
  ```
29
53
 
30
- 3. Add the generate-config script to your package.json:
54
+ ## Automatic Compilation
55
+
56
+ Your `vercel.ts` file is automatically compiled when you run:
57
+
58
+ ```bash
59
+ vercel build # For local builds
60
+ vercel dev # For local development
61
+ vercel deploy # For deployment
62
+ ```
63
+
64
+ The Vercel CLI automatically compiles `vercel.ts` to `.vercel/vercel.json` before building or deploying.
65
+
66
+ ## API Reference
67
+
68
+ ### `createRouter()`
69
+
70
+ Creates a new router instance.
71
+
72
+ ### `router.rewrite(source, destination, options?)`
31
73
 
32
- ```json
33
- {
34
- "scripts": {
35
- "generate-config": "@vercel/router"
74
+ Add a rewrite rule. Options can be an object or a callback function that receives path parameters and environment variables.
75
+
76
+ ```typescript
77
+ // With callback for transforms
78
+ router.rewrite('/users/:userId', 'https://api.example.com/users/$1', ({userId, env}) => ({
79
+ requestHeaders: {
80
+ 'x-user-id': userId,
81
+ 'authorization': `Bearer ${env.API_TOKEN}`
82
+ },
83
+ responseHeaders: {
84
+ 'x-powered-by': 'My API'
85
+ },
86
+ requestQuery: {
87
+ 'version': '2.0'
36
88
  }
37
- }
89
+ }));
90
+
91
+ // Simple rewrite without transforms
92
+ router.rewrite('/api/(.*)', 'https://backend.com/$1');
38
93
  ```
39
94
 
40
- 4. Run the script to generate your `vercel.json`:
95
+ ### `router.redirect(source, destination, options?)`
41
96
 
42
- ```bash
43
- npm run generate-config
97
+ Add a redirect rule. Options include `permanent` and `statusCode`.
98
+
99
+ ```typescript
100
+ router.redirect('/old-page', '/new-page', { permanent: true });
101
+ router.redirect('/temp', '/elsewhere', { statusCode: 302 });
44
102
  ```
45
103
 
46
- This will generate a `vercel.json` file in your project root with all your routing configurations.
104
+ ### `router.header(source, headers, options?)`
105
+
106
+ Add custom headers for a path pattern.
107
+
108
+ ```typescript
109
+ router.header('/api/(.*)', [
110
+ { key: 'X-Custom-Header', value: 'value' }
111
+ ]);
112
+ ```
113
+
114
+ ### `router.cacheControl(source, options)`
115
+
116
+ Set cache control headers. Options include `public`, `private`, `maxAge`, `sMaxAge`, `immutable`, etc.
117
+
118
+ ```typescript
119
+ router.cacheControl('/static/(.*)', {
120
+ public: true,
121
+ maxAge: '1week',
122
+ immutable: true
123
+ });
124
+ ```
125
+
126
+ ### `router.cleanUrls`
127
+
128
+ Set whether to enable clean URLs (removes file extensions).
129
+
130
+ ```typescript
131
+ router.cleanUrls = true;
132
+ ```
133
+
134
+ ### `router.trailingSlash`
135
+
136
+ Set whether to normalize paths to include trailing slashes.
137
+
138
+ ```typescript
139
+ router.trailingSlash = true;
140
+ ```
141
+
142
+ ### `router.bulkRedirectsPath`
143
+
144
+ Set the path to a bulk redirects JSON file.
145
+
146
+ ```typescript
147
+ router.bulkRedirectsPath = './bulkRedirectsDemo.json';
148
+ ```
149
+
150
+ ## Important Notes
151
+
152
+ - **One config file only**: You cannot have both `vercel.ts` and `vercel.json`. The build will fail if both exist.
153
+ - **Automatic gitignore**: The generated `.vercel/vercel.json` file is automatically ignored by git (in the `.vercel/` directory).
154
+ - **No manual compilation needed**: The Vercel CLI handles compilation automatically - no need to run a separate command.
155
+
156
+ ## Learn More
157
+
158
+ - [Vercel Configuration Documentation](https://vercel.com/docs/projects/project-configuration)
159
+ - [Routing Documentation](https://vercel.com/docs/edge-network/routing)
package/dist/router.d.ts CHANGED
@@ -352,6 +352,10 @@ export interface RouterConfig {
352
352
  * When true, routes are normalized to include a trailing slash.
353
353
  */
354
354
  trailingSlash?: boolean;
355
+ /**
356
+ * Path to a JSON file containing bulk redirect rules.
357
+ */
358
+ bulkRedirectsPath?: string;
355
359
  /**
356
360
  * Array of cron definitions for scheduled invocations.
357
361
  */
@@ -378,8 +382,45 @@ export declare class Router {
378
382
  private cronRules;
379
383
  private cleanUrlsConfig;
380
384
  private trailingSlashConfig;
385
+ private bulkRedirectsPathConfig;
381
386
  private buildCommandConfig;
382
387
  private installCommandConfig;
388
+ /**
389
+ * Property setter for cleanUrls configuration.
390
+ */
391
+ set cleanUrls(value: boolean | undefined);
392
+ /**
393
+ * Property getter for cleanUrls configuration.
394
+ */
395
+ get cleanUrls(): boolean | undefined;
396
+ /**
397
+ * Property setter for trailingSlash configuration.
398
+ */
399
+ set trailingSlash(value: boolean | undefined);
400
+ /**
401
+ * Property getter for trailingSlash configuration.
402
+ */
403
+ get trailingSlash(): boolean | undefined;
404
+ /**
405
+ * Property setter for bulkRedirectsPath configuration.
406
+ */
407
+ set bulkRedirectsPath(value: string | undefined);
408
+ /**
409
+ * Property getter for bulkRedirectsPath configuration.
410
+ */
411
+ get bulkRedirectsPath(): string | undefined;
412
+ /**
413
+ * Helper to extract path parameter names from a source pattern.
414
+ * Path parameters are identified by :paramName syntax.
415
+ * @example
416
+ * extractPathParams('/users/:userId/posts/:postId') // Returns ['userId', 'postId']
417
+ */
418
+ private extractPathParams;
419
+ /**
420
+ * Creates a Proxy object that tracks environment variable accesses.
421
+ * Returns both the proxy and a set of accessed environment variable names.
422
+ */
423
+ private createEnvProxy;
383
424
  /**
384
425
  * Helper to extract environment variable names from a string or string array.
385
426
  * Environment variables are identified by the pattern $VAR_NAME where VAR_NAME
@@ -395,26 +436,39 @@ export declare class Router {
395
436
  * Automatically enables rewrite caching by adding the x-vercel-enable-rewrite-caching header.
396
437
  *
397
438
  * @example
398
- * // This will automatically enable caching for the rewrite
399
- * import { createRouter, param, runtimeEnv } from '@vercel/router-sdk';
400
- * const router = createRouter();
439
+ * // Simple rewrite
401
440
  * router.rewrite('/api/(.*)', 'https://old-on-prem.com/$1')
402
441
  *
403
- * // With transforms
442
+ * // With transforms using callback
443
+ * router.rewrite('/users/:userId', 'https://api.example.com/users/$1', ({userId, env}) => ({
444
+ * requestHeaders: {
445
+ * 'x-user-id': userId,
446
+ * 'authorization': `Bearer ${env.API_TOKEN}`
447
+ * }
448
+ * }));
449
+ *
450
+ * // With transforms using object (legacy)
404
451
  * router.rewrite('/users/:userId', 'https://api.example.com/users/$1', {
405
452
  * requestHeaders: {
406
- * 'x-user-id': param('userId'),
407
- * 'authorization': `Bearer ${runtimeEnv('API_TOKEN')}`
453
+ * 'x-user-id': param('userId')
408
454
  * }
409
455
  * });
410
456
  */
411
- rewrite(source: string, destination: string, options?: {
457
+ rewrite(source: string, destination: string, optionsOrCallback?: {
412
458
  has?: Condition[];
413
459
  missing?: Condition[];
414
460
  requestHeaders?: Record<string, string | string[]>;
415
461
  responseHeaders?: Record<string, string | string[]>;
416
462
  requestQuery?: Record<string, string | string[]>;
417
- }): this;
463
+ } | ((params: Record<string, string> & {
464
+ env: any;
465
+ }) => {
466
+ has?: Condition[];
467
+ missing?: Condition[];
468
+ requestHeaders?: Record<string, string | string[]>;
469
+ responseHeaders?: Record<string, string | string[]>;
470
+ requestQuery?: Record<string, string | string[]>;
471
+ })): this;
418
472
  /**
419
473
  * Loads rewrite rules asynchronously and appends them.
420
474
  * Automatically enables rewrite caching for all loaded rules by adding the x-vercel-enable-rewrite-caching header.
@@ -427,9 +481,19 @@ export declare class Router {
427
481
  /**
428
482
  * Adds a single redirect rule (synchronous).
429
483
  * @example
484
+ * // Simple redirect
430
485
  * router.redirect('/old-path', '/new-path', { permanent: true })
431
486
  *
432
- * // With transforms
487
+ * // With transforms using callback
488
+ * router.redirect('/users/:userId', '/new-users/$1', ({userId, env}) => ({
489
+ * permanent: true,
490
+ * requestHeaders: {
491
+ * 'x-user-id': userId,
492
+ * 'x-api-key': env.API_KEY
493
+ * }
494
+ * }))
495
+ *
496
+ * // With transforms using object (legacy)
433
497
  * router.redirect('/users/:userId', '/new-users/$1', {
434
498
  * permanent: true,
435
499
  * requestHeaders: {
@@ -437,7 +501,7 @@ export declare class Router {
437
501
  * }
438
502
  * })
439
503
  */
440
- redirect(source: string, destination: string, options?: {
504
+ redirect(source: string, destination: string, optionsOrCallback?: {
441
505
  permanent?: boolean;
442
506
  statusCode?: number;
443
507
  has?: Condition[];
@@ -445,7 +509,17 @@ export declare class Router {
445
509
  requestHeaders?: Record<string, string | string[]>;
446
510
  responseHeaders?: Record<string, string | string[]>;
447
511
  requestQuery?: Record<string, string | string[]>;
448
- }): this;
512
+ } | ((params: Record<string, string> & {
513
+ env: any;
514
+ }) => {
515
+ permanent?: boolean;
516
+ statusCode?: number;
517
+ has?: Condition[];
518
+ missing?: Condition[];
519
+ requestHeaders?: Record<string, string | string[]>;
520
+ responseHeaders?: Record<string, string | string[]>;
521
+ requestQuery?: Record<string, string | string[]>;
522
+ })): this;
449
523
  /**
450
524
  * Loads redirect rules asynchronously and appends them.
451
525
  */
package/dist/router.js CHANGED
@@ -40,9 +40,77 @@ class Router {
40
40
  this.cronRules = [];
41
41
  this.cleanUrlsConfig = undefined;
42
42
  this.trailingSlashConfig = undefined;
43
+ this.bulkRedirectsPathConfig = undefined;
43
44
  this.buildCommandConfig = undefined;
44
45
  this.installCommandConfig = undefined;
45
46
  }
47
+ /**
48
+ * Property setter for cleanUrls configuration.
49
+ */
50
+ set cleanUrls(value) {
51
+ this.cleanUrlsConfig = value;
52
+ }
53
+ /**
54
+ * Property getter for cleanUrls configuration.
55
+ */
56
+ get cleanUrls() {
57
+ return this.cleanUrlsConfig;
58
+ }
59
+ /**
60
+ * Property setter for trailingSlash configuration.
61
+ */
62
+ set trailingSlash(value) {
63
+ this.trailingSlashConfig = value;
64
+ }
65
+ /**
66
+ * Property getter for trailingSlash configuration.
67
+ */
68
+ get trailingSlash() {
69
+ return this.trailingSlashConfig;
70
+ }
71
+ /**
72
+ * Property setter for bulkRedirectsPath configuration.
73
+ */
74
+ set bulkRedirectsPath(value) {
75
+ this.bulkRedirectsPathConfig = value;
76
+ }
77
+ /**
78
+ * Property getter for bulkRedirectsPath configuration.
79
+ */
80
+ get bulkRedirectsPath() {
81
+ return this.bulkRedirectsPathConfig;
82
+ }
83
+ /**
84
+ * Helper to extract path parameter names from a source pattern.
85
+ * Path parameters are identified by :paramName syntax.
86
+ * @example
87
+ * extractPathParams('/users/:userId/posts/:postId') // Returns ['userId', 'postId']
88
+ */
89
+ extractPathParams(source) {
90
+ const params = [];
91
+ const matches = source.matchAll(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
92
+ for (const match of matches) {
93
+ params.push(match[1]);
94
+ }
95
+ return params;
96
+ }
97
+ /**
98
+ * Creates a Proxy object that tracks environment variable accesses.
99
+ * Returns both the proxy and a set of accessed environment variable names.
100
+ */
101
+ createEnvProxy() {
102
+ const accessedVars = new Set();
103
+ const proxy = new Proxy({}, {
104
+ get(_target, prop) {
105
+ if (typeof prop === 'string') {
106
+ accessedVars.add(prop);
107
+ return `$${prop}`;
108
+ }
109
+ return undefined;
110
+ }
111
+ });
112
+ return { proxy, accessedVars };
113
+ }
46
114
  /**
47
115
  * Helper to extract environment variable names from a string or string array.
48
116
  * Environment variables are identified by the pattern $VAR_NAME where VAR_NAME
@@ -112,22 +180,44 @@ class Router {
112
180
  * Automatically enables rewrite caching by adding the x-vercel-enable-rewrite-caching header.
113
181
  *
114
182
  * @example
115
- * // This will automatically enable caching for the rewrite
116
- * import { createRouter, param, runtimeEnv } from '@vercel/router-sdk';
117
- * const router = createRouter();
183
+ * // Simple rewrite
118
184
  * router.rewrite('/api/(.*)', 'https://old-on-prem.com/$1')
119
185
  *
120
- * // With transforms
186
+ * // With transforms using callback
187
+ * router.rewrite('/users/:userId', 'https://api.example.com/users/$1', ({userId, env}) => ({
188
+ * requestHeaders: {
189
+ * 'x-user-id': userId,
190
+ * 'authorization': `Bearer ${env.API_TOKEN}`
191
+ * }
192
+ * }));
193
+ *
194
+ * // With transforms using object (legacy)
121
195
  * router.rewrite('/users/:userId', 'https://api.example.com/users/$1', {
122
196
  * requestHeaders: {
123
- * 'x-user-id': param('userId'),
124
- * 'authorization': `Bearer ${runtimeEnv('API_TOKEN')}`
197
+ * 'x-user-id': param('userId')
125
198
  * }
126
199
  * });
127
200
  */
128
- rewrite(source, destination, options) {
201
+ rewrite(source, destination, optionsOrCallback) {
129
202
  this.validateSourcePattern(source);
130
203
  (0, validation_1.validateCaptureGroupReferences)(source, destination);
204
+ let options;
205
+ // Handle callback syntax
206
+ if (typeof optionsOrCallback === 'function') {
207
+ const pathParams = this.extractPathParams(source);
208
+ const { proxy: envProxy } = this.createEnvProxy();
209
+ // Create params object with path parameters as $paramName
210
+ const paramsObj = {};
211
+ for (const param of pathParams) {
212
+ paramsObj[param] = `$${param}`;
213
+ }
214
+ paramsObj.env = envProxy;
215
+ // Call the callback to get options
216
+ options = optionsOrCallback(paramsObj);
217
+ }
218
+ else {
219
+ options = optionsOrCallback;
220
+ }
131
221
  // Extract transform options
132
222
  const { requestHeaders, responseHeaders, requestQuery, has, missing } = options || {};
133
223
  const transformOpts = {
@@ -182,9 +272,19 @@ class Router {
182
272
  /**
183
273
  * Adds a single redirect rule (synchronous).
184
274
  * @example
275
+ * // Simple redirect
185
276
  * router.redirect('/old-path', '/new-path', { permanent: true })
186
277
  *
187
- * // With transforms
278
+ * // With transforms using callback
279
+ * router.redirect('/users/:userId', '/new-users/$1', ({userId, env}) => ({
280
+ * permanent: true,
281
+ * requestHeaders: {
282
+ * 'x-user-id': userId,
283
+ * 'x-api-key': env.API_KEY
284
+ * }
285
+ * }))
286
+ *
287
+ * // With transforms using object (legacy)
188
288
  * router.redirect('/users/:userId', '/new-users/$1', {
189
289
  * permanent: true,
190
290
  * requestHeaders: {
@@ -192,9 +292,26 @@ class Router {
192
292
  * }
193
293
  * })
194
294
  */
195
- redirect(source, destination, options) {
295
+ redirect(source, destination, optionsOrCallback) {
196
296
  this.validateSourcePattern(source);
197
297
  (0, validation_1.validateCaptureGroupReferences)(source, destination);
298
+ let options;
299
+ // Handle callback syntax
300
+ if (typeof optionsOrCallback === 'function') {
301
+ const pathParams = this.extractPathParams(source);
302
+ const { proxy: envProxy } = this.createEnvProxy();
303
+ // Create params object with path parameters as $paramName
304
+ const paramsObj = {};
305
+ for (const param of pathParams) {
306
+ paramsObj[param] = `$${param}`;
307
+ }
308
+ paramsObj.env = envProxy;
309
+ // Call the callback to get options
310
+ options = optionsOrCallback(paramsObj);
311
+ }
312
+ else {
313
+ options = optionsOrCallback;
314
+ }
198
315
  // Extract transform options
199
316
  const { requestHeaders, responseHeaders, requestQuery, permanent, statusCode, has, missing, } = options || {};
200
317
  // If transforms are provided, create a route instead of a redirect
@@ -380,7 +497,10 @@ class Router {
380
497
  const config = {
381
498
  routes: allRoutes,
382
499
  };
383
- // Only include buildCommand/installCommand if they're explicitly set
500
+ // Only include optional fields if they're explicitly set
501
+ if (this.bulkRedirectsPathConfig !== undefined) {
502
+ config.bulkRedirectsPath = this.bulkRedirectsPathConfig;
503
+ }
384
504
  if (this.buildCommandConfig !== undefined) {
385
505
  config.buildCommand = this.buildCommandConfig;
386
506
  }
@@ -398,7 +518,10 @@ class Router {
398
518
  trailingSlash: this.trailingSlashConfig,
399
519
  crons: this.cronRules,
400
520
  };
401
- // Only include buildCommand/installCommand if they're explicitly set
521
+ // Only include optional fields if they're explicitly set
522
+ if (this.bulkRedirectsPathConfig !== undefined) {
523
+ config.bulkRedirectsPath = this.bulkRedirectsPathConfig;
524
+ }
402
525
  if (this.buildCommandConfig !== undefined) {
403
526
  config.buildCommand = this.buildCommandConfig;
404
527
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/config",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "A TypeScript SDK for programmatically generating Vercel configuration files",
5
5
  "bugs": {
6
6
  "url": "https://github.com/vercel/router-sdk/issues"