create-convex-monorepo 0.2.0
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/CONTRIBUTING.md +55 -0
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/assets/backend/PROVENANCE.md +3 -0
- package/assets/backend/convex/_generated/api.d.ts +51 -0
- package/assets/backend/convex/_generated/api.js +23 -0
- package/assets/backend/convex/_generated/dataModel.d.ts +60 -0
- package/assets/backend/convex/_generated/server.d.ts +162 -0
- package/assets/backend/convex/_generated/server.js +101 -0
- package/assets/backend/convex/messages.ts +38 -0
- package/assets/backend/convex/schema.ts +9 -0
- package/assets/backend/convex/tsconfig.json +26 -0
- package/assets/backend-blank/PROVENANCE.md +3 -0
- package/assets/backend-blank/convex/_generated/api.d.ts +45 -0
- package/assets/backend-blank/convex/_generated/api.js +23 -0
- package/assets/backend-blank/convex/_generated/dataModel.d.ts +60 -0
- package/assets/backend-blank/convex/_generated/server.d.ts +162 -0
- package/assets/backend-blank/convex/_generated/server.js +101 -0
- package/assets/backend-blank/convex/schema.ts +3 -0
- package/assets/setup/convex-setup.mjs +250 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +19 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/create.d.ts +8 -0
- package/dist/commands/create.js +182 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/generator/context.d.ts +3 -0
- package/dist/generator/context.js +81 -0
- package/dist/generator/context.js.map +1 -0
- package/dist/generator/index.d.ts +12 -0
- package/dist/generator/index.js +123 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/options.d.ts +15 -0
- package/dist/generator/options.js +85 -0
- package/dist/generator/options.js.map +1 -0
- package/dist/generator/types.d.ts +47 -0
- package/dist/generator/types.js +2 -0
- package/dist/generator/types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/auth/clerk/index.d.ts +2 -0
- package/dist/integrations/auth/clerk/index.js +114 -0
- package/dist/integrations/auth/clerk/index.js.map +1 -0
- package/dist/integrations/auth/index.d.ts +2 -0
- package/dist/integrations/auth/index.js +7 -0
- package/dist/integrations/auth/index.js.map +1 -0
- package/dist/integrations/auth/none/index.d.ts +2 -0
- package/dist/integrations/auth/none/index.js +18 -0
- package/dist/integrations/auth/none/index.js.map +1 -0
- package/dist/integrations/auth/shared.d.ts +11 -0
- package/dist/integrations/auth/shared.js +40 -0
- package/dist/integrations/auth/shared.js.map +1 -0
- package/dist/package-manager/index.d.ts +6 -0
- package/dist/package-manager/index.js +22 -0
- package/dist/package-manager/index.js.map +1 -0
- package/dist/templates/apps/expo/index.d.ts +2 -0
- package/dist/templates/apps/expo/index.js +91 -0
- package/dist/templates/apps/expo/index.js.map +1 -0
- package/dist/templates/apps/index.d.ts +2 -0
- package/dist/templates/apps/index.js +11 -0
- package/dist/templates/apps/index.js.map +1 -0
- package/dist/templates/apps/next/index.d.ts +2 -0
- package/dist/templates/apps/next/index.js +66 -0
- package/dist/templates/apps/next/index.js.map +1 -0
- package/dist/templates/apps/shared.d.ts +24 -0
- package/dist/templates/apps/shared.js +124 -0
- package/dist/templates/apps/shared.js.map +1 -0
- package/dist/templates/apps/tanstack-start/index.d.ts +2 -0
- package/dist/templates/apps/tanstack-start/index.js +82 -0
- package/dist/templates/apps/tanstack-start/index.js.map +1 -0
- package/dist/templates/apps/vite/index.d.ts +2 -0
- package/dist/templates/apps/vite/index.js +42 -0
- package/dist/templates/apps/vite/index.js.map +1 -0
- package/dist/templates/backend/index.d.ts +2 -0
- package/dist/templates/backend/index.js +58 -0
- package/dist/templates/backend/index.js.map +1 -0
- package/dist/templates/root/index.d.ts +2 -0
- package/dist/templates/root/index.js +178 -0
- package/dist/templates/root/index.js.map +1 -0
- package/dist/templates/versions.d.ts +18 -0
- package/dist/templates/versions.js +19 -0
- package/dist/templates/versions.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/docs/adding-a-framework.md +27 -0
- package/docs/adding-an-auth-provider.md +31 -0
- package/docs/architecture.md +49 -0
- package/docs/releases.md +78 -0
- package/docs/research.md +76 -0
- package/docs/verification.md +78 -0
- package/package.json +77 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { getPackageVersion } from '../../version.js';
|
|
3
|
+
import { versions as v } from '../versions.js';
|
|
4
|
+
export async function generateRoot(ctx) {
|
|
5
|
+
const { options, scope } = ctx;
|
|
6
|
+
await ctx.write('scripts/convex-setup.mjs', await readFile(new URL('../../../assets/setup/convex-setup.mjs', import.meta.url), 'utf8'));
|
|
7
|
+
const scripts = {
|
|
8
|
+
dev: `turbo run dev --ui=stream --concurrency=${options.apps.length + 2}`,
|
|
9
|
+
'convex:dev': `pnpm --filter @${scope}/backend dev`,
|
|
10
|
+
'convex:setup': 'node scripts/convex-setup.mjs',
|
|
11
|
+
'convex:link': 'node scripts/convex-setup.mjs --link-only',
|
|
12
|
+
build: 'turbo run build',
|
|
13
|
+
typecheck: 'turbo run typecheck',
|
|
14
|
+
lint: 'turbo run lint',
|
|
15
|
+
};
|
|
16
|
+
for (const app of options.apps)
|
|
17
|
+
scripts[`dev:${app.name}`] = `pnpm --filter @${scope}/${app.name} dev`;
|
|
18
|
+
await ctx.json('package.json', {
|
|
19
|
+
name: options.name,
|
|
20
|
+
private: true,
|
|
21
|
+
version: '0.0.0',
|
|
22
|
+
type: 'module',
|
|
23
|
+
packageManager: `pnpm@${v.pnpm}`,
|
|
24
|
+
engines: { node: '>=22.12.0' },
|
|
25
|
+
scripts,
|
|
26
|
+
devDependencies: { turbo: v.turbo },
|
|
27
|
+
});
|
|
28
|
+
await ctx.write('pnpm-workspace.yaml', "packages:\n - 'apps/*'\n - 'packages/*'\n\nonlyBuiltDependencies:\n - esbuild\n - sharp\n - unrs-resolver\n");
|
|
29
|
+
await ctx.json('turbo.json', {
|
|
30
|
+
$schema: 'https://turborepo.com/schema.json',
|
|
31
|
+
tasks: {
|
|
32
|
+
dev: {
|
|
33
|
+
cache: false,
|
|
34
|
+
persistent: true,
|
|
35
|
+
passThroughEnv: [
|
|
36
|
+
'CONVEX_*',
|
|
37
|
+
'CLERK_*',
|
|
38
|
+
'NEXT_PUBLIC_*',
|
|
39
|
+
'VITE_*',
|
|
40
|
+
'EXPO_PUBLIC_*',
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
build: {
|
|
44
|
+
dependsOn: ['^build'],
|
|
45
|
+
inputs: ['$TURBO_DEFAULT$', '.env*'],
|
|
46
|
+
outputs: ['.next/**', '!.next/cache/**', 'dist/**', '.output/**'],
|
|
47
|
+
env: ['NEXT_PUBLIC_*', 'VITE_*', 'EXPO_PUBLIC_*'],
|
|
48
|
+
passThroughEnv: ['CLERK_SECRET_KEY'],
|
|
49
|
+
},
|
|
50
|
+
[`@${scope}/backend#build`]: { outputs: [] },
|
|
51
|
+
typecheck: { dependsOn: ['^typecheck'], outputs: [] },
|
|
52
|
+
lint: { dependsOn: ['^lint'], outputs: [] },
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
await ctx.write('.gitignore', 'node_modules/\n.turbo/\n.next/\n.output/\ndist/\n.expo/\n.env*\n!.env.example\n!.env.clerk.example\n*.tsbuildinfo\n.DS_Store\n.convex/\n');
|
|
56
|
+
await ctx.json('convex-monorepo.json', {
|
|
57
|
+
version: 1,
|
|
58
|
+
generator: await getPackageVersion(),
|
|
59
|
+
name: options.name,
|
|
60
|
+
packageManager: options.packageManager,
|
|
61
|
+
monorepo: 'turbo',
|
|
62
|
+
apps: options.apps,
|
|
63
|
+
auth: options.auth,
|
|
64
|
+
example: options.example,
|
|
65
|
+
});
|
|
66
|
+
await ctx.json('packages/typescript-config/package.json', {
|
|
67
|
+
name: `@${scope}/typescript-config`,
|
|
68
|
+
private: true,
|
|
69
|
+
version: '0.0.0',
|
|
70
|
+
exports: { './base.json': './base.json' },
|
|
71
|
+
});
|
|
72
|
+
await ctx.json('packages/typescript-config/base.json', {
|
|
73
|
+
compilerOptions: {
|
|
74
|
+
target: 'ES2023',
|
|
75
|
+
lib: ['ES2023', 'DOM', 'DOM.Iterable'],
|
|
76
|
+
module: 'ESNext',
|
|
77
|
+
moduleResolution: 'Bundler',
|
|
78
|
+
strict: true,
|
|
79
|
+
noEmit: true,
|
|
80
|
+
isolatedModules: true,
|
|
81
|
+
esModuleInterop: true,
|
|
82
|
+
resolveJsonModule: true,
|
|
83
|
+
skipLibCheck: true,
|
|
84
|
+
jsx: 'react-jsx',
|
|
85
|
+
allowJs: true,
|
|
86
|
+
forceConsistentCasingInFileNames: true,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
await ctx.json('packages/eslint-config/package.json', {
|
|
90
|
+
name: `@${scope}/eslint-config`,
|
|
91
|
+
version: '0.0.0',
|
|
92
|
+
private: true,
|
|
93
|
+
type: 'module',
|
|
94
|
+
exports: { '.': './index.js' },
|
|
95
|
+
dependencies: { 'typescript-eslint': v.typescriptEslint },
|
|
96
|
+
peerDependencies: { eslint: `^${v.eslint}` },
|
|
97
|
+
});
|
|
98
|
+
await ctx.write('packages/eslint-config/index.js', `import tseslint from 'typescript-eslint';
|
|
99
|
+
export default tseslint.config(
|
|
100
|
+
{ ignores: ['**/_generated/**', '**/routeTree.gen.ts', '**/node_modules/**', '**/dist/**', '**/.next/**', '**/.expo/**', '**/.output/**'] },
|
|
101
|
+
...tseslint.configs.recommended,
|
|
102
|
+
{ files: ['**/*.cjs'], rules: { '@typescript-eslint/no-require-imports': 'off' } },
|
|
103
|
+
{ rules: { '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }] } },
|
|
104
|
+
);
|
|
105
|
+
`);
|
|
106
|
+
const envTable = options.apps
|
|
107
|
+
.map((app) => `| ${app.name} | ${app.framework === 'next' ? 'NEXT_PUBLIC' : app.framework === 'expo' ? 'EXPO_PUBLIC' : 'VITE'}_CONVEX_URL |`)
|
|
108
|
+
.join('\n');
|
|
109
|
+
await ctx.write('README.md', `# ${options.name}
|
|
110
|
+
|
|
111
|
+
${options.apps.map((a) => a.framework).join(', ')} applications share one Convex backend in packages/backend.
|
|
112
|
+
|
|
113
|
+
## First run
|
|
114
|
+
|
|
115
|
+
Use Node 22.12+ and pnpm ${v.pnpm}.
|
|
116
|
+
|
|
117
|
+
\`\`\`sh
|
|
118
|
+
pnpm install
|
|
119
|
+
pnpm convex:setup
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
The setup command runs Convex in its own package and asks you to select or create a deployment. After a successful push, it copies only CONVEX_URL from packages/backend/.env.local into each app's .env.local using the public variable below. Other settings are preserved. If initialization ran during generation, you can go straight to pnpm dev. Keep backend environment files private. Run pnpm convex:link to refresh frontend URLs after switching deployments; restart the apps after linking. Use a cloud development deployment for physical phones; localhost on a phone is the phone itself.
|
|
123
|
+
|
|
124
|
+
| Application | Public URL variable |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
${envTable}
|
|
127
|
+
|
|
128
|
+
${options.auth === 'clerk'
|
|
129
|
+
? `## Clerk setup
|
|
130
|
+
|
|
131
|
+
Use one Clerk application across all frontends. Create a JWT template named convex using Clerk's Convex preset. Set the issuer on the Convex deployment before the first setup push:
|
|
132
|
+
|
|
133
|
+
\`\`\`sh
|
|
134
|
+
pnpm --filter @${scope}/backend exec convex env set CLERK_JWT_ISSUER_DOMAIN https://your-instance.clerk.accounts.dev
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
On a new deployment, run pnpm convex:setup to select it. If the first push asks for CLERK_JWT_ISSUER_DOMAIN, set it in another terminal with the command above and rerun setup. Repeat this for production. Append each app's .env.clerk.example to its .env.local. These files name publishable keys and any server-only secrets. Never put CLERK_SECRET_KEY in a VITE_, EXPO_PUBLIC_ or NEXT_PUBLIC_ variable.
|
|
138
|
+
|
|
139
|
+
Expo uses Google OAuth. Enable Google's connection and the Native API in Clerk. Register each mobile scheme redirect listed in that app's .env.clerk.example in Clerk Native applications. Use a development build for a stable app scheme. SecureStore persists the token. Additional MFA or session tasks need a custom flow before production rollout.
|
|
140
|
+
|
|
141
|
+
${options.example === 'messages' ? 'The backend checks identity and uses an owner index to keep messages private. All frontends share the same identity and messages when signed into the same account.' : 'Clerk is configured, but there are no backend functions yet. Check ctx.auth.getUserIdentity() and enforce authorization in each protected function you add.'}
|
|
142
|
+
`
|
|
143
|
+
: options.example === 'none'
|
|
144
|
+
? `No example tables or functions are included. Add tables to packages/backend/convex/schema.ts and functions to that directory, then run pnpm convex:dev to regenerate the shared API.\n`
|
|
145
|
+
: `The unauthenticated example is a public message board. Anyone with the deployment URL can read and send messages. Add authentication and abuse controls before exposing sensitive data.
|
|
146
|
+
`}
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
\`\`\`sh
|
|
150
|
+
pnpm dev
|
|
151
|
+
${options.apps.map((a) => `pnpm dev:${a.name}`).join('\n')}
|
|
152
|
+
pnpm convex:dev
|
|
153
|
+
pnpm typecheck
|
|
154
|
+
pnpm lint
|
|
155
|
+
pnpm build
|
|
156
|
+
\`\`\`
|
|
157
|
+
|
|
158
|
+
pnpm dev starts one backend watcher and all apps with streamed Turbo logs. Run setup first so authentication prompts run in a normal terminal. Individual dev commands only start that app; keep convex:dev running separately. Mobile can also use its ios/android scripts. Expo build exports native JavaScript; it is not an Xcode or Gradle binary build.
|
|
159
|
+
|
|
160
|
+
## Shared backend types
|
|
161
|
+
|
|
162
|
+
\`\`\`ts
|
|
163
|
+
import { api } from '@${scope}/backend/api';
|
|
164
|
+
import type { Doc, Id } from '@${scope}/backend/dataModel';
|
|
165
|
+
\`\`\`
|
|
166
|
+
|
|
167
|
+
These package exports point directly to official Convex generated files. Keep convex/_generated committed. Run convex:dev after adding backend modules. Do not bundle declarations or copy backend code into apps. ${options.example === 'messages' ? 'Each app includes convex-api.type-test.ts with positive and negative compile-time assertions.' : 'The API starts empty and gains typed references when you add functions and run Convex code generation.'} Backend build checks types, it does not deploy functions.
|
|
168
|
+
|
|
169
|
+
TanStack Start uses client Convex hooks. Server-side data preloading is not configured. Next uses the App Router. Expo uses the default Metro workspace resolver and the SDK's React/React Native versions. Avoid independently upgrading React in one app.
|
|
170
|
+
|
|
171
|
+
## Deployment and troubleshooting
|
|
172
|
+
|
|
173
|
+
Deploy the backend explicitly with pnpm --filter @${scope}/backend exec convex deploy, then set each hosting provider's matching public URL and build that app. Never use production deploy keys for local development. Public variables are embedded at build time; rebuild after changing them.
|
|
174
|
+
|
|
175
|
+
A missing URL screen means that app's .env.local needs its framework-specific URL. Authentication failures usually mean the Clerk convex JWT template or deployment issuer is missing. If generated types are missing, run convex:dev from the backend package and verify that .d.ts files are committed. Metro cache problems after dependency changes can be cleared with pnpm --filter @${scope}/${options.apps.find((a) => a.framework === 'expo')?.name ?? options.apps[0]?.name} exec expo start --clear when using Expo. No symlink resolver overrides should be needed.
|
|
176
|
+
`);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/root/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,QAAQ,IAAI,CAAC,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAqB;IACtD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAC/B,MAAM,GAAG,CAAC,KAAK,CACb,0BAA0B,EAC1B,MAAM,QAAQ,CACZ,IAAI,GAAG,CAAC,wCAAwC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAClE,MAAM,CACP,CACF,CAAC;IACF,MAAM,OAAO,GAA2B;QACtC,GAAG,EAAE,2CAA2C,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACzE,YAAY,EAAE,kBAAkB,KAAK,cAAc;QACnD,cAAc,EAAE,+BAA+B;QAC/C,aAAa,EAAE,2CAA2C;QAC1D,KAAK,EAAE,iBAAiB;QACxB,SAAS,EAAE,qBAAqB;QAChC,IAAI,EAAE,gBAAgB;KACvB,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,IAAI;QAC5B,OAAO,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,kBAAkB,KAAK,IAAI,GAAG,CAAC,IAAI,MAAM,CAAC;IACzE,MAAM,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE;QAC7B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE,QAAQ;QACd,cAAc,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE;QAChC,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;QAC9B,OAAO;QACP,eAAe,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE;KACpC,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,KAAK,CACb,qBAAqB,EACrB,kHAAkH,CACnH,CAAC;IACF,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE;QAC3B,OAAO,EAAE,mCAAmC;QAC5C,KAAK,EAAE;YACL,GAAG,EAAE;gBACH,KAAK,EAAE,KAAK;gBACZ,UAAU,EAAE,IAAI;gBAChB,cAAc,EAAE;oBACd,UAAU;oBACV,SAAS;oBACT,eAAe;oBACf,QAAQ;oBACR,eAAe;iBAChB;aACF;YACD,KAAK,EAAE;gBACL,SAAS,EAAE,CAAC,QAAQ,CAAC;gBACrB,MAAM,EAAE,CAAC,iBAAiB,EAAE,OAAO,CAAC;gBACpC,OAAO,EAAE,CAAC,UAAU,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,CAAC;gBACjE,GAAG,EAAE,CAAC,eAAe,EAAE,QAAQ,EAAE,eAAe,CAAC;gBACjD,cAAc,EAAE,CAAC,kBAAkB,CAAC;aACrC;YACD,CAAC,IAAI,KAAK,gBAAgB,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YAC5C,SAAS,EAAE,EAAE,SAAS,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;YACrD,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;SAC5C;KACF,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,KAAK,CACb,YAAY,EACZ,0IAA0I,CAC3I,CAAC;IACF,MAAM,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE;QACrC,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,MAAM,iBAAiB,EAAE;QACpC,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,IAAI,CAAC,yCAAyC,EAAE;QACxD,IAAI,EAAE,IAAI,KAAK,oBAAoB;QACnC,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE;KAC1C,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,IAAI,CAAC,sCAAsC,EAAE;QACrD,eAAe,EAAE;YACf,MAAM,EAAE,QAAQ;YAChB,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,cAAc,CAAC;YACtC,MAAM,EAAE,QAAQ;YAChB,gBAAgB,EAAE,SAAS;YAC3B,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,IAAI;YACZ,eAAe,EAAE,IAAI;YACrB,eAAe,EAAE,IAAI;YACrB,iBAAiB,EAAE,IAAI;YACvB,YAAY,EAAE,IAAI;YAClB,GAAG,EAAE,WAAW;YAChB,OAAO,EAAE,IAAI;YACb,gCAAgC,EAAE,IAAI;SACvC;KACF,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,IAAI,CAAC,qCAAqC,EAAE;QACpD,IAAI,EAAE,IAAI,KAAK,gBAAgB;QAC/B,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE;QAC9B,YAAY,EAAE,EAAE,mBAAmB,EAAE,CAAC,CAAC,gBAAgB,EAAE;QACzD,gBAAgB,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;KAC7C,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,KAAK,CACb,iCAAiC,EACjC;;;;;;;CAOH,CACE,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI;SAC1B,GAAG,CACF,CAAC,GAAG,EAAE,EAAE,CACN,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,eAAe,CACjI;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,GAAG,CAAC,KAAK,CACb,WAAW,EACX,KAAK,OAAO,CAAC,IAAI;;EAEnB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;;;2BAItB,CAAC,CAAC,IAAI;;;;;;;;;;;EAW/B,QAAQ;;EAGR,OAAO,CAAC,IAAI,KAAK,OAAO;QACtB,CAAC,CAAC;;;;;iBAKW,KAAK;;;;;;;EAOpB,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,qKAAqK,CAAC,CAAC,CAAC,6JAA6J;CACvW;QACG,CAAC,CAAC,OAAO,CAAC,OAAO,KAAK,MAAM;YAC1B,CAAC,CAAC,wLAAwL;YAC1L,CAAC,CAAC;CAER;;;;;EAKE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;wBAYlC,KAAK;iCACI,KAAK;;;qNAG+K,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,+FAA+F,CAAC,CAAC,CAAC,wGAAwG;;;;;;oDAM5Y,KAAK;;6XAEoU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI;CACpd,CACE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const versions: {
|
|
2
|
+
readonly convex: "1.45.0";
|
|
3
|
+
readonly react: "19.2.3";
|
|
4
|
+
readonly typescript: "6.0.3";
|
|
5
|
+
readonly pnpm: "10.34.5";
|
|
6
|
+
readonly turbo: "2.10.12";
|
|
7
|
+
readonly next: "16.3.4";
|
|
8
|
+
readonly vite: "8.2.2";
|
|
9
|
+
readonly viteReact: "6.1.1";
|
|
10
|
+
readonly tanstackStart: "1.168.50";
|
|
11
|
+
readonly expo: "57.0.21";
|
|
12
|
+
readonly reactNative: "0.86.3";
|
|
13
|
+
readonly clerkNext: "7.9.1";
|
|
14
|
+
readonly clerkReact: "6.15.1";
|
|
15
|
+
readonly clerkExpo: "4.6.5";
|
|
16
|
+
readonly eslint: "10.10.0";
|
|
17
|
+
readonly typescriptEslint: "8.70.0";
|
|
18
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const versions = {
|
|
2
|
+
convex: '1.45.0',
|
|
3
|
+
react: '19.2.3',
|
|
4
|
+
typescript: '6.0.3',
|
|
5
|
+
pnpm: '10.34.5',
|
|
6
|
+
turbo: '2.10.12',
|
|
7
|
+
next: '16.3.4',
|
|
8
|
+
vite: '8.2.2',
|
|
9
|
+
viteReact: '6.1.1',
|
|
10
|
+
tanstackStart: '1.168.50',
|
|
11
|
+
expo: '57.0.21',
|
|
12
|
+
reactNative: '0.86.3',
|
|
13
|
+
clerkNext: '7.9.1',
|
|
14
|
+
clerkReact: '6.15.1',
|
|
15
|
+
clerkExpo: '4.6.5',
|
|
16
|
+
eslint: '10.10.0',
|
|
17
|
+
typescriptEslint: '8.70.0',
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=versions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/templates/versions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,QAAQ;IACf,UAAU,EAAE,OAAO;IACnB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,OAAO;IACb,SAAS,EAAE,OAAO;IAClB,aAAa,EAAE,UAAU;IACzB,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,QAAQ;IACrB,SAAS,EAAE,OAAO;IAClB,UAAU,EAAE,QAAQ;IACpB,SAAS,EAAE,OAAO;IAClB,MAAM,EAAE,SAAS;IACjB,gBAAgB,EAAE,QAAQ;CAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getPackageVersion(): Promise<string>;
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
export async function getPackageVersion() {
|
|
3
|
+
const manifest = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
|
4
|
+
if (typeof manifest !== 'object' ||
|
|
5
|
+
manifest === null ||
|
|
6
|
+
!('version' in manifest) ||
|
|
7
|
+
typeof manifest.version !== 'string')
|
|
8
|
+
throw new Error('The generator package is missing its version. Reinstall it.');
|
|
9
|
+
return manifest.version;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,QAAQ,GAAY,IAAI,CAAC,KAAK,CAClC,MAAM,QAAQ,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CACpE,CAAC;IACF,IACE,OAAO,QAAQ,KAAK,QAAQ;QAC5B,QAAQ,KAAK,IAAI;QACjB,CAAC,CAAC,SAAS,IAAI,QAAQ,CAAC;QACxB,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ;QAEpC,MAAM,IAAI,KAAK,CACb,6DAA6D,CAC9D,CAAC;IACJ,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Adding a framework
|
|
2
|
+
|
|
3
|
+
Start with current framework and Convex documentation, then install a minimal upstream example. Record the exact versions and workspace constraints in `docs/research.md`. A successful single-package app is not enough evidence for a workspace adapter.
|
|
4
|
+
|
|
5
|
+
## Implement the adapter
|
|
6
|
+
|
|
7
|
+
1. Add the framework ID to `Framework` in `src/generator/types.ts` and to the accepted list in `src/generator/options.ts`.
|
|
8
|
+
2. Create `src/templates/apps/<id>/index.ts` exporting an `AppTemplate`. Use `context.write`, `context.json`, and shared manifest helpers. Generate framework files under `apps/${app.name}`.
|
|
9
|
+
3. Register it in `src/templates/apps/index.ts` and add the interactive label in `src/commands/create.ts`.
|
|
10
|
+
4. Generate the public Convex environment variable with the framework's required prefix. Use statically named environment access where the bundler requires it. Add its public variable to `publicVariable` in `assets/setup/convex-setup.mjs`, update the generated README environment table, and test URL linking for the new framework.
|
|
11
|
+
5. Provide a framework entry point that mounts `Providers`, `AuthControls`, and a typed message UI. Auth adapters own `src/providers.tsx` and `src/auth-controls.tsx`; the framework template must not write those files.
|
|
12
|
+
6. Add platform handling in `src/integrations/auth/shared.ts` and a binding in the Clerk adapter. If the integration cannot work, add explicit compatibility validation before output is written.
|
|
13
|
+
7. Add versions, development/build/typecheck/lint scripts, and any route-generation step the framework needs on a clean checkout.
|
|
14
|
+
|
|
15
|
+
The existing Vite template is the smallest example. Next and Expo show framework-specific configuration; do not copy their environment prefixes or resolver behavior into another framework.
|
|
16
|
+
|
|
17
|
+
## Preserve the backend contract
|
|
18
|
+
|
|
19
|
+
Use a `workspace:*` dependency on `@${context.scope}/backend` and the same pinned Convex version as the backend. Import `api` from its `/api` subpath and document types from `/dataModel`. Include the compile-time contract from `common` in `src/templates/apps/shared.ts`.
|
|
20
|
+
|
|
21
|
+
Do not add a declaration bundler, a generic API cast, or copied backend code. Check that the frontend compiler can follow generated declaration imports into backend sources. Its runtime bundle should resolve the generated API JavaScript without bundling backend handlers.
|
|
22
|
+
|
|
23
|
+
## Verify before registration is considered supported
|
|
24
|
+
|
|
25
|
+
Generate the framework alone, with another frontend, and with every advertised auth provider. Cover both `--example messages` and `--example none`; use the shared entry-content helper so blank apps do not import demo files. Install from a clean directory, typecheck, lint, and run a production build. For a native framework, run its actual JavaScript bundler for supported platforms. Exercise the query and mutation against a development backend when credentials or local deployment tooling are available; report separately when that check was skipped.
|
|
26
|
+
|
|
27
|
+
Add focused output assertions, option-selection coverage, and representative generated-project CI coverage. Tests must catch missing generated declarations and widened argument/return types. Update README support notes and this guide if the framework introduces a new integration contract.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Adding an auth provider
|
|
2
|
+
|
|
3
|
+
An auth adapter runs after framework generation and owns authentication wiring across the backend and selected apps. It must implement both frontend sessions and backend authorization. Hiding a screen is not sufficient.
|
|
4
|
+
|
|
5
|
+
## Implement the adapter
|
|
6
|
+
|
|
7
|
+
1. Add the provider ID to `Auth` in `src/generator/types.ts`, normalization in `src/generator/options.ts`, and prompt choices in `src/commands/create.ts`.
|
|
8
|
+
2. Create `src/integrations/auth/<id>/index.ts` exporting an `AuthAdapter`, then register it in `src/integrations/auth/index.ts`.
|
|
9
|
+
3. Write `packages/backend/convex/access.ts` with the `getOwner` function consumed by the example backend. Use a stable authenticated identity and reject unauthenticated access. Generate the provider's supported Convex auth configuration.
|
|
10
|
+
4. For each app, merge SDK dependencies through `context.mergePackage` and write `src/providers.tsx` and `src/auth-controls.tsx`. Add middleware or native configuration where the SDK requires it.
|
|
11
|
+
5. Generate safe environment examples and setup instructions. Separate public keys, framework server secrets, and environment variables configured on the Convex deployment.
|
|
12
|
+
|
|
13
|
+
The none adapter demonstrates file ownership. The Clerk adapter demonstrates a binding map that selects SDKs by framework. Keep provider-specific framework details in the adapter; do not introduce auth branches into every app template.
|
|
14
|
+
|
|
15
|
+
`writeProviders` currently implements no-auth and Clerk-compatible provider wiring. A provider with a different session protocol should supply its own provider module or extend that helper with a concrete contract. Do not force an unrelated SDK through Clerk's `useAuth` shape.
|
|
16
|
+
|
|
17
|
+
## Required behavior
|
|
18
|
+
|
|
19
|
+
Wait for Convex authentication before mounting protected queries, handle loading and sign-in errors, and expose sign-out controls. Backend functions must verify the identity independently. Preserve the message API's arguments and return types so generated contract checks continue to apply.
|
|
20
|
+
|
|
21
|
+
Document the identity key used for ownership. Switching providers in a running deployment can change that key and requires a data migration; v0.1 does not implement auth replacement in existing projects.
|
|
22
|
+
|
|
23
|
+
For native apps, use supported secure token storage and a real redirect flow. State whether the example supports additional verification, MFA, and session tasks. Avoid presenting a successful Metro export as proof of a working identity-provider flow.
|
|
24
|
+
|
|
25
|
+
## Tests and documentation
|
|
26
|
+
|
|
27
|
+
Test unauthenticated rejection and cross-user isolation on the backend. Generate each supported framework with the provider and run clean installation, typechecks, lint, and representative builds. Keep negative compile-time API assertions enabled. Check generated environment files for secret-prefix mistakes and ensure example files can be committed while `.env.local` remains ignored.
|
|
28
|
+
|
|
29
|
+
Use test credentials only for integration checks that need a provider account, and report those checks separately from static tests. Add provider-specific setup and troubleshooting to the generated README, repository README, and research notes. An unsupported framework combination must fail validation before generation rather than silently omitting auth.
|
|
30
|
+
|
|
31
|
+
Cover both starter-content choices in adapter tests. Blank projects retain authentication configuration and providers but omit example-specific access helpers and functions.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Generation has one normalized input, a filesystem context, a framework registry, and an auth registry. It creates a new workspace; it does not mutate existing projects or load arbitrary third-party plugins.
|
|
4
|
+
|
|
5
|
+
## Execution
|
|
6
|
+
|
|
7
|
+
`src/commands/create.ts` parses flags and collects missing interactive answers. `src/generator/options.ts` validates names, resolves app names, rejects unsupported choices, and sets install/git defaults and validates the optional Convex initialization step. Both the CLI and exported `generateProject` API use this normalization.
|
|
8
|
+
|
|
9
|
+
`src/generator/index.ts` checks the destination and creates a sibling staging directory. It generates root files, copies the backend source and official generated assets, runs each selected app template, then applies one auth adapter. Only after template composition succeeds does it copy files exclusively into the destination and run optional installation, `git init`, and Convex initialization. The commit is not an atomic rename. A failure rolls back only files and directories created by that invocation, preserving an existing empty directory and concurrent user files.
|
|
10
|
+
|
|
11
|
+
Template errors clean up staging. Installation, git and Convex initialization failures preserve completed project files so users can retry setup. Existing non-empty directories and symlinks are rejected. No git commit or registry publication occurs during generation.
|
|
12
|
+
|
|
13
|
+
## Contracts and ownership
|
|
14
|
+
|
|
15
|
+
`src/generator/types.ts` defines `AppSpec`, `ProjectOptions`, `AppTemplate`, `AuthAdapter`, and `GeneratorContext`.
|
|
16
|
+
|
|
17
|
+
An app template implements `generate(context, app)`. It owns its framework entry points, application manifest, bundler configuration, environment example, and demo. Shared helpers in `src/templates/apps/shared.ts` generate manifests, environment files, type contracts, and web message components.
|
|
18
|
+
|
|
19
|
+
An auth adapter implements `apply(context)`. It owns backend `access.ts`, optional auth configuration, and app `providers.tsx` and `auth-controls.tsx`. Framework templates import those stable module names without importing Clerk. Clerk's binding map selects the framework SDK, middleware and native dependencies in `src/integrations/auth/clerk/index.ts`. Adding a provider requires platform-specific integration, but does not require embedding provider branches throughout framework templates.
|
|
20
|
+
|
|
21
|
+
`context.write` and `context.json` create files once and reject unsafe paths and collisions. `context.mergePackage` can update only manifests created by that context. Dependency maps merge by key and reject conflicting versions; script patches replace matching script keys. Other top-level manifest fields replace their previous values. This is intentionally not a generic deep-merge engine.
|
|
22
|
+
|
|
23
|
+
The package-manager contract lives in `src/package-manager/index.ts`. pnpm owns installation; subprocesses receive argument arrays and inherited terminal output. The root template owns Turborepo and workspace configuration. There is no Nx adapter or speculative migration engine.
|
|
24
|
+
|
|
25
|
+
## Shared Convex API
|
|
26
|
+
|
|
27
|
+
The backend is a source workspace package. Its `/api` export has a `types` condition for `convex/_generated/api.d.ts` and a runtime condition for `api.js`. `/dataModel` exposes only declarations. Consumers keep backend modules available because Convex's dynamic declarations derive types from their exports.
|
|
28
|
+
|
|
29
|
+
Do not bundle those declarations, export backend implementation modules to clients, replace the generated API with a generic proxy, or add TypeScript project-reference boundaries that prevent source resolution. The official runtime uses a proxy, while the original declarations preserve function signatures. Each messages-example frontend's `src/convex-api.type-test.ts` checks argument types, return types, document IDs, and invalid API keys.
|
|
30
|
+
|
|
31
|
+
`assets/backend/convex` contains the example and official generated output. Refresh generated output with a supported Convex development workflow after changing backend modules. Do not edit generated internals. New projects can typecheck the committed example before selecting their own deployment. Subsequent code generation requires normal Convex setup.
|
|
32
|
+
|
|
33
|
+
## Environment and process boundaries
|
|
34
|
+
|
|
35
|
+
The backend owns deployment configuration. Each app owns its public URL and publishable auth key with the prefix required by its bundler. Server secrets never receive public prefixes. Auth adapters add `.env.clerk.example` alongside the framework's `.env.example`; users combine their values in `.env.local`.
|
|
36
|
+
|
|
37
|
+
The generated `scripts/convex-setup.mjs` is a standalone Node script copied from `assets/setup`. It resolves the installed Convex CLI from the backend package and runs `convex dev --once` with inherited terminal output. Setup runs outside Turbo so account and deployment prompts have a terminal. Only a successful push triggers URL linking. The helper reads backend `.env` and `.env.local`, with local values taking precedence, and copies only `CONVEX_URL`. It preflights application paths, rejects symlinks, and appends a framework-specific assignment while preserving existing settings. Repeating the same link leaves files unchanged. Cancellation and failures return a nonzero exit status, preserve the project, and allow setup to be retried. `convex:link` runs just the linking stage. Both commands work without the generator installed. Development runs one persistent backend watcher and the selected apps with streamed logs. Build and typecheck tasks do not deploy or start watchers. Public environment variables participate in build inputs.
|
|
38
|
+
|
|
39
|
+
## Extension and compatibility policy
|
|
40
|
+
|
|
41
|
+
Registries and TypeScript unions make supported choices explicit. Dependencies are pinned in `src/templates/versions.ts` and in framework bindings where necessary. New adapters should add concrete capabilities, with install, type, and bundler evidence for each supported combination. Reject an unsupported combination clearly rather than emitting code known to fail.
|
|
42
|
+
|
|
43
|
+
`convex-monorepo.json` records a versioned description of the output for future commands. It is not currently consumed by an upgrade or migration command. The exported API supports programmatic generation; custom runtime registry injection is not implemented.
|
|
44
|
+
|
|
45
|
+
## Starter content
|
|
46
|
+
|
|
47
|
+
`example` is independent of framework and auth selection. `messages` keeps the existing example; `none` selects blank apps and the empty backend assets. The backend template chooses the matching official generated declarations. App entry content comes from a shared helper, while framework setup and auth adapters keep their existing responsibilities. Auth adapters omit the demo's access helper in blank mode but still configure the selected provider.
|
|
48
|
+
|
|
49
|
+
Blank apps do not include tests that assume an empty API forever. The generated-project test runner injects temporary compile-time assertions for an exactly empty API and table list, rejects unknown modules, and checks for widened types. Those files belong only to the disposable test fixture.
|
package/docs/releases.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
This repository uses Conventional Commits and release-please for versions, changelogs, release PRs, and GitHub releases. GitHub Actions tests and publishes the release tarball to npm. The first publication uses a bootstrap token; subsequent releases use trusted publishing. This follows the publishing model in [convex-cloudflare-email](https://github.com/adamtrip-solutions/convex-cloudflare-email/blob/main/.github/workflows/release.yml).
|
|
4
|
+
|
|
5
|
+
## Version policy
|
|
6
|
+
|
|
7
|
+
| Commit | Bump from 0.1.0 |
|
|
8
|
+
| --------------------------------------- | ---------------- |
|
|
9
|
+
| `fix(cli): handle cancellation` | 0.1.1 |
|
|
10
|
+
| `perf: reduce template reads` | 0.1.1 |
|
|
11
|
+
| `feat(cli): initialize Convex` | 0.2.0 |
|
|
12
|
+
| `feat(cli)!: replace application flags` | 1.0.0 |
|
|
13
|
+
| `docs: update setup instructions` | No release alone |
|
|
14
|
+
| `chore: update tooling` | No release alone |
|
|
15
|
+
|
|
16
|
+
A `BREAKING CHANGE:` commit footer also requests a major bump. Explain migration steps when using it. The two pre-major downgrade options are explicitly disabled, so breaking changes still request a major before 1.0. For several commits, the largest requested bump wins. This follows the [release-please versioning strategy](https://github.com/googleapis/release-please/blob/main/src/versioning-strategies/default.ts).
|
|
17
|
+
|
|
18
|
+
The manifest records 0.1.0 as the existing baseline. `bootstrap-sha` points to the last committed v0.1 work, `0373d22b000dc9cba036a1d2f1cff689b217fd8b`, so initial history is not replayed as new features. This does not publish 0.1.0 or create a historical release. After the first automated release, release-please uses release history. See its [manifest documentation](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md).
|
|
19
|
+
|
|
20
|
+
## GitHub configuration
|
|
21
|
+
|
|
22
|
+
The public repository is `adamtrip-solutions/create-convex-monorepo`. The package's repository URL must match it for npm provenance.
|
|
23
|
+
|
|
24
|
+
- Use `main` as the release branch. Enable Actions and allow GitHub Actions to create pull requests.
|
|
25
|
+
- Enable squash merging with the PR title as the commit title. Require the Conventional PR title check and `CI passed` before merging.
|
|
26
|
+
- Create a GitHub environment named `npm`. Restrict deployments to tags matching `v*`. Protect release tags against updates and deletion.
|
|
27
|
+
- Configure `NPM_BOOTSTRAP_TOKEN` in the `npm` environment before the first release, or configure npm trusted publishing if the package already exists. No publishing-enable variable is required.
|
|
28
|
+
|
|
29
|
+
The release workflow uses `GITHUB_TOKEN` by default. GitHub may require approval before running CI on a bot-created release PR. An optional `RELEASE_PLEASE_TOKEN` with repository contents, issues and pull-request write permissions can avoid that interruption. The npm dispatch step always uses `GITHUB_TOKEN` with `actions: write`; it does not require Actions permission on the optional token. [GitHub token event rules](https://docs.github.com/en/actions/concepts/security/github_token).
|
|
30
|
+
|
|
31
|
+
## First npm publication
|
|
32
|
+
|
|
33
|
+
1. Use your existing npm account with a verified email and two-factor authentication. The package is unscoped, so a separate npm organization is not required.
|
|
34
|
+
2. Create a short-lived granular npm token with Read and write package permissions that allow creation of `create-convex-monorepo`. Enable Bypass two-factor authentication for this first unattended publish. A token limited to the existing `convex-cloudflare-email` package cannot create this new package.
|
|
35
|
+
3. Save it as `NPM_BOOTSTRAP_TOKEN` in this repository's [npm environment](https://github.com/adamtrip-solutions/create-convex-monorepo/settings/environments). Use GitHub's secret UI, or run the following command in your own terminal and paste the token at its hidden prompt:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
gh secret set NPM_BOOTSTRAP_TOKEN --env npm --repo adamtrip-solutions/create-convex-monorepo
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
GitHub secrets are write-only; a stored token in another repository cannot be read back and copied. Do not paste it into chat or commit it.
|
|
42
|
+
|
|
43
|
+
4. Merge the first release-please PR after CI passes. The release workflow creates the version tag and dispatches **Publish npm package**. It tests the release commit and automatically publishes the verified archive with provenance. There is no local `npm publish` or archive download step.
|
|
44
|
+
5. After that first publication succeeds, configure a GitHub Actions trusted publisher in the npm package settings:
|
|
45
|
+
|
|
46
|
+
| Field | Value |
|
|
47
|
+
| ----------------- | ------------------------------------------------------- |
|
|
48
|
+
| Organization/user | `adamtrip-solutions` |
|
|
49
|
+
| Repository | `create-convex-monorepo` |
|
|
50
|
+
| Workflow filename | `publish.yml` |
|
|
51
|
+
| Environment | `npm` |
|
|
52
|
+
| Allowed action | Enable direct `npm publish`, not only staged publishing |
|
|
53
|
+
|
|
54
|
+
6. Delete the `NPM_BOOTSTRAP_TOKEN` GitHub secret and revoke that npm token. Subsequent releases use OIDC without a stored npm credential. Once OIDC is configured, npm can require two-factor authentication and disallow token-based publishing.
|
|
55
|
+
|
|
56
|
+
The bootstrap token is available only to the final publish step, after all validation jobs pass. Current npm documentation requires the package to exist before configuring its trusted publisher; the bootstrap token lets CI create it. [Token setup](https://docs.npmjs.com/creating-and-viewing-access-tokens/), [trusted publisher configuration](https://docs.npmjs.com/trusted-publishers/).
|
|
57
|
+
|
|
58
|
+
## Automated release flow
|
|
59
|
+
|
|
60
|
+
Merge regular PRs with Conventional Commit titles. Release-please maintains the version/changelog PR. Merging it creates a GitHub release and explicitly dispatches `publish.yml` at its tag. Dispatch works even when the release was created with `GITHUB_TOKEN`.
|
|
61
|
+
|
|
62
|
+
The publishing workflow rejects branches, prereleases, missing releases, moved tags, and commits outside `main` history. It runs reusable CI against the exact release SHA: lint, formatting, typecheck, unit/generator tests, tarball smoke tests, Windows checks, and the generated-project build matrix. The Linux job uploads the tarball only after its smoke test passes.
|
|
63
|
+
|
|
64
|
+
A separate job in the `npm` environment downloads that same artifact, validates its package name/version/bin/repository, and publishes it with provenance. It does not rebuild the archive. Publishing is serialized across versions so concurrent releases cannot race to update the latest tag. Node 24 and an OIDC-capable npm CLI are configured explicitly. No PR job receives npm publishing credentials or an OIDC write permission.
|
|
65
|
+
|
|
66
|
+
The dispatch uses the tag rather than checking out a tag inside a workflow started on `main`. npm provenance reads the workflow event's commit, so both the event and checked-out source must identify the release. [npm provenance](https://docs.npmjs.com/generating-provenance-statements/).
|
|
67
|
+
|
|
68
|
+
## Retry a failed publication
|
|
69
|
+
|
|
70
|
+
Fix account, environment or registry configuration, then rerun **Publish npm package** at the same release tag:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
gh workflow run publish.yml --repo adamtrip-solutions/create-convex-monorepo --ref vX.Y.Z
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Select the tag in the Actions UI when triggering manually. Running from a branch is rejected. If npm already contains that exact tarball, the publish step skips it. A version with different contents fails explicitly; release a new version instead of moving tags or trying to overwrite npm history. If the workflow itself needs a code fix, include it in a new release because retries execute the workflow stored at the original tag.
|
|
77
|
+
|
|
78
|
+
Normal changes do not need manual version edits. If the release PR stalls, inspect the workflow logs, token permissions and `autorelease` labels. Keep release-please's generated title and body intact. After the first successful npm publication, remove the prepublication notice in README.md.
|
package/docs/research.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Research
|
|
2
|
+
|
|
3
|
+
Investigated 2026-09-09 before implementation. Registry releases inspected: Convex 1.45.0, Next 16.3.4, Vite 8.2.2, TanStack Start 1.168.50, Expo 57.0.21, Clerk Next 7.9.1, Clerk React 6.15.1, Clerk Expo 4.6.5. Templates pin tested versions. Expo's official blank TypeScript template 57.0.23 selects React 19.2.3, React Native 0.86.3 and TypeScript 6.0.3; we use that compatibility set across apps.
|
|
4
|
+
|
|
5
|
+
## Convex types and code generation
|
|
6
|
+
|
|
7
|
+
Inspected the published `convex@1.45.0` tarball, especially `src/cli/codegen.ts`, `src/cli/lib/components.ts`, `src/cli/lib/codegen.ts`, and `src/cli/codegen_templates/{api,dataModel,server,tsconfig}.ts`. [Source](https://github.com/get-convex/convex-js/tree/main/src/cli).
|
|
8
|
+
|
|
9
|
+
The default API consists of runtime `api.js` and `api.d.ts`. The latter derives function references through `ApiFromModules` and imports the actual backend modules as types. `dataModel.d.ts` imports the schema; `server.d.ts` supplies typed query and mutation builders. TypeScript follows those imports across workspace boundaries. Keeping only the runtime file loses function inference. Runtime `anyApi` in official generated JavaScript is expected; it does not imply that the paired declarations are untyped. The official generated declarations themselves contain generic `any` constraints. They must remain unmodified.
|
|
10
|
+
|
|
11
|
+
The package exposes `./api` with an explicit `types` condition pointing to the original `.d.ts` and a `default` condition pointing to the original `.js`. It exposes `./dataModel` for type imports. It does not bundle declarations, re-export from a barrel, or expose schema and server functions as runtime entry points. This keeps the original declaration graph intact. Source backend files must remain available in the workspace. Every framework is tested with compile-time assertions against this public subpath. Messages starters include the assertions; blank starters are checked in disposable test fixtures.
|
|
12
|
+
|
|
13
|
+
The current CLI requires deployment selection and credentials before `codegen`, even without components. A hidden `--system-udfs` path exists but is not a supported offline application workflow and must not be used. Ship official generated output for the example backend, then refresh it with normal `convex dev`. Generated code belongs in version control. [Generated API](https://docs.convex.dev/generated-api/api), [data model](https://docs.convex.dev/generated-api/data-model), [configuration](https://docs.convex.dev/production/project-configuration).
|
|
14
|
+
|
|
15
|
+
Static API and data model generation now exist as beta options. They avoid some inference costs but update only while codegen runs, lose jump-to-definition, and require returns validators to avoid weak return types. v0.1 keeps dynamic codegen and supplies returns validators. Do not flatten types or edit generated internals.
|
|
16
|
+
|
|
17
|
+
## Workspaces and bundlers
|
|
18
|
+
|
|
19
|
+
Run Convex commands from `packages/backend`, which owns the Convex dependency, configuration and deployment `.env.local`. A root command uses `pnpm --filter` to select that package. Only one backend watcher runs. Frontends declare a `workspace:*` backend dependency and the same pinned Convex version.
|
|
20
|
+
|
|
21
|
+
No TypeScript project references, `composite`, declaration output or frontend `rootDir` boundary is imposed. Dynamic Convex types cause backend sources to enter frontend TypeScript programs. Frontend and backend checks use bundler resolution. Runtime frontend bundles should reach only `api.js` and Convex's client runtime, never backend implementations. Next supports workspace packages through `transpilePackages`; Vite understands workspace links. [Next documentation](https://nextjs.org/docs/app/api-reference/config/next-config-js/transpilePackages), [Vite guide](https://vite.dev/guide/).
|
|
22
|
+
|
|
23
|
+
TanStack Start currently uses the Vite `tanstackStart()` plugin before the React plugin, a router factory and file routes. v0.1 uses standard Convex React hooks with client-rendered query data. This is explicitly supported by Convex. SSR prefetching and authenticated server queries are outside this release. [Convex with Start](https://docs.convex.dev/client/tanstack/tanstack-start), [Start setup](https://tanstack.com/start/latest/docs/framework/react/build-from-scratch).
|
|
24
|
+
|
|
25
|
+
Expo SDK 54+ supports isolated pnpm installations. SDK 52+ automatically configures workspace watching and resolution when using `expo/metro-config`. Use the default config, not old `watchFolders`, `extraNodeModules` or hoisting workarounds. SDK 55+ enables autolinking module resolution in monorepos. React and native dependencies must match the SDK; duplicate React runtimes are a real risk. Native uses `ConvexReactClient` with `unsavedChangesWarning: false`. Typechecking alone does not prove Metro works; test iOS and Android JS exports too. [Expo monorepos](https://docs.expo.dev/guides/monorepos/), [autolinking](https://docs.expo.dev/modules/autolinking/), [Convex native quickstart](https://docs.convex.dev/quickstart/react-native).
|
|
26
|
+
|
|
27
|
+
## Official example and known reports
|
|
28
|
+
|
|
29
|
+
The [official Turbo/Expo/Next/Clerk monorepo](https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo) confirms the shared backend package, package-scoped Convex commands and default Metro config. Its current pins lag the registry, including Expo 55 and Convex 1.35, and it uses hoisted pnpm installs. Use it as architectural evidence, not a current dependency manifest.
|
|
30
|
+
|
|
31
|
+
Reports checked in both requested repositories:
|
|
32
|
+
|
|
33
|
+
- [convex-backend #361](https://github.com/get-convex/convex-backend/issues/361), open: API types degrade through package re-exports. Maintainer suggests TypeScript codegen; the reporter then sees bundle growth. Avoid that barrel path and test the original JS/declaration export.
|
|
34
|
+
- [convex-js #154](https://github.com/get-convex/convex-js/issues/154), open: generated types pull backend source dependencies into client TypeScript programs. This is a consequence of dynamic inference, not evidence that runtime code must ship to clients.
|
|
35
|
+
- [convex-js #147](https://github.com/get-convex/convex-js/issues/147), open: Windows pnpm component codegen and symlinks. No components in v0.1; Windows CI must cover the generator and workspace behavior.
|
|
36
|
+
- [convex-js #20](https://github.com/get-convex/convex-js/issues/20) and [#121](https://github.com/get-convex/convex-js/issues/121), open: workspace hoisting and Node external package resolution. The example uses Convex's default runtime and has no Node external packages.
|
|
37
|
+
|
|
38
|
+
Open issue status is a research observation, not a claim that each failure reproduces on 1.45.0. See tests for behavior actually verified here.
|
|
39
|
+
|
|
40
|
+
## Environment and auth
|
|
41
|
+
|
|
42
|
+
Convex's CLI writes deployment configuration beside its package, not into every frontend. Next reads `NEXT_PUBLIC_CONVEX_URL`, Vite and Start read `VITE_CONVEX_URL`, Expo reads `EXPO_PUBLIC_CONVEX_URL`. Public values are embedded by bundlers and must use statically named accesses. Do not copy backend `.env.local` files into clients. The generated setup helper copies only the public URL after a successful initialization.
|
|
43
|
+
|
|
44
|
+
Clerk uses `ConvexProviderWithClerk` and its SDK `useAuth`. Browser controls must wait for Convex authentication before mounting protected queries. The backend independently verifies identity; UI gating is not authorization. Clerk demos keep each user's messages private using a token-identifier index. Configure the `convex` JWT template in Clerk and the issuer on the Convex deployment. Next server middleware uses a server-only `CLERK_SECRET_KEY`; Expo and Vite must never receive it. Current SDK names are `@clerk/react` and `@clerk/expo`, replacing their legacy names. Next 16 calls middleware `proxy.ts`.
|
|
45
|
+
|
|
46
|
+
Expo Clerk uses SecureStore token caching and a native OAuth flow. Enable the Native API, Google connection and the app's scheme redirect in Clerk. Full native builds and real identity-provider credentials are separate validation from Metro export. [Convex Clerk](https://docs.convex.dev/auth/clerk), [Clerk Expo](https://clerk.com/docs/expo/getting-started/quickstart), [native OAuth](https://clerk.com/docs/expo/guides/development/custom-flows/authentication/oauth-connections), [Clerk Next](https://clerk.com/docs/nextjs/getting-started/quickstart).
|
|
47
|
+
|
|
48
|
+
## CLI publishing and process model
|
|
49
|
+
|
|
50
|
+
`pnpm create convex-monorepo` resolves the `create-convex-monorepo` npm package. Publish one matching bin with a Node shebang. `npx create-convex-monorepo` uses the same bin. A local checkout does not make that registry command use unpublished source; document a local bin and tarball test. [pnpm create](https://pnpm.io/cli/create), [npm init](https://docs.npmjs.com/cli/v11/commands/npm-init/).
|
|
51
|
+
|
|
52
|
+
Turborepo persistent tasks must not depend on other persistent tasks. Run backend initialization directly in a terminal first, then use uncached persistent dev tasks with streamed logs. Build/typecheck must not start a deployment. Public environment values participate in build hashes; secret values stay server-side. [Turbo development](https://turborepo.com/docs/crafting-your-repository/developing-applications), [environment variables](https://turborepo.com/docs/crafting-your-repository/using-environment-variables).
|
|
53
|
+
|
|
54
|
+
## Verified implementation findings
|
|
55
|
+
|
|
56
|
+
- TypeScript 6 did not implicitly expose Node globals in the package-local Convex config. Clerk's `process.env` read failed until `types: ["node"]` was explicitly configured alongside the backend's direct `@types/node` dependency. This changes the supported tsconfig, not generated API internals.
|
|
57
|
+
- Turbo 2.10.12 rejects `interactive: true` with `--ui=stream`. The generated dev task omits that field and sets concurrency to the selected app count plus two. Direct `convex:setup` owns first-run prompts; individual Expo commands provide keyboard interaction when needed. Actual streamed startup of all four apps and one backend passed.
|
|
58
|
+
- Expo URI schemes must start with a letter even when npm project names may start with a number. Generated schemes use `ccm-<project>-<app>` consistently in app configuration and Clerk redirects.
|
|
59
|
+
- The current Clerk Expo dependency graph emits optional `ws` native accelerator and Solana TypeScript 5 peer warnings under this TypeScript 6 stack. Installation, generated TypeScript checks and both native Metro exports passed without overrides or ignored TypeScript errors. Optional postinstall scripts are not blanket-approved. Native wallet integration is outside this example. TanStack's route CLI also emits an upstream circular-export warning while generating routes successfully.
|
|
60
|
+
- The npm registry returned 404 for `create-convex-monorepo` during this session. That does not reserve the name. Publishing and registry ownership are separate maintainer steps.
|
|
61
|
+
|
|
62
|
+
Additional issue status checks: [convex-js #53](https://github.com/get-convex/convex-js/issues/53) remains open for excessive type-instantiation depth on larger monorepos; [#153](https://github.com/get-convex/convex-js/issues/153) closed without a planned root-config lookup change; [convex-backend #254](https://github.com/get-convex/convex-backend/issues/254) closed after a reported fix in 1.29.3. These are not reasons to downgrade the pinned release.
|
|
63
|
+
|
|
64
|
+
See [verification](verification.md) for executed commands and limits. The installed tarball test checks that npm packaging includes both runtime assets and generated declarations.
|
|
65
|
+
|
|
66
|
+
## Optional initialization and URL linking
|
|
67
|
+
|
|
68
|
+
The supported first-run command is `convex dev --once` in the backend package. It performs normal account/deployment selection, code generation and a push, then exits. The helper resolves the installed package's CLI through its exported `package.json` and invokes `bin/main.js` with Node. This avoids shell quoting and pnpm executable differences while preserving the backend working directory and terminal. The CLI entry path is pinned-package behavior verified against Convex 1.45.0. [CLI documentation](https://docs.convex.dev/cli), [published CLI source](https://github.com/get-convex/convex-js/blob/main/src/cli/dev.ts).
|
|
69
|
+
|
|
70
|
+
Convex writes the selected deployment and public URL to backend environment files. Linking reads `.env` followed by `.env.local`, parses quoted values and comments, and uses only `CONVEX_URL`. It ignores ambient URL variables, deployment keys, and auth secrets. Frontend files retain their existing content; when the URL changes, a final assignment overrides the old value. Repeating an unchanged link does not rewrite the file. [Environment handling source](https://github.com/get-convex/convex-js/blob/main/src/cli/lib/envvars.ts).
|
|
71
|
+
|
|
72
|
+
Clerk's issuer must exist on the deployment before its auth configuration can be pushed. A fresh setup can therefore stop after deployment selection and before URL linking. The project stays available: configure `CLERK_JWT_ISSUER_DOMAIN` and rerun `pnpm convex:setup`. The generator does not use hidden skip-push flags or weaken auth to make initialization appear successful. Local deployment URLs also need care with Expo: a physical device cannot reach the host computer through its own localhost address.
|
|
73
|
+
|
|
74
|
+
## Blank backend
|
|
75
|
+
|
|
76
|
+
Verified `defineSchema({})` with the installed Convex 1.45.0 CLI using a normal anonymous local `convex dev --once`. It pushes successfully without registered functions. The official output uses `ApiFromModules<{}>` and derives the data model from the empty schema. Blank generation copies those artifacts unchanged from `assets/backend-blank`; it does not remove imports from the messages example's generated declarations. Keeping a schema preserves strict table-name types as users add tables. [Convex schemas](https://docs.convex.dev/database/schemas), [generated API](https://docs.convex.dev/generated-api/api).
|