mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
chore: standardised the eslint config structure accross the project (#11814)
This commit is contained in:
@@ -26,7 +26,7 @@ export default async function Layout(props: { children: ReactNode; params: Promi
|
||||
}
|
||||
});
|
||||
|
||||
let tree = source.pageTree;
|
||||
let tree: typeof source.pageTree;
|
||||
if (currentVersion !== 'latest') {
|
||||
// Hoist the version folder to root to flatten sidebar
|
||||
// versionSource.pageTree -> [ v4.10 folder, v4.9 folder ... ]
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
import { RootProvider } from 'fumadocs-ui/provider/next';
|
||||
import dynamic from "next/dynamic";
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import MixpanelProvider from '@/components/mixpanel-provider';
|
||||
import PlausibleProvider from '@/components/plausible-provider';
|
||||
|
||||
const SearchDialog = dynamic(() => import("@/components/inkeep-search"));
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
export function Providers({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<RootProvider
|
||||
search={{
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import yaml from 'js-yaml';
|
||||
import { Check, X } from 'lucide-react';
|
||||
import { cn } from '@/utils/cn'; // Assuming utils/cn exists, or I'll use clsx/tailwind-merge directly if not sure
|
||||
|
||||
// Fallback utility if generic cn doesn't exist, checking imports first is safer, but I'll inline a simple merge if needed
|
||||
// Checking package.json I saw clsx and tailwind-merge are dependencies.
|
||||
import { Check } from 'lucide-react';
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { initMixpanel, mixpanelClient, optInTracking, optOutTracking } from '@/utils/mixpanel';
|
||||
import { initMixpanel, optInTracking, optOutTracking } from '@/utils/mixpanel';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
export default function MixpanelProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
+90
-96
@@ -1,101 +1,58 @@
|
||||
import { createRequire } from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
const mdx = require('eslint-plugin-mdx');
|
||||
const mdxParser = require('eslint-mdx');
|
||||
const tsParser = require('@typescript-eslint/parser');
|
||||
const tsPlugin = require('@typescript-eslint/eslint-plugin');
|
||||
import globals from 'globals';
|
||||
|
||||
import js from '@eslint/js';
|
||||
// import { FlatCompat } from '@eslint/eslintrc';
|
||||
// import path from 'path';
|
||||
// import { fileURLToPath } from 'url';
|
||||
|
||||
// const __filename = fileURLToPath(import.meta.url);
|
||||
// const __dirname = path.dirname(__filename);
|
||||
|
||||
// const compat = new FlatCompat({
|
||||
// baseDirectory: __dirname,
|
||||
// });
|
||||
import js from "@eslint/js";
|
||||
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import prettierConfig from "eslint-config-prettier/flat";
|
||||
import importPlugin from "eslint-plugin-import";
|
||||
import * as mdx from "eslint-plugin-mdx";
|
||||
import globals from "globals";
|
||||
|
||||
export default [
|
||||
// 1. Base JS Rules
|
||||
{
|
||||
ignores: [
|
||||
"node_modules/",
|
||||
".next/",
|
||||
"out/",
|
||||
"build/",
|
||||
".source/",
|
||||
"next-env.d.ts",
|
||||
],
|
||||
},
|
||||
js.configs.recommended,
|
||||
|
||||
// 2. Import Next.js Core Web Vitals (via compat because it doesn't support Flat Config natively yet)
|
||||
// ...compat.extends('next/core-web-vitals'),
|
||||
// ...compat.extends('next', 'next/typescript'), // Causes circular JSON error
|
||||
|
||||
|
||||
// ...
|
||||
// 3. MDX Configuration
|
||||
{
|
||||
files: ['*.mdx'],
|
||||
...mdx.flat,
|
||||
processor: mdx.createRemarkProcessor({
|
||||
lintCodeBlocks: false,
|
||||
}),
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
props: 'readonly',
|
||||
},
|
||||
parser: mdxParser,
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
'react/no-unescaped-entities': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-irregular-whitespace': 'off',
|
||||
'no-useless-escape': 'off',
|
||||
},
|
||||
},
|
||||
// ...
|
||||
|
||||
// 4. Code Blocks inside MDX (virtual files)
|
||||
{
|
||||
files: ['*.mdx/*.{js,jsx,ts,tsx}'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
props: 'readonly', // common in MDX
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'import/no-anonymous-default-export': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
// 5. Global Ignores
|
||||
{
|
||||
ignores: ["node_modules/", ".next/", "out/", "build/", ".source/", "next-env.d.ts"],
|
||||
},
|
||||
|
||||
// 6. Global options for JS files
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs}'],
|
||||
files: ["**/*.{js,mjs,cjs}"],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
// 7. Manual TypeScript Configuration
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
files: ["**/*.jsx"],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^React$" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.{ts,tsx}"],
|
||||
plugins: {
|
||||
'@typescript-eslint': tsPlugin,
|
||||
"@typescript-eslint": tsPlugin,
|
||||
import: importPlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
globals: {
|
||||
@@ -103,24 +60,61 @@ export default [
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...tsPlugin.configs.recommended.rules,
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
||||
'@typescript-eslint/no-explicit-any': 'off', // Disable for legacy code
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
...importPlugin.configs.recommended.rules,
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{ argsIgnorePattern: "^_" },
|
||||
],
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
},
|
||||
// 8. Scripts Config
|
||||
{
|
||||
files: ['scripts/**/*.{js,mjs,ts}'],
|
||||
files: ["**/*.mdx"],
|
||||
...mdx.flat,
|
||||
processor: mdx.createRemarkProcessor({
|
||||
lintCodeBlocks: false,
|
||||
}),
|
||||
languageOptions: {
|
||||
...mdx.flat.languageOptions,
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
props: "readonly",
|
||||
},
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'no-undef': 'off',
|
||||
'no-useless-escape': 'off',
|
||||
'no-empty': 'off',
|
||||
'no-async-promise-executor': 'off',
|
||||
}
|
||||
}
|
||||
"no-unused-vars": "off",
|
||||
"no-irregular-whitespace": "off",
|
||||
"no-useless-escape": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.mdx/*.{js,jsx,ts,tsx}"],
|
||||
plugins: {
|
||||
import: importPlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
props: "readonly",
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
},
|
||||
},
|
||||
prettierConfig,
|
||||
];
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"generate:index-pages": "tsx scripts/generate-index-pages.ts",
|
||||
"fetch:remote-content": "node scripts/fetch-remote-content.mjs",
|
||||
"generate:proto-docs": "node scripts/generate-proto-docs.mjs",
|
||||
"generate:api-reference": "tsx scripts/generate-api-reference.mts --only-generate",
|
||||
"fix:api-links": "tsx scripts/generate-api-reference.mts --only-fix",
|
||||
"generate:api-reference": "tsx scripts/generate-api-reference.ts --only-generate",
|
||||
"fix:api-links": "tsx scripts/generate-api-reference.ts --only-fix",
|
||||
"build:mdx-source": "fumadocs-mdx",
|
||||
"build": "next build",
|
||||
"dev": "next dev",
|
||||
@@ -16,7 +16,7 @@
|
||||
"start": "next start",
|
||||
"clean": "node scripts/clean.mjs",
|
||||
"check:types": "fumadocs-mdx && next typegen && tsc --noEmit",
|
||||
"check:links": "tsx scripts/check-links.mts",
|
||||
"check:links": "tsx scripts/check-links.ts",
|
||||
"test:scripts": "node --test scripts/*.test.mjs",
|
||||
"postinstall": "fumadocs-mdx",
|
||||
"lint": "eslint ."
|
||||
@@ -49,7 +49,6 @@
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
@@ -57,11 +56,13 @@
|
||||
"@types/node": "^25.2.3",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
||||
"@typescript-eslint/parser": "^8.56.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
||||
"@typescript-eslint/parser": "^8.57.0",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-next": "16.1.6",
|
||||
"eslint-plugin-mdx": "^3.6.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-mdx": "^3.7.0",
|
||||
"glob": "^13.0.5",
|
||||
"globals": "^17.3.0",
|
||||
"next-validate-link": "^1.6.4",
|
||||
|
||||
@@ -10,7 +10,6 @@ register('fumadocs-mdx/node/loader', import.meta.url);
|
||||
import {
|
||||
type FileObject,
|
||||
printErrors,
|
||||
scanURLs,
|
||||
validateFiles,
|
||||
} from 'next-validate-link';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
const V1_BASE_PATHS: Record<string, string> = {
|
||||
'admin.openapi.json': '/admin/v1',
|
||||
@@ -32,7 +32,7 @@ async function run() {
|
||||
}
|
||||
|
||||
// Versioned content and protos
|
||||
const cleanVersioned = (dir, pattern) => {
|
||||
const cleanVersioned = (dir) => {
|
||||
if (!fs.existsSync(dir)) return;
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
|
||||
@@ -71,7 +71,7 @@ function getCurrentRef() {
|
||||
cachedRef = branch;
|
||||
return cachedRef;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore git errors
|
||||
}
|
||||
console.log(`[ref] Defaulting to ${FALLBACK_BRANCH}`);
|
||||
@@ -143,7 +143,7 @@ function copyDirectorySafely(src, dest) {
|
||||
if (versionDirPattern.test(item) && fs.statSync(join(src, item)).isDirectory()) {
|
||||
isVersionDir = true;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore stat errors
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ async function downloadFileContent(tagOrBranch, repoPath) {
|
||||
let decodedRepoPath;
|
||||
try {
|
||||
decodedRepoPath = decodeURIComponent(repoPath.replace(/\\/g, '/'));
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// If decoding fails (malformed escape sequences), fall back to original
|
||||
decodedRepoPath = repoPath;
|
||||
}
|
||||
@@ -370,7 +370,7 @@ async function fixRelativeImports(versionDir, tagOrBranch) {
|
||||
});
|
||||
|
||||
// 2. Markdown Images: 
|
||||
const mdImgRegex = /(!\[.*?\]\()([^\)]+)(\))/g;
|
||||
const mdImgRegex = /(!\[.*?\]\()([^)]+)(\))/g;
|
||||
content = content.replace(mdImgRegex, (match, p1, p2, p3) => {
|
||||
if (!p2.startsWith('.')) return match;
|
||||
const rewritten = rewritePath(filePath, p2);
|
||||
@@ -452,7 +452,7 @@ function getLocalVersion() {
|
||||
if (!branch) {
|
||||
try {
|
||||
branch = execSync('git branch --show-current').toString().trim();
|
||||
} catch (e) {}
|
||||
} catch { /* ignore git errors */ }
|
||||
}
|
||||
|
||||
if (branch && branch !== 'main' && branch !== 'master') {
|
||||
@@ -467,7 +467,7 @@ function getLocalVersion() {
|
||||
if (semver.valid(tag) && semver.gt(tag, CUTOFF)) {
|
||||
return { label: tag, isUnreleased: false };
|
||||
}
|
||||
} catch (e) { }
|
||||
} catch { /* ignore git errors */ }
|
||||
|
||||
return { label: 'v4.11.0', isUnreleased: true };
|
||||
}
|
||||
|
||||
+2
-3
@@ -4,7 +4,6 @@ import { writeFileSync, mkdirSync, readdirSync, lstatSync, readFileSync, existsS
|
||||
import path, { join, dirname, basename } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { glob } from 'glob';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
// Suppress "Generated: ..." logs to avoid Vercel log limits
|
||||
const originalLog = console.log;
|
||||
@@ -147,7 +146,7 @@ async function fixAllGeneratedLinks() {
|
||||
const versionMap = fileIndex.get(version);
|
||||
|
||||
// [Text](apis/resources/service_name/file_name.api.mdx)
|
||||
const linkRegex = /\[([^\]]+)\]\(([\/]?apis\/resources\/([^\/]+)\/([^\/)]+)\.api\.mdx)\)/g;
|
||||
const linkRegex = /\[([^\]]+)\]\(([/]?apis\/resources\/([^/]+)\/([^/)]+)\.api\.mdx)\)/g;
|
||||
|
||||
let modified = false;
|
||||
let newContent = content.replace(linkRegex, (match, text, fullLink, serviceSlug, fileSlug) => {
|
||||
@@ -178,7 +177,7 @@ async function fixAllGeneratedLinks() {
|
||||
|
||||
// Fix v2beta links that were likely in the source proto comments
|
||||
// and also remove potential double /docs prefixing from source comments
|
||||
const internalLinkRegex = /\[([^\]]+)\]\(([\/]?docs\/)?reference\/api\/([^\/]+)\/([^\s)]+)\)/g;
|
||||
const internalLinkRegex = /\[([^\]]+)\]\(([/]?docs\/)?reference\/api\/([^/]+)\/([^\s)]+)\)/g;
|
||||
newContent = newContent.replace(internalLinkRegex, (match, text, docsPrefix, service, fileSlug) => {
|
||||
let targetFileSlug = fileSlug;
|
||||
let isV2Beta = fileSlug.includes('.v2beta.');
|
||||
@@ -87,6 +87,7 @@ async function run() {
|
||||
|
||||
|
||||
// Use spawn (async) instead of spawnSync to avoid blocking the event loop
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
await new Promise(async (resolvePromise, rejectPromise) => {
|
||||
// Dynamic discovery of excluded paths
|
||||
const getExcludedPaths = async () => {
|
||||
|
||||
@@ -43,10 +43,6 @@ walk(CONTENT_DIR, (filepath) => {
|
||||
// Replaces /docs/img/ -> correctPrefix + img/
|
||||
// Replaces /docs/vX.Y/ -> correctPrefix + vX.Y/
|
||||
|
||||
const PUBLIC_PREFIX = '/docs/img/';
|
||||
const VERSION_PREFIXES = ['/docs/v4.10/', '/docs/v4.9/', '/docs/v4.8/'];
|
||||
const TARGET_PATHS = [PUBLIC_PREFIX, ...VERSION_PREFIXES];
|
||||
|
||||
// Pattern: (!\[.*?\]\(|src=["'])(/docs/(?:img|v\d+\.\d+)/.*?)(\)|["'])
|
||||
const pattern = /(!\[.*?\]\(|src=["'])\/docs\/((?:img|v\d+\.\d+)\/.*?)(\)|["'])/g;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const REDIRECTS_PATH = '/home/ffo/git/zitadel/zitadel/apps/docs/redirects.json';
|
||||
|
||||
@@ -37,7 +36,6 @@ function validateRedirects() {
|
||||
});
|
||||
|
||||
// 2. Circular & Chains
|
||||
const checked = new Set();
|
||||
sources.forEach((value, source) => {
|
||||
let currentSource = source;
|
||||
const path = [currentSource];
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
module.exports = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
extends: ["next", "prettier"],
|
||||
plugins: ["@typescript-eslint", "import"],
|
||||
rules: {
|
||||
"import/first": "error",
|
||||
"@next/next/no-html-link-for-pages": "off",
|
||||
"@next/next/no-img-element": "off",
|
||||
"react/no-unescaped-entities": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error", {
|
||||
argsIgnorePattern: "^_" ,
|
||||
varsIgnorePattern: "^_" ,
|
||||
}],
|
||||
"no-undef": "off",
|
||||
"no-restricted-imports": ["error", {
|
||||
"paths": [{
|
||||
"name": "next/image",
|
||||
"message": "Use of next/image is forbidden. Use regular <img> elements instead."
|
||||
}]
|
||||
}],
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
// Server components and server actions: must use logger, not console.*
|
||||
// Files in src/lib/server/*, src/app/**/route.ts, src/middleware.ts, and files with "use server"
|
||||
files: [
|
||||
"src/lib/server/**/*.ts",
|
||||
"src/app/**/route.ts",
|
||||
"src/middleware.ts",
|
||||
"src/lib/cookies.ts",
|
||||
],
|
||||
rules: {
|
||||
"no-console": "warn",
|
||||
},
|
||||
},
|
||||
{
|
||||
// instrumentation.ts uses console.log during OTEL initialization (before logger is available)
|
||||
files: ["src/instrumentation.ts"],
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
// Client components: logger import is forbidden (enforced by server-only at build time)
|
||||
// This override documents the expectation for client components
|
||||
files: ["src/components/**/*.tsx"],
|
||||
rules: {
|
||||
"no-restricted-imports": ["error", {
|
||||
"paths": [
|
||||
{
|
||||
"name": "next/image",
|
||||
"message": "Use of next/image is forbidden. Use regular <img> elements instead."
|
||||
},
|
||||
{
|
||||
"name": "@/lib/logger",
|
||||
"message": "Cannot import logger in client components. Use console.* instead. The logger uses 'server-only' and will fail at build time."
|
||||
}
|
||||
]
|
||||
}],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test files: allow console.*
|
||||
files: ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/tests/**/*"],
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
// Use basic ESLint config since the login app has its own detailed config
|
||||
extends: ["eslint:recommended"],
|
||||
settings: {
|
||||
next: {
|
||||
rootDir: ["apps/*/"],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import js from "@eslint/js";
|
||||
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import prettierConfig from "eslint-config-prettier/flat";
|
||||
|
||||
export default [
|
||||
{ ignores: ["node_modules/**"] },
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tsPlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
rules: {
|
||||
...tsPlugin.configs.recommended.rules,
|
||||
"no-unused-vars": "off",
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ argsIgnorePattern: "^_" },
|
||||
],
|
||||
},
|
||||
},
|
||||
prettierConfig,
|
||||
];
|
||||
@@ -0,0 +1,149 @@
|
||||
import js from "@eslint/js";
|
||||
import nextPlugin from "@next/eslint-plugin-next";
|
||||
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import prettierConfig from "eslint-config-prettier/flat";
|
||||
import importPlugin from "eslint-plugin-import";
|
||||
import reactHooksPlugin from "eslint-plugin-react-hooks";
|
||||
import globals from "globals";
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"node_modules/**",
|
||||
".next/**",
|
||||
"dockerized/**",
|
||||
"cypress/**",
|
||||
"acceptance/**",
|
||||
"integration/**",
|
||||
"vitest.config*.ts",
|
||||
"next-env.d.ts",
|
||||
],
|
||||
},
|
||||
js.configs.recommended,
|
||||
nextPlugin.configs["core-web-vitals"],
|
||||
{
|
||||
files: ["**/*.{ts,tsx,jsx}"],
|
||||
plugins: {
|
||||
"react-hooks": reactHooksPlugin,
|
||||
},
|
||||
rules: {
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.{js,mjs,cjs}"],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.{ts,tsx}"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tsPlugin,
|
||||
import: importPlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
parserOptions: {
|
||||
ecmaFeatures: { jsx: true },
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...tsPlugin.configs.recommended.rules,
|
||||
...importPlugin.configs.recommended.rules,
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@next/next/no-img-element": "off",
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: "next/image",
|
||||
message:
|
||||
"Use of next/image is forbidden. Use regular <img> elements instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Server components and server actions: must use logger, not console.*
|
||||
{
|
||||
files: [
|
||||
"src/lib/server/**/*.ts",
|
||||
"src/app/**/route.ts",
|
||||
"src/middleware.ts",
|
||||
"src/lib/cookies.ts",
|
||||
],
|
||||
rules: {
|
||||
"no-console": "warn",
|
||||
},
|
||||
},
|
||||
// instrumentation.ts uses console.log during OTEL initialization (before logger is available)
|
||||
{
|
||||
files: ["src/instrumentation.ts"],
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
},
|
||||
// Client components: logger import is forbidden (enforced by server-only at build time)
|
||||
{
|
||||
files: ["src/components/**/*.tsx"],
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: "next/image",
|
||||
message:
|
||||
"Use of next/image is forbidden. Use regular <img> elements instead.",
|
||||
},
|
||||
{
|
||||
name: "@/lib/logger",
|
||||
message:
|
||||
"Cannot import logger in client components. Use console.* instead. The logger uses 'server-only' and will fail at build time.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Test files: allow console.*
|
||||
{
|
||||
files: [
|
||||
"**/*.test.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/tests/**/*",
|
||||
],
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
},
|
||||
prettierConfig,
|
||||
];
|
||||
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
// Use basic ESLint config since the login app has its own detailed config
|
||||
extends: ["eslint:recommended"],
|
||||
settings: {
|
||||
next: {
|
||||
rootDir: ["apps/*/"],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import js from "@eslint/js";
|
||||
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import prettierConfig from "eslint-config-prettier/flat";
|
||||
import pluginCypress from "eslint-plugin-cypress";
|
||||
|
||||
export default [
|
||||
{ ignores: ["node_modules/**"] },
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tsPlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
rules: {
|
||||
...tsPlugin.configs.recommended.rules,
|
||||
"no-unused-vars": "off",
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ argsIgnorePattern: "^_" },
|
||||
],
|
||||
},
|
||||
},
|
||||
pluginCypress.configs.recommended,
|
||||
prettierConfig,
|
||||
];
|
||||
+12
-8
@@ -8,11 +8,11 @@
|
||||
"build-vercel": "next build",
|
||||
"dev": "HOSTNAME=127.0.0.1 ./scripts/entrypoint.sh next dev",
|
||||
"prod": "cd ./.next/standalone && HOSTNAME=127.0.0.1 ./entrypoint.sh node apps/login/server.js",
|
||||
"lint-check-next": "next lint",
|
||||
"lint-check-next": "eslint .",
|
||||
"lint-check-prettier": "prettier --check .",
|
||||
"lint-fix": "prettier --write .",
|
||||
"test-unit": "vitest --run",
|
||||
"test-dockerized": "vitest --run --config vitest.config.dockerized.mts",
|
||||
"test-dockerized": "vitest --run --config vitest.config.dockerized.ts",
|
||||
"lint-staged": "lint-staged",
|
||||
"clean": "rm -rf node_modules .next cypress"
|
||||
},
|
||||
@@ -55,8 +55,9 @@
|
||||
"winston": "^3.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.28.6",
|
||||
"@eslint/js": "^10.0.0",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@next/eslint-plugin-next": "16.1.6",
|
||||
"@opentelemetry/sdk-trace-base": "^2.5.0",
|
||||
"@opentelemetry/sdk-trace-node": "^2.5.0",
|
||||
"@otplib/core": "^12.0.1",
|
||||
@@ -72,8 +73,8 @@
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
||||
"@typescript-eslint/parser": "^8.57.0",
|
||||
"@vercel/git-hooks": "1.0.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@zitadel/client": "workspace:*",
|
||||
@@ -84,11 +85,14 @@
|
||||
"dotenv-cli": "^8.0.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"esbuild": "^0.27.3",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-next": "15.5.9",
|
||||
"eslint-config-prettier": "^9.1.2",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-cypress": "^4.2.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"gaxios": "^7.1.3",
|
||||
"globals": "^17.0.0",
|
||||
"grpc-tools": "^1.13.1",
|
||||
"jsdom": "^26.1.0",
|
||||
"lint-staged": "15.5.1",
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"commands": [
|
||||
"pnpm vitest --run --config vitest.config.dockerized.mts"
|
||||
"pnpm vitest --run --config vitest.config.dockerized.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -149,7 +149,7 @@
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"commands": [
|
||||
"pnpm vitest --run --config vitest.config.dockerized.mts dockerized/ssl"
|
||||
"pnpm vitest --run --config vitest.config.dockerized.ts dockerized/ssl"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,30 +13,21 @@ interface AvatarProps {
|
||||
}
|
||||
|
||||
export function getInitials(name: string, loginName: string) {
|
||||
let credentials = "";
|
||||
if (name) {
|
||||
const split = name.split(" ");
|
||||
if (split) {
|
||||
const initials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
credentials = initials;
|
||||
} else {
|
||||
credentials = name.charAt(0);
|
||||
}
|
||||
} else {
|
||||
const username = loginName.split("@")[0];
|
||||
let separator = "_";
|
||||
if (username.includes("-")) {
|
||||
separator = "-";
|
||||
}
|
||||
if (username.includes(".")) {
|
||||
separator = ".";
|
||||
}
|
||||
const split = username.split(separator);
|
||||
const initials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
credentials = initials;
|
||||
return split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
}
|
||||
|
||||
return credentials;
|
||||
const username = loginName.split("@")[0];
|
||||
let separator = "_";
|
||||
if (username.includes("-")) {
|
||||
separator = "-";
|
||||
}
|
||||
if (username.includes(".")) {
|
||||
separator = ".";
|
||||
}
|
||||
const split = username.split(separator);
|
||||
return split[0].charAt(0) + (split[1] ? split[1].charAt(0) : "");
|
||||
}
|
||||
|
||||
// Helper function to get avatar roundness from theme
|
||||
|
||||
@@ -65,7 +65,6 @@ function getDefaultButtonAppearance(): string {
|
||||
return appearance?.button || "border border-button-light-border dark:border-button-dark-border"; // Fallback to flat design
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ function getDefaultCardStyling(): string {
|
||||
return appearance?.card || "shadow-sm border-0"; // Fallback to material design
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const Card = forwardRef<HTMLDivElement, CardProps>(
|
||||
(
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(function Che
|
||||
checked={enabled}
|
||||
onChange={(event) => {
|
||||
setEnabled(event.target?.checked);
|
||||
onChangeVal && onChangeVal(event.target?.checked);
|
||||
if (onChangeVal) onChangeVal(event.target?.checked);
|
||||
}}
|
||||
disabled={disabled}
|
||||
type="checkbox"
|
||||
|
||||
@@ -17,7 +17,7 @@ export function CopyToClipboard({ value }: Props) {
|
||||
const to = setTimeout(setCopied, 1000, false);
|
||||
return () => clearTimeout(to);
|
||||
}
|
||||
}, [copied]);
|
||||
}, [copied, value]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center px-2">
|
||||
|
||||
@@ -91,7 +91,7 @@ export function IdpProcessHandler({
|
||||
setError(err instanceof Error ? err.message : t("processing.unexpectedError"));
|
||||
setLoading(false);
|
||||
});
|
||||
}, [provider, id, token, requestId, organization, link, sessionId, postErrorRedirectUrl, router]);
|
||||
}, [provider, id, token, requestId, organization, link, sessionId, linkFingerprint, postErrorRedirectUrl, router, t]);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
|
||||
@@ -37,7 +37,6 @@ function getDefaultInputRoundness(): string {
|
||||
return getComponentRoundness("input");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
(
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Button, ButtonVariants } from "./button";
|
||||
import { Spinner } from "./spinner";
|
||||
import { Translated } from "./translated";
|
||||
|
||||
type Inputs = {};
|
||||
type Inputs = Record<string, never>;
|
||||
|
||||
type Props = {
|
||||
sessionId?: string;
|
||||
@@ -44,6 +44,35 @@ export function RegisterPasskey({
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const continueAndLogin = useCallback(
|
||||
(loginName?: string) => {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
if (requestId) {
|
||||
params.set("requestId", requestId);
|
||||
}
|
||||
|
||||
if (sessionId) {
|
||||
params.set("sessionId", sessionId);
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
params.set("userId", userId);
|
||||
}
|
||||
|
||||
if (loginName ?? initialLoginName) {
|
||||
params.set("loginName", loginName ?? initialLoginName ?? "");
|
||||
}
|
||||
|
||||
router.push("/passkey?" + params);
|
||||
},
|
||||
[organization, requestId, sessionId, userId, initialLoginName, router],
|
||||
);
|
||||
|
||||
async function submitVerify(
|
||||
passkeyId: string,
|
||||
passkeyName: string,
|
||||
@@ -166,7 +195,7 @@ export function RegisterPasskey({
|
||||
}
|
||||
|
||||
continueAndLogin(verificationResponse.loginName);
|
||||
}, [sessionId, userId, code]);
|
||||
}, [sessionId, userId, code, codeId, continueAndLogin]);
|
||||
|
||||
// Auto-submit when code is provided (similar to VerifyForm)
|
||||
useEffect(() => {
|
||||
@@ -175,32 +204,6 @@ export function RegisterPasskey({
|
||||
}
|
||||
}, [code, submitRegisterAndContinue]);
|
||||
|
||||
function continueAndLogin(loginName?: string) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
if (requestId) {
|
||||
params.set("requestId", requestId);
|
||||
}
|
||||
|
||||
if (sessionId) {
|
||||
params.set("sessionId", sessionId);
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
params.set("userId", userId);
|
||||
}
|
||||
|
||||
if (loginName ?? initialLoginName) {
|
||||
params.set("loginName", loginName ?? initialLoginName ?? "");
|
||||
}
|
||||
|
||||
router.push("/passkey?" + params);
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="w-full">
|
||||
{error && (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { clearSession } from "@/lib/server/session";
|
||||
import { timestampDate } from "@zitadel/client";
|
||||
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||
import { redirect, useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Alert, AlertType } from "./alert";
|
||||
import { SessionClearItem } from "./session-clear-item";
|
||||
import { Translated } from "./translated";
|
||||
@@ -20,7 +20,7 @@ export function SessionsClearList({ sessions, logoutHint, postLogoutRedirectUri,
|
||||
const [list, setList] = useState<Session[]>(sessions);
|
||||
const router = useRouter();
|
||||
|
||||
async function clearHintedSession() {
|
||||
const clearHintedSession = useCallback(async () => {
|
||||
console.log("Clearing session for login hint:", logoutHint);
|
||||
// If a login hint is provided, we logout that specific session
|
||||
const sessionIdToBeCleared = sessions.find((session) => {
|
||||
@@ -53,13 +53,13 @@ export function SessionsClearList({ sessions, logoutHint, postLogoutRedirectUri,
|
||||
} else {
|
||||
console.warn(`No session found for login hint: ${logoutHint}`);
|
||||
}
|
||||
}
|
||||
}, [logoutHint, sessions, postLogoutRedirectUri, organization, router]);
|
||||
|
||||
useEffect(() => {
|
||||
if (logoutHint) {
|
||||
clearHintedSession();
|
||||
}
|
||||
}, []);
|
||||
}, [logoutHint, clearHintedSession]);
|
||||
|
||||
return sessions ? (
|
||||
<div className="flex flex-col space-y-2">
|
||||
|
||||
@@ -5,7 +5,7 @@ import { sendLoginname } from "@/lib/server/loginname";
|
||||
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Alert } from "./alert";
|
||||
import { AutoSubmitForm } from "./auto-submit-form";
|
||||
@@ -55,34 +55,37 @@ export function UsernameForm({
|
||||
const [error, setError] = useState<string>("");
|
||||
const [samlData, setSamlData] = useState<{ url: string; fields: Record<string, string> } | null>(null);
|
||||
|
||||
async function submitLoginName(values: Inputs, organization?: string) {
|
||||
setLoading(true);
|
||||
const submitLoginName = useCallback(
|
||||
async (values: Inputs, organization?: string) => {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const res = await sendLoginname({
|
||||
loginName: values.loginName,
|
||||
organization,
|
||||
defaultOrganization,
|
||||
requestId,
|
||||
suffix,
|
||||
ignoreUnknownUsernames: loginSettings?.ignoreUnknownUsernames,
|
||||
});
|
||||
try {
|
||||
const res = await sendLoginname({
|
||||
loginName: values.loginName,
|
||||
organization,
|
||||
defaultOrganization,
|
||||
requestId,
|
||||
suffix,
|
||||
ignoreUnknownUsernames: loginSettings?.ignoreUnknownUsernames,
|
||||
});
|
||||
|
||||
handleServerActionResponse(res, router, setSamlData, setError);
|
||||
return res;
|
||||
} catch {
|
||||
setError(t("errors.internalError"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
handleServerActionResponse(res, router, setSamlData, setError);
|
||||
return res;
|
||||
} catch {
|
||||
setError(t("errors.internalError"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[defaultOrganization, requestId, suffix, loginSettings, router, t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (submit && loginName) {
|
||||
// When we navigate to this page, we always want to be redirected if submit is true and the parameters are valid.
|
||||
submitLoginName({ loginName }, organization);
|
||||
}
|
||||
}, []);
|
||||
}, [submit, loginName, organization, submitLoginName]);
|
||||
|
||||
let inputLabel = t("labels.loginname");
|
||||
if (loginSettings?.disableLoginWithEmail && loginSettings?.disableLoginWithPhone) {
|
||||
|
||||
@@ -101,7 +101,7 @@ export function VerifyForm({ userId, loginName, organization, requestId, code, i
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[isInvite, userId],
|
||||
[isInvite, userId, loginName, organization, requestId, router, t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -341,58 +341,39 @@ export function hashCode(str: string, seed = 0): number {
|
||||
|
||||
export function getMembershipColor(role: string): ColorShade {
|
||||
const hash = hashCode(role);
|
||||
let color = COLORS[hash % COLORS.length];
|
||||
|
||||
switch (role) {
|
||||
case "IAM_OWNER":
|
||||
color = COLORS[0];
|
||||
break;
|
||||
return COLORS[0];
|
||||
case "IAM_OWNER_VIEWER":
|
||||
color = COLORS[14];
|
||||
break;
|
||||
return COLORS[14];
|
||||
case "IAM_ORG_MANAGER":
|
||||
color = COLORS[11];
|
||||
break;
|
||||
return COLORS[11];
|
||||
case "IAM_USER_MANAGER":
|
||||
color = COLORS[8];
|
||||
break;
|
||||
return COLORS[8];
|
||||
|
||||
case "ORG_OWNER":
|
||||
color = COLORS[16];
|
||||
break;
|
||||
return COLORS[16];
|
||||
case "ORG_USER_MANAGER":
|
||||
color = COLORS[8];
|
||||
break;
|
||||
return COLORS[8];
|
||||
case "ORG_OWNER_VIEWER":
|
||||
color = COLORS[14];
|
||||
break;
|
||||
return COLORS[14];
|
||||
case "ORG_USER_PERMISSION_EDITOR":
|
||||
color = COLORS[7];
|
||||
break;
|
||||
return COLORS[7];
|
||||
case "ORG_PROJECT_PERMISSION_EDITOR":
|
||||
color = COLORS[11];
|
||||
break;
|
||||
return COLORS[11];
|
||||
case "ORG_PROJECT_CREATOR":
|
||||
color = COLORS[12];
|
||||
break;
|
||||
return COLORS[12];
|
||||
|
||||
case "PROJECT_OWNER":
|
||||
color = COLORS[9];
|
||||
break;
|
||||
return COLORS[9];
|
||||
case "PROJECT_OWNER_VIEWER":
|
||||
color = COLORS[10];
|
||||
break;
|
||||
return COLORS[10];
|
||||
case "PROJECT_OWNER_GLOBAL":
|
||||
color = COLORS[11];
|
||||
break;
|
||||
return COLORS[11];
|
||||
case "PROJECT_OWNER_VIEWER_GLOBAL":
|
||||
color = COLORS[12];
|
||||
break;
|
||||
return COLORS[12];
|
||||
|
||||
default:
|
||||
color = COLORS[hash % COLORS.length];
|
||||
break;
|
||||
return COLORS[hash % COLORS.length];
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export default getRequestConfig(async () => {
|
||||
|
||||
const i18nOrganization = _headers.get("x-zitadel-i18n-organization") || ""; // You may need to set this header in middleware
|
||||
|
||||
let translations: JsonObject | {} = {};
|
||||
let translations: JsonObject | Record<string, never> = {};
|
||||
try {
|
||||
const i18nJSON = await getHostedLoginTranslation({
|
||||
serviceConfig,
|
||||
|
||||
@@ -46,6 +46,8 @@ export async function loginServiceKeyToken() {
|
||||
key: key,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to read login service key file "${keyFile}": ${err instanceof Error ? err.message : err}`);
|
||||
throw new Error(`Failed to read login service key file "${keyFile}": ${err instanceof Error ? err.message : err}`, {
|
||||
cause: err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,24 +374,26 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
};
|
||||
}
|
||||
|
||||
const paramsPassword = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
{
|
||||
const paramsPassword = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
|
||||
if (organization) {
|
||||
paramsPassword.append("organization", organization);
|
||||
if (organization) {
|
||||
paramsPassword.append("organization", organization);
|
||||
}
|
||||
|
||||
if (command.requestId) {
|
||||
paramsPassword.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: "/password?" + paramsPassword,
|
||||
};
|
||||
}
|
||||
|
||||
if (command.requestId) {
|
||||
paramsPassword.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: "/password?" + paramsPassword,
|
||||
};
|
||||
|
||||
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
|
||||
if (userLoginSettings?.passkeysType === PasskeysType.NOT_ALLOWED || !userLoginSettings?.allowLocalAuthentication) {
|
||||
if (command.ignoreUnknownUsernames) {
|
||||
@@ -402,22 +404,24 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
};
|
||||
}
|
||||
|
||||
const paramsPasskey = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
if (command.requestId) {
|
||||
paramsPasskey.append("requestId", command.requestId);
|
||||
{
|
||||
const paramsPasskey = new URLSearchParams({
|
||||
loginName: command.ignoreUnknownUsernames
|
||||
? command.loginName
|
||||
: (session?.factors?.user?.loginName ?? user.preferredLoginName),
|
||||
});
|
||||
if (command.requestId) {
|
||||
paramsPasskey.append("requestId", command.requestId);
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
paramsPasskey.append("organization", organization);
|
||||
}
|
||||
|
||||
return { redirect: "/passkey?" + paramsPasskey };
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
paramsPasskey.append("organization", organization);
|
||||
}
|
||||
|
||||
return { redirect: "/passkey?" + paramsPasskey };
|
||||
|
||||
case AuthenticationMethodType.IDP:
|
||||
case AuthenticationMethodType.IDP: {
|
||||
const resp = await redirectUserToIDP(userId, organization);
|
||||
|
||||
if (resp?.error) {
|
||||
@@ -425,6 +429,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// prefer passkey in favor of other methods
|
||||
|
||||
@@ -317,7 +317,6 @@ describe("sendPassword", () => {
|
||||
const { completeFlowOrGetUrl } = await import("../client");
|
||||
vi.mocked(completeFlowOrGetUrl).mockResolvedValue({ redirect: "https://example.com" });
|
||||
|
||||
// eslint-disable-next-line
|
||||
const verifyHelper = await import("../verify-helper");
|
||||
vi.mocked(verifyHelper.checkPasswordChangeRequired).mockReturnValue(undefined);
|
||||
vi.mocked(verifyHelper.checkEmailVerification).mockReturnValue(undefined);
|
||||
|
||||
@@ -293,7 +293,7 @@ export async function sendPassword(
|
||||
return {
|
||||
error: t(messageKey, {
|
||||
failedAttempts: error.failedAttempts,
|
||||
maxPasswordAttempts: hasLimit ? (lockoutSettings?.maxPasswordAttempts).toString() : "?",
|
||||
maxPasswordAttempts: hasLimit ? String(lockoutSettings?.maxPasswordAttempts ?? 0) : "?",
|
||||
lockoutMessage: locked ? t("errors.accountLockedContactAdmin") : "",
|
||||
}),
|
||||
};
|
||||
@@ -565,7 +565,7 @@ export async function checkSessionAndSetPassword({
|
||||
return {
|
||||
error: t(messageKey, {
|
||||
failedAttempts: error.failedAttempts,
|
||||
maxPasswordAttempts: hasLimit ? (lockoutSettings?.maxPasswordAttempts).toString() : "?",
|
||||
maxPasswordAttempts: hasLimit ? String(lockoutSettings?.maxPasswordAttempts ?? 0) : "?",
|
||||
lockoutMessage: locked ? t("errors.accountLockedContactAdmin") : "",
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -164,6 +164,7 @@ export async function checkMFAFactors(
|
||||
|
||||
// TODO: provide a way to setup passkeys on mfa page?
|
||||
return { redirect: `/mfa/set?` + params };
|
||||
/* eslint-disable no-dupe-else-if -- TODO: this branch is unreachable, conditions overlap with the previous branch */
|
||||
} else if (
|
||||
loginSettings?.mfaInitSkipLifetime &&
|
||||
(loginSettings.mfaInitSkipLifetime.nanos > 0 || loginSettings.mfaInitSkipLifetime.seconds > 0) &&
|
||||
@@ -171,6 +172,7 @@ export async function checkMFAFactors(
|
||||
session?.factors?.user?.id &&
|
||||
shouldEnforceMFA(session, loginSettings)
|
||||
) {
|
||||
/* eslint-enable no-dupe-else-if */
|
||||
const userResponse = await getUserByID({ serviceConfig, userId: session.factors?.user?.id });
|
||||
|
||||
const humanUser = userResponse?.user?.type.case === "human" ? userResponse?.user.type.value : undefined;
|
||||
|
||||
@@ -1237,20 +1237,18 @@ export interface ServiceConfig {
|
||||
/**
|
||||
* Base type that all function parameters must extend to ensure serviceConfig is always required
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export type WithServiceConfig<T = {}> = T & {
|
||||
serviceConfig: ServiceConfig;
|
||||
};
|
||||
|
||||
export function createServerTransport(token: string, serviceConfig: ServiceConfig) {
|
||||
// Build interceptors list - OTEL interceptor is always included for trace propagation
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const interceptors: any[] = [otelGrpcInterceptor];
|
||||
|
||||
// Add custom headers interceptor if needed
|
||||
if (process.env.CUSTOM_REQUEST_HEADERS || serviceConfig.instanceHost || serviceConfig.publicHost) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
interceptors.push((next: any) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (req: any) => {
|
||||
// Apply headers from serviceConfig
|
||||
if (serviceConfig.instanceHost) {
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"node_modules",
|
||||
"integration",
|
||||
"acceptance",
|
||||
"dockerized"
|
||||
"dockerized",
|
||||
"vitest.config*.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
ignorePatterns: ['projects/**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts'],
|
||||
parserOptions: {
|
||||
project: ['tsconfig.json', 'e2e/tsconfig.json'],
|
||||
createDefaultProgram: true,
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
extends: ['plugin:@angular-eslint/recommended', 'plugin:@angular-eslint/template/process-inline-templates'],
|
||||
rules: {
|
||||
'@angular-eslint/no-conflicting-lifecycle': 'off',
|
||||
'@angular-eslint/no-host-metadata-property': 'off',
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
prefix: 'cnsl',
|
||||
style: 'kebab-case',
|
||||
type: 'element',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
prefix: 'cnsl',
|
||||
style: 'camelCase',
|
||||
type: 'attribute',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.html'],
|
||||
extends: ['plugin:@angular-eslint/template/recommended'],
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
import js from "@eslint/js";
|
||||
import angularPlugin from "@angular-eslint/eslint-plugin";
|
||||
import angularTemplatePlugin from "@angular-eslint/eslint-plugin-template";
|
||||
import angularTemplateParser from "@angular-eslint/template-parser";
|
||||
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import importPlugin from "eslint-plugin-import";
|
||||
import prettierConfig from "eslint-config-prettier/flat";
|
||||
import globals from "globals";
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"projects/**/*",
|
||||
"dist/**",
|
||||
"node_modules/**",
|
||||
".angular/**",
|
||||
"src/app/proto/generated/**",
|
||||
],
|
||||
},
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ["**/*.{js,mjs,cjs}"],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tsPlugin,
|
||||
"@angular-eslint": angularPlugin,
|
||||
import: importPlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
parserOptions: {
|
||||
project: ["tsconfig.json", "e2e/tsconfig.json"],
|
||||
createDefaultProgram: true,
|
||||
},
|
||||
},
|
||||
processor: angularTemplatePlugin.processors["extract-inline-html"],
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...tsPlugin.configs.recommended.rules,
|
||||
...importPlugin.configs.recommended.rules,
|
||||
// Proto imports are generated at build time and don't exist during lint
|
||||
"import/no-unresolved": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{ argsIgnorePattern: "^_" },
|
||||
],
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-require-imports": "off",
|
||||
"@typescript-eslint/no-unsafe-function-type": "off",
|
||||
"@typescript-eslint/no-wrapper-object-types": "off",
|
||||
"@typescript-eslint/no-empty-object-type": "off",
|
||||
"no-case-declarations": "off",
|
||||
...angularPlugin.configs.recommended.rules,
|
||||
// Disable rules added in angular-eslint 21.x that weren't in the original 18.x config
|
||||
"@angular-eslint/prefer-inject": "off",
|
||||
"@angular-eslint/prefer-standalone": "off",
|
||||
"@angular-eslint/no-conflicting-lifecycle": "off",
|
||||
"@angular-eslint/no-host-metadata-property": "off",
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
prefix: "cnsl",
|
||||
style: "kebab-case",
|
||||
type: "element",
|
||||
},
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
prefix: "cnsl",
|
||||
style: "camelCase",
|
||||
type: "attribute",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.html"],
|
||||
plugins: {
|
||||
"@angular-eslint/template": angularTemplatePlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: angularTemplateParser,
|
||||
},
|
||||
rules: {
|
||||
...angularTemplatePlugin.configs.recommended.rules,
|
||||
// Disable rules added in angular-eslint 21.x that weren't in the original 18.x config
|
||||
"@angular-eslint/template/prefer-control-flow": "off",
|
||||
"@angular-eslint/template/eqeqeq": "off",
|
||||
},
|
||||
},
|
||||
prettierConfig,
|
||||
];
|
||||
+12
-9
@@ -5,7 +5,7 @@
|
||||
"ng": "ng",
|
||||
"dev": "wait-on --verbose --interval 2000 --simultaneous 1 --timeout 30m ${ENVIRONMENT_JSON_URL} && node prebuild.development.js && ng serve --host=${DEVCONTAINER_LISTEN_HOST:-127.0.0.1}",
|
||||
"build": "ng build --configuration production --base-href=/ui/console/",
|
||||
"lint-check-ng": "ng lint",
|
||||
"lint-check-ng": "eslint .",
|
||||
"lint-check-prettier": "prettier --check src",
|
||||
"lint-fix": "prettier --write src",
|
||||
"generate": "pnpm exec buf generate ../proto --include-imports --include-wkt",
|
||||
@@ -59,15 +59,14 @@
|
||||
"zone.js": "~0.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-eslint/builder": "18.3.0",
|
||||
"@angular-eslint/eslint-plugin": "18.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "18.0.0",
|
||||
"@angular-eslint/schematics": "19.8.1",
|
||||
"@angular-eslint/template-parser": "19.8.1",
|
||||
"@angular-eslint/eslint-plugin": "21.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "21.3.0",
|
||||
"@angular-eslint/template-parser": "21.3.0",
|
||||
"@angular/build": "^20.3.15",
|
||||
"@angular/cli": "^20.3.15",
|
||||
"@angular/compiler-cli": "^20.3.16",
|
||||
"@angular/language-service": "^20.3.16",
|
||||
"@eslint/js": "^10.0.0",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/google-protobuf": "~3.15.12",
|
||||
"@types/jasmine": "~5.1.15",
|
||||
@@ -77,11 +76,15 @@
|
||||
"@types/opentype.js": "^1.3.9",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
||||
"@typescript-eslint/parser": "^8.57.0",
|
||||
"@zitadel/client": "workspace:*",
|
||||
"@zitadel/proto": "workspace:*",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"globals": "^17.0.0",
|
||||
"jasmine-core": "~5.12.1",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"karma": "^6.4.4",
|
||||
|
||||
@@ -25,6 +25,8 @@ export class ScrollableDirective {
|
||||
if (top === 0) {
|
||||
this.scrollPosition.emit('top');
|
||||
}
|
||||
} catch (err) {}
|
||||
} catch {
|
||||
// scroll position detection may fail in some browsers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ export class DisplayJsonDialogComponent {
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.event = data.event;
|
||||
if ((data.event as Event) && data.event.payload) {
|
||||
}
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
|
||||
@@ -235,6 +235,7 @@ export class IdpTableComponent implements OnInit, OnDestroy {
|
||||
} else if (this.service instanceof ManagementService) {
|
||||
return ['/org', 'idp', 'create'];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public routerLinkForRow(row: Provider.AsObject): any {
|
||||
|
||||
@@ -248,6 +248,7 @@ export class InputDirective implements MatFormFieldControl<any>, OnChanges, OnDe
|
||||
this._previousNativeValue = this.value;
|
||||
|
||||
// Force setter to be called in case id was not specified.
|
||||
// eslint-disable-next-line no-self-assign
|
||||
this.id = this.id;
|
||||
|
||||
// On some versions of iOS the caret gets stuck in the wrong place when holding down the delete
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ export class DialogAddSMSProviderComponent {
|
||||
});
|
||||
|
||||
this.smsProviders = data.smsProviders;
|
||||
if (!!this.twilio) {
|
||||
if (this.twilio) {
|
||||
this.twilioForm.patchValue(this.twilio);
|
||||
} else {
|
||||
this.twilioForm.addControl('token', new FormControl('', requiredValidator));
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ export class NotificationSMSProviderComponent implements OnInit {
|
||||
|
||||
dialogRef.afterClosed().subscribe((req: AddSMSProviderTwilioRequest | UpdateSMSProviderTwilioRequest) => {
|
||||
if (req) {
|
||||
if (!!this.twilio) {
|
||||
if (this.twilio) {
|
||||
this.service
|
||||
.updateSMSProviderTwilio(req as UpdateSMSProviderTwilioRequest)
|
||||
.then(() => {
|
||||
|
||||
@@ -72,10 +72,9 @@ export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
|
||||
removeParam(key: string, sourceURL: string) {
|
||||
var rtn = sourceURL.split('?')[0],
|
||||
param,
|
||||
params_arr = [],
|
||||
queryString = sourceURL.indexOf('?') !== -1 ? sourceURL.split('?')[1] : '';
|
||||
if (queryString !== '') {
|
||||
params_arr = queryString.split('&');
|
||||
var params_arr = queryString.split('&');
|
||||
for (var i = params_arr.length - 1; i >= 0; i -= 1) {
|
||||
param = params_arr[i].split('=')[0];
|
||||
if (param === key) {
|
||||
|
||||
@@ -72,7 +72,7 @@ export function getColor(toGen: string): string {
|
||||
|
||||
export function getMembershipColor(role: string): Color {
|
||||
const hash = hashCode(role);
|
||||
let color = COLORS[hash % COLORS.length];
|
||||
let color: Color;
|
||||
|
||||
switch (role) {
|
||||
case 'IAM_OWNER':
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
env: {
|
||||
node: true,
|
||||
es2022: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: "module",
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }]
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import js from "@eslint/js";
|
||||
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import prettierConfig from "eslint-config-prettier/flat";
|
||||
import importPlugin from "eslint-plugin-import";
|
||||
import globals from "globals";
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: ["dist/**", "node_modules/**"],
|
||||
},
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
plugins: {
|
||||
"@typescript-eslint": tsPlugin,
|
||||
import: importPlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 2022,
|
||||
sourceType: "module",
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...tsPlugin.configs.recommended.rules,
|
||||
...importPlugin.configs.recommended.rules,
|
||||
"no-unused-vars": "off",
|
||||
"no-undef": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ argsIgnorePattern: "^_" },
|
||||
],
|
||||
},
|
||||
},
|
||||
prettierConfig,
|
||||
];
|
||||
@@ -65,7 +65,7 @@
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"lint-check-eslint": "eslint 'src/**/*.ts*'",
|
||||
"lint-check-eslint": "eslint .",
|
||||
"lint-check-knip": "knip",
|
||||
"test-unit": "vitest --run",
|
||||
"clean": "rm -rf node_modules dist"
|
||||
@@ -80,10 +80,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bufbuild/protocompile": "^0.0.1",
|
||||
"@eslint/js": "^10.0.0",
|
||||
"@types/node": "^24.10.9",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"eslint": "^8.57.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
||||
"@typescript-eslint/parser": "^8.57.0",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"globals": "^17.0.0",
|
||||
"knip": "^5.82.1",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.9.3",
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
},
|
||||
"lint-check-eslint": {
|
||||
"description": "Checks code formatting with ESLint",
|
||||
"dependsOn": ["^generate"],
|
||||
"inputs": [
|
||||
"default"
|
||||
],
|
||||
|
||||
Generated
+367
-1541
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user