create-convex-monorepo 0.2.0 → 0.3.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 +19 -1
- package/README.md +53 -2
- package/assets/setup/convex-setup.d.mts +16 -0
- package/dist/cli/workspace.d.ts +2 -0
- package/dist/cli/workspace.js +19 -0
- package/dist/cli/workspace.js.map +1 -0
- package/dist/commands/workspace.d.ts +20 -0
- package/dist/commands/workspace.js +272 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/workspace/add.d.ts +9 -0
- package/dist/workspace/add.js +326 -0
- package/dist/workspace/add.js.map +1 -0
- package/dist/workspace/changes.d.ts +23 -0
- package/dist/workspace/changes.js +157 -0
- package/dist/workspace/changes.js.map +1 -0
- package/dist/workspace/doctor.d.ts +16 -0
- package/dist/workspace/doctor.js +483 -0
- package/dist/workspace/doctor.js.map +1 -0
- package/dist/workspace/env.d.ts +12 -0
- package/dist/workspace/env.js +50 -0
- package/dist/workspace/env.js.map +1 -0
- package/dist/workspace/project.d.ts +28 -0
- package/dist/workspace/project.js +147 -0
- package/dist/workspace/project.js.map +1 -0
- package/dist/workspace/upgrade.d.ts +17 -0
- package/dist/workspace/upgrade.js +86 -0
- package/dist/workspace/upgrade.js.map +1 -0
- package/docs/adding-a-framework.md +6 -0
- package/docs/adding-an-auth-provider.md +8 -0
- package/docs/architecture.md +13 -1
- package/docs/releases.md +18 -24
- package/docs/verification.md +29 -1
- package/package.json +6 -3
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { readdir, readFile, realpath } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
4
|
+
import { parseEnv } from 'node:util';
|
|
5
|
+
import { deploymentUrl, publicVariable, } from '../../assets/setup/convex-setup.mjs';
|
|
6
|
+
import { versions } from '../templates/versions.js';
|
|
7
|
+
import { readJson, readText } from './project.js';
|
|
8
|
+
const frameworks = {
|
|
9
|
+
next: ['next', 'react-dom'],
|
|
10
|
+
vite: ['vite', '@vitejs/plugin-react', 'react-dom'],
|
|
11
|
+
'tanstack-start': ['@tanstack/react-start', 'vite', 'react-dom'],
|
|
12
|
+
expo: ['expo', 'react-native'],
|
|
13
|
+
};
|
|
14
|
+
const clerk = {
|
|
15
|
+
next: '@clerk/nextjs',
|
|
16
|
+
vite: '@clerk/react',
|
|
17
|
+
'tanstack-start': '@clerk/tanstack-react-start',
|
|
18
|
+
expo: '@clerk/expo',
|
|
19
|
+
};
|
|
20
|
+
const baselines = {
|
|
21
|
+
convex: versions.convex,
|
|
22
|
+
react: versions.react,
|
|
23
|
+
typescript: versions.typescript,
|
|
24
|
+
next: versions.next,
|
|
25
|
+
vite: versions.vite,
|
|
26
|
+
expo: versions.expo,
|
|
27
|
+
'react-native': versions.reactNative,
|
|
28
|
+
'@tanstack/react-start': versions.tanstackStart,
|
|
29
|
+
};
|
|
30
|
+
function object(value) {
|
|
31
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
32
|
+
? value
|
|
33
|
+
: {};
|
|
34
|
+
}
|
|
35
|
+
function validUrl(value) {
|
|
36
|
+
try {
|
|
37
|
+
deploymentUrl(value);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Recognize the generator's block list and simple inline lists, not general YAML. */
|
|
45
|
+
function workspacePatterns(source) {
|
|
46
|
+
const lines = source.split(/\r?\n/);
|
|
47
|
+
const headers = lines
|
|
48
|
+
.map((line, index) => (/^packages\s*:\s*(.*)$/.test(line) ? index : -1))
|
|
49
|
+
.filter((index) => index >= 0);
|
|
50
|
+
if (headers.length !== 1)
|
|
51
|
+
return { patterns: [], unsupported: true };
|
|
52
|
+
const index = headers[0];
|
|
53
|
+
const inline = /^packages\s*:\s*(.*)$/.exec(lines[index])[1].trim();
|
|
54
|
+
const patterns = [];
|
|
55
|
+
let unsupported = false;
|
|
56
|
+
function entry(raw) {
|
|
57
|
+
const trimmed = raw.trim();
|
|
58
|
+
const quoted = /^(['"])([^'"]*)\1\s*(?:#.*)?$/.exec(trimmed);
|
|
59
|
+
const pattern = quoted ? quoted[2] : trimmed.replace(/\s+#.*$/, '').trim();
|
|
60
|
+
if ((!quoted && /^[!&*]/.test(pattern)) ||
|
|
61
|
+
!/^!?[A-Za-z0-9_.*\/-]+$/.test(pattern) ||
|
|
62
|
+
pattern.includes('**') ||
|
|
63
|
+
pattern
|
|
64
|
+
.split('/')
|
|
65
|
+
.some((part) => part !== '*' &&
|
|
66
|
+
(part.includes('*') || part === '..' || part === '.'))) {
|
|
67
|
+
unsupported = true;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
patterns.push(pattern);
|
|
71
|
+
}
|
|
72
|
+
if (inline && !inline.startsWith('#')) {
|
|
73
|
+
const list = /^\[([^\]]*)\]\s*(?:#.*)?$/.exec(inline);
|
|
74
|
+
if (!list)
|
|
75
|
+
return { patterns, unsupported: true };
|
|
76
|
+
if (list[1].trim())
|
|
77
|
+
for (const item of list[1].split(','))
|
|
78
|
+
entry(item);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
for (const line of lines.slice(index + 1)) {
|
|
82
|
+
if (!line.trim() || line.trimStart().startsWith('#'))
|
|
83
|
+
continue;
|
|
84
|
+
if (/^[^\s-]/.test(line))
|
|
85
|
+
break;
|
|
86
|
+
const item = /^ *-\s+(.+)$/.exec(line);
|
|
87
|
+
if (!item)
|
|
88
|
+
unsupported = true;
|
|
89
|
+
else
|
|
90
|
+
entry(item[1]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { patterns, unsupported };
|
|
94
|
+
}
|
|
95
|
+
function matchesWorkspacePattern(pattern, path) {
|
|
96
|
+
const segments = pattern.split('/');
|
|
97
|
+
const target = path.split('/');
|
|
98
|
+
return (segments.length === target.length &&
|
|
99
|
+
segments.every((segment, index) => segment === '*' || segment === target[index]));
|
|
100
|
+
}
|
|
101
|
+
async function installedVersion(root, directory, name) {
|
|
102
|
+
const require = createRequire(join(root, directory, 'package.json'));
|
|
103
|
+
let manifest;
|
|
104
|
+
try {
|
|
105
|
+
manifest = require.resolve(`${name}/package.json`);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
try {
|
|
109
|
+
let cursor = dirname(require.resolve(name));
|
|
110
|
+
for (;;) {
|
|
111
|
+
const candidate = join(cursor, 'package.json');
|
|
112
|
+
try {
|
|
113
|
+
const data = JSON.parse(await readFile(candidate, 'utf8'));
|
|
114
|
+
if (object(data).name === name) {
|
|
115
|
+
manifest = candidate;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
/* Continue toward the package root. */
|
|
121
|
+
}
|
|
122
|
+
const parent = dirname(cursor);
|
|
123
|
+
if (parent === cursor)
|
|
124
|
+
return null;
|
|
125
|
+
cursor = parent;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Avoid accidentally treating a dependency in an ancestor project as installed here.
|
|
133
|
+
const path = relative(await realpath(root), await realpath(manifest));
|
|
134
|
+
if (path.startsWith('..') || !path.split(/[\\/]/).includes('node_modules'))
|
|
135
|
+
return null;
|
|
136
|
+
try {
|
|
137
|
+
const data = JSON.parse(await readFile(manifest, 'utf8'));
|
|
138
|
+
return typeof object(data).version === 'string'
|
|
139
|
+
? object(data).version
|
|
140
|
+
: null;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Inspect files and installed types without running project scripts or writing probes. */
|
|
147
|
+
export async function doctor(workspace, options = {}) {
|
|
148
|
+
const { root, config } = workspace;
|
|
149
|
+
const result = { issues: [], checks: [] };
|
|
150
|
+
const issue = (code, message, fix, severity = 'error') => result.issues.push({ code, severity, message, fix });
|
|
151
|
+
const checkAbort = () => options.signal?.throwIfAborted();
|
|
152
|
+
async function text(path) {
|
|
153
|
+
checkAbort();
|
|
154
|
+
try {
|
|
155
|
+
return await readText(root, path);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
issue('unsafe-file', `Cannot safely read ${path}.`, 'Use a regular file inside this workspace.');
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async function environment(directory) {
|
|
163
|
+
let values = {};
|
|
164
|
+
for (const filename of ['.env', '.env.local']) {
|
|
165
|
+
const path = `${directory}/${filename}`;
|
|
166
|
+
const contents = await text(path);
|
|
167
|
+
try {
|
|
168
|
+
values = { ...values, ...parseEnv(contents ?? '') };
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
issue('env-invalid', `${path} could not be parsed.`, 'Repair this environment file without sharing its values.');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return new Map(Object.entries(values));
|
|
175
|
+
}
|
|
176
|
+
async function manifest(path) {
|
|
177
|
+
checkAbort();
|
|
178
|
+
try {
|
|
179
|
+
return await readJson(root, path);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
issue('manifest-invalid', `${path} is missing, unsafe, or invalid JSON.`, 'Restore a valid package manifest.');
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const rootManifest = await manifest('package.json');
|
|
187
|
+
if (rootManifest.name !== config.name)
|
|
188
|
+
issue('workspace-name', 'The root package name does not match convex-monorepo.json.', 'Align the root package name with the workspace metadata.');
|
|
189
|
+
if (rootManifest.packageManager !== `pnpm@${versions.pnpm}`) {
|
|
190
|
+
issue('package-manager-baseline', 'The root packageManager differs from this CLI’s tested pnpm version.', `Review packageManager against pnpm@${versions.pnpm}.`, 'warning');
|
|
191
|
+
}
|
|
192
|
+
for (const path of [
|
|
193
|
+
'pnpm-workspace.yaml',
|
|
194
|
+
'turbo.json',
|
|
195
|
+
'packages/backend/convex/tsconfig.json',
|
|
196
|
+
'packages/backend/convex/schema.ts',
|
|
197
|
+
]) {
|
|
198
|
+
if ((await text(path)) === null)
|
|
199
|
+
issue('file-missing', `${path} is missing.`, 'Restore the workspace configuration file.');
|
|
200
|
+
}
|
|
201
|
+
const workspaceYaml = await text('pnpm-workspace.yaml');
|
|
202
|
+
if (workspaceYaml !== null) {
|
|
203
|
+
const layout = workspacePatterns(workspaceYaml);
|
|
204
|
+
if (layout.unsupported)
|
|
205
|
+
issue('workspace-layout-unverified', 'pnpm-workspace.yaml uses syntax or package patterns outside the doctor’s static checks.', 'Review pnpm package selection manually or use the generated apps/* and packages/* lists.', 'warning');
|
|
206
|
+
for (const directory of [
|
|
207
|
+
'packages/backend',
|
|
208
|
+
...config.apps.map((app) => `apps/${app.name}`),
|
|
209
|
+
]) {
|
|
210
|
+
const excluded = layout.patterns.some((pattern) => pattern.startsWith('!') &&
|
|
211
|
+
matchesWorkspacePattern(pattern.slice(1), directory));
|
|
212
|
+
const included = layout.patterns.some((pattern) => !pattern.startsWith('!') &&
|
|
213
|
+
matchesWorkspacePattern(pattern, directory));
|
|
214
|
+
if (excluded || (!included && !layout.unsupported))
|
|
215
|
+
issue('workspace-package-excluded', `pnpm-workspace.yaml excludes ${directory} from the workspace.`, 'Include each recorded app and packages/backend in the pnpm packages list.');
|
|
216
|
+
}
|
|
217
|
+
if (!layout.unsupported &&
|
|
218
|
+
!result.issues.some((item) => item.code === 'workspace-package-excluded'))
|
|
219
|
+
result.checks.push('The pnpm package list includes all recorded apps and the backend under supported static patterns.');
|
|
220
|
+
}
|
|
221
|
+
for (const path of ['turbo.json', 'packages/backend/convex.json']) {
|
|
222
|
+
const source = await text(path);
|
|
223
|
+
if (source !== null) {
|
|
224
|
+
try {
|
|
225
|
+
const parsed = JSON.parse(source);
|
|
226
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
227
|
+
throw new Error();
|
|
228
|
+
if (path === 'turbo.json') {
|
|
229
|
+
const tasks = object(object(parsed).tasks);
|
|
230
|
+
for (const task of ['dev', 'build', 'typecheck', 'lint']) {
|
|
231
|
+
if (!tasks[task] ||
|
|
232
|
+
typeof tasks[task] !== 'object' ||
|
|
233
|
+
Array.isArray(tasks[task]))
|
|
234
|
+
issue('turbo-task-missing', `turbo.json has no valid ${task} task configuration.`, 'Restore the generated Turbo task or review the workspace scripts that invoke it.');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
issue('config-invalid', `${path} must contain a JSON object.`, 'Restore valid workspace configuration.');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else if (path !== 'turbo.json')
|
|
243
|
+
issue('file-missing', `${path} is missing.`, 'Restore the backend configuration.');
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
// Validate the apps parent before enumerating, since it may itself be a symlink.
|
|
247
|
+
await readText(root, 'apps/.doctor-parent-check');
|
|
248
|
+
for (const entry of await readdir(join(root, 'apps'), {
|
|
249
|
+
withFileTypes: true,
|
|
250
|
+
})) {
|
|
251
|
+
if ((entry.isDirectory() || entry.isSymbolicLink()) &&
|
|
252
|
+
!config.apps.some((app) => app.name === entry.name)) {
|
|
253
|
+
issue('untracked-app', `apps/${entry.name} is not recorded in convex-monorepo.json.`, 'Review this directory and add matching app metadata if it belongs to the workspace.', 'warning');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
issue('apps-directory', 'The apps directory is missing or unsafe.', 'Restore the apps directory inside the workspace.');
|
|
259
|
+
}
|
|
260
|
+
if (config.auth === 'clerk' &&
|
|
261
|
+
(await text('packages/backend/convex/auth.config.ts')) === null) {
|
|
262
|
+
issue('auth-config-missing', 'Clerk is recorded in metadata but the backend auth.config.ts is missing.', 'Restore the backend Clerk auth configuration before deploying.');
|
|
263
|
+
}
|
|
264
|
+
const backend = await manifest('packages/backend/package.json');
|
|
265
|
+
const backendName = typeof backend.name === 'string' ? backend.name : '';
|
|
266
|
+
if (backendName !== `@${config.name}/backend`)
|
|
267
|
+
issue('backend-name', 'The backend package name does not match the workspace metadata.', 'Align the backend package name and app workspace dependencies with the metadata.');
|
|
268
|
+
const exports = object(backend.exports);
|
|
269
|
+
for (const [key, expected] of [
|
|
270
|
+
['./api', 'api.d.ts'],
|
|
271
|
+
['./dataModel', 'dataModel.d.ts'],
|
|
272
|
+
]) {
|
|
273
|
+
const declaration = object(exports[key]).types;
|
|
274
|
+
if (typeof declaration !== 'string') {
|
|
275
|
+
issue('backend-exports', `The backend ${key} export has no types condition.`, 'Restore a types export pointing to a Convex generated declaration.');
|
|
276
|
+
}
|
|
277
|
+
else if ((await text(`packages/backend/${declaration.replace(/^\.\//, '')}`)) ===
|
|
278
|
+
null) {
|
|
279
|
+
issue('backend-declarations', `The backend ${key} declaration is missing or unsafe.`, `Run pnpm --filter ${backendName || './packages/backend'} exec convex codegen after reviewing the deployment configuration.`);
|
|
280
|
+
}
|
|
281
|
+
else
|
|
282
|
+
result.checks.push(`Backend ${key} declaration exists (${expected}).`);
|
|
283
|
+
}
|
|
284
|
+
const apiRuntime = object(exports['./api']).default;
|
|
285
|
+
if (typeof apiRuntime !== 'string' ||
|
|
286
|
+
(await text(`packages/backend/${apiRuntime.replace(/^\.\//, '')}`)) === null) {
|
|
287
|
+
issue('backend-runtime', 'The backend API runtime export is missing or unsafe.', 'Restore the API default export and generated api.js.');
|
|
288
|
+
}
|
|
289
|
+
const backendEnv = await environment('packages/backend');
|
|
290
|
+
const backendUrl = backendEnv.get('CONVEX_URL');
|
|
291
|
+
if (!validUrl(backendUrl))
|
|
292
|
+
issue('backend-url', 'The backend CONVEX_URL is missing or invalid.', 'Run pnpm convex:setup to configure the backend deployment.');
|
|
293
|
+
else
|
|
294
|
+
result.checks.push('Backend CONVEX_URL is a valid HTTP(S) origin.');
|
|
295
|
+
const resolved = new Map();
|
|
296
|
+
async function dependencies(directory, pkg, required) {
|
|
297
|
+
const declared = {
|
|
298
|
+
...object(pkg.dependencies),
|
|
299
|
+
...object(pkg.devDependencies),
|
|
300
|
+
};
|
|
301
|
+
const found = new Map();
|
|
302
|
+
resolved.set(directory, found);
|
|
303
|
+
for (const name of required) {
|
|
304
|
+
checkAbort();
|
|
305
|
+
if (typeof declared[name] !== 'string') {
|
|
306
|
+
issue('dependency-missing', `${directory || 'root'} does not declare ${name}.`, 'Restore the required dependency and run pnpm install.');
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const installed = await installedVersion(root, directory, name);
|
|
310
|
+
if (!installed) {
|
|
311
|
+
issue('dependency-uninstalled', `${name} cannot be resolved from ${directory || 'root'}.`, 'Run pnpm install from the workspace root.');
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
found.set(name, installed);
|
|
315
|
+
if (/^\d+\.\d+\.\d+$/.test(declared[name]) &&
|
|
316
|
+
declared[name] !== installed) {
|
|
317
|
+
issue('dependency-version', `${directory || 'root'} resolves ${name}@${installed}, which differs from its declared version.`, 'Run pnpm install and review the lockfile.');
|
|
318
|
+
}
|
|
319
|
+
if (baselines[name] && baselines[name] !== installed) {
|
|
320
|
+
issue('dependency-baseline', `${directory || 'root'} resolves ${name}@${installed}, outside this CLI’s tested baseline.`, `Review compatibility with the tested ${name}@${baselines[name]} baseline.`, 'warning');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
await dependencies('', rootManifest, ['turbo']);
|
|
325
|
+
await dependencies('packages/backend', backend, ['convex', 'typescript']);
|
|
326
|
+
for (const app of config.apps) {
|
|
327
|
+
const directory = `apps/${app.name}`;
|
|
328
|
+
const pkg = await manifest(`${directory}/package.json`);
|
|
329
|
+
await dependencies(directory, pkg, [
|
|
330
|
+
'convex',
|
|
331
|
+
'react',
|
|
332
|
+
'typescript',
|
|
333
|
+
...frameworks[app.framework],
|
|
334
|
+
...(config.auth === 'clerk' ? [clerk[app.framework]] : []),
|
|
335
|
+
]);
|
|
336
|
+
if (!backendName ||
|
|
337
|
+
typeof object(pkg.dependencies)[backendName] !== 'string') {
|
|
338
|
+
issue('backend-dependency', `${directory} does not declare the backend workspace package.`, 'Add the backend package with a workspace dependency.');
|
|
339
|
+
}
|
|
340
|
+
const prefix = app.framework === 'next'
|
|
341
|
+
? 'NEXT_PUBLIC'
|
|
342
|
+
: app.framework === 'expo'
|
|
343
|
+
? 'EXPO_PUBLIC'
|
|
344
|
+
: 'VITE';
|
|
345
|
+
const env = await environment(directory);
|
|
346
|
+
for (const secret of ['CLERK_SECRET_KEY', 'CONVEX_DEPLOY_KEY']) {
|
|
347
|
+
if (env.get(`${prefix}_${secret}`))
|
|
348
|
+
issue('public-secret', `${directory} exposes ${secret} through a public environment variable.`, 'Remove the public assignment, rotate the exposed credential, and rebuild the app.');
|
|
349
|
+
}
|
|
350
|
+
const key = publicVariable(app.framework);
|
|
351
|
+
if (!validUrl(env.get(key)))
|
|
352
|
+
issue('app-url', `${directory} has a missing or invalid ${key}.`, 'Run pnpm convex:link after configuring the backend.');
|
|
353
|
+
else if (validUrl(backendUrl) &&
|
|
354
|
+
new URL(env.get(key)).origin !== new URL(backendUrl).origin) {
|
|
355
|
+
issue('app-url-mismatch', `${directory} ${key} does not match the backend deployment URL.`, 'Run pnpm convex:link, then restart the app.');
|
|
356
|
+
}
|
|
357
|
+
else if (validUrl(backendUrl))
|
|
358
|
+
result.checks.push(`${directory} uses the backend deployment URL.`);
|
|
359
|
+
if (config.auth === 'clerk') {
|
|
360
|
+
const required = [
|
|
361
|
+
`${prefix}_CLERK_PUBLISHABLE_KEY`,
|
|
362
|
+
...(app.framework === 'next' || app.framework === 'tanstack-start'
|
|
363
|
+
? ['CLERK_SECRET_KEY']
|
|
364
|
+
: []),
|
|
365
|
+
];
|
|
366
|
+
for (const name of required)
|
|
367
|
+
if (!env.get(name))
|
|
368
|
+
issue('auth-env-missing', `${directory} is missing ${name}.`, 'Set the key in this app’s private .env.local.');
|
|
369
|
+
}
|
|
370
|
+
if ((await text(`${directory}/tsconfig.json`)) === null)
|
|
371
|
+
issue('app-tsconfig', `${directory}/tsconfig.json is missing.`, 'Restore the app TypeScript configuration.');
|
|
372
|
+
if (app.framework === 'expo') {
|
|
373
|
+
const metro = (await text(`${directory}/metro.config.cjs`)) ??
|
|
374
|
+
(await text(`${directory}/metro.config.js`));
|
|
375
|
+
if (metro === null)
|
|
376
|
+
issue('metro-config', `${directory} has no Metro configuration.`, 'Restore the Expo Metro configuration.');
|
|
377
|
+
else {
|
|
378
|
+
if (!/['"]expo\/metro-config['"]/.test(metro)) {
|
|
379
|
+
issue('metro-custom', `${directory} Metro configuration does not directly reference expo/metro-config.`, 'Review that the custom configuration inherits Expo workspace resolution defaults.', 'warning');
|
|
380
|
+
}
|
|
381
|
+
if (/disableHierarchicalLookup\s*:\s*true/.test(metro)) {
|
|
382
|
+
issue('metro-resolution', `${directory} Metro configuration disables hierarchical lookup.`, 'Review this override against Expo workspace package resolution.', 'warning');
|
|
383
|
+
}
|
|
384
|
+
result.checks.push(`${directory} Metro configuration exists; custom configuration and native execution were not evaluated.`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (backendName && resolved.get(directory)?.has('typescript')) {
|
|
388
|
+
checkAbort();
|
|
389
|
+
try {
|
|
390
|
+
const failure = probeTypes(root, directory, backendName);
|
|
391
|
+
if (failure)
|
|
392
|
+
issue('backend-types', `${directory} cannot consume typed backend exports (${failure}).`, 'Restore backend exports and generated declarations, then install dependencies and run the app typecheck.');
|
|
393
|
+
else
|
|
394
|
+
result.checks.push(`${directory} resolves backend API and data model types without an untyped API.`);
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
issue('type-probe', `${directory} TypeScript probe could not run.`, 'Install this app’s TypeScript dependency and review tsconfig.json.');
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
for (const name of ['convex', 'react']) {
|
|
402
|
+
const versionsFound = new Set([...resolved.values()]
|
|
403
|
+
.map((deps) => deps.get(name))
|
|
404
|
+
.filter((value) => value !== undefined));
|
|
405
|
+
if (versionsFound.size > 1)
|
|
406
|
+
issue('dependency-incompatible', `Workspace packages resolve different ${name} versions.`, `Align ${name} versions across packages and run pnpm install.`);
|
|
407
|
+
}
|
|
408
|
+
if (config.auth === 'clerk')
|
|
409
|
+
result.checks.push('Clerk deployment issuer configuration requires a separate Convex deployment check; no remote secrets were read.');
|
|
410
|
+
checkAbort();
|
|
411
|
+
return result;
|
|
412
|
+
}
|
|
413
|
+
function probeTypes(root, directory, backend) {
|
|
414
|
+
const require = createRequire(join(root, directory, 'package.json'));
|
|
415
|
+
const compiler = require('typescript');
|
|
416
|
+
const configPath = join(root, directory, 'tsconfig.json');
|
|
417
|
+
const config = compiler.readConfigFile(configPath, compiler.sys.readFile);
|
|
418
|
+
if (config.error)
|
|
419
|
+
return `TS${config.error.code}`;
|
|
420
|
+
const parsed = compiler.parseJsonConfigFileContent(config.config, compiler.sys, dirname(configPath));
|
|
421
|
+
if (parsed.errors.some((error) => error.code !== 18003))
|
|
422
|
+
return `TS${parsed.errors[0].code}`;
|
|
423
|
+
const file = join(root, directory, '__ccm_doctor_probe__.ts');
|
|
424
|
+
const source = `import { api } from ${JSON.stringify(`${backend}/api`)};\nimport type { DataModel } from ${JSON.stringify(`${backend}/dataModel`)};\ntype Assert<T extends false> = T;\nexport type ApiIsTyped = Assert<0 extends (1 & typeof api) ? true : false>;\nexport type ModelIsTyped = Assert<0 extends (1 & DataModel) ? true : false>;\nexport type ApiHasKnownKeys = Assert<string extends keyof typeof api ? true : false>;\n`;
|
|
425
|
+
const options = {
|
|
426
|
+
...parsed.options,
|
|
427
|
+
noEmit: true,
|
|
428
|
+
incremental: false,
|
|
429
|
+
composite: false,
|
|
430
|
+
skipLibCheck: true,
|
|
431
|
+
};
|
|
432
|
+
const host = compiler.createCompilerHost(options);
|
|
433
|
+
const originalRead = host.readFile.bind(host);
|
|
434
|
+
const originalExists = host.fileExists.bind(host);
|
|
435
|
+
const originalSource = host.getSourceFile.bind(host);
|
|
436
|
+
host.readFile = (path) => resolve(path) === file ? source : originalRead(path);
|
|
437
|
+
host.fileExists = (path) => resolve(path) === file || originalExists(path);
|
|
438
|
+
host.getSourceFile = (path, languageVersion, onError, shouldCreateNewSourceFile) => resolve(path) === file
|
|
439
|
+
? compiler.createSourceFile(file, source, languageVersion, true)
|
|
440
|
+
: originalSource(path, languageVersion, onError, shouldCreateNewSourceFile);
|
|
441
|
+
const program = compiler.createProgram([file], options, host);
|
|
442
|
+
const errors = compiler
|
|
443
|
+
.getPreEmitDiagnostics(program)
|
|
444
|
+
.filter((diagnostic) => diagnostic.category === compiler.DiagnosticCategory.Error);
|
|
445
|
+
if (errors.length)
|
|
446
|
+
return [...new Set(errors.map((error) => `TS${error.code}`))].join(', ');
|
|
447
|
+
const probe = program.getSourceFile(file);
|
|
448
|
+
if (!probe)
|
|
449
|
+
return 'probe source unavailable';
|
|
450
|
+
const checker = program.getTypeChecker();
|
|
451
|
+
const seen = new Set();
|
|
452
|
+
function untypedBranch(type, node) {
|
|
453
|
+
if (type.flags & compiler.TypeFlags.Any)
|
|
454
|
+
return true;
|
|
455
|
+
if (!(type.flags & compiler.TypeFlags.Object) || seen.has(type))
|
|
456
|
+
return false;
|
|
457
|
+
seen.add(type);
|
|
458
|
+
// Convex function references are leaves. Their argument and return validators
|
|
459
|
+
// may intentionally accept arbitrary values, which this check does not audit.
|
|
460
|
+
if (checker.getPropertyOfType(type, '_type'))
|
|
461
|
+
return false;
|
|
462
|
+
if (checker.getIndexTypeOfType(type, compiler.IndexKind.String))
|
|
463
|
+
return true;
|
|
464
|
+
return checker
|
|
465
|
+
.getPropertiesOfType(type)
|
|
466
|
+
.some((property) => untypedBranch(checker.getTypeOfSymbolAtLocation(property, node), node));
|
|
467
|
+
}
|
|
468
|
+
for (const statement of probe.statements) {
|
|
469
|
+
if (!compiler.isImportDeclaration(statement))
|
|
470
|
+
continue;
|
|
471
|
+
const bindings = statement.importClause?.namedBindings;
|
|
472
|
+
if (!bindings || !compiler.isNamedImports(bindings))
|
|
473
|
+
continue;
|
|
474
|
+
for (const binding of bindings.elements) {
|
|
475
|
+
if (binding.name.text === 'api' &&
|
|
476
|
+
untypedBranch(checker.getTypeAtLocation(binding.name), binding.name)) {
|
|
477
|
+
return 'untyped API module or function reference';
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/workspace/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EACL,aAAa,EACb,cAAc,GACf,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAkB,MAAM,cAAc,CAAC;AAclE,MAAM,UAAU,GAAgC;IAC9C,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC;IAC3B,IAAI,EAAE,CAAC,MAAM,EAAE,sBAAsB,EAAE,WAAW,CAAC;IACnD,gBAAgB,EAAE,CAAC,uBAAuB,EAAE,MAAM,EAAE,WAAW,CAAC;IAChE,IAAI,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC;CAC/B,CAAC;AACF,MAAM,KAAK,GAA8B;IACvC,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,cAAc;IACpB,gBAAgB,EAAE,6BAA6B;IAC/C,IAAI,EAAE,aAAa;CACpB,CAAC;AACF,MAAM,SAAS,GAA2B;IACxC,MAAM,EAAE,QAAQ,CAAC,MAAM;IACvB,KAAK,EAAE,QAAQ,CAAC,KAAK;IACrB,UAAU,EAAE,QAAQ,CAAC,UAAU;IAC/B,IAAI,EAAE,QAAQ,CAAC,IAAI;IACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;IACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;IACnB,cAAc,EAAE,QAAQ,CAAC,WAAW;IACpC,uBAAuB,EAAE,QAAQ,CAAC,aAAa;CAChD,CAAC;AACF,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAChE,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AACD,SAAS,QAAQ,CAAC,KAAyB;IACzC,IAAI,CAAC;QACH,aAAa,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,SAAS,iBAAiB,CAAC,MAAc;IAIvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK;SAClB,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACvE,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACrE,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAE,CAAE,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;IACvE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,SAAS,KAAK,CAAC,GAAW;QACxB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5E,IACE,CAAC,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC;YACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;YACtB,OAAO;iBACJ,KAAK,CAAC,GAAG,CAAC;iBACV,IAAI,CACH,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,KAAK,GAAG;gBACZ,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,CAAC,CACxD,EACH,CAAC;YACD,WAAW,GAAG,IAAI,CAAC;YACnB,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAClD,IAAI,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE;YAAE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3E,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC/D,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,MAAM;YAChC,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI;gBAAE,WAAW,GAAG,IAAI,CAAC;;gBACzB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC;AACD,SAAS,uBAAuB,CAAC,OAAe,EAAE,IAAY;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,CACL,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;QACjC,QAAQ,CAAC,KAAK,CACZ,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,CACjE,CACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,IAAY,EACZ,SAAiB,EACjB,IAAY;IAEZ,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;IACrE,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,eAAe,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YACH,IAAI,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5C,SAAS,CAAC;gBACR,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;gBAC/C,IAAI,CAAC;oBACH,MAAM,IAAI,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;oBACpE,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;wBAC/B,QAAQ,GAAG,SAAS,CAAC;wBACrB,MAAM;oBACR,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,uCAAuC;gBACzC,CAAC;gBACD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC/B,IAAI,MAAM,KAAK,MAAM;oBAAE,OAAO,IAAI,CAAC;gBACnC,MAAM,GAAG,MAAM,CAAC;YAClB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,qFAAqF;IACrF,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtE,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,IAAI,CAAC;QACH,MAAM,IAAI,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QACnE,OAAO,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,QAAQ;YAC7C,CAAC,CAAE,MAAM,CAAC,IAAI,CAAC,CAAC,OAAkB;YAClC,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,SAAoB,EACpB,UAAoC,EAAE;IAEtC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACnC,MAAM,MAAM,GAAiB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACxD,MAAM,KAAK,GAAG,CACZ,IAAY,EACZ,OAAe,EACf,GAAW,EACX,WAAoC,OAAO,EAC3C,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;IAC1D,KAAK,UAAU,IAAI,CAAC,IAAY;QAC9B,UAAU,EAAE,CAAC;QACb,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CACH,aAAa,EACb,sBAAsB,IAAI,GAAG,EAC7B,2CAA2C,CAC5C,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,KAAK,UAAU,WAAW,CACxB,SAAiB;QAEjB,IAAI,MAAM,GAAuC,EAAE,CAAC;QACpD,KAAK,MAAM,QAAQ,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,GAAG,SAAS,IAAI,QAAQ,EAAE,CAAC;YACxC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;YACtD,CAAC;YAAC,MAAM,CAAC;gBACP,KAAK,CACH,aAAa,EACb,GAAG,IAAI,uBAAuB,EAC9B,0DAA0D,CAC3D,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,CAAC;IACD,KAAK,UAAU,QAAQ,CAAC,IAAY;QAClC,UAAU,EAAE,CAAC;QACb,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CACH,kBAAkB,EAClB,GAAG,IAAI,uCAAuC,EAC9C,mCAAmC,CACpC,CAAC;YACF,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,CAAC;IACpD,IAAI,YAAY,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;QACnC,KAAK,CACH,gBAAgB,EAChB,4DAA4D,EAC5D,0DAA0D,CAC3D,CAAC;IACJ,IAAI,YAAY,CAAC,cAAc,KAAK,QAAQ,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5D,KAAK,CACH,0BAA0B,EAC1B,sEAAsE,EACtE,sCAAsC,QAAQ,CAAC,IAAI,GAAG,EACtD,SAAS,CACV,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,IAAI,IAAI;QACjB,qBAAqB;QACrB,YAAY;QACZ,uCAAuC;QACvC,mCAAmC;KACpC,EAAE,CAAC;QACF,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI;YAC7B,KAAK,CACH,cAAc,EACd,GAAG,IAAI,cAAc,EACrB,2CAA2C,CAC5C,CAAC;IACN,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACxD,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAC;QAChD,IAAI,MAAM,CAAC,WAAW;YACpB,KAAK,CACH,6BAA6B,EAC7B,yFAAyF,EACzF,0FAA0F,EAC1F,SAAS,CACV,CAAC;QACJ,KAAK,MAAM,SAAS,IAAI;YACtB,kBAAkB;YAClB,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;SAChD,EAAE,CAAC;YACF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CACnC,CAAC,OAAO,EAAE,EAAE,CACV,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBACvB,uBAAuB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CACvD,CAAC;YACF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CACnC,CAAC,OAAO,EAAE,EAAE,CACV,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBACxB,uBAAuB,CAAC,OAAO,EAAE,SAAS,CAAC,CAC9C,CAAC;YACF,IAAI,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;gBAChD,KAAK,CACH,4BAA4B,EAC5B,gCAAgC,SAAS,sBAAsB,EAC/D,2EAA2E,CAC5E,CAAC;QACN,CAAC;QACD,IACE,CAAC,MAAM,CAAC,WAAW;YACnB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,4BAA4B,CAAC;YAEzE,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,mGAAmG,CACpG,CAAC;IACN,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,8BAA8B,CAAC,EAAE,CAAC;QAClE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC3C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;oBAChE,MAAM,IAAI,KAAK,EAAE,CAAC;gBACpB,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;oBAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;oBAC3C,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC;wBACzD,IACE,CAAC,KAAK,CAAC,IAAI,CAAC;4BACZ,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ;4BAC/B,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;4BAE1B,KAAK,CACH,oBAAoB,EACpB,2BAA2B,IAAI,sBAAsB,EACrD,kFAAkF,CACnF,CAAC;oBACN,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,KAAK,CACH,gBAAgB,EAChB,GAAG,IAAI,8BAA8B,EACrC,wCAAwC,CACzC,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,KAAK,YAAY;YAC9B,KAAK,CACH,cAAc,EACd,GAAG,IAAI,cAAc,EACrB,oCAAoC,CACrC,CAAC;IACN,CAAC;IACD,IAAI,CAAC;QACH,iFAAiF;QACjF,MAAM,QAAQ,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;QAClD,KAAK,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE;YACpD,aAAa,EAAE,IAAI;SACpB,CAAC,EAAE,CAAC;YACH,IACE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;gBAC/C,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,EACnD,CAAC;gBACD,KAAK,CACH,eAAe,EACf,QAAQ,KAAK,CAAC,IAAI,2CAA2C,EAC7D,qFAAqF,EACrF,SAAS,CACV,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,CACH,gBAAgB,EAChB,0CAA0C,EAC1C,kDAAkD,CACnD,CAAC;IACJ,CAAC;IACD,IACE,MAAM,CAAC,IAAI,KAAK,OAAO;QACvB,CAAC,MAAM,IAAI,CAAC,wCAAwC,CAAC,CAAC,KAAK,IAAI,EAC/D,CAAC;QACD,KAAK,CACH,qBAAqB,EACrB,0EAA0E,EAC1E,gEAAgE,CACjE,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,+BAA+B,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,IAAI,WAAW,KAAK,IAAI,MAAM,CAAC,IAAI,UAAU;QAC3C,KAAK,CACH,cAAc,EACd,iEAAiE,EACjE,kFAAkF,CACnF,CAAC;IACJ,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI;QAC5B,CAAC,OAAO,EAAE,UAAU,CAAC;QACrB,CAAC,aAAa,EAAE,gBAAgB,CAAC;KAClC,EAAE,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,GAAI,CAAC,CAAC,CAAC,KAAK,CAAC;QAChD,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACpC,KAAK,CACH,iBAAiB,EACjB,eAAe,GAAG,iCAAiC,EACnD,oEAAoE,CACrE,CAAC;QACJ,CAAC;aAAM,IACL,CAAC,MAAM,IAAI,CAAC,oBAAoB,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;YACpE,IAAI,EACJ,CAAC;YACD,KAAK,CACH,sBAAsB,EACtB,eAAe,GAAG,oCAAoC,EACtD,qBAAqB,WAAW,IAAI,oBAAoB,oEAAoE,CAC7H,CAAC;QACJ,CAAC;;YACC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,GAAG,wBAAwB,QAAQ,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACpD,IACE,OAAO,UAAU,KAAK,QAAQ;QAC9B,CAAC,MAAM,IAAI,CAAC,oBAAoB,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,EAC5E,CAAC;QACD,KAAK,CACH,iBAAiB,EACjB,sDAAsD,EACtD,sDAAsD,CACvD,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,kBAAkB,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAChD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;QACvB,KAAK,CACH,aAAa,EACb,+CAA+C,EAC/C,4DAA4D,CAC7D,CAAC;;QACC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IACxD,KAAK,UAAU,YAAY,CACzB,SAAiB,EACjB,GAA4B,EAC5B,QAAkB;QAElB,MAAM,QAAQ,GAAG;YACf,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;YAC3B,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC;SAC/B,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;QACxC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,UAAU,EAAE,CAAC;YACb,IAAI,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACvC,KAAK,CACH,oBAAoB,EACpB,GAAG,SAAS,IAAI,MAAM,qBAAqB,IAAI,GAAG,EAClD,uDAAuD,CACxD,CAAC;gBACF,SAAS;YACX,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAChE,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,KAAK,CACH,wBAAwB,EACxB,GAAG,IAAI,4BAA4B,SAAS,IAAI,MAAM,GAAG,EACzD,2CAA2C,CAC5C,CAAC;gBACF,SAAS;YACX,CAAC;YACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC3B,IACE,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACtC,QAAQ,CAAC,IAAI,CAAC,KAAK,SAAS,EAC5B,CAAC;gBACD,KAAK,CACH,oBAAoB,EACpB,GAAG,SAAS,IAAI,MAAM,aAAa,IAAI,IAAI,SAAS,4CAA4C,EAChG,2CAA2C,CAC5C,CAAC;YACJ,CAAC;YACD,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;gBACrD,KAAK,CACH,qBAAqB,EACrB,GAAG,SAAS,IAAI,MAAM,aAAa,IAAI,IAAI,SAAS,uCAAuC,EAC3F,wCAAwC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,YAAY,EAC3E,SAAS,CACV,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,YAAY,CAAC,EAAE,EAAE,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,MAAM,YAAY,CAAC,kBAAkB,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IAC1E,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,SAAS,eAAe,CAAC,CAAC;QACxD,MAAM,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE;YACjC,QAAQ;YACR,OAAO;YACP,YAAY;YACZ,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;YAC5B,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3D,CAAC,CAAC;QACH,IACE,CAAC,WAAW;YACZ,OAAO,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,WAAW,CAAC,KAAK,QAAQ,EACzD,CAAC;YACD,KAAK,CACH,oBAAoB,EACpB,GAAG,SAAS,kDAAkD,EAC9D,sDAAsD,CACvD,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GACV,GAAG,CAAC,SAAS,KAAK,MAAM;YACtB,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM;gBACxB,CAAC,CAAC,aAAa;gBACf,CAAC,CAAC,MAAM,CAAC;QACf,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,SAAS,CAAC,CAAC;QACzC,KAAK,MAAM,MAAM,IAAI,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EAAE,CAAC;YAC/D,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;gBAChC,KAAK,CACH,eAAe,EACf,GAAG,SAAS,YAAY,MAAM,yCAAyC,EACvE,mFAAmF,CACpF,CAAC;QACN,CAAC;QACD,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACzB,KAAK,CACH,SAAS,EACT,GAAG,SAAS,6BAA6B,GAAG,GAAG,EAC/C,qDAAqD,CACtD,CAAC;aACC,IACH,QAAQ,CAAC,UAAU,CAAC;YACpB,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,UAAW,CAAC,CAAC,MAAM,EAC7D,CAAC;YACD,KAAK,CACH,kBAAkB,EAClB,GAAG,SAAS,IAAI,GAAG,6CAA6C,EAChE,6CAA6C,CAC9C,CAAC;QACJ,CAAC;aAAM,IAAI,QAAQ,CAAC,UAAU,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,mCAAmC,CAAC,CAAC;QACtE,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG;gBACf,GAAG,MAAM,wBAAwB;gBACjC,GAAG,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM,IAAI,GAAG,CAAC,SAAS,KAAK,gBAAgB;oBAChE,CAAC,CAAC,CAAC,kBAAkB,CAAC;oBACtB,CAAC,CAAC,EAAE,CAAC;aACR,CAAC;YACF,KAAK,MAAM,IAAI,IAAI,QAAQ;gBACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;oBAChB,KAAK,CACH,kBAAkB,EAClB,GAAG,SAAS,eAAe,IAAI,GAAG,EAClC,+CAA+C,CAChD,CAAC;QACR,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,gBAAgB,CAAC,CAAC,KAAK,IAAI;YACrD,KAAK,CACH,cAAc,EACd,GAAG,SAAS,4BAA4B,EACxC,2CAA2C,CAC5C,CAAC;QACJ,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;YAC7B,MAAM,KAAK,GACT,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,mBAAmB,CAAC,CAAC;gBAC7C,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,kBAAkB,CAAC,CAAC,CAAC;YAC/C,IAAI,KAAK,KAAK,IAAI;gBAChB,KAAK,CACH,cAAc,EACd,GAAG,SAAS,8BAA8B,EAC1C,uCAAuC,CACxC,CAAC;iBACC,CAAC;gBACJ,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC9C,KAAK,CACH,cAAc,EACd,GAAG,SAAS,qEAAqE,EACjF,mFAAmF,EACnF,SAAS,CACV,CAAC;gBACJ,CAAC;gBACD,IAAI,sCAAsC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBACvD,KAAK,CACH,kBAAkB,EAClB,GAAG,SAAS,oDAAoD,EAChE,iEAAiE,EACjE,SAAS,CACV,CAAC;gBACJ,CAAC;gBACD,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,GAAG,SAAS,4FAA4F,CACzG,CAAC;YACJ,CAAC;QACH,CAAC;QACD,IAAI,WAAW,IAAI,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9D,UAAU,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;gBACzD,IAAI,OAAO;oBACT,KAAK,CACH,eAAe,EACf,GAAG,SAAS,0CAA0C,OAAO,IAAI,EACjE,0GAA0G,CAC3G,CAAC;;oBAEF,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,GAAG,SAAS,oEAAoE,CACjF,CAAC;YACN,CAAC;YAAC,MAAM,CAAC;gBACP,KAAK,CACH,YAAY,EACZ,GAAG,SAAS,kCAAkC,EAC9C,oEAAoE,CACrE,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;aACnB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;aAC7B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAC1C,CAAC;QACF,IAAI,aAAa,CAAC,IAAI,GAAG,CAAC;YACxB,KAAK,CACH,yBAAyB,EACzB,wCAAwC,IAAI,YAAY,EACxD,SAAS,IAAI,iDAAiD,CAC/D,CAAC;IACN,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;QACzB,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB,iHAAiH,CAClH,CAAC;IACJ,UAAU,EAAE,CAAC;IACb,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CACjB,IAAY,EACZ,SAAiB,EACjB,OAAe;IAEf,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;IACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAc,CAAC;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1E,IAAI,MAAM,CAAC,KAAK;QAAE,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAClD,MAAM,MAAM,GAAG,QAAQ,CAAC,0BAA0B,CAChD,MAAM,CAAC,MAAM,EACb,QAAQ,CAAC,GAAG,EACZ,OAAO,CAAC,UAAU,CAAC,CACpB,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC;QACrD,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,yBAAyB,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,uBAAuB,IAAI,CAAC,SAAS,CAAC,GAAG,OAAO,MAAM,CAAC,qCAAqC,IAAI,CAAC,SAAS,CAAC,GAAG,OAAO,YAAY,CAAC,0RAA0R,CAAC;IAC5a,MAAM,OAAO,GAAuB;QAClC,GAAG,MAAM,CAAC,OAAO;QACjB,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,KAAK;QAClB,SAAS,EAAE,KAAK;QAChB,YAAY,EAAE,IAAI;KACnB,CAAC;IACF,MAAM,IAAI,GAAG,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,EAAE,EAAE,CACvB,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;IAC3E,IAAI,CAAC,aAAa,GAAG,CACnB,IAAI,EACJ,eAAe,EACf,OAAO,EACP,yBAAyB,EACzB,EAAE,CACF,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI;QACpB,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,CAAC;QAChE,CAAC,CAAC,cAAc,CACZ,IAAI,EACJ,eAAe,EACf,OAAO,EACP,yBAAyB,CAC1B,CAAC;IACR,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,QAAQ;SACpB,qBAAqB,CAAC,OAAO,CAAC;SAC9B,MAAM,CACL,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,QAAQ,CAAC,kBAAkB,CAAC,KAAK,CAC1E,CAAC;IACJ,IAAI,MAAM,CAAC,MAAM;QACf,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,0BAA0B,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAW,CAAC;IAChC,SAAS,aAAa,CAAC,IAAa,EAAE,IAAa;QACjD,IAAI,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACrD,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAC7D,OAAO,KAAK,CAAC;QACf,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,8EAA8E;QAC9E,8EAA8E;QAC9E,IAAI,OAAO,CAAC,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QAC3D,IAAI,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,OAAO,OAAO;aACX,mBAAmB,CAAC,IAAI,CAAC;aACzB,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CACjB,aAAa,CAAC,OAAO,CAAC,yBAAyB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CACvE,CAAC;IACN,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,SAAS,CAAC;YAAE,SAAS;QACvD,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,EAAE,aAAa,CAAC;QACvD,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC;YAAE,SAAS;QAC9D,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxC,IACE,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK;gBAC3B,aAAa,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,EACpE,CAAC;gBACD,OAAO,0CAA0C,CAAC;YACpD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChangePlan } from './changes.js';
|
|
2
|
+
import { type Workspace } from './project.js';
|
|
3
|
+
export declare function readBackendEnvironment(workspace: Workspace): Promise<{
|
|
4
|
+
values: Record<string, string | undefined>;
|
|
5
|
+
guards: Array<{
|
|
6
|
+
path: string;
|
|
7
|
+
contents: string | null;
|
|
8
|
+
}>;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function planEnvSync(workspace: Workspace, options?: {
|
|
11
|
+
app?: string;
|
|
12
|
+
}): Promise<ChangePlan>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseEnv } from 'node:util';
|
|
2
|
+
import { deploymentUrl, linkEnvironment, publicVariable, } from '../../assets/setup/convex-setup.mjs';
|
|
3
|
+
import { readText } from './project.js';
|
|
4
|
+
export async function readBackendEnvironment(workspace) {
|
|
5
|
+
let values = {};
|
|
6
|
+
const guards = [];
|
|
7
|
+
for (const path of ['packages/backend/.env', 'packages/backend/.env.local']) {
|
|
8
|
+
const contents = await readText(workspace.root, path);
|
|
9
|
+
guards.push({ path, contents });
|
|
10
|
+
values = { ...values, ...parseEnv(contents ?? '') };
|
|
11
|
+
}
|
|
12
|
+
return { values, guards };
|
|
13
|
+
}
|
|
14
|
+
export async function planEnvSync(workspace, options = {}) {
|
|
15
|
+
const apps = options.app === undefined
|
|
16
|
+
? workspace.config.apps
|
|
17
|
+
: workspace.config.apps.filter((app) => app.name === options.app);
|
|
18
|
+
if (!apps.length)
|
|
19
|
+
throw new Error(`Unknown application: ${options.app}.`);
|
|
20
|
+
const backend = await readBackendEnvironment(workspace);
|
|
21
|
+
const url = deploymentUrl(backend.values.CONVEX_URL);
|
|
22
|
+
const plan = {
|
|
23
|
+
root: workspace.root,
|
|
24
|
+
changes: [],
|
|
25
|
+
guards: [
|
|
26
|
+
{ path: 'convex-monorepo.json', contents: workspace.configText },
|
|
27
|
+
...backend.guards,
|
|
28
|
+
],
|
|
29
|
+
notes: [
|
|
30
|
+
'Only the public Convex URL is linked. Restart affected development servers after syncing.',
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
for (const app of apps) {
|
|
34
|
+
const manifestPath = `apps/${app.name}/package.json`;
|
|
35
|
+
const manifest = await readText(workspace.root, manifestPath);
|
|
36
|
+
if (manifest === null)
|
|
37
|
+
throw new Error(`Missing application: apps/${app.name}. Restore the application before linking its URL.`);
|
|
38
|
+
plan.guards?.push({ path: manifestPath, contents: manifest });
|
|
39
|
+
const path = `apps/${app.name}/.env.local`;
|
|
40
|
+
const before = await readText(workspace.root, path);
|
|
41
|
+
const after = linkEnvironment(before ?? '', publicVariable(app.framework), url);
|
|
42
|
+
if (before !== after)
|
|
43
|
+
plan.changes.push({ path, before, after, mode: 0o600 });
|
|
44
|
+
}
|
|
45
|
+
if (apps.some((app) => app.framework === 'expo') &&
|
|
46
|
+
['localhost', '127.0.0.1', '[::1]'].includes(new URL(url).hostname))
|
|
47
|
+
plan.notes.push('Physical phones need a reachable Convex URL. localhost points to the phone itself.');
|
|
48
|
+
return plan;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/workspace/env.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AppSpec, Auth, Example } from '../generator/types.js';
|
|
2
|
+
export interface WorkspaceApp extends AppSpec {
|
|
3
|
+
example?: Example;
|
|
4
|
+
}
|
|
5
|
+
export interface WorkspaceConfig {
|
|
6
|
+
version: 1;
|
|
7
|
+
generator: string;
|
|
8
|
+
name: string;
|
|
9
|
+
packageManager: 'pnpm';
|
|
10
|
+
monorepo: 'turbo';
|
|
11
|
+
apps: WorkspaceApp[];
|
|
12
|
+
auth: Auth;
|
|
13
|
+
example: Example;
|
|
14
|
+
}
|
|
15
|
+
export interface Workspace {
|
|
16
|
+
root: string;
|
|
17
|
+
config: WorkspaceConfig;
|
|
18
|
+
rawConfig: Record<string, unknown>;
|
|
19
|
+
configText: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
22
|
+
export declare function isMissing(error: unknown): boolean;
|
|
23
|
+
/** Check every existing path component, including the workspace root. */
|
|
24
|
+
export declare function safePath(root: string, path: string): Promise<string>;
|
|
25
|
+
export declare function readText(root: string, path: string): Promise<string | null>;
|
|
26
|
+
export declare function readJson(root: string, path: string): Promise<Record<string, unknown>>;
|
|
27
|
+
export declare function parseWorkspaceConfig(value: unknown): WorkspaceConfig;
|
|
28
|
+
export declare function loadWorkspace(cwd?: string): Promise<Workspace>;
|