-
Notifications
You must be signed in to change notification settings - Fork 96
feat(packages): added create miniapp CLI #6887
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a new create-miniapp CLI package and npm script; implements interactive scaffolding, templated client/server/fullstack projects, Helm chart generation and secret handling, OIDC middleware in templates, install/format/prepare helpers, utilities, and many template assets and configs. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant npm as npm script
participant CLI as create-miniapp CLI
participant Prompts as Interactive prompts
participant FS as File system
participant Helm as Helm setup
participant Installer as Dependency installer
participant Prepare as prepareApp
User->>npm: npm run create-miniapp
npm->>CLI: invoke CLI entry
CLI->>Prompts: ask for name, type, resources, flags
Prompts->>CLI: return choices
CLI->>FS: scaffold template -> copy files (handle overwrite)
CLI->>FS: update package.json, replace aliases
CLI->>Helm: setupHelm(app config)
Helm->>FS: write templates, update services & secrets, update values
CLI->>Installer: optionally install dependencies
Installer->>FS: run package manager install in projectDir
CLI->>Prepare: run prepareApp steps (build, migrations, types)
Prepare->>FS: run build/migration scripts
CLI->>User: log next steps and exit
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| console.log('process.cwd(): ', process.cwd()) | ||
| console.log('projectName: ', projectName) | ||
| const rootDir = path.resolve(process.cwd(), '../..') // relative to CLI package | ||
| const appsDir = path.join(rootDir, 'apps') | ||
| const projectDir = path.resolve(appsDir, projectName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scaffolding path escapes repository root
The project directory is derived from path.resolve(process.cwd(), '../..'), which assumes the process is launched from packages/create-miniapp-cli. The root package.json introduces a create-miniapp script that runs the CLI from the repo root, so executing yarn create-miniapp new-app now resolves ../../apps/new-app, i.e. two directories above the repo. This either fails with ENOENT or creates files outside the repository, making the CLI unusable from its advertised entry point.
Useful? React with 👍 / 👎.
| files.forEach((file) => { | ||
| const filePath = path.join(directoryPath, file) | ||
| if (fs.statSync(filePath).isDirectory()) { | ||
| replaceTextInFiles(filePath, search, replacement) | ||
| } else { | ||
| const data = fs.readFileSync(filePath, 'utf8') | ||
| const updatedData = data.replace(new RegExp(search, 'g'), replacement) | ||
| fs.writeFileSync(filePath, updatedData, 'utf8') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import alias replacement corrupts binary assets
replaceTextInFiles recurses through every file in the scaffolded project and rewrites them with fs.readFileSync(filePath, 'utf8') / writeFileSync(..., 'utf8'). The CLI runs this over the entire template directory to swap aliases, which includes PNGs, PDFs and other binaries. Reading and writing those files as UTF‑8 will rewrite their bytes and break the assets even when no replacement occurs. The traversal should skip non‑text files or operate on buffered data without encoding.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 56
🧹 Nitpick comments (24)
packages/create-miniapp-cli/template/client/_gitignore (1)
1-37: Consider adding section headers for maintainability.The AI summary mentions organized sections (DEPENDENCIES, TESTING, BUILD, ENV FILES, LOGS, MISC, MAC, TS/BUILD), but the file lacks explicit section headers or comments. Adding brief comments to group related patterns would improve maintainability and make it easier for developers to locate or add patterns in the future.
Consider adding section headers:
+# Dependencies node_modules bin/.local + +# Build artifacts dist out .next + +# Environment & Configuration .idea .env .kmigrator + +# OS & IDE .DS_Store .docker-cache-warming .expo .expo-shared web-build + +# Generated files coverage __generated__This would improve readability and make the file easier to maintain as it grows.
packages/create-miniapp-cli/template/fullstack/pages/global.css (3)
1-1: Consider hosting fonts locally to reduce external dependencies.Importing fonts from googleapis.com adds a runtime external dependency and introduces a potential single point of failure if Google's services are unavailable. For better reliability and performance, consider:
- Downloading the font files and bundling them locally within the template
- Implementing font fallback chains to gracefully degrade if the import fails
- Using the
font-display: swapproperty to ensure text remains readable while fonts loadThis is especially important for a CLI-generated template that should be as self-contained and resilient as possible.
9-9: Replace deprecatedtext-rendering: optimizelegibilityproperty.The
text-rendering: optimizelegibilityvalue is deprecated in modern CSS. Replace it withtext-rendering: optimizeSpeed, which is the modern standard.- text-rendering: optimizelegibility; + text-rendering: optimizeSpeed;
42-42: Reconsider!importantflag in favor of specificity or a wrapper class.While
!importantis pragmatic for overriding Ant Design styles in a template, it reduces maintainability. Consider alternative approaches:
- Wrap the component in a scoped class (e.g.,
.miniapp-form .react-tel-input .ant-input)- Use CSS modules to isolate and scope styles naturally
- Document the rationale for
!importantwith an inline comment if it must remainThis will make the template easier to customize and extend without cascading specificity issues.
packages/create-miniapp-cli/template/fullstack/_gitignore (1)
1-37: Well-structured gitignore template with minor enhancements recommended.The ignore patterns are comprehensive and appropriate for a fullstack mini-app template, covering build outputs, IDE files, dependency directories, and security-sensitive files.
Recommended improvements:
Expand environment file patterns (line 7): Consider using
.env*or adding specific patterns like.env.local,.env.*.localto catch environment-specific files that developers might create.Add trailing newline: Git convention suggests files should end with a newline character for better compatibility with various tools.
Apply this diff:
-.env +.env* +!.env.exampleAnd ensure the file ends with a newline:
# Turborepo local cache .turbo +Note: The
.env*pattern with!.env.exampleallows committing example environment files while protecting actual secrets.packages/create-miniapp-cli/template/server/jest.setupSpec.js (1)
5-14: Consider logging specific test properties for clearer output.The test lifecycle logging infrastructure is functional, but consider these improvements:
Log specific properties: Logging the entire
jasmine.currentTestobject may produce verbose output. Consider loggingjasmine.currentTest.fullNameorjasmine.currentTest.descriptionfor cleaner, more readable test output.Inconsistent property access: Lines 8-9 use dot notation (
jasmine.currentTest), while lines 12-13 use bracket notation (jasmine['currentTest']). Using consistent dot notation throughout would improve readability.Apply this diff for clearer logging:
- beforeEach(() => console.log('[BEGIN] TEST:', jasmine['currentTest'])) // eslint-disable-line - afterEach(() => console.log('[END] TEST:', jasmine['currentTest'])) // eslint-disable-line + beforeEach(() => console.log('[BEGIN] TEST:', jasmine.currentTest?.fullName)) // eslint-disable-line + afterEach(() => console.log('[END] TEST:', jasmine.currentTest?.fullName)) // eslint-disable-linepackages/create-miniapp-cli/template/server/domains/user/utils/clientSchema/User.ts (1)
16-30: Consider exporting all available hooks for completeness.The
generateReactHooksfunction returns 10 hooks, but only 5 are exported. The omitted hooks are:
useCreateMany- for batch creationuseUpdateMany- for batch updatesuseSoftDeleteMany- for batch soft deletesuseCount- for counting recordsuseAllObjects- for loading all records with automatic paginationWhile the selective export might be intentional to keep the API surface minimal, consider whether template users might need these additional hooks. If they're commonly needed, exporting all hooks would provide a more complete toolkit.
If you decide to export all hooks, apply this diff:
const { useObject, useObjects, useCreate, + useCreateMany, useUpdate, + useUpdateMany, useSoftDelete, + useSoftDeleteMany, + useCount, + useAllObjects, } = generateReactHooks<User, UserCreateInput, UserUpdateInput, QueryAllUsersArgs>(UserGQL) export { useObject, useObjects, useCreate, + useCreateMany, useUpdate, + useUpdateMany, useSoftDelete, + useSoftDeleteMany, + useCount, + useAllObjects, }packages/create-miniapp-cli/template/server/clients/condoClient.js (1)
5-8: Consider removing the no-op constructor or documenting its purpose.The constructor only forwards arguments to the superclass without adding any behavior. If this is intentional scaffolding for future auto-generated methods (as suggested by the comment on line 10), consider adding a comment to clarify. Otherwise, you can remove the constructor entirely and rely on the inherited one.
class CondoClient extends ApolloServerClient { - constructor ({ endpoint, authRequisites, opts }) { - super(endpoint, authRequisites, opts) - } - - // AUTO_GENERATED: use needed methods here + // Note: Constructor inherited from ApolloServerClient + // AUTO_GENERATED: add custom methods here as needed }packages/create-miniapp-cli/template/fullstack/jest.setupTest.js (1)
13-22: Consider extracting duplicated Jasmine patching logic.This Jasmine reporter patching code (lines 13-22) is identical to
jest.setupSpec.jslines 3-12. While the duplication is minimal, extracting it to a shared utility function would improve maintainability.You could create a shared
setupJasmineLogging.jsutility:// setupJasmineLogging.js module.exports = function setupJasmineLogging() { const EXTRA_LOGGING = !process.env.DISABLE_LOGGING if (EXTRA_LOGGING) { jasmine.getEnv().addReporter({ // eslint-disable-line specStarted: result => jasmine.currentTest = result, // eslint-disable-line specDone: result => jasmine.currentTest = result, // eslint-disable-line }) beforeEach(() => console.log('[BEGIN] TEST:', jasmine['currentTest'])) // eslint-disable-line afterEach(() => console.log('[END] TEST:', jasmine['currentTest'])) // eslint-disable-line } }Then import and call it in both setup files.
packages/create-miniapp-cli/.gitignore (1)
45-45: Remove duplicate*.tsbuildinfoentry.The pattern
*.tsbuildinfoappears twice in the gitignore file (lines 45 and 60). While this doesn't cause issues functionally, removing the duplicate improves maintainability..docusaurus -*.tsbuildinfo # MACAlso applies to: 60-60
packages/create-miniapp-cli/src/utils/validateImportAlias.ts (1)
1-6: Consider adding explicit return type annotation.The function implicitly returns
string | undefined, but having an explicit return type annotation improves code clarity and type safety.-export const validateImportAlias = (input: string) => { +export const validateImportAlias = (input: string): string | undefined => { if (input.startsWith('.') || input.startsWith('/')) { return 'Import alias can\'t start with \'.\' or \'/\'' } - return }Note: You can also remove the explicit
returnon line 5 since TypeScript will inferundefinedwhen no value is returned.packages/create-miniapp-cli/src/utils/addPackageScript.ts (1)
7-26: Consider adding error handling for missing package.json.The function assumes package.json exists and has a valid structure. If the file doesn't exist or is malformed,
readJSONSyncwill throw an unclear error.Consider adding a guard:
export const addPackageScript = (opts: { scripts: Record<string, string> projectDir: string }) => { const { scripts, projectDir } = opts const packageJsonPath = path.join(projectDir, 'package.json') + if (!fs.existsSync(packageJsonPath)) { + throw new Error(`package.json not found at ${packageJsonPath}`) + } const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson packageJsonContent.scripts = { - ...packageJsonContent.scripts, + ...(packageJsonContent.scripts || {}), ...scripts, }packages/create-miniapp-cli/template/fullstack/README.md (2)
8-48: Add language specifiers to code blocks.The code blocks at lines 8-48, 52-61, and 65-68 are missing language specifiers, which reduces readability and syntax highlighting support.
Apply language specifiers to improve clarity:
-``` +```bash cat > .env << ENDOFFILE ...And for the other blocks: ```diff -``` +```bash # create migration script at migrations/20201212124723-00xx_name.js ...```diff -``` +```bash # create ./schema.d.ts file with all GQL types ...--- `76-87`: **Fix list indentation.** The list items have inconsistent indentation (1 space instead of 0), which doesn't follow markdown best practices. Apply this diff to fix indentation: ```diff Main folders: - - `./<domain>/constants` -- contains various constants. Used on the client and server side - - `./<domain>/gql` -- contains all GraphQL queries. Used on the server and client side +- `./<domain>/constants` -- contains various constants. Used on the client and server side +- `./<domain>/gql` -- contains all GraphQL queries. Used on the server and client side ...packages/create-miniapp-cli/template/server/package.json (2)
8-8: Fix script formatting.Line 8 contains extra spaces and trailing whitespace that should be cleaned up.
Apply this diff to clean up the spacing:
- "dev": "yarn build:deps && NODE_ENV=development && yarn dev:start ", + "dev": "yarn build:deps && NODE_ENV=development && yarn dev:start",
35-35: Consider using caret range for openid-client.The
openid-clientdependency is pinned to an exact version5.7.1without a caret, which prevents minor and patch updates. Unless there's a specific reason for pinning, consider using^5.7.1to allow compatible updates.Apply this diff if no pinning is required:
- "openid-client": "5.7.1", + "openid-client": "^5.7.1",packages/create-miniapp-cli/src/utils/removeTrailingSlash.ts (1)
1-7: Avoid mutating the input parameter.While this works correctly, reassigning the
inputparameter is unnecessary since strings are immutable in JavaScript.Apply this diff for a cleaner implementation:
export const removeTrailingSlash = (input: string) => { - if (input.length > 1 && input.endsWith('/')) { - input = input.slice(0, -1) - } - - return input + return input.length > 1 && input.endsWith('/') ? input.slice(0, -1) : input }packages/create-miniapp-cli/src/helpers/format.ts (1)
9-24: Add error handling for formatting failures.The
execacall can fail if the format:write script errors (e.g., prettier config issues, syntax errors in generated code). Without error handling, the spinner won't show failure state and the exception will propagate uncaught, potentially crashing the CLI or leaving the user confused.Apply this diff to add error handling:
export const formatProject = async ({ pkgManager, projectDir, }: { pkgManager: PackageManager projectDir: string }) => { logger.info(`Formatting project with ${'prettier'}...`) const spinner = ora('Running format command\n').start() - await execa(pkgManager, ['run', 'format:write'], { - cwd: projectDir, - }) + try { + await execa(pkgManager, ['run', 'format:write'], { + cwd: projectDir, + }) + spinner.succeed(`${chalk.green('Successfully formatted project')}`) + } catch (error) { + spinner.warn(`${chalk.yellow('Formatting failed, but project was created')}`) + logger.warn('You may need to run formatting manually later.') + } +}
- spinner.succeed(
${chalk.green('Successfully formatted project')})
-}packages/create-miniapp-cli/src/installers/helm/servicesUrls.ts (1)
10-25: Add error handling for file operations.The file read/write operations lack error handling. If
.helm/templates/000-services-urls.yamldoesn't exist, has permission issues, or write fails, the uncaught error will crash the installer or leave it in an inconsistent state.Consider adding error handling:
export async function updateServicesUrls (appName: string) { - const content = await fs.readFile(SERVICES_URLS, 'utf8') + try { + const content = await fs.readFile(SERVICES_URLS, 'utf8') + } catch (error) { + throw new Error(`Failed to read services URLs file: ${error.message}`) + } + const upperUnderscoredAppName = appName.replace(/-/g, '_').toUpperCase() const line = `${upperUnderscoredAppName}_DOMAIN: {{ ( printf "https://%s" $.Values.global.ci_${upperUnderscoredAppName}_url ) | b64enc }}` const idx = content.lastIndexOf('{{- end') let out = '' if (idx !== -1) { out = content.slice(0, idx).trimEnd() + '\n' + line + '\n\n' + content.slice(idx) } else { out = content.trimEnd() + '\n' + line + '\n' } - await fs.writeFile(SERVICES_URLS, out, 'utf8') + + try { + await fs.writeFile(SERVICES_URLS, out, 'utf8') + } catch (error) { + throw new Error(`Failed to write services URLs file: ${error.message}`) + } return SERVICES_URLS }packages/create-miniapp-cli/src/helpers/createProject.ts (2)
30-32: Hard-coded path resolution is fragile.The path resolution
path.resolve(process.cwd(), '../..')assumes a specific directory structure relative to the CLI package. This could break if the CLI is invoked from different contexts or the project structure changes.Consider using a more robust approach, such as:
- Using
PKG_ROOTconstant if available- Using
__dirnamewith a known offset- Adding a verification step to ensure the resolved path exists and is valid
44-67: Remove commented-out code or add TODO.Large blocks of commented code reduce readability. If this code is intended for future implementation, add a TODO comment with context. Otherwise, remove it.
packages/create-miniapp-cli/src/runCli.ts (3)
113-114: Extract repeated default value logic.The expression
(results.name ? \@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAliasis duplicated for bothdefaultValueandplaceholder`. Extract to a variable for better maintainability.Example:
const defaultAlias = (results.name ? `@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAlias return p.text({ message: 'What import alias would you like to use?', defaultValue: defaultAlias, placeholder: defaultAlias, validate: validateImportAlias, })
161-161: Formatting inconsistency.Missing space before opening brace:
if (v){should beif (v) {to match the style used elsewhere in the file (e.g., lines 25, 160).
206-206: Type cast followed by nullish coalescing is redundant.The expression
project.importAlias as string ?? cliResults.flags.importAliascasts to string but then uses nullish coalescing, suggesting importAlias might be undefined. The cast doesn't provide type safety here.Consider removing the unnecessary cast:
- importAlias: project.importAlias as string ?? cliResults.flags.importAlias, + importAlias: project.importAlias ?? cliResults.flags.importAlias,
| "createapp": "node ./bin/createapp", | ||
| "prepare": "husky install" | ||
| "prepare": "husky install", | ||
| "create-miniapp": "node ./packages/create-miniapp-cli/dist/index.js" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify the CLI package build step is documented or automated.
The script references dist/index.js, which is a build artifact that must exist before this command can run. Users attempting to run this script without first building the CLI package will encounter a runtime error.
Consider one of the following approaches:
- Add a build script that ensures the CLI is built before running (e.g., using a wrapper that checks for dist/index.js and builds if missing)
- Document the build requirement in the README or contributing guide
- Use a prepare script or hook to automatically build the CLI package
Run the following to verify if there's a build automation:
🏁 Script executed:
#!/bin/bash
# Check if there's any build automation for the CLI package
echo "=== Checking for build scripts in create-miniapp-cli package ==="
cat packages/create-miniapp-cli/package.json | jq -r '.scripts'
echo -e "\n=== Checking for prepare/postinstall hooks ==="
cat packages/create-miniapp-cli/package.json | jq -r '.scripts | with_entries(select(.key | test("prepare|postinstall")))'
echo -e "\n=== Checking turbo.json for build pipeline ==="
if [ -f turbo.json ]; then
cat turbo.json | jq -r '.pipeline.build // "No build pipeline found"'
fiLength of output: 328
I need to gather more information to understand the build automation at the root level.
I need to check the root turbo.json configuration and .gitignore to understand the full build orchestration:
🏁 Script executed:
#!/bin/bash
# Check root turbo.json pipeline
echo "=== Root turbo.json ==="
if [ -f turbo.json ]; then
cat turbo.json | jq '.' 2>/dev/null || cat turbo.json
else
echo "No turbo.json found"
fi
# Check if dist is in .gitignore
echo -e "\n=== Checking .gitignore for dist patterns ==="
grep -E 'dist|build' .gitignore 2>/dev/null || echo "No dist/build patterns found in .gitignore"
# Check root package.json scripts
echo -e "\n=== Root package.json scripts ==="
cat package.json | jq '.scripts' 2>/dev/null || echo "Could not parse scripts"Length of output: 213
Let me locate the repository structure and check if dist artifacts are version controlled:
🏁 Script executed:
#!/bin/bash
# Find all package.json files
echo "=== Package.json files in repo ==="
fd -t f 'package\.json$' -x ls -la {} \;
# Check if dist files are tracked in git
echo -e "\n=== Git tracking of dist directories ==="
git ls-files | grep -E 'dist/' | head -20 || echo "No dist files tracked in git"
# Check .gitignore existence and content
echo -e "\n=== .gitignore content (first 50 lines) ==="
if [ -f .gitignore ]; then
head -50 .gitignore
else
echo "No .gitignore found"
fi
# Check for CI workflows
echo -e "\n=== CI workflows in .github/workflows ==="
ls -la .github/workflows/ 2>/dev/null || echo "No .github/workflows found"Length of output: 5454
Now let me check the root package.json and relevant CI workflow:
🏁 Script executed:
#!/bin/bash
# Check root package.json content
echo "=== Root package.json ==="
cat ./package.json
# Check if create-miniapp script is used anywhere in CI
echo -e "\n=== Usage of 'create-miniapp' in CI workflows ==="
grep -r "create-miniapp" .github/workflows/ || echo "No 'create-miniapp' found in CI workflows"
# Check for build/prepare hooks in root package.json
echo -e "\n=== Looking for build/prepare pattern in workflows ==="
grep -E "(npm.*build|yarn.*build|pnpm.*build|prepare|postinstall)" .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null | head -20Length of output: 5431
Add a prepare script to auto-build the CLI package before the create-miniapp command can run.
The script references a build artifact (dist/index.js) from a package that isn't automatically built. Users who clone the repository and run yarn create-miniapp without first building will encounter a missing file error.
While yarn build (which uses turbo) can build all packages including create-miniapp-cli, this dependency is implicit and undocumented. Add a prepare hook or modify the create-miniapp script to ensure the dist directory exists:
"prepare": "husky install && yarn build:packages",Or create a wrapper script that checks for and builds the CLI if needed before delegating to the actual command.
🤖 Prompt for AI Agents
In package.json around line 25, the "create-miniapp" script points at a
non-built artifact (packages/create-miniapp-cli/dist/index.js) causing failures
for fresh clones; add a prepare script to build the CLI automatically (for
example run husky install && yarn build:packages) or replace the create-miniapp
script with a small wrapper that checks for the dist/index.js and runs the
package build if missing before executing the CLI; update the scripts section
accordingly so users can run yarn create-miniapp without manually building.
| interface CreateProjectOptions { | ||
| projectName: string | ||
| scopedAppName: string | ||
| noInstall: boolean | ||
| importAlias: string | ||
| appType: AppType | ||
| packages?: PkgInstallerMap | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Unused parameter: importAlias.
The CreateProjectOptions interface includes an importAlias parameter (line 15) that is never used in the function body. Either use it or remove it from the interface.
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/createProject.ts around lines 11 to
18, the CreateProjectOptions interface declares an unused importAlias field;
either remove it or wire it into the function. Fix by deleting importAlias from
the interface and from any call sites/types that reference it (or if it was
intended to control imports, add a parameter to the createProject function and
use importAlias where imports/aliases are configured), then update tests/usage
to match.
| console.log('process.cwd(): ', process.cwd()) | ||
| console.log('projectName: ', projectName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove debug console.log statements.
Debug logging statements should be replaced with proper logging via the logger utility or removed before merging.
Apply this diff:
const pkgManager = getUserPkgManager()
- console.log('process.cwd(): ', process.cwd())
- console.log('projectName: ', projectName)
const rootDir = path.resolve(process.cwd(), '../..')📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.log('process.cwd(): ', process.cwd()) | |
| console.log('projectName: ', projectName) | |
| const pkgManager = getUserPkgManager() | |
| const rootDir = path.resolve(process.cwd(), '../..') |
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/createProject.ts around lines 28 to
29, remove the two debug console.log statements (console.log('process.cwd(): ',
process.cwd()) and console.log('projectName: ', projectName')) or replace them
with the project's logger utility (e.g., logger.debug or logger.info) if that
context needs to be recorded; if you choose to log, import the logger at the top
of the file and use a single structured message rather than raw console.log
calls.
| export const installDependencies = async ({ | ||
| projectDir, | ||
| }: { | ||
| projectDir: string | ||
| }) => { | ||
| const pkgManager = getUserPkgManager() | ||
|
|
||
| await runInstallCommand(pkgManager, projectDir) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing return value propagation.
The runInstallCommand returns a boolean indicating success/failure, but installDependencies doesn't return or propagate this value. Callers have no way to determine if installation succeeded.
Apply this diff:
-export const installDependencies = async ({
+export const installDependencies = async ({
projectDir,
}: {
projectDir: string
-}) => {
+}): Promise<boolean> => {
const pkgManager = getUserPkgManager()
- await runInstallCommand(pkgManager, projectDir)
+ return await runInstallCommand(pkgManager, projectDir)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const installDependencies = async ({ | |
| projectDir, | |
| }: { | |
| projectDir: string | |
| }) => { | |
| const pkgManager = getUserPkgManager() | |
| await runInstallCommand(pkgManager, projectDir) | |
| } | |
| export const installDependencies = async ({ | |
| projectDir, | |
| }: { | |
| projectDir: string | |
| }): Promise<boolean> => { | |
| const pkgManager = getUserPkgManager() | |
| return await runInstallCommand(pkgManager, projectDir) | |
| } |
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/installDependencies.ts around lines
39 to 47, installDependencies currently calls runInstallCommand but does not
return its boolean result; update the function to propagate that value by
returning the awaited result (e.g., return await runInstallCommand(pkgManager,
projectDir)) and adjust the function signature to reflect Promise<boolean> if
needed so callers can determine success/failure.
| for (const [name, pkgOpts] of Object.entries(packages)) { | ||
| if (pkgOpts.inUse) { | ||
| const spinner = ora(`Boilerplating ${name}...`).start() | ||
| pkgOpts.installer(options) | ||
| spinner.succeed( | ||
| chalk.green( | ||
| `Successfully setup boilerplate for ${chalk.green.bold(name)}`, | ||
| ), | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Await installers so boilerplate actually finishes.
Installer implementations are async; calling them without await resolves immediately, the spinner reports success, and any rejection becomes an unhandled promise. The boilerplate may still be writing files while the CLI moves on. Make the function async, await each installer, and fail the spinner on error so the CLI stops cleanly.
Apply this diff:
-export const installPackages = (options: InstallPackagesOptions) => {
+export const installPackages = async (options: InstallPackagesOptions) => {
const { packages } = options
logger.info('Adding boilerplate...')
for (const [name, pkgOpts] of Object.entries(packages)) {
if (pkgOpts.inUse) {
const spinner = ora(`Boilerplating ${name}...`).start()
- pkgOpts.installer(options)
- spinner.succeed(
- chalk.green(
- `Successfully setup boilerplate for ${chalk.green.bold(name)}`,
- ),
- )
+ try {
+ await pkgOpts.installer(options)
+ spinner.succeed(
+ chalk.green(
+ `Successfully setup boilerplate for ${chalk.green.bold(name)}`,
+ ),
+ )
+ } catch (error) {
+ spinner.fail(`Failed to setup boilerplate for ${name}`)
+ throw error
+ }
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (const [name, pkgOpts] of Object.entries(packages)) { | |
| if (pkgOpts.inUse) { | |
| const spinner = ora(`Boilerplating ${name}...`).start() | |
| pkgOpts.installer(options) | |
| spinner.succeed( | |
| chalk.green( | |
| `Successfully setup boilerplate for ${chalk.green.bold(name)}`, | |
| ), | |
| ) | |
| } | |
| export const installPackages = async (options: InstallPackagesOptions) => { | |
| const { packages } = options | |
| logger.info('Adding boilerplate...') | |
| for (const [name, pkgOpts] of Object.entries(packages)) { | |
| if (pkgOpts.inUse) { | |
| const spinner = ora(`Boilerplating ${name}...`).start() | |
| try { | |
| await pkgOpts.installer(options) | |
| spinner.succeed( | |
| chalk.green( | |
| `Successfully setup boilerplate for ${chalk.green.bold(name)}`, | |
| ), | |
| ) | |
| } catch (error) { | |
| spinner.fail(`Failed to setup boilerplate for ${name}`) | |
| throw error | |
| } | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/installPackages.ts around lines 18 to
27, the installer calls are not awaited so async installers resolve immediately,
the spinner reports success prematurely, and rejections become unhandled; make
the surrounding function async, await pkgOpts.installer(options) for each
package, wrap each await in try/catch to call spinner.fail(...) on error and
rethrow or return a rejected promise so the CLI stops cleanly, and keep
spinner.succeed only after the awaited installer completes.
|
|
||
| const conf = require('@open-condo/config') | ||
|
|
||
| const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swap the ~ alias for the real template path.
The scaffolded project does not define a ~ resolver, so this require fails at runtime and violates the repository’s no-restricted-modules rule. Please import via the relative path that exists within the template tree.
-const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser')
+const { createOrUpdateUser } = require('../domains/user/utils/serverSchema/createOrUpdateUser')📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser') | |
| const { createOrUpdateUser } = require('../domains/user/utils/serverSchema/createOrUpdateUser') |
🧰 Tools
🪛 ESLint
[error] 7-7: '~/domains/user/utils/serverSchema/createOrUpdateUser' module is restricted from being used by a pattern.
(no-restricted-modules)
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/middlewares/oidc.js around line
7, the require uses the non-resolved '~' alias which breaks runtime imports and
violates the no-restricted-modules rule; replace the alias with the relative
template path by changing the import to
require('../domains/user/utils/serverSchema/createOrUpdateUser') (adjust the
relative traversal if your file location differs) so the module resolves within
the scaffolded template tree.
| req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/' | ||
| req.session[oidcSessionKey] = checks | ||
| await req.session.save() | ||
| try { | ||
| const redirectUrl = helper.authorizationUrlWithParams(checks) | ||
| return res.redirect(redirectUrl) | ||
| } catch (error) { | ||
| return next(error) | ||
| } | ||
| }) | ||
| app.get('/oidc/callback', async (req, res, next) => { | ||
| try { | ||
| const checks = req.session[oidcSessionKey] | ||
| const redirect = req.session[redirectKey] || '/' | ||
|
|
||
| if (!isObject(checks) || !checks) { | ||
| return res.status(400).send('ERROR: Invalid nonce and state') | ||
| } | ||
| const { userInfo, token } = await helper.completeAuth(req, checks) | ||
| const user = await createOrUpdateUser(keystone, userInfo) | ||
| const appToken = await keystone._sessionManager.startAuthedSession(req, { | ||
| item: { id: user.id }, | ||
| list: keystone.lists['User'], | ||
| meta: { | ||
| source: 'oidc', | ||
| provider: 'condo', | ||
| clientID: helper.clientID, | ||
| }, | ||
| }) | ||
|
|
||
| delete req.session[oidcSessionKey] | ||
| delete req.session[redirectKey] | ||
| req.session[CONDO_OIDC_TOKEN_KEY] = token | ||
| req.session[APP_TOKEN_KEY] = appToken | ||
| await req.session.save() | ||
|
|
||
| return res.redirect(redirect) | ||
| } catch (error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate the next redirect target to avoid open redirects.
Storing the raw req.query.next and later redirecting to it allows an attacker to send users to an arbitrary external site after login. That’s a classic open-redirect vulnerability. Clamp the value to same-origin paths (e.g. require it to start with /) before saving and before redirecting.
- req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/'
+ const nextParam = typeof req.query.next === 'string' ? req.query.next : '/'
+ req.session[redirectKey] = nextParam.startsWith('/') ? nextParam : '/'
@@
- const redirect = req.session[redirectKey] || '/'
+ const storedRedirect = req.session[redirectKey]
+ const redirect = (typeof storedRedirect === 'string' && storedRedirect.startsWith('/')) ? storedRedirect : '/'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/' | |
| req.session[oidcSessionKey] = checks | |
| await req.session.save() | |
| try { | |
| const redirectUrl = helper.authorizationUrlWithParams(checks) | |
| return res.redirect(redirectUrl) | |
| } catch (error) { | |
| return next(error) | |
| } | |
| }) | |
| app.get('/oidc/callback', async (req, res, next) => { | |
| try { | |
| const checks = req.session[oidcSessionKey] | |
| const redirect = req.session[redirectKey] || '/' | |
| if (!isObject(checks) || !checks) { | |
| return res.status(400).send('ERROR: Invalid nonce and state') | |
| } | |
| const { userInfo, token } = await helper.completeAuth(req, checks) | |
| const user = await createOrUpdateUser(keystone, userInfo) | |
| const appToken = await keystone._sessionManager.startAuthedSession(req, { | |
| item: { id: user.id }, | |
| list: keystone.lists['User'], | |
| meta: { | |
| source: 'oidc', | |
| provider: 'condo', | |
| clientID: helper.clientID, | |
| }, | |
| }) | |
| delete req.session[oidcSessionKey] | |
| delete req.session[redirectKey] | |
| req.session[CONDO_OIDC_TOKEN_KEY] = token | |
| req.session[APP_TOKEN_KEY] = appToken | |
| await req.session.save() | |
| return res.redirect(redirect) | |
| } catch (error) { | |
| const nextParam = typeof req.query.next === 'string' ? req.query.next : '/' | |
| req.session[redirectKey] = nextParam.startsWith('/') ? nextParam : '/' | |
| req.session[oidcSessionKey] = checks | |
| await req.session.save() | |
| try { | |
| const redirectUrl = helper.authorizationUrlWithParams(checks) | |
| return res.redirect(redirectUrl) | |
| } catch (error) { | |
| return next(error) | |
| } | |
| }) | |
| app.get('/oidc/callback', async (req, res, next) => { | |
| try { | |
| const checks = req.session[oidcSessionKey] | |
| const storedRedirect = req.session[redirectKey] | |
| const redirect = (typeof storedRedirect === 'string' && storedRedirect.startsWith('/')) ? storedRedirect : '/' | |
| if (!isObject(checks) || !checks) { | |
| return res.status(400).send('ERROR: Invalid nonce and state') | |
| } | |
| const { userInfo, token } = await helper.completeAuth(req, checks) | |
| const user = await createOrUpdateUser(keystone, userInfo) | |
| const appToken = await keystone._sessionManager.startAuthedSession(req, { | |
| item: { id: user.id }, | |
| list: keystone.lists['User'], | |
| meta: { | |
| source: 'oidc', | |
| provider: 'condo', | |
| clientID: helper.clientID, | |
| }, | |
| }) | |
| delete req.session[oidcSessionKey] | |
| delete req.session[redirectKey] | |
| req.session[CONDO_OIDC_TOKEN_KEY] = token | |
| req.session[APP_TOKEN_KEY] = appToken | |
| await req.session.save() | |
| return res.redirect(redirect) | |
| } catch (error) { |
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/middlewares/oidc.js around lines
70 to 107, the code saves req.query.next directly to session and later redirects
to it which permits open-redirects; validate and normalize the next target
before saving and before redirecting by only accepting same-origin paths
(require it to be a non-empty string that starts with a single '/' and does not
start with '//' or contain a full origin), otherwise fall back to '/' or a
configured safe default; implement this check where you set
req.session[redirectKey] and again before res.redirect(redirect), ensuring you
canonicalize the value (trim and remove query fragments if desired) and only
assign the validated value to the session/redirect variable.
| "mkdirp": "^3.0.1", | ||
| "openid-client": "5.7.1", | ||
| "react": "^18.3.1", | ||
| "zod": "^4.0.8" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify zod version.
The specified version ^4.0.8 doesn't exist. The latest stable version of zod is 3.x.
🌐 Web query:
What is the latest stable version of zod npm package?
💡 Result:
The latest stable published version on the npm package root is 3.25.20. [1][2]
Sources:
[1] npm listing for zod v3.25.20. (npmjs.com)
[2] Zod maintainer note: Zod v4 is published under the subpath "zod/v4" while the package root continues to publish v3. (github.com)
Citations:
- 1: https://www.npmjs.com/package/zod/v/3.25.20?utm_source=openai
- 2: Versioning with subpaths (
"zod/v4") to enable incremental migration colinhacks/zod#4371
Correct the zod version specification.
The latest stable published version on the npm package root is 3.25.20, and zod v4 is published under the subpath "zod/v4" while the package root continues to publish v3. The current specification "zod": "^4.0.8" will fail because zod v4 is not available at the main package root.
Update to either "zod": "^3.25.20" or, if v4 is required, use "zod/v4": "^4.0.8".
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/package.json around line 37, the
dependency is incorrectly set to "zod": "^4.0.8" which will fail because zod v4
is published under the "zod/v4" subpath while the package root publishes v3;
update the dependency to either "zod": "^3.25.20" to use the package root v3, or
if you need v4 change the spec to "zod/v4": "^4.0.8" so npm resolves the correct
package path.
| "./src/*" | ||
| ] | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove trailing comma.
JSON does not allow trailing commas. This will cause a syntax error when parsing the tsconfig.json file.
Apply this diff to remove the trailing comma:
"@cli/*": [
"./src/*"
]
},
- },
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| }, | |
| } |
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/tsconfig.json around line 23, remove the trailing
comma after the closing brace on that line so the JSON is valid; update the file
to end the object/array without a comma, save, and re-validate the JSON (e.g.,
run a JSON linter or tsc) to ensure no syntax errors remain.
| minify: true, | ||
| target: 'esnext', | ||
| outDir: 'dist', | ||
| onSuccess: 'node dist/index.js', // TODO: remove for prod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove onSuccess hook before production release.
The onSuccess hook automatically executes the built CLI, which is useful during development but should not be included in the production build.
Apply this diff to remove the onSuccess hook:
export default defineConfig({
clean: true,
entry: ['src/index.ts'],
format: ['esm'],
minify: true,
target: 'esnext',
outDir: 'dist',
- onSuccess: 'node dist/index.js', // TODO: remove for prod
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onSuccess: 'node dist/index.js', // TODO: remove for prod | |
| export default defineConfig({ | |
| clean: true, | |
| entry: ['src/index.ts'], | |
| format: ['esm'], | |
| minify: true, | |
| target: 'esnext', | |
| outDir: 'dist', | |
| }) |
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/tsup.config.ts around line 10, the tsup
configuration includes an onSuccess: 'node dist/index.js' hook used for
development; remove that onSuccess entry from the exported config (delete the
onSuccess property and trailing comma if required) so the production build does
not automatically execute the built CLI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 22
♻️ Duplicate comments (38)
packages/create-miniapp-cli/template/server/domains/common/schema/fields.js (1)
10-10: Typo already flagged: "devise" should be "device".This typo has already been identified in a previous review. Please apply the suggested fix to correct "devise" to "device" in the schemaDoc string.
packages/create-miniapp-cli/tsconfig.json (1)
23-25: Remove trailing commas to fix JSON syntax errors.JSON does not allow trailing commas. The file has two trailing comma violations that will prevent it from being parsed as valid JSON:
- Line 23: Trailing comma after the
compilerOptionsobject (already flagged in a previous review)- Line 24: Trailing comma after the
includearray before the final closing braceApply this diff to remove both trailing commas:
}, - }, + } "include": ["src/**/*"], -} + ] +}Actually, let me provide the correct fix:
}, - }, + } "include": ["src/**/*"] -} +}packages/create-miniapp-cli/tsup.config.ts (1)
3-11: Remove dev-onlyonSuccesshook from tsup config before merge.Auto-running
node dist/index.json every build is useful locally but unexpected/undesirable in CI and production builds (side effects, slower builds, surprise executions). The existing// TODO: remove for prodindicates this is temporary; it should be cleaned up now.Consider applying:
export default defineConfig({ clean: true, entry: ['src/index.ts'], format: ['esm'], minify: true, target: 'esnext', outDir: 'dist', - onSuccess: 'node dist/index.js', // TODO: remove for prod })The rest of the config looks reasonable for a small ESM CLI bundle.
Please double-check that your local/dev workflow doesn’t depend on this hook anymore (e.g. switch to
npm run devor similar) before removing it.packages/create-miniapp-cli/template/fullstack/tsconfig.json (2)
22-26: Remove trailing comma to fix JSON syntax error.Line 25 has a trailing comma after the last array element, which is invalid JSON syntax and will cause parsing errors when tsconfig.json is loaded.
Apply this diff:
"include": [ "next-env.d.ts", "**/*.ts", - "**/*.tsx", + "**/*.tsx" ],
11-11: Enable strict mode for better type safety.Setting
"strict": falsein a new project template disables all strict type-checking options, which can lead to runtime errors that TypeScript could catch. New projects should enable strict mode by default to benefit from TypeScript's full type safety.Apply this diff:
- "strict": false, + "strict": true,packages/create-miniapp-cli/template/config/next-config-appdir.js (1)
5-5: Remove trailing semicolons to match project style.The project follows a no-semicolon convention. Remove semicolons from lines 5, 8, and 10.
Apply this diff:
-import "./src/env.js"; +import "./src/env.js" /** @type {import("next").NextConfig} */ -const config = {}; +const config = {} -export default config; +export default configAlso applies to: 8-8, 10-10
packages/create-miniapp-cli/template/fullstack/lang/en/messages/1S_INSTRUCTION/email.html.njk (1)
12-48: Russian text in EN locale template (previously flagged).The localization mismatch identified in the previous review remains unresolved. This EN template still contains Russian text in the CTA (line 12: "В Дома →"), header (line 14: "Оферта принята"), and footer (line 48), while the body text (line 16) is correctly in English. This inconsistency will confuse English-speaking recipients.
packages/create-miniapp-cli/template/server/clients/index.spec.js (1)
32-39: Mock external dependencies to prevent real network calls.This test calls
await getCondoClient()which instantiates a CondoClient, connects to CONDO_DOMAIN, and callssignIn(). Without mocking, the test makes real network requests and will fail if the server is unavailable or environment variables are not set.Mock the CondoClient constructor and signIn method to isolate the test:
test('should return the same instance on second call (singleton)', async () => { + // Mock CondoClient to prevent real network calls + jest.mock('./condoClient', () => ({ + CondoClient: jest.fn().mockImplementation(() => ({ + signIn: jest.fn().mockResolvedValue(undefined), + })), + })) + const { getCondoClient } = require('./index') const firstCall = await getCondoClient() const secondCall = await getCondoClient() expect(firstCall).toBe(secondCall) })packages/create-miniapp-cli/template/server/clients/index.js (3)
5-9: Fix the API_TARGET validation logic.The current approach sets a placeholder default and then throws an error if that placeholder is detected. This is a trap pattern. If API_TARGET is required, validate it directly without a misleading fallback. Also use strict equality (
===) instead of loose equality (==).Apply this diff:
-// TODO: (AUTO_GENERATED): provide a fallback option for API_TARGET -const API_TARGET = conf['API_TARGET'] || 'API TARGET OF YOUR MINIAPP' -if (API_TARGET == 'API TARGET OF YOUR MINIAPP') { - throw new Error('You need to provide API_TARGET environment variable and provide a fallback value') -} +const API_TARGET = conf['API_TARGET'] +if (!API_TARGET) { + throw new Error('API_TARGET environment variable is required') +}
18-36: The clientName parameter is ignored after the first initialization.The singleton pattern caches a single instance, meaning only the first
clientNamevalue is used. Subsequent calls with differentclientNamevalues return the same cached instance, which may lead to unexpected behavior.Consider one of these solutions:
Option 1: Remove the parameter if all clients should share the same name:
-async function getCondoClient (clientName = 'template') { +async function getCondoClient () { if (!condoClient) { const condoAuthConfig = JSON.parse(conf['CONDO_CLIENT_AUTH_CONFIG'] || '{}') const condoServerUrl = conf['CONDO_DOMAIN'] condoClient = new CondoClient({ endpoint: `${condoServerUrl}/admin/api`, authRequisites: condoAuthConfig, opts: { - clientName, + clientName: 'template', customHeaders: { 'x-target': API_TARGET }, }, })Option 2: Cache per clientName if different names need different instances:
-let condoClient +const condoClients = {} async function getCondoClient (clientName = 'template') { - if (!condoClient) { + if (!condoClients[clientName]) { const condoAuthConfig = JSON.parse(conf['CONDO_CLIENT_AUTH_CONFIG'] || '{}') const condoServerUrl = conf['CONDO_DOMAIN'] - condoClient = new CondoClient({ + condoClients[clientName] = new CondoClient({ endpoint: `${condoServerUrl}/admin/api`, authRequisites: condoAuthConfig, opts: { clientName, customHeaders: { 'x-target': API_TARGET }, }, }) - await condoClient.signIn() + await condoClients[clientName].signIn() } - return condoClient + return condoClients[clientName] }
20-24: Validate CONDO_DOMAIN before constructing the endpoint.If
CONDO_DOMAINis undefined or empty, the endpoint will be malformed (e.g.,undefined/admin/api), causing connection errors with unclear messages.Apply this diff:
const condoAuthConfig = JSON.parse(conf['CONDO_CLIENT_AUTH_CONFIG'] || '{}') const condoServerUrl = conf['CONDO_DOMAIN'] + if (!condoServerUrl) { + throw new Error('CONDO_DOMAIN environment variable is required') + } condoClient = new CondoClient({ endpoint: `${condoServerUrl}/admin/api`,packages/create-miniapp-cli/template/server/jest.setupTest.js (1)
1-22: Template still uses restricted@app/*and repo‑internal modulesThis template depends on
@app/~/indexand@open-condo/*modules, which (a) violate theno-restricted-modulesrule for files underpackages/, and (b) are tightly coupled to the main Condo repo layout, so a generated mini‑app will not have these modules available by default.For a template intended to scaffold projects, consider either:
- Rewriting these imports to template‑local paths/utilities (or injecting them via the CLI at generation time), and/or
- Adding explicit ESLint overrides that exempt
packages/create-miniapp-cli/template/**from the@app/*restriction, if coupling to the monorepo is intentional.Until this is addressed, lint or runtime will fail in generated projects.
packages/create-miniapp-cli/template/fullstack/jest.setupTest.js (1)
1-22: Repo‑specific imports and inconsistent logging flag in fullstack test setupTwo points here:
Repo‑specific / restricted imports
The template relies on@app/billing-connector/indexand@open-condo/*modules. These are specific to the main repo and, together with the@app/*restriction underpackages/, will cause lint and runtime failures in generated mini‑apps unless they live inside this monorepo with matching aliases. Either decouple the template from these imports (e.g. local stubs, CLI‑injected paths, or published packages added to the generatedpackage.json), or explicitly relax ESLint for the template directory.EXTRA_LOGGING semantics differ from server templates
HereEXTRA_LOGGING = !process.env.DISABLE_LOGGING, while server templates useprocess.env.DISABLE_LOGGING !== 'true'. That changes how values like'false'or'0'behave. For consistency across templates, I’d reuse the explicit'true'check:-const EXTRA_LOGGING = !process.env.DISABLE_LOGGING
+const EXTRA_LOGGING = process.env.DISABLE_LOGGING !== 'true'</blockquote></details> <details> <summary>packages/create-miniapp-cli/template/server/jest.config.js (1)</summary><blockquote> `1-25`: **Fix uuid transform regex and make reporter path template‑safe** Two issues here: 1. **`transformIgnorePatterns` typo in `spec` project** The `spec` project config has a typo and an extra space: ```js // NOTE: need to pass uid export syntax through babel transformIgnorePatterns: ['/node_modules/(?! (uuid)/)'],That space means
uuidis still ignored by Babel, leading back toUnexpected token 'export'errors. Align this block with the workingschemaproject configuration and fix the comment:- // NOTE: need to pass uid export syntax through babel - transformIgnorePatterns: ['/node_modules/(?! (uuid)/)'], + // NOTE: need to pass uuid export syntax through babel + transformIgnorePatterns: ['/node_modules/(?!(uuid)/)'],
Reporter path assumes monorepo structure
reporters: ['default', '<rootDir>/../../bin/report-failed-tests.js']assumes the generated project sits inside the Condo monorepo with that exactbin/layout. In a standalone mini‑app this file won’t exist, and Jest will fail to start.For a safe default in templates, I’d drop the custom reporter (or copy a reporter implementation into the template and point to it):
- reporters: ['default', '/../../bin/report-failed-tests.js'],
- reporters: ['default'],
</blockquote></details> <details> <summary>packages/create-miniapp-cli/template/fullstack/jest.config.js (1)</summary><blockquote> `1-36`: **Reporter path is not safe for generated fullstack projects** The custom reporter `<rootDir>/../../bin/report-failed-tests.js` depends on the Condo monorepo’s directory layout. A mini‑app generated from this template won’t have that file, so Jest will error out when loading the config. For template safety, I’d either ship a reporter inside the template and reference it via a project‑local path, or just rely on the default reporter: ```diff - reporters: ['default', '<rootDir>/../../bin/report-failed-tests.js'], + reporters: ['default'],packages/create-miniapp-cli/template/config/postcss.config.js (1)
1-5: Fix ESLint indent and trailing semicolon in PostCSS config.ESLint is still flagging this file for 2‑space indent and the extra semicolon after the exported object; this will break lint/CI. Reindent to 4 spaces and drop the final semicolon.
-export default { - plugins: { - "@tailwindcss/postcss": {}, - }, -}; +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}package.json (1)
24-25: Ensure the create-miniapp CLI is built before runningdist/index.js.
create-miniapppoints directly to./packages/create-miniapp-cli/dist/index.js. On a fresh clone, if the CLI package hasn’t been built (anddistisn’t checked in), this command will fail. Either make the build explicit here or in a hook so users can safely runyarn create-miniappwithout prior manual build, for example:- "prepare": "husky install", - "create-miniapp": "node ./packages/create-miniapp-cli/dist/index.js" + "prepare": "husky install && yarn build:packages", + "create-miniapp": "node ./packages/create-miniapp-cli/dist/index.js"(or an equivalent wrapper that builds the CLI if
distis missing).packages/create-miniapp-cli/template/fullstack/public/robots.txt (1)
1-5: ConsolidateUser-agent: *rules into a single robots.txt block.The two
User-agent: *sections conflict; better to group them so intent is unambiguous (allow/auth/, block everything else):-User-agent: * -Allow: /auth/ - -User-agent: * -Disallow: * +User-agent: * +Allow: /auth/ +Disallow: /packages/create-miniapp-cli/template/config/_eslint.drizzle.js (1)
1-58: Fix flat ESLint config (removeextendsproperty, clean up imports/formatting)This config still has the same structural issues called out earlier:
- In flat config,
extendsinside a config object is not supported; you need to spread the recommended presets at the top level.- Static analysis also flags import order, extra semicolons, and inconsistent indentation, which will cause lint failures in scaffolded projects.
You can apply essentially the same restructuring that was previously suggested:
-import { FlatCompat } from "@eslint/eslintrc"; -import tseslint from 'typescript-eslint'; -// @ts-ignore -- no types for this plugin -import drizzle from "eslint-plugin-drizzle"; - -const compat = new FlatCompat({ - baseDirectory: import.meta.dirname, -}); - -export default tseslint.config( - { - ignores: ['.next'] - }, - ...compat.extends("next/core-web-vitals"), - { - files: ['**/*.ts', '**/*.tsx'], - plugins: { - drizzle, - }, - extends: [ - ...tseslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - ...tseslint.configs.stylisticTypeChecked - ], - rules: { - "@typescript-eslint/array-type": "off", - "@typescript-eslint/consistent-type-definitions": "off", - "@typescript-eslint/consistent-type-imports": [ - "warn", - { prefer: "type-imports", fixStyle: "inline-type-imports" }, - ], - "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], - "@typescript-eslint/require-await": "off", - "@typescript-eslint/no-misused-promises": [ - "error", - { checksVoidReturn: { attributes: false } }, - ], - "drizzle/enforce-delete-with-where": [ - "error", - { drizzleObjectName: ["db", "ctx.db"] }, - ], - "drizzle/enforce-update-with-where": [ - "error", - { drizzleObjectName: ["db", "ctx.db"] }, - ], - }, - }, - { - linterOptions: { - reportUnusedDisableDirectives: true - }, - languageOptions: { - parserOptions: { - projectService: true - } - } - } -) +import { FlatCompat } from "@eslint/eslintrc" +// @ts-ignore -- no types for this plugin +import drizzle from "eslint-plugin-drizzle" +import tseslint from "typescript-eslint" + +const compat = new FlatCompat({ + baseDirectory: import.meta.dirname, +}) + +export default tseslint.config( + { + ignores: [".next"], + }, + ...compat.extends("next/core-web-vitals"), + ...tseslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + { + files: ["**/*.ts", "**/*.tsx"], + plugins: { + drizzle, + }, + rules: { + "@typescript-eslint/array-type": "off", + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/consistent-type-imports": [ + "warn", + { prefer: "type-imports", fixStyle: "inline-type-imports" }, + ], + "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-misused-promises": [ + "error", + { checksVoidReturn: { attributes: false } }, + ], + "drizzle/enforce-delete-with-where": [ + "error", + { drizzleObjectName: ["db", "ctx.db"] }, + ], + "drizzle/enforce-update-with-where": [ + "error", + { drizzleObjectName: ["db", "ctx.db"] }, + ], + }, + }, + { + linterOptions: { + reportUnusedDisableDirectives: true, + }, + languageOptions: { + parserOptions: { + projectService: true, + }, + }, + }, +)This keeps the drizzle rules as‑is while giving you a valid flat config and satisfying the lint hints.
packages/create-miniapp-cli/template/fullstack/package.json (1)
25-58: Re‑evaluate the exactopenid-clientpin for this new template.
openid-clientis pinned to4.7.4while most other deps use ranges; for a fresh template that will be cloned over time, it’s safer to either (a) move to the latest compatible 4.x patch or (b) clearly justify/document why4.7.4must stay pinned (e.g. known regressions in later 4.x or 6.x). Please re-run your SCA /npm view openid-clientchecks and adjust or document accordingly.packages/create-miniapp-cli/src/helpers/createProject.ts (1)
11-70: Fix projectDir resolution and clean up unused/temporary bits.Three issues here:
- Path resolution escapes the repo root (functional/ safety issue).
rootDir = path.resolve(process.cwd(), '../..')assumes the process is started inpackages/create-miniapp-cli. From the advertised root script (yarn create-miniapp ...),process.cwd()will be the repo root, so../..points outside the repo andappsDirends up two levels above it. That can either fail or scaffold into the wrong location. DeriveappsDirfrom a stable anchor (e.g. aPKG_ROOTconstant or__dirname) instead ofprocess.cwd(). For example (adjust paths to your existing consts):-import path from 'path' +import path from 'path' +import { PKG_ROOT } from '@cli/consts.js' @@ - const rootDir = path.resolve(process.cwd(), '../..') // relative to CLI package - const appsDir = path.join(rootDir, 'apps') + // Anchor to the repo/apps directory regardless of where the CLI is invoked from + const appsDir = path.join(PKG_ROOT, '..', '..', 'apps') const projectDir = path.resolve(appsDir, projectName)Please adapt the relative segments to match how
PKG_ROOTis defined and verify thatprojectDirresolves to<repo>/apps/<projectName>.
Unused option:
importAlias.
CreateProjectOptions.importAliasis never read. Either wire it into the scaffolding flow or remove it from the options type (and any call sites) to avoid a misleading API surface.Leftover debug logging.
Theconsole.logcalls forprocess.cwd()andprojectNameare noisy for an end‑user CLI and should be removed or routed through your logger at an appropriate level if you genuinely need them.packages/create-miniapp-cli/template/server/bin/generate-condo-schema.js (1)
10-11: Replace~alias import with a real relative path so the bin script can run.This Node script runs outside of the webpack/Next alias setup, so
require('~/domains/condo/gql')will fail to resolve and is also blocked by ESLint’sno-restricted-modulesrule. Use the actual relative location frombin/instead:-const { CONDO_CONFIG } = require('~/domains/condo/gql') +const { CONDO_CONFIG } = require('../domains/condo/gql')This keeps the template self-contained and makes the script executable immediately after scaffold.
packages/create-miniapp-cli/template/config/_prettier.config.js (1)
2-2: Remove the trailing semicolon.The trailing semicolon violates the project's
semi: "never"lint rule, as confirmed by static analysis. This causes generated projects to start with a lint error.Apply this diff:
-export default {}; +export default {}packages/create-miniapp-cli/src/helpers/installPackages.ts (1)
14-27: Installer calls must be awaited to prevent premature success reporting and unhandled rejections.The
pkgOpts.installer(options)call on line 21 is not awaited, causing the spinner to report success immediately while the installer may still be running or could fail silently. This matches the concern raised in the previous review.Apply this diff to fix:
-export const installPackages = (options: InstallPackagesOptions) => { +export const installPackages = async (options: InstallPackagesOptions) => { const { packages } = options logger.info('Adding boilerplate...') for (const [name, pkgOpts] of Object.entries(packages)) { if (pkgOpts.inUse) { const spinner = ora(`Boilerplating ${name}...`).start() - pkgOpts.installer(options) - spinner.succeed( - chalk.green( - `Successfully setup boilerplate for ${chalk.green.bold(name)}`, - ), - ) + try { + await pkgOpts.installer(options) + spinner.succeed( + chalk.green( + `Successfully setup boilerplate for ${chalk.green.bold(name)}`, + ), + ) + } catch (error) { + spinner.fail(`Failed to setup boilerplate for ${name}`) + throw error + } } }packages/create-miniapp-cli/template/config/_tailwind.prettier.config.js (1)
2-4: Fix formatting to align with project lint rules.The file uses 2-space indentation and trailing semicolons, which conflict with the project's ESLint rules (
@typescript-eslint/indentexpects 4 spaces,@typescript-eslint/semidisallows semicolons). This matches the concern raised in the previous review.Apply this diff:
export default { - plugins: ["prettier-plugin-tailwindcss"], -}; + plugins: ["prettier-plugin-tailwindcss"], +}packages/create-miniapp-cli/src/utils/validateAppName.ts (2)
12-17: Remove duplicate findIndex call.Line 15 repeats the same
findIndexoperation already computed on line 12.Apply this diff:
const indexOfDelimiter = paths.findIndex((p) => p.startsWith('@')) let appName = paths[paths.length - 1] - if (paths.findIndex((p) => p.startsWith('@')) !== -1) { + if (indexOfDelimiter !== -1) { appName = paths.slice(indexOfDelimiter).join('/') }
4-4: Align error message with regex or tighten regex.The regex permits
.,~, and*but the error message claims only lowercase alphanumeric,-, and_are allowed.Either update the error message:
- return 'App name must consist of only lowercase alphanumeric characters, \'-\', and \'_\'' + return 'App name must consist of only lowercase alphanumeric characters, \'-\', \'_\', \'.\', \'~\', and \'*\''Or tighten the regex:
const validationRegExp = - /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/ + /^(?:@[a-z0-9-_][a-z0-9-_]*\/)?[a-z0-9-_][a-z0-9-_]*$/Also applies to: 22-22
packages/create-miniapp-cli/src/utils/addPackageDependency.ts (1)
25-29: Dependencies silently fail to install if package.json objects don't exist.If
pkgJson.devDependenciesorpkgJson.dependenciesis undefined, the assignment on lines 26 or 28 silently fails, resulting in missing dependencies in the generated package.json.Apply this diff to initialize missing objects:
dependencies.forEach((pkgName) => { const version = dependencyVersionMap[pkgName] if (devMode && pkgJson.devDependencies) { pkgJson.devDependencies[pkgName] = version + } else if (devMode) { + pkgJson.devDependencies = { [pkgName]: version } } else if (pkgJson.dependencies) { pkgJson.dependencies[pkgName] = version + } else { + pkgJson.dependencies = { [pkgName]: version } } })packages/create-miniapp-cli/template/config/_eslint.base.js (1)
13-34: Flat config cannot useextendsinside config objects.ESLint's flat config mode doesn't recognize the
extendskey inside configuration objects (line 15). This will cause "Key 'extends' was unexpected" errors when runningeslintin scaffolded projects, breaking the generated codebase.Spread the TypeScript-ESLint presets at the top level instead:
export default tseslint.config( { - ignores: ['.next'] + ignores: [".next"], }, ...compat.extends("next/core-web-vitals"), + ...tseslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, { - files: ['**/*.ts', '**/*.tsx'], - extends: [ - ...tseslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - ...tseslint.configs.stylisticTypeChecked - ], - rules: { - "@typescript-eslint/array-type": "off", - "@typescript-eslint/consistent-type-definitions": "off", - "@typescript-eslint/consistent-type-imports": [ - "warn", - { prefer: "type-imports", fixStyle: "inline-type-imports" }, - ], - "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], - "@typescript-eslint/require-await": "off", - "@typescript-eslint/no-misused-promises": [ - "error", - { checksVoidReturn: { attributes: false } }, - ], - }, + files: ["**/*.ts", "**/*.tsx"], + rules: { + "@typescript-eslint/array-type": "off", + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/consistent-type-imports": [ + "warn", + { prefer: "type-imports", fixStyle: "inline-type-imports" }, + ], + "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-misused-promises": [ + "error", + { checksVoidReturn: { attributes: false } }, + ], + }, }, { linterOptions: { - reportUnusedDisableDirectives: true + reportUnusedDisableDirectives: true, }, languageOptions: { parserOptions: { - projectService: true - } - } - } + projectService: true, + }, + }, + }, )packages/create-miniapp-cli/src/installers/helm/reviewSecrets.ts (1)
17-17: All three secret values reference the same field.Line 17 uses
.Values.review.pg_${appUnderscore}._defaultfor USER, PASS, and DB, which will result in identical base64-encoded values for all three secrets. This is incorrect and will break database connectivity.Reference distinct fields for each secret:
-const insert = ` PG_REVIEW_${upperUnderscore}_USER: {{ .Values.review.pg_${appUnderscore}._default | b64enc }}\n PG_REVIEW_${upperUnderscore}_PASS: {{ .Values.review.pg_${appUnderscore}._default | b64enc }}\n PG_REVIEW_${upperUnderscore}_DB: {{ .Values.review.pg_${appUnderscore}._default | b64enc }}` +const insert = ` PG_REVIEW_${upperUnderscore}_USER: {{ .Values.review.pg_${appUnderscore}.user | b64enc }}\n PG_REVIEW_${upperUnderscore}_PASS: {{ .Values.review.pg_${appUnderscore}.password | b64enc }}\n PG_REVIEW_${upperUnderscore}_DB: {{ .Values.review.pg_${appUnderscore}.database | b64enc }}`packages/create-miniapp-cli/src/helpers/installDependencies.ts (1)
39-47: Missing return value propagation.The
runInstallCommandreturns a boolean indicating success/failure, butinstallDependenciesdoesn't return or propagate this value. Callers have no way to determine if installation succeeded.Apply this diff:
-export const installDependencies = async ({ +export const installDependencies = async ({ projectDir, }: { projectDir: string -}) => { +}): Promise<boolean> => { const pkgManager = getUserPkgManager() - await runInstallCommand(pkgManager, projectDir) + return await runInstallCommand(pkgManager, projectDir) }packages/create-miniapp-cli/src/installers/prepare.ts (1)
159-165: Run the missing prepare steps.You define
BUILD_DEPS,LOCAL_PREPARE, andBUILD, but never execute them; insteadGLOBAL_PREPAREruns twice. As written, the CLI skips dependency builds, local prepare, and the final build entirely. Add those scripts to the execution order (and drop the duplicate global run) so every step actually runs.Apply this diff:
SCRIPTS.BUILD_PACKAGES, - SCRIPTS.GLOBAL_PREPARE, // base env vars + SCRIPTS.GLOBAL_PREPARE, + SCRIPTS.BUILD_DEPS, SCRIPTS.MAKE_MIGRATIONS, - SCRIPTS.GLOBAL_PREPARE, // migrate and run local prepare + SCRIPTS.LOCAL_PREPARE, SCRIPTS.MAKE_TYPES, + SCRIPTS.BUILD, ]packages/create-miniapp-cli/src/helpers/setImportAlias.ts (1)
11-18: Import alias replacement corrupts binary assets.
replaceTextInFilesrecurses through every file in the scaffolded project and rewrites them withfs.readFileSync(filePath, 'utf8')/writeFileSync(..., 'utf8'). The CLI runs this over the entire template directory to swap aliases, which includes PNGs, PDFs and other binaries. Reading and writing those files as UTF‑8 will rewrite their bytes and break the assets even when no replacement occurs.The traversal should skip non‑text files. Apply this diff to filter by extension:
+const TEXT_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.json', '.md', '.txt', '.css', '.scss', '.html', '.yml', '.yaml']) + export function replaceTextInFiles ( directoryPath: string, search: string, replacement: string, ): void { const files = fs.readdirSync(directoryPath) files.forEach((file) => { const filePath = path.join(directoryPath, file) if (fs.statSync(filePath).isDirectory()) { replaceTextInFiles(filePath, search, replacement) } else { + const ext = path.extname(filePath) + if (!TEXT_EXTENSIONS.has(ext)) return const data = fs.readFileSync(filePath, 'utf8') const updatedData = data.replace(new RegExp(search, 'g'), replacement) fs.writeFileSync(filePath, updatedData, 'utf8') } }) }packages/create-miniapp-cli/template/fullstack/pages/1S/connected.tsx (1)
86-89: Redundant loading check (duplicate).The conditional on line 87 duplicates the
isLoadingvariable defined on line 86.Apply this diff:
const isLoading = launchParamsAreLoading || billingContextLoading || !billingContext - if (launchParamsAreLoading || billingContextLoading || !billingContext) { + if (isLoading) { return <Loader loading={isLoading} size='large' /> }packages/create-miniapp-cli/template/server/middlewares/oidc.js (2)
7-7: Swap the~alias for the real template path (duplicate).The scaffolded project does not define a
~resolver, so thisrequirefails at runtime.-const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser') +const { createOrUpdateUser } = require('../domains/user/utils/serverSchema/createOrUpdateUser')
69-106: Validate thenextredirect target to avoid open redirects (duplicate).Storing raw
req.query.nextand redirecting to it allows an attacker to send users to arbitrary external sites after login.- req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/' + const nextParam = typeof req.query.next === 'string' ? req.query.next : '/' + req.session[redirectKey] = nextParam.startsWith('/') && !nextParam.startsWith('//') ? nextParam : '/' @@ - const redirect = req.session[redirectKey] || '/' + const storedRedirect = req.session[redirectKey] + const redirect = (typeof storedRedirect === 'string' && storedRedirect.startsWith('/') && !storedRedirect.startsWith('//')) ? storedRedirect : '/'packages/create-miniapp-cli/template/server/package.json (1)
37-37: Correct the zod version specification (duplicate).The specified version
^4.0.8doesn't exist at the package root. Zod v4 is published under the subpath "zod/v4" while the package root publishes v3.Update to either:
- "zod": "^4.0.8" + "zod": "^3.25.20"Or if v4 is required:
- "zod": "^4.0.8" + "zod/v4": "^4.0.8"packages/create-miniapp-cli/src/index.ts (1)
60-64: Use the sanitized app name when updating@appreferences (duplicate).Line 61 passes the raw CLI input (which may include path segments) into
setImportAlias. PassscopedAppNameinstead to ensure consistent generated code.- setImportAlias(projectDir, importAlias, appName) + setImportAlias(projectDir, importAlias, scopedAppName)
🧹 Nitpick comments (34)
packages/create-miniapp-cli/template/fullstack/pages/global.css (3)
1-14: Generally sound global styles; consider modernizing CSS practices.The body resets provide reasonable defaults for the fullstack template. However, a few practices warrant attention:
- Line 9 (
text-rendering: optimizelegibility): This property is deprecated and no longer recommended for modern browsers. Consider removing it, as modern browsers handle text rendering automatically.- Line 5 (
overflow-x: hidden): This could hide content that legitimately overflows horizontally. Verify this is intentional and won't cause usability issues on smaller viewports or with dynamic content.Consider removing the deprecated
text-renderingproperty:text-decoration-skip: ink; - text-rendering: optimizelegibility; -ms-overflow-style: -ms-autohiding-scrollbar;
40-43: Avoid!important; refactor CSS specificity instead.The
!importantflag on line 42 is a code smell indicating a CSS specificity conflict with react-tel-input. Rather than using!important, increase selector specificity or remove conflicting styles.-.react-tel-input .ant-input { +.ant-input.react-tel-input-input { width: 100%; - background-color: rgb(255 255 255 / 90%) !important; + background-color: rgb(255 255 255 / 90%); }Alternatively, if this selector is genuinely necessary, document why
!importantis needed.Verify that the react-tel-input selector aligns with the library's actual DOM structure to avoid overly broad rules.
24-26: Verify color palette against design system.The color values (#82879f for labels, #222 for disabled text) should be documented or aligned with your design system tokens. Using semantic color variables (e.g.,
--color-text-muted,--color-text-disabled) would improve maintainability.Also applies to: 45-50
packages/create-miniapp-cli/template/fullstack/pages/_document.tsx (1)
7-7: Addlangattribute to<Html>for accessibility.The
<Html>component should include alangattribute to specify the document's language. This is important for accessibility (screen readers) and SEO best practices.Apply this diff to add the language attribute:
- <Html> + <Html lang="en">packages/create-miniapp-cli/template/fullstack/public/500-error.html (1)
4-7: Remove duplicate charset meta tag.The charset is declared twice (line 4 and line 7), which is redundant. The first declaration is sufficient.
Apply this diff to clean up:
<head> <meta charset="UTF-8"> - <title>Улучшаем сервис</title> + <title>Улучшаем сервис</title> <meta name="viewport" content="width=device-width"/> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> </head>packages/create-miniapp-cli/template/server/clients/condoClient.js (1)
5-11: Consider removing the redundant constructor.The constructor only passes arguments to the superclass without additional logic. In ES6 classes, this constructor can be omitted entirely—JavaScript will automatically call the parent constructor with the provided arguments.
Apply this diff:
class CondoClient extends ApolloServerClient { - constructor ({ endpoint, authRequisites, opts }) { - super(endpoint, authRequisites, opts) - } - // AUTO_GENERATED: use needed methods here }packages/create-miniapp-cli/template/fullstack/pages/_app.tsx (3)
50-51: Simplify redundant fallback logic.Line 50 already provides a fallback to
defaultLocale, making the|| intl.localefallback on line 51 redundant in normal cases. However, this does protect against undefineddefaultLocale. Once line 24 has a proper fallback, this can be simplified.const locale = get(launchContext, 'condoLocale', defaultLocale) -localeContext.setLocale(locale || intl.locale) +localeContext.setLocale(locale)
86-99: Consider using proper GraphQL fragment composition.The current approach embeds
userFragmentas a template string, which works but limits IDE/tooling support for GraphQL. For better type safety and tooling, consider using proper fragment composition.-const userFragment = ` - id - name - isAdmin - isSupport -` - -export const USER_QUERY = gql` +export const USER_FRAGMENT = gql` + fragment UserFragment on User { + id + name + isAdmin + isSupport + } +` + +export const USER_QUERY = gql` + ${USER_FRAGMENT} query { authenticatedUser { - ${userFragment} + ...UserFragment } } `
81-84: Consider validating locale before dynamic import.While the locale appears to be application-controlled, defensive validation would prevent potential path traversal if the source is ever compromised or changes.
async function messagesImporter (locale) { + // Validate locale to prevent path traversal + const validLocales = ['en', 'ru'] + const safeLocale = validLocales.includes(locale) ? locale : 'en' + - const localeData = await import(`../lang/${locale}/${locale}`) + const localeData = await import(`../lang/${safeLocale}/${safeLocale}`) return { ...localeData.default } }packages/create-miniapp-cli/template/fullstack/jest.setupSpec.js (1)
1-12: Align EXTRA_LOGGING flag semantics with server templatesHere
EXTRA_LOGGINGis!process.env.DISABLE_LOGGING, while server templates useprocess.env.DISABLE_LOGGING !== 'true'. That difference can make logging toggle behave inconsistently across templates (e.g.DISABLE_LOGGING='false'disables logging here but not on the server side). I’d align on the explicit'true'check for predictability.-const EXTRA_LOGGING = !process.env.DISABLE_LOGGING +const EXTRA_LOGGING = process.env.DISABLE_LOGGING !== 'true'packages/create-miniapp-cli/template/server/jest.setupSpec.js (1)
1-14: LGTM; this can be the canonical EXTRA_LOGGING patternThis setup looks good: explicit
DISABLE_LOGGING !== 'true'check, timeout, and Jasmine reporter wiring are coherent. I’d use this'true'-based predicate as the reference for other templates so the logging toggle behaves consistently everywhere.packages/create-miniapp-cli/src/utils/validateImportAlias.ts (1)
1-6: Make validator’s return type explicit (string | undefined).To make the contract clearer for callers (and avoid a
string | voidinference), consider annotating the return type and returningundefinedexplicitly on success:-export const validateImportAlias = (input: string) => { - if (input.startsWith('.') || input.startsWith('/')) { - return 'Import alias can\'t start with \'.\' or \'/\'' - } - return -} +export const validateImportAlias = (input: string): string | undefined => { + if (input.startsWith('.') || input.startsWith('/')) { + return "Import alias can't start with '.' or '/'" + } + return undefined +}packages/create-miniapp-cli/.gitignore (1)
36-60: Remove duplicate*.tsbuildinfoentry (optional cleanup).
*.tsbuildinfois listed twice; it’s harmless but a bit noisy. You can drop one of the entries to keep the .gitignore DRY.packages/create-miniapp-cli/template/fullstack/README.md (1)
1-87: Tidy up markdownlint and minor wording issuesA few small tweaks will keep markdown linting clean and improve readability:
- Add languages to fenced code blocks (for example
```basharound shell snippets and```shor```bashfor docker-compose commands at Lines 8–28, 52–61, 65–68).- Use “billing self-service” (hyphenated) in the heading / text (Lines 6, 20).
- Replace the generic link text “here” with something descriptive like “Domain README” (Line 73).
- Normalize list indentation so list items are not indented with an extra leading space (Lines 76–87).
- Optional: use “client‑side”, “server‑side”, “test‑side” as compound adjectives in bullet descriptions (Lines 82–84).
packages/create-miniapp-cli/src/utils/isAppType.ts (1)
1-5: AlignAppTypedefinition with whatisAppTypeactually checksRight now
AppTypeis defined askeyof typeof APP_TYPES, butisAppTypechecks againstObject.values(APP_TYPES). This is safe only as long as keys and values stay identical; if they ever diverge, the type guard becomes misleading.Two cleaner options:
- If you intend
AppTypeto represent the values, change the type alias:// in consts.ts export type AppType = (typeof APP_TYPES)[keyof typeof APP_TYPES]
- If you intend
AppTypeto represent the keys, change the check:export function isAppType (value: string): value is AppType { return Object.hasOwn(APP_TYPES, value) }Either way, keeping the runtime check and the type alias in sync will avoid subtle bugs later.
packages/create-miniapp-cli/src/consts.ts (1)
19-30: Fix formatting inconsistencies.Minor formatting issues found:
- Line 23: Space before colon in
production :'500m'- Line 29: Extra space after closing brace
Apply this diff:
cpu: { default: '250m', development: '250m', - production :'500m', + production: '500m', }, memory: { default: '512Mi', development: '1024Mi', production: '1024Mi', - }, + }, }packages/create-miniapp-cli/src/installers/helm/utils.ts (2)
77-94: Consider explicit undefined return.Line 91 has a bare
returnstatement. For clarity in TypeScript, considerreturn undefinedor omitting the return entirely, letting the function fall through.if (res !== true) return res } } - - return + return undefined },
117-132: Replaceanywith proper types.Lines 120-121 use
anytype, which bypasses TypeScript's type safety. Since the return type isResourceSettings, consider usingPartial<ResourceSettings['cpu']>andPartial<ResourceSettings['memory']>or explicitly typing the objects.- const cpuObj: any = { default: cpuParts[0] } - const memObj: any = { default: memParts[0] } + const cpuObj: Partial<ResourceSettings['cpu']> = { default: cpuParts[0] } + const memObj: Partial<ResourceSettings['memory']> = { default: memParts[0] }packages/create-miniapp-cli/src/utils/removeTrailingSlash.ts (1)
1-7: Consider avoiding parameter reassignment.Line 3 mutates the
inputparameter. While functional, avoiding parameter reassignment improves code clarity and follows immutability patterns.export const removeTrailingSlash = (input: string) => { - if (input.length > 1 && input.endsWith('/')) { - input = input.slice(0, -1) - } - - return input + return input.length > 1 && input.endsWith('/') + ? input.slice(0, -1) + : input }packages/create-miniapp-cli/template/server/domains/condo/constants.js (1)
1-6: Remove extra blank line.Line 3 has an unnecessary extra blank line before the exports. While this doesn't affect functionality, removing it improves consistency.
const CONDO_ORGANIZATION_ID_KEY = 'condoOrganizationId' - module.exports = { CONDO_ORGANIZATION_ID_KEY, }Otherwise, the constant definition is clean and well-named.
packages/create-miniapp-cli/src/utils/renderTitle.ts (2)
7-14: Inconsistent color sourcing.The
themeColorsobject mixes colors from@open-condo/ui/colors(blue, green) with hardcoded hex strings (cyan, magenta, red, yellow). For maintainability and consistency, consider either:
- Using all colors from the
@open-condo/ui/colorspalette, or- Documenting why certain colors must be hardcoded
19-23: Clarify the workaround comment.The comment "resolves weird behavior where the ascii is offset" is vague. Consider documenting the specific issue more precisely, for example:
- // resolves weird behavior where the ascii is offset + // Compensates for extra line consumed by yarn/pnpm output during package manager detectionThis helps future maintainers understand why this workaround exists.
packages/create-miniapp-cli/src/helpers/format.ts (1)
16-16: Remove unnecessary quotes around 'prettier'.The string
'prettier'is quoted as if it's a variable placeholder, but it's just a literal string. Consider either removing the quotes or using template literal interpolation if this is meant to be dynamic.- logger.info(`Formatting project with ${'prettier'}...`) + logger.info('Formatting project with prettier...')packages/create-miniapp-cli/template/fullstack/pages/_error.tsx (1)
19-19: Fix formatting: add space before opening brace.- if (statusCode === 404){ + if (statusCode === 404) {packages/create-miniapp-cli/template/server/domains/user/schema/User.js (1)
14-14: Simplify labelResolver: filter is redundant.The
filter(Boolean)is unnecessary since the default values'noname'and'noemail'are always truthy strings.Apply this diff:
- labelResolver: ({ name = 'noname', email = 'noemail' }) => [name, email].filter(Boolean).join(', '), + labelResolver: ({ name = 'noname', email = 'noemail' }) => [name, email].join(', '),packages/create-miniapp-cli/src/installers/helm/servicesUrls.ts (1)
15-15: Remove unnecessary empty line.const idx = content.lastIndexOf('{{- end') - + let out = ''packages/create-miniapp-cli/src/runCli.ts (2)
113-114: Simplify default value expression.The conditional logic
(results.name ? @${results.name}:@${cliProvidedName})is duplicated for bothdefaultValueandplaceholder. SincecliProvidedNameis already defined andresults.namewould only exist ifcliProvidedName` was not provided (line 81-88), this can be simplified.Consider simplifying:
-defaultValue: (results.name ? `@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAlias, -placeholder: (results.name ? `@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAlias, +defaultValue: `@${results.name ?? cliProvidedName}` || defaultOptions.flags.importAlias, +placeholder: `@${results.name ?? cliProvidedName}` || defaultOptions.flags.importAlias,
206-212: Unnecessary nullish coalescing operators.The
??operators on lines 206 and 210-212 are unnecessary because the Clack prompts always return the specified type (string, ResourceSettings, MaxOldSpace) and cannot return undefined when the user completes them. The fallback tocliResults.flags.*will never be used.Consider removing the unnecessary fallbacks for clarity:
-importAlias: project.importAlias as string ?? cliResults.flags.importAlias, +importAlias: project.importAlias as string, appType: project.appType ?? cliResults.flags.appType, hasReview: project.hasReview ?? cliResults.flags.hasReview, hasWorker: project.hasWorker ?? cliResults.flags.hasWorker, -appResources: project.appResources as ResourceSettings ?? cliResults.flags.appResources, -workerResources: project.workerResources as ResourceSettings ?? cliResults.flags.workerResources, -maxOldSpace: project.maxOldSpace as MaxOldSpace ?? cliResults.flags.maxOldSpace, +appResources: project.appResources as ResourceSettings, +workerResources: project.workerResources as ResourceSettings | undefined, +maxOldSpace: project.maxOldSpace as MaxOldSpace,packages/create-miniapp-cli/src/installers/helm/index.ts (1)
44-49: Use logger instead of console.error for consistency.The error handling block uses
console.errordirectly, but the module importsloggerand uses it elsewhere. For consistency and centralized logging, uselogger.errorinstead.Apply this diff:
- console.error('\n[error]') + logger.error('\n[error]') if (err.stack) { - console.error(err.stack) + logger.error(err.stack) } else { - console.error(err.message || err) + logger.error(err.message || err) }packages/create-miniapp-cli/src/helpers/setImportAlias.ts (1)
17-17: Consider escaping the search string for RegExp safety.While the current callers in
setImportAliasuse hardcoded patterns, constructing a RegExp from a variable without escaping can be fragile if the function is reused elsewhere. Consider escaping special regex characters.Apply this diff:
+ const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - const updatedData = data.replace(new RegExp(search, 'g'), replacement) + const updatedData = data.replace(new RegExp(escapedSearch, 'g'), replacement)packages/create-miniapp-cli/src/installers/helm/templates.ts (1)
29-30: Consider implementing template filtering based on app configuration.The TODO indicates templates should be filtered by
appTypeand user input, but currently all 5 templates (includingworker) are always generated. Looking at the broader PR context,setupHelminhelm/index.tsreceives ahasWorkerflag. Consider passing this flag towriteHelmTemplatesand conditionally generating the worker template only when needed.Example approach:
export async function writeHelmTemplates ( appName: string, nextPrefix: number, reviewEnabled: boolean, hasWorker: boolean = true ) { const created: string[] = [] const names = ['app', 'secrets', 'worker', 'migrations', 'ingress'] as const for (let i = 0; i < names.length; i++) { const role = names[i] if (role === 'worker' && !hasWorker) continue // ... rest of the logic } return created }packages/create-miniapp-cli/src/installers/index.ts (1)
16-27: MakeInstallerasync‑friendly to match IO‑heavy installersGiven installers like Helm will likely be
async(fs, execa, etc.), typingInstalleras returningvoidis restrictive and will either cause type friction or encourage unsafe casts. Consider allowing both sync and async installers:-export type Installer = (opts: InstallerOptions) => void +export type Installer = (opts: InstallerOptions) => void | Promise<void>This keeps current synchronous callers valid while allowing installers that naturally return a
Promise<void>.packages/create-miniapp-cli/src/installers/helm/secretValues.ts (2)
16-18: Reuse sharedHELM_DIRconstant instead of recomputing here
HELM_DIRis computed here and also exported frompackages/create-miniapp-cli/src/installers/helm/constants.ts. Duplicating the path logic risks subtle drift if the base path changes later. Prefer importing the shared constant:-import { CONDO_ROOT } from '@cli/consts' +import { HELM_DIR } from '@cli/installers/helm/constants' @@ -const HELM_DIR = path.resolve(CONDO_ROOT, './.helm') -const SECRET_VALUES = path.join(HELM_DIR, 'secret-values.yaml') +const SECRET_VALUES = path.join(HELM_DIR, 'secret-values.yaml')(or the equivalent import path that matches your alias setup).
88-96: Ensure temporary Werf YAML is always cleaned up on failure
tmpNewAppFileis only unlinked after a successfulwerf ... encryptcall. If encryption throws, the tmp file is left behind inos.tmpdir(). Not critical per‑run, but over time this can accumulate.Wrap the unlink in a
finallyor guard it in the catch:- const tmpNewAppFile = path.join(os.tmpdir(), `werf-newapp-${crypto.randomBytes(6).toString('hex')}.yaml`) - await fs.writeFile(tmpNewAppFile, yaml.stringify({ [appUnderscore]: newAppData }, { indent: 2 }), 'utf8') - - const { stdout: encryptedNewApp } = await execa('werf', [ - 'helm', 'secret', 'values', 'encrypt', tmpNewAppFile, - ], { cwd: CONDO_ROOT }) - - await fs.unlink(tmpNewAppFile) + const tmpNewAppFile = path.join(os.tmpdir(), `werf-newapp-${crypto.randomBytes(6).toString('hex')}.yaml`) + try { + await fs.writeFile(tmpNewAppFile, yaml.stringify({ [appUnderscore]: newAppData }, { indent: 2 }), 'utf8') + + const { stdout: encryptedNewApp } = await execa('werf', [ + 'helm', 'secret', 'values', 'encrypt', tmpNewAppFile, + ], { cwd: CONDO_ROOT }) + + // ... use encryptedNewApp as before + } finally { + await fs.unlink(tmpNewAppFile).catch(() => {}) + }This keeps the tmp directory tidy even when Werf fails.
apps/resident-app
Outdated
| @@ -1 +1 @@ | |||
| Subproject commit 1690ce1cb1cf3834ac98c5545179b26b476699b5 | |||
| Subproject commit 1d86075f9213f0c24a1053c4b8759793b97bb2ae | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify that this submodule update is intentional.
The apps/resident-app submodule pointer has been updated, but the PR objectives focus on adding a create-miniapp CLI tool. Submodule updates in unrelated PRs are often unintentional (e.g., from local development changes).
Please confirm:
- Is this submodule update intentional and necessary for this PR?
- If so, what changes in the resident-app submodule are required and why?
- If not, please revert this change to avoid unrelated modifications.
🏁 Script executed:
#!/bin/bash
# Description: Check if there are references to resident-app in the create-miniapp-cli code
# Search for any references to resident-app in the new CLI package
rg -n "resident-app" --type ts --type js --type json -g '!node_modules' -g '!.git'Length of output: 1186
I need to gather more context to determine if the submodule update is justified. Let me search for information about the create-miniapp CLI changes and any dependencies.
---
🏁 Script executed:
```shell
#!/bin/bash
# Check what files are in the create-miniapp-cli package
fd -t f "package\.json" | grep -i "miniapp\|create-miniapp"
# Also check if there are any new files or significant changes in packages
fd -t f ".*\.json$" | head -20
Length of output: 1011
🏁 Script executed:
#!/bin/bash
# Look for any references to resident-app in package.json or dependency files that might explain the update
rg -n "resident-app" --type json -A2 -B2Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search for any comments or documentation about why resident-app is being updated
rg -n "resident-app.*update|submodule.*resident-app|resident-app.*change" -i -C2Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check git log or commit messages for context (if available in PR description)
# This would help understand if there's a documented reason
echo "Checking if there's documented context about the submodule update..."Length of output: 68
🏁 Script executed:
#!/bin/bash
# Check the actual dependencies in create-miniapp-cli package.json
cat packages/create-miniapp-cli/package.json | head -50Length of output: 1204
🏁 Script executed:
#!/bin/bash
# Search for any postinstall or setup scripts that might involve resident-app
rg -n "postinstall|setup|resident-app" packages/create-miniapp-cli/ -C2Length of output: 23947
🏁 Script executed:
#!/bin/bash
# Check if resident-app is a workspace dependency or referenced anywhere in the root
rg -n "resident-app" -g "package.json" -g "pnpm-workspace.yaml" -g "tsconfig.json" -C2Length of output: 0
Revert the apps/resident-app submodule update—it's unrelated to this PR.
The create-miniapp-cli package has no dependencies on or references to resident-app, and there is no documented reason for the submodule pointer change. This appears to be an accidental inclusion of local development changes. Please revert the commit hash update to keep this PR focused on adding the create-miniapp CLI tool.
🤖 Prompt for AI Agents
In apps/resident-app around line 1, the PR accidentally updated the submodule
commit pointer; revert that submodule update by checking out the original commit
and committing the revert: find the intended commit hash from the target branch
(e.g., origin/main), then either run git submodule update --init --recursive to
sync or cd into apps/resident-app, git fetch && git checkout <original-hash>, cd
.. && git add apps/resident-app && commit the reversion with a message like
"revert accidental resident-app submodule update" so the PR no longer contains
unrelated submodule pointer changes.
| { | ||
| "name": "@open-condo/create-miniapp-cli", | ||
| "version": "1.0.0", | ||
| "description": "Create Condo-compatible miniapp", | ||
| "license": "MIT", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/open-condo-software/condo.git", | ||
| "directory": "packages/create-miniapp-cli" | ||
| }, | ||
| "author": { | ||
| "name": "Daria Abushenko", | ||
| "email": "dabushenko@doma.ai", | ||
| "url": "https://github.com/abshnko" | ||
| }, | ||
| "type": "module", | ||
| "exports": "./dist/index.js", | ||
| "bin": "./dist/index.js", | ||
| "files": [ | ||
| "dist", | ||
| "template", | ||
| "README.md", | ||
| "package.json" | ||
| ], | ||
| "scripts": { | ||
| "typecheck": "tsc", | ||
| "build": "tsup", | ||
| "dev": "tsup --watch", | ||
| "clean": "rm -rf dist node_modules", | ||
| "start": "node dist/index.js" | ||
| }, | ||
| "dependencies": { | ||
| "@clack/prompts": "^0.6.3", | ||
| "chalk": "5.4.0", | ||
| "commander": "14.0.1", | ||
| "execa": "^7.2.0", | ||
| "fs-extra": "^11.2.0", | ||
| "gradient-string": "^2.0.2", | ||
| "ora": "6.3.1", | ||
| "yaml": "^2.8.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/fs-extra": "^11.0.4", | ||
| "@types/gradient-string": "^1.1.6", | ||
| "@types/node": "^22", | ||
| "tsup": "^8.4.0", | ||
| "type-fest": "^3.13.1", | ||
| "typescript": "^5.8.3", | ||
| "zod": "^4.0.8" | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Add missing runtime dependency and double‑check zod placement
Two manifest nits that can cause runtime issues:
-
sort-package-jsonis not declared
addPackageScript.tsimportssort-package-json, but it’s missing from bothdependenciesanddevDependencieshere. In a published package or in an isolated install, this will throwCannot find module 'sort-package-json'.Add it under
dependencies(it’s used at runtime by the CLI):
"dependencies": {
"@clack/prompts": "^0.6.3",
"chalk": "5.4.0",
"commander": "14.0.1",
"execa": "^7.2.0",
"fs-extra": "^11.2.0",
"gradient-string": "^2.0.2",
"ora": "6.3.1",
- "yaml": "^2.8.1"
+ "yaml": "^2.8.1",
+ "sort-package-json": "^X.Y.Z"
},(Pick whatever version you’re targeting for sort-package-json.)
- Verify whether
zodis runtime or dev‑only
zodis currently indevDependencies. If it’s imported by CLI runtime code (not just tests or type‑level tooling), it should move todependenciesso consumers don’t hit missing‑module errors when this package is published or used outside the monorepo.
🏁 Script executed:
#!/bin/bash
# Search for imports/usages of sort-package-json and zod in the codebase
echo "=== Searching for sort-package-json imports ==="
rg -n 'sort-package-json|sort_package_json' --type=ts --type=js -B2 -A2
echo ""
echo "=== Searching for zod imports ==="
rg -n 'import.*zod|from.*zod' --type=ts --type=js -B2 -A2
echo ""
echo "=== Checking package.json structure ==="
cat packages/create-miniapp-cli/package.json | grep -A 20 '"dependencies"'Length of output: 4298
🏁 Script executed:
#!/bin/bash
# Final check: search specifically for zod usage anywhere in create-miniapp-cli package
echo "=== Searching for zod in create-miniapp-cli package only ==="
rg -n 'zod' packages/create-miniapp-cli/ --type=ts --type=js --type=json -B1 -A1
echo ""
echo "=== Checking if zod is referenced in any config or test files ==="
find packages/create-miniapp-cli -type f \( -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.json' \) -exec grep -l 'zod' {} \;Length of output: 935
Add missing runtime dependency sort-package-json and remove unused zod
Two manifest issues found:
sort-package-jsonis not declared but is used at runtime
Confirmed inpackages/create-miniapp-cli/src/utils/addPackageScript.ts:4andpackages/create-miniapp-cli/src/utils/addPackageDependency.ts:8. This will break the CLI when packaged or run outside the monorepo. Add todependencies:
"dependencies": {
"@clack/prompts": "^0.6.3",
"chalk": "5.4.0",
"commander": "14.0.1",
"execa": "^7.2.0",
"fs-extra": "^11.2.0",
"gradient-string": "^2.0.2",
"ora": "6.3.1",
- "yaml": "^2.8.1"
+ "yaml": "^2.8.1",
+ "sort-package-json": "^2.8.2"
},zodindevDependenciesis unused
Zod appears only in template subdirectories and the manifest itself, never imported by CLI code. Remove it fromdevDependencies.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { | |
| "name": "@open-condo/create-miniapp-cli", | |
| "version": "1.0.0", | |
| "description": "Create Condo-compatible miniapp", | |
| "license": "MIT", | |
| "repository": { | |
| "type": "git", | |
| "url": "git+https://github.com/open-condo-software/condo.git", | |
| "directory": "packages/create-miniapp-cli" | |
| }, | |
| "author": { | |
| "name": "Daria Abushenko", | |
| "email": "dabushenko@doma.ai", | |
| "url": "https://github.com/abshnko" | |
| }, | |
| "type": "module", | |
| "exports": "./dist/index.js", | |
| "bin": "./dist/index.js", | |
| "files": [ | |
| "dist", | |
| "template", | |
| "README.md", | |
| "package.json" | |
| ], | |
| "scripts": { | |
| "typecheck": "tsc", | |
| "build": "tsup", | |
| "dev": "tsup --watch", | |
| "clean": "rm -rf dist node_modules", | |
| "start": "node dist/index.js" | |
| }, | |
| "dependencies": { | |
| "@clack/prompts": "^0.6.3", | |
| "chalk": "5.4.0", | |
| "commander": "14.0.1", | |
| "execa": "^7.2.0", | |
| "fs-extra": "^11.2.0", | |
| "gradient-string": "^2.0.2", | |
| "ora": "6.3.1", | |
| "yaml": "^2.8.1" | |
| }, | |
| "devDependencies": { | |
| "@types/fs-extra": "^11.0.4", | |
| "@types/gradient-string": "^1.1.6", | |
| "@types/node": "^22", | |
| "tsup": "^8.4.0", | |
| "type-fest": "^3.13.1", | |
| "typescript": "^5.8.3", | |
| "zod": "^4.0.8" | |
| } | |
| } | |
| { | |
| "name": "@open-condo/create-miniapp-cli", | |
| "version": "1.0.0", | |
| "description": "Create Condo-compatible miniapp", | |
| "license": "MIT", | |
| "repository": { | |
| "type": "git", | |
| "url": "git+https://github.com/open-condo-software/condo.git", | |
| "directory": "packages/create-miniapp-cli" | |
| }, | |
| "author": { | |
| "name": "Daria Abushenko", | |
| "email": "dabushenko@doma.ai", | |
| "url": "https://github.com/abshnko" | |
| }, | |
| "type": "module", | |
| "exports": "./dist/index.js", | |
| "bin": "./dist/index.js", | |
| "files": [ | |
| "dist", | |
| "template", | |
| "README.md", | |
| "package.json" | |
| ], | |
| "scripts": { | |
| "typecheck": "tsc", | |
| "build": "tsup", | |
| "dev": "tsup --watch", | |
| "clean": "rm -rf dist node_modules", | |
| "start": "node dist/index.js" | |
| }, | |
| "dependencies": { | |
| "@clack/prompts": "^0.6.3", | |
| "chalk": "5.4.0", | |
| "commander": "14.0.1", | |
| "execa": "^7.2.0", | |
| "fs-extra": "^11.2.0", | |
| "gradient-string": "^2.0.2", | |
| "ora": "6.3.1", | |
| "yaml": "^2.8.1", | |
| "sort-package-json": "^2.8.2" | |
| }, | |
| "devDependencies": { | |
| "@types/fs-extra": "^11.0.4", | |
| "@types/gradient-string": "^1.1.6", | |
| "@types/node": "^22", | |
| "tsup": "^8.4.0", | |
| "type-fest": "^3.13.1", | |
| "typescript": "^5.8.3", | |
| "zod": "^4.0.8" | |
| } | |
| } |
🤖 Prompt for AI Agents
packages/create-miniapp-cli/package.json lines 1-51: the package.json is missing
the runtime dependency "sort-package-json" which is required by runtime files
(src/utils/addPackageScript.ts and src/utils/addPackageDependency.ts), and it
still lists "zod" in devDependencies though unused by the CLI; add
"sort-package-json" to the "dependencies" section (pick a compatible semver,
e.g. ^1.x) and remove "zod" from "devDependencies", then run npm/yarn install
and rebuild to ensure the CLI bundles the new dependency.
| const spinner = ora('Running format command\n').start() | ||
|
|
||
| await execa(pkgManager, ['run', 'format:write'], { | ||
| cwd: projectDir, | ||
| }) | ||
|
|
||
| spinner.succeed(`${chalk.green('Successfully formatted project')}`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add error handling for format command failures.
If the execa call fails (e.g., missing dependencies, syntax errors in generated code), the spinner will hang and the error will propagate unhandled. Add try-catch to fail the spinner gracefully and provide a clear error message.
const spinner = ora('Running format command\n').start()
- await execa(pkgManager, ['run', 'format:write'], {
- cwd: projectDir,
- })
-
- spinner.succeed(`${chalk.green('Successfully formatted project')}`)
+ try {
+ await execa(pkgManager, ['run', 'format:write'], {
+ cwd: projectDir,
+ })
+ spinner.succeed(`${chalk.green('Successfully formatted project')}`)
+ } catch (error) {
+ spinner.fail('Failed to format project')
+ throw error
+ }🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/format.ts around lines 17 to 23, the
execa call that runs the formatter is not wrapped in error handling, so a failed
format will leave the spinner spinning and the error unhandled; wrap the await
execa(...) call in a try-catch, on success call spinner.succeed as now, and in
the catch call spinner.fail with a clear message that includes the caught error
message (or error.toString()), then rethrow the error (or exit with non-zero) so
the failure surfaces to the caller; ensure the spinner is always stopped by
using the try-catch (no additional finally needed if you handle both branches).
| export const dependencyVersionMap = { | ||
| // eslint / prettier | ||
| prettier: '^3.5.3', | ||
| '@eslint/eslintrc': '^3.3.1', | ||
| 'prettier-plugin-tailwindcss': '^0.6.11', | ||
| eslint: '^9.23.0', | ||
| 'eslint-config-next': '^15.2.3', | ||
| 'eslint-plugin-drizzle': '^0.2.3', | ||
| 'typescript-eslint': '^8.27.0', | ||
| } as const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify dependency versions are current and secure.
Since these versions are pinned and my knowledge may be outdated, ensure all dependencies are up-to-date and free from known vulnerabilities.
🌐 Web query:
Are there any known security vulnerabilities in prettier version 3.5.3, eslint version 9.23.0, eslint-config-next version 15.2.3, eslint-plugin-drizzle version 0.2.3, or typescript-eslint version 8.27.0?
💡 Result:
Short answer:
- Prettier 3.5.3 — Known issue: a ReDoS (regular‑expression DoS) reported as CVE‑2025‑8263 affects Prettier up through 3.6.2, so 3.5.3 is within the affected range. Patch/mitigation info is in the Prettier issue and CVE entries. [1][2][3]
- ESLint 9.23.0 — I found no public CVE or supply‑chain advisory specifically affecting eslint v9.23.0. (No direct vulnerabilities listed in major vulnerability databases for that release.) [4]
- eslint-config-next 15.2.3 — No reported vulnerabilities for that package/version in Snyk/GitHub advisory databases. [5]
- eslint-plugin-drizzle 0.2.3 — No known vulnerabilities reported for that version in Snyk. [6]
- @typescript-eslint / typescript‑eslint (8.27.0) — No direct vulnerabilities reported for the 8.27.0 line in Snyk/GitHub advisory listings. [7]
Notes / context:
- There was a separate high‑severity supply‑chain compromise (CVE‑2025‑54313) that injected malicious postinstall scripts into certain eslint‑config‑prettier and eslint‑plugin‑prettier releases (not the packages/versions you listed). If you use any Prettier/ESLint ecosystem packages, verify you are not pulling the specific compromised versions and upgrade to the patched releases. [8][9]
Sources:
- NVD / CVE-2025-8263 and Prettier issue tracking (ReDoS affecting Prettier ≤ 3.6.2). [1][2]
- Prettier GitHub issue / discussion for the regex/parseNestedCSS problem. [3]
- Snyk / package advisories for eslint, eslint-config-next, eslint-plugin-drizzle, @typescript-eslint (no direct vulnerabilities listed). [4][5][6][7]
- GitHub Advisory / Snyk / industry coverage for the supply‑chain compromise CVE‑2025‑54313 (eslint-config-prettier / eslint-plugin‑prettier). [8][9]
If you want, I can fetch the exact CVE/NVD and Prettier issue URLs and the recommended patched versions for each affected package.
Upgrade Prettier to address CVE-2025-8263 (ReDoS vulnerability).
Prettier 3.5.3 is affected by CVE-2025-8263, a ReDoS vulnerability affecting Prettier versions up through 3.6.2. Upgrade to a patched version above 3.6.2. The other dependencies (ESLint 9.23.0, eslint-config-next 15.2.3, eslint-plugin-drizzle 0.2.3, and typescript-eslint 8.27.0) have no reported vulnerabilities.
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/installers/dependencyVersionMap.ts around
lines 5 to 14, Prettier is pinned to 3.5.3 which is vulnerable (CVE-2025-8263);
update the Prettier entry to a patched version greater than 3.6.2 (for example
'^3.6.3'), update package lock / yarn lock by reinstalling dependencies, and run
the project's lint/tests to verify nothing breaks.
| export const HELM_TEMPLATES = { | ||
| app: `\${REVIEW_NE_WRAPPER_START} | ||
| --- | ||
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: {{ .Chart.Name }}-\${APP}-app | ||
| labels: | ||
| service: {{ .Chart.Name }}-\${APP}-app | ||
| spec: | ||
| replicas: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.replicas | first | default .Values.\${APP_UNDERSCORE}.app.replicas._default }} | ||
| selector: | ||
| matchLabels: | ||
| service: {{ .Chart.Name }}-\${APP}-app | ||
| template: | ||
| metadata: | ||
| labels: | ||
| service: {{ .Chart.Name }}-\${APP}-app | ||
| annotations: | ||
| checksum/secret: {{ include (print $.Template.BasePath "/271-\${APP}-secret.yaml") . | sha256sum }} | ||
| spec: | ||
| nodeSelector: | ||
| nodepool: application-node-pool | ||
| imagePullSecrets: | ||
| - name: {{ required ".Values.registry.secret_name" .Values.registry.secret_name }} | ||
| securityContext: | ||
| fsGroup: 999 | ||
| runAsUser: 999 | ||
| containers: | ||
| - name: app | ||
| image: {{ .Values.werf.image.\${APP_UNDERSCORE} }} | ||
| workingDir: /app | ||
| resources: | ||
| requests: | ||
| cpu: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.requests.cpu | first | default .Values.\${APP_UNDERSCORE}.app.requests.cpu._default | quote }} | ||
| memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.requests.memory | first | default .Values.\${APP_UNDERSCORE}.app.requests.memory._default | quote }} | ||
| limits: | ||
| memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.requests.memory | first | default .Values.\${APP_UNDERSCORE}.app.requests.memory._default | quote }} | ||
| command: ['yarn', 'workspace', '@app/\${APP}', 'start'] | ||
| ports: | ||
| - containerPort: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }} | ||
| name: app | ||
| protocol: TCP | ||
| envFrom: | ||
| - secretRef: | ||
| name: \${APP}-secrets | ||
| --- | ||
| apiVersion: v1 | ||
| kind: Service | ||
| metadata: | ||
| name: {{ .Chart.Name }}-\${APP} | ||
| spec: | ||
| type: ClusterIP | ||
| selector: | ||
| service: {{ .Chart.Name }}-\${APP}-app | ||
| ports: | ||
| - name: {{ .Chart.Name }}-\${APP}-app | ||
| port: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }} | ||
| protocol: TCP | ||
| targetPort: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }} | ||
| \${REVIEW_NE_WRAPPER_END}`, | ||
|
|
||
| secrets: `\${REVIEW_NE_WRAPPER_START} | ||
| --- | ||
| apiVersion: v1 | ||
| kind: Secret | ||
| metadata: | ||
| name: \${APP}-secrets | ||
| annotations: | ||
| "helm.sh/hook": pre-install, pre-upgrade | ||
| "helm.sh/hook-weight": "0" | ||
| type: Opaque | ||
| data: | ||
| {{- include "services_urls" . | indent 2 }} | ||
| {{- range $env, $val := .Values.envs.\${APP_UNDERSCORE} }} | ||
| {{- if and (eq $.Values.werf.env "review") (eq $env "server_url") }} | ||
| {{ $env | upper }}: {{ ( printf "https://%s" $.Values.global.ci_\${APP_UNDERSCORE}_url ) | b64enc }} | ||
| {{- else }} | ||
| {{ $env | upper }}: {{ pluck $.Values.werf.env $val | first | default $val._default | b64enc }} | ||
| {{- end }} | ||
| {{- end }} | ||
| \${REVIEW_NE_WRAPPER_END}`, | ||
|
|
||
| worker: `\${REVIEW_NE_WRAPPER_START} | ||
| --- | ||
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: {{ .Chart.Name }}-\${APP}-worker | ||
| labels: | ||
| service: {{ .Chart.Name }}-\${APP}-worker | ||
| spec: | ||
| replicas: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.replicas | first | default .Values.\${APP_UNDERSCORE}.worker.replicas._default }} | ||
| selector: | ||
| matchLabels: | ||
| service: {{ .Chart.Name }}-\${APP}-worker | ||
| template: | ||
| metadata: | ||
| labels: | ||
| service: {{ .Chart.Name }}-\${APP}-worker | ||
| annotations: | ||
| checksum/secret: {{ include (print $.Template.BasePath "/271-\${APP}-secret.yaml") . | sha256sum }} | ||
| spec: | ||
| nodeSelector: | ||
| nodepool: application-node-pool | ||
| imagePullSecrets: | ||
| - name: {{ required ".Values.registry.secret_name" .Values.registry.secret_name }} | ||
| containers: | ||
| - name: \${APP}-worker | ||
| image: {{ .Values.werf.image.\${APP_UNDERSCORE} }} | ||
| workingDir: /app | ||
| resources: | ||
| requests: | ||
| cpu: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.requests.cpu | first | default .Values.\${APP_UNDERSCORE}.worker.requests.cpu._default | quote }} | ||
| memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.requests.memory | first | default .Values.\${APP_UNDERSCORE}.worker.requests.memory._default | quote }} | ||
| limits: | ||
| memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.requests.memory | first | default .Values.\${APP_UNDERSCORE}.worker.requests.memory._default | quote }} | ||
| command: ['yarn', 'workspace', '@app/\${APP}', 'worker'] | ||
| ports: | ||
| - containerPort: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.port | first | default .Values.\${APP_UNDERSCORE}.worker.port._default }} | ||
| name: worker | ||
| protocol: TCP | ||
| envFrom: | ||
| - secretRef: | ||
| name: \${APP}-secrets | ||
| \${REVIEW_NE_WRAPPER_END}`, | ||
|
|
||
| migrations: `\${REVIEW_NE_WRAPPER_START} | ||
| --- | ||
| apiVersion: batch/v1 | ||
| kind: Job | ||
| metadata: | ||
| name: {{ printf "%s-\${APP}-migrations-%s" .Chart.Name (now | date "2006-01-02-15-04-05")}} | ||
| labels: | ||
| app: {{ printf "%s-\${APP}-migrations" .Chart.Name }} | ||
| annotations: | ||
| "helm.sh/hook": pre-install, pre-upgrade | ||
| "helm.sh/hook-weight": "2" | ||
| spec: | ||
| ttlSecondsAfterFinished: 100 | ||
| template: | ||
| metadata: | ||
| annotations: | ||
| checksum/secret: {{ include (print $.Template.BasePath "/271-\${APP}-secret.yaml") . | sha256sum }} | ||
| spec: | ||
| nodeSelector: | ||
| nodepool: application-node-pool | ||
| imagePullSecrets: | ||
| - name: {{ required ".Values.registry.secret_name" .Values.registry.secret_name }} | ||
| restartPolicy: Never | ||
| containers: | ||
| - name: migrate-condo-\${APP}-app | ||
| image: {{ .Values.werf.image.\${APP_UNDERSCORE} }} | ||
| command: ['yarn', 'workspace', '@app/\${APP}', 'migrate'] | ||
| workingDir: /app | ||
| envFrom: | ||
| - secretRef: | ||
| name: \${APP}-secrets | ||
| \${REVIEW_NE_WRAPPER_END}`, | ||
|
|
||
| ingress: `\${REVIEW_NE_WRAPPER_START} | ||
| --- | ||
| apiVersion: networking.k8s.io/v1 | ||
| kind: Ingress | ||
| metadata: | ||
| name: {{ .Chart.Name }}-\${APP} | ||
| annotations: | ||
| kubernetes.io/ingress.class: nginx | ||
| nginx.ingress.kubernetes.io/configuration-snippet: | | ||
| more_set_headers "Strict-Transport-Security: max-age=31536000; preload"; | ||
| more_set_headers "X-Request-Id: $request_id"; | ||
| spec: | ||
| tls: | ||
| - hosts: | ||
| - {{ .Values.global.ci_\${APP_UNDERSCORE}_url }} | ||
| secretName: {{ if hasPrefix "production" .Values.werf.env }}doma-wildcard{{ else }}doma-development-wildcard{{ end }} | ||
| rules: | ||
| - host: {{ .Values.global.ci_\${APP_UNDERSCORE}_url }} | ||
| http: | ||
| paths: | ||
| - path: / | ||
| pathType: Prefix | ||
| backend: | ||
| service: | ||
| name: {{ .Chart.Name }}-\${APP} | ||
| port: | ||
| number: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }} | ||
| \${REVIEW_NE_WRAPPER_END}`, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Avoid having two divergent sources of truth for Helm templates
This HELM_TEMPLATES object defines app/secrets/worker/migrations/ingress YAML with APP/APP_UNDERSCORE placeholders, while packages/create-miniapp-cli/src/utils/getHelmTemplates.ts now generates very similar templates programmatically.
Maintaining both will be brittle:
- There are already behavioral deltas (e.g.
secretshandling ofserver_urlfor review env;ingressTLSsecretNamelogic), so which behavior is authoritative depends on which path is used. - Any future tweak to resource shape, labels, or env wiring must be duplicated in two places.
To keep behavior predictable and maintainable, consider:
- Either:
- Make
getHelmTemplates.tssimply wrapHELM_TEMPLATES(performing placeholder substitution likeAPP/APP_UNDERSCORE→ concrete names), and treatHELM_TEMPLATESas the single YAML source of truth.
- Make
- Or:
- Move the canonical YAML definitions into
getHelmTemplates.tsand deprecate/remove the overlapping entries fromHELM_TEMPLATES, leaving this file only for path constants (HELM_DIR, TEMPLATES_DIR, REVIEW_DIR, SERVICES_URLS) and any shared non‑duplicated fragments.
- Move the canonical YAML definitions into
This will reduce the chance of subtle environment‑specific or review‑mode regressions when templates evolve.
| <svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo"> | ||
| <path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/> | ||
| <path d="M32.0611 7.63988C26.9311 7.66988 23.8111 10.3099 23.8111 15.4699C23.8111 20.7199 27.1711 23.3599 31.7911 23.3599C36.8911 23.3299 40.0411 20.7199 40.0411 15.5299C40.0411 10.2799 36.6811 7.63988 32.0611 7.63988ZM28.4611 15.4399C28.4611 12.7399 29.7211 11.5999 31.9111 11.5999C33.9511 11.6299 35.3911 12.7699 35.3911 15.4699V15.5599C35.3911 18.2599 34.1311 19.3999 31.9411 19.3999C29.8711 19.3999 28.4611 18.2599 28.4611 15.5299V15.4399Z"/> | ||
| <path d="M52.0266 22.9999H56.5566V14.3899C56.5266 12.4999 57.5166 11.6599 59.1966 11.6599H59.2566C60.6366 11.6899 61.2366 12.4699 61.2366 14.1199V22.9999H65.7666V12.8299C65.7666 9.46988 63.9366 7.72988 60.9066 7.69988H60.8766C58.4466 7.69988 56.8266 9.16988 56.3166 10.9699C55.7166 8.83988 54.0666 7.72988 51.7266 7.69988H51.6666C49.2666 7.69988 47.6166 9.25988 47.1066 10.9099V7.99988H42.8766V22.9999H47.3766V14.3899C47.2866 12.3799 48.2466 11.6899 49.9566 11.6599C51.4566 11.6599 52.0266 12.4399 52.0266 14.1199V22.9999Z"/> | ||
| <path d="M69.8109 14.2099C70.8309 12.7099 72.2409 11.6299 75.4209 11.6299C77.2809 11.6299 77.8509 12.2599 77.8509 13.9399V14.0599H74.6109C71.2209 14.0599 68.5809 15.0199 68.5809 18.5899C68.5809 21.3799 70.2009 23.2399 73.3209 23.2399C75.6609 23.2399 77.3709 22.0399 78.0909 20.2999V22.9999H82.3509V13.0999C82.3509 9.37988 80.3709 7.69988 75.4209 7.69988C72.6009 7.69988 70.8009 8.23988 69.8109 8.98988V14.2099ZM74.8809 19.2799C73.7409 19.2799 73.1109 18.7999 73.1109 17.9899C73.1109 17.0299 73.9209 16.6699 75.3609 16.6699H77.8509V17.5399C77.7009 18.5599 76.5909 19.2799 74.8809 19.2799Z"/> | ||
| <path d="M86.753 20.6299C86.753 22.0999 87.383 23.2999 89.453 23.2999C91.613 23.2999 92.243 22.0999 92.243 20.6299C92.243 19.1299 91.613 17.8999 89.453 17.8999C87.383 17.8999 86.753 19.1299 86.753 20.6299Z"/> | ||
| <path d="M97.2914 14.2099C98.3114 12.7099 99.7214 11.6299 102.901 11.6299C104.761 11.6299 105.331 12.2599 105.331 13.9399V14.0599H102.091C98.7014 14.0599 96.0614 15.0199 96.0614 18.5899C96.0614 21.3799 97.6814 23.2399 100.801 23.2399C103.141 23.2399 104.851 22.0399 105.571 20.2999V22.9999H109.831V13.0999C109.831 9.37988 107.851 7.69988 102.901 7.69988C100.081 7.69988 98.2814 8.23988 97.2914 8.98988V14.2099ZM102.361 19.2799C101.221 19.2799 100.591 18.7999 100.591 17.9899C100.591 17.0299 101.401 16.6699 102.841 16.6699H105.331V17.5399C105.181 18.5599 104.071 19.2799 102.361 19.2799Z"/> | ||
| <path d="M113.213 2.68988C113.213 4.12988 114.203 5.05988 115.733 5.05988C117.203 5.05988 118.193 4.12988 118.193 2.68988C118.193 1.27988 117.203 0.379883 115.733 0.379883C114.203 0.379883 113.213 1.27988 113.213 2.68988ZM113.453 22.9999H117.983V7.99988H113.453V22.9999Z"/> | ||
| </svg> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add semantic markup or ARIA labels to the SVG logo for accessibility.
The SVG logo lacks an alt text, title element, or ARIA attributes, making it inaccessible to screen readers. Assistive technology will skip over this element entirely.
Apply this diff to add proper semantic markup:
- <svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo">
+ <svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo" role="img" aria-label="Logo">
<path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/>Alternatively, add a <title> child element inside the SVG for better semantics.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo"> | |
| <path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/> | |
| <path d="M32.0611 7.63988C26.9311 7.66988 23.8111 10.3099 23.8111 15.4699C23.8111 20.7199 27.1711 23.3599 31.7911 23.3599C36.8911 23.3299 40.0411 20.7199 40.0411 15.5299C40.0411 10.2799 36.6811 7.63988 32.0611 7.63988ZM28.4611 15.4399C28.4611 12.7399 29.7211 11.5999 31.9111 11.5999C33.9511 11.6299 35.3911 12.7699 35.3911 15.4699V15.5599C35.3911 18.2599 34.1311 19.3999 31.9411 19.3999C29.8711 19.3999 28.4611 18.2599 28.4611 15.5299V15.4399Z"/> | |
| <path d="M52.0266 22.9999H56.5566V14.3899C56.5266 12.4999 57.5166 11.6599 59.1966 11.6599H59.2566C60.6366 11.6899 61.2366 12.4699 61.2366 14.1199V22.9999H65.7666V12.8299C65.7666 9.46988 63.9366 7.72988 60.9066 7.69988H60.8766C58.4466 7.69988 56.8266 9.16988 56.3166 10.9699C55.7166 8.83988 54.0666 7.72988 51.7266 7.69988H51.6666C49.2666 7.69988 47.6166 9.25988 47.1066 10.9099V7.99988H42.8766V22.9999H47.3766V14.3899C47.2866 12.3799 48.2466 11.6899 49.9566 11.6599C51.4566 11.6599 52.0266 12.4399 52.0266 14.1199V22.9999Z"/> | |
| <path d="M69.8109 14.2099C70.8309 12.7099 72.2409 11.6299 75.4209 11.6299C77.2809 11.6299 77.8509 12.2599 77.8509 13.9399V14.0599H74.6109C71.2209 14.0599 68.5809 15.0199 68.5809 18.5899C68.5809 21.3799 70.2009 23.2399 73.3209 23.2399C75.6609 23.2399 77.3709 22.0399 78.0909 20.2999V22.9999H82.3509V13.0999C82.3509 9.37988 80.3709 7.69988 75.4209 7.69988C72.6009 7.69988 70.8009 8.23988 69.8109 8.98988V14.2099ZM74.8809 19.2799C73.7409 19.2799 73.1109 18.7999 73.1109 17.9899C73.1109 17.0299 73.9209 16.6699 75.3609 16.6699H77.8509V17.5399C77.7009 18.5599 76.5909 19.2799 74.8809 19.2799Z"/> | |
| <path d="M86.753 20.6299C86.753 22.0999 87.383 23.2999 89.453 23.2999C91.613 23.2999 92.243 22.0999 92.243 20.6299C92.243 19.1299 91.613 17.8999 89.453 17.8999C87.383 17.8999 86.753 19.1299 86.753 20.6299Z"/> | |
| <path d="M97.2914 14.2099C98.3114 12.7099 99.7214 11.6299 102.901 11.6299C104.761 11.6299 105.331 12.2599 105.331 13.9399V14.0599H102.091C98.7014 14.0599 96.0614 15.0199 96.0614 18.5899C96.0614 21.3799 97.6814 23.2399 100.801 23.2399C103.141 23.2399 104.851 22.0399 105.571 20.2999V22.9999H109.831V13.0999C109.831 9.37988 107.851 7.69988 102.901 7.69988C100.081 7.69988 98.2814 8.23988 97.2914 8.98988V14.2099ZM102.361 19.2799C101.221 19.2799 100.591 18.7999 100.591 17.9899C100.591 17.0299 101.401 16.6699 102.841 16.6699H105.331V17.5399C105.181 18.5599 104.071 19.2799 102.361 19.2799Z"/> | |
| <path d="M113.213 2.68988C113.213 4.12988 114.203 5.05988 115.733 5.05988C117.203 5.05988 118.193 4.12988 118.193 2.68988C118.193 1.27988 117.203 0.379883 115.733 0.379883C114.203 0.379883 113.213 1.27988 113.213 2.68988ZM113.453 22.9999H117.983V7.99988H113.453V22.9999Z"/> | |
| </svg> | |
| <svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo" role="img" aria-label="Logo"> | |
| <path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/> | |
| <path d="M32.0611 7.63988C26.9311 7.66988 23.8111 10.3099 23.8111 15.4699C23.8111 20.7199 27.1711 23.3599 31.7911 23.3599C36.8911 23.3299 40.0411 20.7199 40.0411 15.5299C40.0411 10.2799 36.6811 7.63988 32.0611 7.63988ZM28.4611 15.4399C28.4611 12.7399 29.7211 11.5999 31.9111 11.5999C33.9511 11.6299 35.3911 12.7699 35.3911 15.4699V15.5599C35.3911 18.2599 34.1311 19.3999 31.9411 19.3999C29.8711 19.3999 28.4611 18.2599 28.4611 15.5299V15.4399Z"/> | |
| <path d="M52.0266 22.9999H56.5566V14.3899C56.5266 12.4999 57.5166 11.6599 59.1966 11.6599H59.2566C60.6366 11.6899 61.2366 12.4699 61.2366 14.1199V22.9999H65.7666V12.8299C65.7666 9.46988 63.9366 7.72988 60.9066 7.69988H60.8766C58.4466 7.69988 56.8266 9.16988 56.3166 10.9699C55.7166 8.83988 54.0666 7.72988 51.7266 7.69988H51.6666C49.2666 7.69988 47.6166 9.25988 47.1066 10.9099V7.99988H42.8766V22.9999H47.3766V14.3899C47.2866 12.3799 48.2466 11.6899 49.9566 11.6599C51.4566 11.6599 52.0266 12.4399 52.0266 14.1199V22.9999Z"/> | |
| <path d="M69.8109 14.2099C70.8309 12.7099 72.2409 11.6299 75.4209 11.6299C77.2809 11.6299 77.8509 12.2599 77.8509 13.9399V14.0599H74.6109C71.2209 14.0599 68.5809 15.0199 68.5809 18.5899C68.5809 21.3799 70.2009 23.2399 73.3209 23.2399C75.6609 23.2399 77.3709 22.0399 78.0909 20.2999V22.9999H82.3509V13.0999C82.3509 9.37988 80.3709 7.69988 75.4209 7.69988C72.6009 7.69988 70.8009 8.23988 69.8109 8.98988V14.2099ZM74.8809 19.2799C73.7409 19.2799 73.1109 18.7999 73.1109 17.9899C73.1109 17.0299 73.9209 16.6699 75.3609 16.6699H77.8509V17.5399C77.7009 18.5599 76.5909 19.2799 74.8809 19.2799Z"/> | |
| <path d="M86.753 20.6299C86.753 22.0999 87.383 23.2999 89.453 23.2999C91.613 23.2999 92.243 22.0999 92.243 20.6299C92.243 19.1299 91.613 17.8999 89.453 17.8999C87.383 17.8999 86.753 19.1299 86.753 20.6299Z"/> | |
| <path d="M97.2914 14.2099C98.3114 12.7099 99.7214 11.6299 102.901 11.6299C104.761 11.6299 105.331 12.2599 105.331 13.9399V14.0599H102.091C98.7014 14.0599 96.0614 15.0199 96.0614 18.5899C96.0614 21.3799 97.6814 23.2399 100.801 23.2399C103.141 23.2399 104.851 22.0399 105.571 20.2999V22.9999H109.831V13.0999C109.831 9.37988 107.851 7.69988 102.901 7.69988C100.081 7.69988 98.2814 8.23988 97.2914 8.98988V14.2099ZM102.361 19.2799C101.221 19.2799 100.591 18.7999 100.591 17.9899C100.591 17.0299 101.401 16.6699 102.841 16.6699H105.331V17.5399C105.181 18.5599 104.071 19.2799 102.361 19.2799Z"/> | |
| <path d="M113.213 2.68988C113.213 4.12988 114.203 5.05988 115.733 5.05988C117.203 5.05988 118.193 4.12988 118.193 2.68988C118.193 1.27988 117.203 0.379883 115.733 0.379883C114.203 0.379883 113.213 1.27988 113.213 2.68988ZM113.453 22.9999H117.983V7.99988H113.453V22.9999Z"/> | |
| </svg> |
🤖 Prompt for AI Agents
packages/create-miniapp-cli/template/fullstack/public/500-error.html around
lines 125 to 133: the inline SVG logo is missing accessible semantics; add a
<title> element inside the SVG (with a unique id) and set role="img" plus
aria-labelledby="that-id" (or aria-label="Descriptive text") so screen readers
can announce it; if the logo is purely decorative instead, set
aria-hidden="true" and keep it out of the accessibility tree.
| async function setupBaseConfigs () { | ||
| const condoUrl = await getAppEnvValue(APP_NAME, 'CONDO_DOMAIN') | ||
| await updateAppEnvFile(APP_NAME, 'CONDO_URL', JSON.stringify(condoUrl)) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify CONDO_DOMAIN → CONDO_URL variable name transformation.
Line 8 reads CONDO_DOMAIN but line 9 writes it as CONDO_URL. Confirm this variable name transformation is intentional and that downstream code expects CONDO_URL rather than CONDO_DOMAIN.
🏁 Script executed:
#!/bin/bash
# Check if CONDO_URL is referenced elsewhere in the template
rg -n --type=js --type=ts 'CONDO_URL' packages/create-miniapp-cli/template/Length of output: 136
I need to verify if this transformation is intentional by checking for CONDO_DOMAIN references and looking for broader context about these environment variables.
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for CONDO_DOMAIN across entire codebase
echo "=== CONDO_DOMAIN references ==="
rg -n 'CONDO_DOMAIN' --type=js --type=ts
echo -e "\n=== CONDO_URL references outside template ==="
rg -n 'CONDO_URL' --type=js --type=ts -g '!packages/create-miniapp-cli/template/**'
echo -e "\n=== Check for env config or documentation files ==="
fd -e md -e json -e txt 'config|env|README' packages/create-miniapp-cli/ | head -20
Length of output: 2243
Fix incorrect environment variable name on line 9: change CONDO_URL to CONDO_DOMAIN.
The variable name transformation is a bug, not intentional. Throughout the codebase, all downstream code (clients/index.js, middlewares/oidc.js, next.config.js) and similar prepare.js scripts consistently read and write CONDO_DOMAIN, never CONDO_URL. Line 9 should write to CONDO_DOMAIN to match what line 8 reads and what the rest of the template expects.
# Current (incorrect):
await updateAppEnvFile(APP_NAME, 'CONDO_URL', JSON.stringify(condoUrl))
# Should be:
await updateAppEnvFile(APP_NAME, 'CONDO_DOMAIN', JSON.stringify(condoUrl))🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/bin/prepare.js around lines 7 to
10, the code writes the environment variable under the wrong name; change the
call that currently updates CONDO_URL to instead update CONDO_DOMAIN so the
written key matches the value retrieved from CONDO_DOMAIN and the rest of the
codebase; modify the updateAppEnvFile call to use 'CONDO_DOMAIN' (and leave the
JSON.stringify(condoUrl) as-is).
| const createdUser = await User.create(context, { | ||
| sender: OIDC_SENDER_FIELD_VALUE, | ||
| name: userInfo.name, | ||
| dv: userInfo.dv, | ||
| isSupport: userInfo.isSupport, | ||
| isAdmin: userInfo.isAdmin, | ||
| }) | ||
| // UserCreateInput does not have `id` field, so, update it manually | ||
| return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for the adapter.update call.
The direct adapter.update at line 59 sets a custom id from userInfo.sub. If this operation fails (e.g., due to a constraint violation or database error), the newly created user will remain with an auto-generated id instead of the OIDC sub, causing authentication issues. Consider wrapping this in a try-catch and either rolling back the user creation or logging a critical error.
Example approach:
} else {
const createdUser = await User.create(context, {
sender: OIDC_SENDER_FIELD_VALUE,
name: userInfo.name,
dv: userInfo.dv,
isSupport: userInfo.isSupport,
isAdmin: userInfo.isAdmin,
})
- // UserCreateInput does not have `id` field, so, update it manually
- return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub })
+ try {
+ // UserCreateInput does not have `id` field, so, update it manually
+ return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub })
+ } catch (err) {
+ // Clean up the user or log critical error
+ await User.delete(context, createdUser.id)
+ throw new Error(`Failed to set OIDC sub as user id: ${err.message}`)
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const createdUser = await User.create(context, { | |
| sender: OIDC_SENDER_FIELD_VALUE, | |
| name: userInfo.name, | |
| dv: userInfo.dv, | |
| isSupport: userInfo.isSupport, | |
| isAdmin: userInfo.isAdmin, | |
| }) | |
| // UserCreateInput does not have `id` field, so, update it manually | |
| return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub }) | |
| } | |
| const createdUser = await User.create(context, { | |
| sender: OIDC_SENDER_FIELD_VALUE, | |
| name: userInfo.name, | |
| dv: userInfo.dv, | |
| isSupport: userInfo.isSupport, | |
| isAdmin: userInfo.isAdmin, | |
| }) | |
| try { | |
| // UserCreateInput does not have `id` field, so, update it manually | |
| return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub }) | |
| } catch (err) { | |
| // Clean up the user or log critical error | |
| await User.delete(context, createdUser.id) | |
| throw new Error(`Failed to set OIDC sub as user id: ${err.message}`) | |
| } | |
| } |
🤖 Prompt for AI Agents
In
packages/create-miniapp-cli/template/server/domains/user/utils/serverSchema/createOrUpdateUser.js
around lines 51 to 60, the call to keystone.lists.User.adapter.update that sets
the custom id can fail and currently has no error handling; wrap the
adapter.update call in a try-catch, and on failure delete the just-created user
(use keystone.lists.User.adapter.delete with createdUser.id) or otherwise roll
back the creation, log a clear error (including the error object and context:
createdUser.id and userInfo.sub), and rethrow the error so callers know the
operation failed; ensure the function returns the updated record only on
successful update.
| const schemas = () => [ | ||
| require('~/domains/user/schema'), | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve ESLint no-restricted-modules on ~/domains/user/schema.
ESLint is complaining that '~/domains/user/schema' is restricted by no-restricted-modules; as-is, this will cause lint failures for the template. Either:
- Change this to an allowed schema import path that fits your boundaries rules, or
- Explicitly exempt this template file from that rule (e.g., via an override in the ESLint config or a targeted
// eslint-disable-next-line no-restricted-modulesif you’re comfortable with that here).
🧰 Tools
🪛 ESLint
[error] 18-18: '~/domains/user/schema' module is restricted from being used by a pattern.
(no-restricted-modules)
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/index.js around lines 17 to 19,
ESLint flags require('~/domains/user/schema') as a restricted module; fix by
replacing the tilde import with an allowed path (preferably a relative path like
'./domains/user/schema' or the correct package-scoped path used in this
template) so it conforms to your boundaries, or if that’s not possible, add a
targeted exemption (either a single-line comment // eslint-disable-next-line
no-restricted-modules immediately above the require or an ESLint override for
this template file) to silence the rule.
2645500 to
8dfff98
Compare
|
… services urls file
…fying the original content
…ml instead of js-yaml
8dfff98 to
685b875
Compare
|




todo:
Unused dependencies (4)
dayjs apps/payments-gateway/package.json
graphql-tag apps/payments-gateway/package.json
react apps/payments-gateway/package.json
zod apps/payments-gateway/package.json
Unused devDependencies (2)
@graphql-codegen/typescript apps/payments-gateway/package.json
@types/react apps/payments-gateway/package.json
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.