@tanstack/cta-engine 0.10.0-alpha.13 → 0.10.0-alpha.15

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/dist/cli.js CHANGED
@@ -7,29 +7,30 @@ import { SUPPORTED_TOOLCHAINS } from './toolchain.js';
7
7
  import runServer from './mcp.js';
8
8
  import { listAddOns } from './add-ons.js';
9
9
  import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js';
10
- // import { initAddOn } from './custom-add-on.js'
10
+ import { initAddOn } from './custom-add-on.js';
11
11
  import { createDefaultEnvironment } from './environment.js';
12
+ import { add } from './add.js';
12
13
  export function cli({ name, appName, forcedMode, forcedAddOns, }) {
13
14
  const program = new Command();
14
15
  program.name(name).description(`CLI to create a new ${appName} application`);
15
- // program
16
- // .command('add')
17
- // .argument('add-on', 'Name of the add-on (or add-ons separated by commas)')
18
- // .action(async (addOn: string) => {
19
- // await add(addOn.split(',').map((addon) => addon.trim()))
20
- // })
21
- // program
22
- // .command('update-add-on')
23
- // .description('Create or update an add-on from the current project')
24
- // .action(async () => {
25
- // await initAddOn('add-on')
26
- // })
27
- // program
28
- // .command('update-overlay')
29
- // .description('Create or update a project overlay from the current project')
30
- // .action(async () => {
31
- // await initAddOn('overlay')
32
- // })
16
+ program
17
+ .command('add')
18
+ .argument('add-on', 'Name of the add-on (or add-ons separated by commas)')
19
+ .action(async (addOn) => {
20
+ await add(addOn.split(',').map((addon) => addon.trim()));
21
+ });
22
+ program
23
+ .command('update-add-on')
24
+ .description('Create or update an add-on from the current project')
25
+ .action(async () => {
26
+ await initAddOn('add-on');
27
+ });
28
+ program
29
+ .command('update-starter')
30
+ .description('Create or update a project starter from the current project')
31
+ .action(async () => {
32
+ await initAddOn('starter');
33
+ });
33
34
  program.argument('[project-name]', 'name of the project');
34
35
  if (!forcedMode) {
35
36
  program.option('--template <type>', 'project template (typescript, javascript, file-router)', (value) => {
@@ -48,7 +49,7 @@ export function cli({ name, appName, forcedMode, forcedAddOns, }) {
48
49
  }
49
50
  return value;
50
51
  }, DEFAULT_FRAMEWORK)
51
- // .option('--overlay [url]', 'add an overlay from a URL', false)
52
+ .option('--starter [url]', 'initialize this project from a starter URL', false)
52
53
  .option(`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`, `Explicitly tell the CLI to use this package manager`, (value) => {
53
54
  if (!SUPPORTED_PACKAGE_MANAGERS.includes(value)) {
54
55
  throw new InvalidArgumentError(`Invalid package manager: ${value}. The following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`);
@@ -71,7 +72,7 @@ export function cli({ name, appName, forcedMode, forcedAddOns, }) {
71
72
  })
72
73
  .option('--list-add-ons', 'list all available add-ons', false)
73
74
  .option('--no-git', 'do not create a git repository')
74
- .option('--target-dir <path>', 'the directory to create the project in')
75
+ .option('--target-dir <path>', 'the target directory for the application root')
75
76
  .option('--mcp', 'run the MCP server', false)
76
77
  .option('--mcp-sse', 'run the MCP server in SSE mode', false);
77
78
  program.action(async (projectName, options) => {
@@ -332,9 +332,9 @@ export async function createApp(options, { silent = false, environment, cwd, app
332
332
  }
333
333
  }
334
334
  }
335
- if (options.overlay) {
336
- if (options.overlay.shadcnComponents) {
337
- for (const component of options.overlay.shadcnComponents) {
335
+ if (options.starter) {
336
+ if (options.starter.shadcnComponents) {
337
+ for (const component of options.starter.shadcnComponents) {
338
338
  shadcnComponents.add(component);
339
339
  }
340
340
  }
@@ -433,11 +433,11 @@ export async function createApp(options, { silent = false, environment, cwd, app
433
433
  }
434
434
  // Create the README.md
435
435
  await templateFile(templateDirBase, 'README.md.ejs');
436
- // Adding overlay
437
- if (options.overlay) {
438
- s?.start(`Setting up overlay ${options.overlay.name}...`);
439
- await runAddOn(options.overlay);
440
- s?.stop(`Overlay ${options.overlay.name} setup complete`);
436
+ // Adding starter
437
+ if (options.starter) {
438
+ s?.start(`Setting up starter ${options.starter.name}...`);
439
+ await runAddOn(options.starter);
440
+ s?.stop(`Starter ${options.starter.name} setup complete`);
441
441
  }
442
442
  // Install dependencies
443
443
  s?.start(`Installing dependencies via ${options.packageManager}...`);
@@ -492,6 +492,6 @@ Use the following commands to start your app:
492
492
  % cd ${options.projectName}
493
493
  % ${startCommand}
494
494
 
495
- Please read the README.md for more information on testing, styling, adding routes, react-query, etc.${errorStatement}`);
495
+ Please check the README.md for more information on testing, styling, adding routes, react-query, etc.${errorStatement}`);
496
496
  }
497
497
  }
@@ -9,11 +9,11 @@ import { finalizeAddOns } from './add-ons.js';
9
9
  import { readFileHelper } from './file-helper.js';
10
10
  const INFO_FILE = {
11
11
  'add-on': '.add-on/info.json',
12
- overlay: 'overlay-info.json',
12
+ starter: 'starter-info.json',
13
13
  };
14
14
  const COMPILED_FILE = {
15
15
  'add-on': 'add-on.json',
16
- overlay: 'overlay.json',
16
+ starter: 'starter.json',
17
17
  };
18
18
  const ADD_ON_DIR = '.add-on';
19
19
  const ASSETS_DIR = 'assets';
@@ -76,7 +76,7 @@ export default (parentRoute: RootRoute) => createRoute({
76
76
  .trim();
77
77
  return { url: path, code, name };
78
78
  }
79
- async function createOptions(json) {
79
+ export async function createAppOptionsFromPersisted(json) {
80
80
  return {
81
81
  ...json,
82
82
  chosenAddOns: await finalizeAddOns(json.framework, json.mode, [...json.existingAddOns]),
@@ -154,12 +154,13 @@ To create an add-on, the project must be created with TypeScript.`);
154
154
  : {
155
155
  name: `${persistedOptions.projectName}-${mode}`,
156
156
  version: '0.0.1',
157
- description: mode === 'add-on' ? 'Add-on' : 'Project overlay',
157
+ description: mode === 'add-on' ? 'Add-on' : 'Project starter',
158
158
  author: 'Jane Smith <jane.smith@example.com>',
159
159
  license: 'MIT',
160
160
  link: `https://github.com/jane-smith/${persistedOptions.projectName}-${mode}`,
161
161
  command: {},
162
162
  shadcnComponents: [],
163
+ framework: persistedOptions.framework,
163
164
  templates: [persistedOptions.mode],
164
165
  routes: [],
165
166
  warning: '',
@@ -173,7 +174,7 @@ To create an add-on, the project must be created with TypeScript.`);
173
174
  },
174
175
  };
175
176
  const compiledInfo = JSON.parse(JSON.stringify(info));
176
- const originalOutput = await runCreateApp(await createOptions(persistedOptions));
177
+ const originalOutput = await runCreateApp(await createAppOptionsFromPersisted(persistedOptions));
177
178
  const originalPackageJson = JSON.parse(originalOutput.files[resolve(process.cwd(), 'package.json')]);
178
179
  const currentPackageJson = JSON.parse((await readFile('package.json')).toString());
179
180
  for (const script of Object.keys(currentPackageJson.scripts)) {
@@ -201,7 +202,7 @@ To create an add-on, the project must be created with TypeScript.`);
201
202
  // Find altered files
202
203
  const changedFiles = {};
203
204
  await compareFiles('.', IGNORE_FILES, originalOutput.files, changedFiles);
204
- if (mode === 'overlay') {
205
+ if (mode === 'starter') {
205
206
  compiledInfo.files = changedFiles;
206
207
  }
207
208
  else {
@@ -235,7 +236,8 @@ To create an add-on, the project must be created with TypeScript.`);
235
236
  compiledInfo.routes = info.routes;
236
237
  compiledInfo.framework = persistedOptions.framework;
237
238
  compiledInfo.addDependencies = persistedOptions.existingAddOns;
238
- if (mode === 'overlay') {
239
+ compiledInfo.packageAdditions = info.packageAdditions;
240
+ if (mode === 'starter') {
239
241
  compiledInfo.mode = persistedOptions.mode;
240
242
  compiledInfo.typescript = persistedOptions.typescript;
241
243
  compiledInfo.tailwind = persistedOptions.tailwind;
package/dist/index.js CHANGED
@@ -1 +1,5 @@
1
1
  export { cli } from './cli.js';
2
+ export { getAllAddOns } from './add-ons.js';
3
+ export { createApp } from './create-app.js';
4
+ export { createAppOptionsFromPersisted } from './custom-add-on.js';
5
+ export { createMemoryEnvironment, createDefaultEnvironment, } from './environment.js';
package/dist/options.js CHANGED
@@ -23,22 +23,22 @@ export async function normalizeOptions(cliOptions, forcedAddOns) {
23
23
  tailwind = true;
24
24
  }
25
25
  let mode = cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER;
26
- const overlay = cliOptions.overlay
27
- ? (await loadRemoteAddOn(cliOptions.overlay))
26
+ const starter = cliOptions.starter
27
+ ? (await loadRemoteAddOn(cliOptions.starter))
28
28
  : undefined;
29
- if (overlay) {
30
- tailwind = overlay.tailwind;
31
- typescript = overlay.typescript;
32
- cliOptions.framework = overlay.framework;
33
- mode = overlay.mode;
29
+ if (starter) {
30
+ tailwind = starter.tailwind;
31
+ typescript = starter.typescript;
32
+ cliOptions.framework = starter.framework;
33
+ mode = starter.mode;
34
34
  }
35
35
  let addOns = false;
36
36
  let chosenAddOns = [];
37
37
  if (Array.isArray(cliOptions.addOns) ||
38
- overlay?.dependsOn ||
38
+ starter?.dependsOn ||
39
39
  forcedAddOns) {
40
40
  addOns = true;
41
- let finalAddOns = Array.from(new Set([...(overlay?.dependsOn || []), ...(forcedAddOns || [])]));
41
+ let finalAddOns = Array.from(new Set([...(starter?.dependsOn || []), ...(forcedAddOns || [])]));
42
42
  if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
43
43
  finalAddOns = Array.from(new Set([
44
44
  ...(forcedAddOns || []),
@@ -64,7 +64,7 @@ export async function normalizeOptions(cliOptions, forcedAddOns) {
64
64
  addOns,
65
65
  chosenAddOns,
66
66
  variableValues: {},
67
- overlay,
67
+ starter,
68
68
  };
69
69
  }
70
70
  }
@@ -1,3 +1,6 @@
1
- type AddOnMode = 'add-on' | 'overlay';
1
+ import type { Options } from './types.js';
2
+ import type { PersistedOptions } from './config-file.js';
3
+ type AddOnMode = 'add-on' | 'starter';
4
+ export declare function createAppOptionsFromPersisted(json: PersistedOptions): Promise<Required<Options>>;
2
5
  export declare function initAddOn(mode: AddOnMode): Promise<void>;
3
6
  export {};
@@ -1 +1,7 @@
1
1
  export { cli } from './cli.js';
2
+ export { getAllAddOns } from './add-ons.js';
3
+ export { createApp } from './create-app.js';
4
+ export { createAppOptionsFromPersisted } from './custom-add-on.js';
5
+ export { createMemoryEnvironment, createDefaultEnvironment, } from './environment.js';
6
+ export type { AddOn, Framework, Mode } from './types.js';
7
+ export type { PersistedOptions } from './config-file.js';
@@ -3,6 +3,7 @@ import type { PackageManager } from './package-manager.js';
3
3
  import type { ToolChain } from './toolchain.js';
4
4
  export type Framework = 'solid' | 'react';
5
5
  export type TemplateOptions = 'typescript' | 'javascript' | 'file-router';
6
+ export type Mode = typeof CODE_ROUTER | typeof FILE_ROUTER;
6
7
  export interface Options {
7
8
  framework: Framework;
8
9
  projectName: string;
@@ -10,12 +11,12 @@ export interface Options {
10
11
  tailwind: boolean;
11
12
  packageManager: PackageManager;
12
13
  toolchain: ToolChain;
13
- mode: typeof CODE_ROUTER | typeof FILE_ROUTER;
14
+ mode: Mode;
14
15
  addOns: boolean;
15
16
  chosenAddOns: Array<AddOn>;
16
17
  git: boolean;
17
18
  variableValues: Record<string, string | number | boolean>;
18
- overlay?: AddOn | undefined;
19
+ starter?: AddOn | undefined;
19
20
  }
20
21
  export interface CliOptions {
21
22
  template?: TemplateOptions;
@@ -29,7 +30,7 @@ export interface CliOptions {
29
30
  listAddOns?: boolean;
30
31
  mcp?: boolean;
31
32
  mcpSse?: boolean;
32
- overlay?: string;
33
+ starter?: string;
33
34
  targetDir?: string;
34
35
  }
35
36
  export type Environment = {
@@ -69,7 +70,7 @@ export type AddOn = {
69
70
  id: string;
70
71
  name: string;
71
72
  description: string;
72
- type: 'add-on' | 'example' | 'overlay';
73
+ type: 'add-on' | 'example' | 'starter';
73
74
  link: string;
74
75
  templates: Array<string>;
75
76
  routes: Array<{
@@ -94,13 +95,13 @@ export type AddOn = {
94
95
  files?: Record<string, string>;
95
96
  deletedFiles?: Array<string>;
96
97
  };
97
- export type Overlay = AddOn & {
98
- type: 'overlay';
98
+ export type Starter = AddOn & {
99
+ type: 'starter';
99
100
  version: string;
100
101
  author: string;
101
102
  link: string;
102
103
  license: string;
103
- mode: typeof CODE_ROUTER | typeof FILE_ROUTER;
104
+ mode: Mode;
104
105
  framework: Framework;
105
106
  typescript: boolean;
106
107
  tailwind: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cta-engine",
3
- "version": "0.10.0-alpha.13",
3
+ "version": "0.10.0-alpha.15",
4
4
  "description": "Tanstack Application Builder Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/cli.ts CHANGED
@@ -9,10 +9,10 @@ import { SUPPORTED_TOOLCHAINS } from './toolchain.js'
9
9
  import runServer from './mcp.js'
10
10
  import { listAddOns } from './add-ons.js'
11
11
  import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js'
12
- // import { initAddOn } from './custom-add-on.js'
12
+ import { initAddOn } from './custom-add-on.js'
13
13
 
14
14
  import { createDefaultEnvironment } from './environment.js'
15
- // import { add } from './add.js'
15
+ import { add } from './add.js'
16
16
 
17
17
  import type { PackageManager } from './package-manager.js'
18
18
  import type { ToolChain } from './toolchain.js'
@@ -33,26 +33,26 @@ export function cli({
33
33
 
34
34
  program.name(name).description(`CLI to create a new ${appName} application`)
35
35
 
36
- // program
37
- // .command('add')
38
- // .argument('add-on', 'Name of the add-on (or add-ons separated by commas)')
39
- // .action(async (addOn: string) => {
40
- // await add(addOn.split(',').map((addon) => addon.trim()))
41
- // })
36
+ program
37
+ .command('add')
38
+ .argument('add-on', 'Name of the add-on (or add-ons separated by commas)')
39
+ .action(async (addOn: string) => {
40
+ await add(addOn.split(',').map((addon) => addon.trim()))
41
+ })
42
42
 
43
- // program
44
- // .command('update-add-on')
45
- // .description('Create or update an add-on from the current project')
46
- // .action(async () => {
47
- // await initAddOn('add-on')
48
- // })
43
+ program
44
+ .command('update-add-on')
45
+ .description('Create or update an add-on from the current project')
46
+ .action(async () => {
47
+ await initAddOn('add-on')
48
+ })
49
49
 
50
- // program
51
- // .command('update-overlay')
52
- // .description('Create or update a project overlay from the current project')
53
- // .action(async () => {
54
- // await initAddOn('overlay')
55
- // })
50
+ program
51
+ .command('update-starter')
52
+ .description('Create or update a project starter from the current project')
53
+ .action(async () => {
54
+ await initAddOn('starter')
55
+ })
56
56
 
57
57
  program.argument('[project-name]', 'name of the project')
58
58
 
@@ -90,7 +90,11 @@ export function cli({
90
90
  },
91
91
  DEFAULT_FRAMEWORK,
92
92
  )
93
- // .option('--overlay [url]', 'add an overlay from a URL', false)
93
+ .option(
94
+ '--starter [url]',
95
+ 'initialize this project from a starter URL',
96
+ false,
97
+ )
94
98
  .option<PackageManager>(
95
99
  `--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`,
96
100
  `Explicitly tell the CLI to use this package manager`,
@@ -133,7 +137,10 @@ export function cli({
133
137
  )
134
138
  .option('--list-add-ons', 'list all available add-ons', false)
135
139
  .option('--no-git', 'do not create a git repository')
136
- .option('--target-dir <path>', 'the directory to create the project in')
140
+ .option(
141
+ '--target-dir <path>',
142
+ 'the target directory for the application root',
143
+ )
137
144
  .option('--mcp', 'run the MCP server', false)
138
145
  .option('--mcp-sse', 'run the MCP server in SSE mode', false)
139
146
 
package/src/create-app.ts CHANGED
@@ -529,9 +529,9 @@ export async function createApp(
529
529
  }
530
530
  }
531
531
  }
532
- if (options.overlay) {
533
- if (options.overlay.shadcnComponents) {
534
- for (const component of options.overlay.shadcnComponents) {
532
+ if (options.starter) {
533
+ if (options.starter.shadcnComponents) {
534
+ for (const component of options.starter.shadcnComponents) {
535
535
  shadcnComponents.add(component)
536
536
  }
537
537
  }
@@ -714,11 +714,11 @@ export async function createApp(
714
714
  // Create the README.md
715
715
  await templateFile(templateDirBase, 'README.md.ejs')
716
716
 
717
- // Adding overlay
718
- if (options.overlay) {
719
- s?.start(`Setting up overlay ${options.overlay.name}...`)
720
- await runAddOn(options.overlay)
721
- s?.stop(`Overlay ${options.overlay.name} setup complete`)
717
+ // Adding starter
718
+ if (options.starter) {
719
+ s?.start(`Setting up starter ${options.starter.name}...`)
720
+ await runAddOn(options.starter)
721
+ s?.stop(`Starter ${options.starter.name} setup complete`)
722
722
  }
723
723
 
724
724
  // Install dependencies
@@ -799,6 +799,6 @@ Use the following commands to start your app:
799
799
  % cd ${options.projectName}
800
800
  % ${startCommand}
801
801
 
802
- Please read the README.md for more information on testing, styling, adding routes, react-query, etc.${errorStatement}`)
802
+ Please check the README.md for more information on testing, styling, adding routes, react-query, etc.${errorStatement}`)
803
803
  }
804
804
  }
@@ -12,15 +12,15 @@ import { readFileHelper } from './file-helper.js'
12
12
  import type { Framework, Options } from './types.js'
13
13
  import type { PersistedOptions } from './config-file.js'
14
14
 
15
- type AddOnMode = 'add-on' | 'overlay'
15
+ type AddOnMode = 'add-on' | 'starter'
16
16
 
17
17
  const INFO_FILE: Record<AddOnMode, string> = {
18
18
  'add-on': '.add-on/info.json',
19
- overlay: 'overlay-info.json',
19
+ starter: 'starter-info.json',
20
20
  }
21
21
  const COMPILED_FILE: Record<AddOnMode, string> = {
22
22
  'add-on': 'add-on.json',
23
- overlay: 'overlay.json',
23
+ starter: 'starter.json',
24
24
  }
25
25
 
26
26
  const ADD_ON_DIR = '.add-on'
@@ -102,7 +102,7 @@ export default (parentRoute: RootRoute) => createRoute({
102
102
  return { url: path, code, name }
103
103
  }
104
104
 
105
- async function createOptions(
105
+ export async function createAppOptionsFromPersisted(
106
106
  json: PersistedOptions,
107
107
  ): Promise<Required<Options>> {
108
108
  return {
@@ -198,12 +198,13 @@ To create an add-on, the project must be created with TypeScript.`)
198
198
  : {
199
199
  name: `${persistedOptions.projectName}-${mode}`,
200
200
  version: '0.0.1',
201
- description: mode === 'add-on' ? 'Add-on' : 'Project overlay',
201
+ description: mode === 'add-on' ? 'Add-on' : 'Project starter',
202
202
  author: 'Jane Smith <jane.smith@example.com>',
203
203
  license: 'MIT',
204
204
  link: `https://github.com/jane-smith/${persistedOptions.projectName}-${mode}`,
205
205
  command: {},
206
206
  shadcnComponents: [],
207
+ framework: persistedOptions.framework,
207
208
  templates: [persistedOptions.mode],
208
209
  routes: [],
209
210
  warning: '',
@@ -220,7 +221,7 @@ To create an add-on, the project must be created with TypeScript.`)
220
221
  const compiledInfo = JSON.parse(JSON.stringify(info))
221
222
 
222
223
  const originalOutput = await runCreateApp(
223
- await createOptions(persistedOptions),
224
+ await createAppOptionsFromPersisted(persistedOptions),
224
225
  )
225
226
 
226
227
  const originalPackageJson = JSON.parse(
@@ -264,7 +265,7 @@ To create an add-on, the project must be created with TypeScript.`)
264
265
  // Find altered files
265
266
  const changedFiles: Record<string, string> = {}
266
267
  await compareFiles('.', IGNORE_FILES, originalOutput.files, changedFiles)
267
- if (mode === 'overlay') {
268
+ if (mode === 'starter') {
268
269
  compiledInfo.files = changedFiles
269
270
  } else {
270
271
  const assetsDir = resolve(ADD_ON_DIR, ASSETS_DIR)
@@ -302,8 +303,9 @@ To create an add-on, the project must be created with TypeScript.`)
302
303
  compiledInfo.routes = info.routes
303
304
  compiledInfo.framework = persistedOptions.framework
304
305
  compiledInfo.addDependencies = persistedOptions.existingAddOns
306
+ compiledInfo.packageAdditions = info.packageAdditions
305
307
 
306
- if (mode === 'overlay') {
308
+ if (mode === 'starter') {
307
309
  compiledInfo.mode = persistedOptions.mode
308
310
  compiledInfo.typescript = persistedOptions.typescript
309
311
  compiledInfo.tailwind = persistedOptions.tailwind
package/src/index.ts CHANGED
@@ -1 +1,11 @@
1
1
  export { cli } from './cli.js'
2
+ export { getAllAddOns } from './add-ons.js'
3
+ export { createApp } from './create-app.js'
4
+ export { createAppOptionsFromPersisted } from './custom-add-on.js'
5
+ export {
6
+ createMemoryEnvironment,
7
+ createDefaultEnvironment,
8
+ } from './environment.js'
9
+
10
+ export type { AddOn, Framework, Mode } from './types.js'
11
+ export type { PersistedOptions } from './config-file.js'
package/src/options.ts CHANGED
@@ -16,7 +16,7 @@ import { DEFAULT_TOOLCHAIN, SUPPORTED_TOOLCHAINS } from './toolchain.js'
16
16
  import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js'
17
17
  import { finalizeAddOns, getAllAddOns, loadRemoteAddOn } from './add-ons.js'
18
18
 
19
- import type { AddOn, CliOptions, Options, Overlay, Variable } from './types.js'
19
+ import type { AddOn, CliOptions, Options, Starter, Variable } from './types.js'
20
20
 
21
21
  // If all CLI options are provided, use them directly
22
22
  export async function normalizeOptions(
@@ -47,27 +47,27 @@ export async function normalizeOptions(
47
47
  let mode: typeof FILE_ROUTER | typeof CODE_ROUTER =
48
48
  cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER
49
49
 
50
- const overlay = cliOptions.overlay
51
- ? ((await loadRemoteAddOn(cliOptions.overlay)) as Overlay)
50
+ const starter = cliOptions.starter
51
+ ? ((await loadRemoteAddOn(cliOptions.starter)) as Starter)
52
52
  : undefined
53
53
 
54
- if (overlay) {
55
- tailwind = overlay.tailwind
56
- typescript = overlay.typescript
57
- cliOptions.framework = overlay.framework
58
- mode = overlay.mode
54
+ if (starter) {
55
+ tailwind = starter.tailwind
56
+ typescript = starter.typescript
57
+ cliOptions.framework = starter.framework
58
+ mode = starter.mode
59
59
  }
60
60
 
61
61
  let addOns = false
62
62
  let chosenAddOns: Array<AddOn> = []
63
63
  if (
64
64
  Array.isArray(cliOptions.addOns) ||
65
- overlay?.dependsOn ||
65
+ starter?.dependsOn ||
66
66
  forcedAddOns
67
67
  ) {
68
68
  addOns = true
69
69
  let finalAddOns = Array.from(
70
- new Set([...(overlay?.dependsOn || []), ...(forcedAddOns || [])]),
70
+ new Set([...(starter?.dependsOn || []), ...(forcedAddOns || [])]),
71
71
  )
72
72
  if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
73
73
  finalAddOns = Array.from(
@@ -102,7 +102,7 @@ export async function normalizeOptions(
102
102
  addOns,
103
103
  chosenAddOns,
104
104
  variableValues: {},
105
- overlay,
105
+ starter,
106
106
  }
107
107
  }
108
108
  }
package/src/types.ts CHANGED
@@ -6,6 +6,8 @@ export type Framework = 'solid' | 'react'
6
6
 
7
7
  export type TemplateOptions = 'typescript' | 'javascript' | 'file-router'
8
8
 
9
+ export type Mode = typeof CODE_ROUTER | typeof FILE_ROUTER
10
+
9
11
  export interface Options {
10
12
  framework: Framework
11
13
  projectName: string
@@ -13,12 +15,12 @@ export interface Options {
13
15
  tailwind: boolean
14
16
  packageManager: PackageManager
15
17
  toolchain: ToolChain
16
- mode: typeof CODE_ROUTER | typeof FILE_ROUTER
18
+ mode: Mode
17
19
  addOns: boolean
18
20
  chosenAddOns: Array<AddOn>
19
21
  git: boolean
20
22
  variableValues: Record<string, string | number | boolean>
21
- overlay?: AddOn | undefined
23
+ starter?: AddOn | undefined
22
24
  }
23
25
 
24
26
  export interface CliOptions {
@@ -33,7 +35,7 @@ export interface CliOptions {
33
35
  listAddOns?: boolean
34
36
  mcp?: boolean
35
37
  mcpSse?: boolean
36
- overlay?: string
38
+ starter?: string
37
39
  targetDir?: string
38
40
  }
39
41
 
@@ -81,7 +83,7 @@ export type AddOn = {
81
83
  id: string
82
84
  name: string
83
85
  description: string
84
- type: 'add-on' | 'example' | 'overlay'
86
+ type: 'add-on' | 'example' | 'starter'
85
87
  link: string
86
88
  templates: Array<string>
87
89
  routes: Array<{
@@ -108,13 +110,13 @@ export type AddOn = {
108
110
  deletedFiles?: Array<string>
109
111
  }
110
112
 
111
- export type Overlay = AddOn & {
112
- type: 'overlay'
113
+ export type Starter = AddOn & {
114
+ type: 'starter'
113
115
  version: string
114
116
  author: string
115
117
  link: string
116
118
  license: string
117
- mode: typeof CODE_ROUTER | typeof FILE_ROUTER
119
+ mode: Mode
118
120
  framework: Framework
119
121
  typescript: boolean
120
122
  tailwind: boolean
@@ -1,2 +1,2 @@
1
- # Clerk configuration, get this key from your [Dashboard](https://clerk.com/dashboard).
1
+ # Clerk configuration, get this key from your [Dashboard](dashboard.clerk.com)
2
2
  VITE_CLERK_PUBLISHABLE_KEY=
@@ -1,3 +1,3 @@
1
- # Convex configuration, get this URL from your [Dashboard](https://convex.dev/dashboard).
1
+ # Convex configuration, get this URL from your [Dashboard](dashboard.convex.dev)
2
2
  CONVEX_DEPLOYMENT=
3
- VITE_CONVEX_URL=
3
+ VITE_CONVEX_URL=
@@ -13,7 +13,7 @@ function getUrl() {
13
13
  if (typeof window !== "undefined") return "";
14
14
  return `http://localhost:${process.env.PORT ?? 3000}`;
15
15
  })();
16
- return base + "/api/trpc";
16
+ return `${base}/api/trpc`;
17
17
  }
18
18
 
19
19
  export const trpcClient = createTRPCClient<TRPCRouter>({
@@ -67,4 +67,4 @@ export function Provider({ children }: { children: React.ReactNode }) {
67
67
  <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
68
68
  )
69
69
  }
70
- <% } %>
70
+ <% } %>
@@ -0,0 +1,24 @@
1
+ import { createAPIFileRoute } from "@tanstack/react-start/api";
2
+ import { getEvent } from "vinxi/http";
3
+
4
+ import { transports } from "@/utils/demo.sse";
5
+
6
+ export const APIRoute = createAPIFileRoute("/api/messages")({
7
+ // @ts-ignore
8
+ POST: async ({ request, params }) => {
9
+ const body = await request.json();
10
+ const url = new URL(request.url);
11
+ const sessionId = url.searchParams.get("sessionId") as string;
12
+ const transport = transports[sessionId];
13
+ if (transport) {
14
+ try {
15
+ getEvent().node.res.statusCode = 200;
16
+ await transport.handleMessage(body);
17
+ } catch (error) {
18
+ getEvent().node.res.send("Error handling message");
19
+ }
20
+ } else {
21
+ getEvent().node.res.send("No transport found for sessionId");
22
+ }
23
+ },
24
+ });
@@ -0,0 +1,23 @@
1
+ import { createAPIFileRoute } from '@tanstack/react-start/api'
2
+ import { getEvent } from 'vinxi/http'
3
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
4
+
5
+ import { transports, server } from '@/utils/demo.sse'
6
+
7
+ export const APIRoute = createAPIFileRoute('/api/sse')({
8
+ // @ts-ignore
9
+ GET: async ({}) => {
10
+ const transport = new SSEServerTransport(
11
+ '/api/messages',
12
+ getEvent().node.res,
13
+ )
14
+ transports[transport.sessionId] = transport
15
+ transport.onerror = (error) => {
16
+ console.error(error)
17
+ }
18
+ getEvent().node.res.on('close', () => {
19
+ delete transports[transport.sessionId]
20
+ })
21
+ await server.connect(transport)
22
+ },
23
+ })
@@ -0,0 +1,31 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
3
+ import { getWebRequest } from "vinxi/http";
4
+
5
+ import guitars from "@/data/example-guitars";
6
+
7
+ export const server = new McpServer({
8
+ name: "guitar-server",
9
+ version: "1.0.0",
10
+ });
11
+ export const transports: { [sessionId: string]: SSEServerTransport } = {};
12
+
13
+ server.tool("getGuitars", {}, async ({}) => {
14
+ const port = new URL(getWebRequest().url).port;
15
+ return {
16
+ content: [
17
+ {
18
+ type: "text",
19
+ text: JSON.stringify(
20
+ guitars.map((guitar) => ({
21
+ id: guitar.id,
22
+ name: guitar.name,
23
+ description: guitar.description,
24
+ price: guitar.price,
25
+ image: `http://localhost:${port}${guitar.image}`,
26
+ }))
27
+ ),
28
+ },
29
+ ],
30
+ };
31
+ });
@@ -2,6 +2,7 @@
2
2
  "dependencies": {
3
3
  "@ai-sdk/anthropic": "^1.1.17",
4
4
  "@ai-sdk/react": "^1.1.23",
5
+ "@modelcontextprotocol/sdk": "^1.8.0",
5
6
  "ai": "^4.1.65",
6
7
  "highlight.js": "^11.11.1",
7
8
  "react-markdown": "^9.0.1",