Move to infisical. Create local dev environment. Add ci gates. Modernize repo
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+4
-5
@@ -11,12 +11,11 @@
|
||||
import type {
|
||||
DataModelFromSchemaDefinition,
|
||||
DocumentByName,
|
||||
SystemTableNames,
|
||||
TableNamesInDataModel,
|
||||
} from 'convex/server';
|
||||
import type { GenericId } from 'convex/values';
|
||||
|
||||
import schema from '../schema.js';
|
||||
SystemTableNames,
|
||||
} from "convex/server";
|
||||
import type { GenericId } from "convex/values";
|
||||
import schema from "../schema.js";
|
||||
|
||||
/**
|
||||
* The names of all of your Convex tables.
|
||||
|
||||
@@ -11,16 +11,16 @@ export default function UseSendProvider(config: EmailUserConfig): EmailConfig {
|
||||
from: process.env.USESEND_FROM_EMAIL ?? 'noreply@example.com',
|
||||
maxAge: 24 * 60 * 60, // 24 hours
|
||||
|
||||
generateVerificationToken() {
|
||||
generateVerificationToken: () => {
|
||||
const random: RandomReader = {
|
||||
read(bytes) {
|
||||
crypto.getRandomValues(bytes);
|
||||
read: (bytes) => {
|
||||
crypto.getRandomValues(bytes as Uint8Array<ArrayBuffer>);
|
||||
},
|
||||
};
|
||||
return generateRandomString(random, alphabet('0-9'), 6);
|
||||
},
|
||||
|
||||
async sendVerificationRequest(params) {
|
||||
sendVerificationRequest: async (params) => {
|
||||
const { identifier: to, provider, url, token } = params;
|
||||
// Derive a display name from the site URL, fallback to 'App'
|
||||
const siteUrl = process.env.USESEND_FROM_EMAIL ?? '';
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env --"
|
||||
"test:unit": "vitest run --project unit",
|
||||
"test:integration": "vitest run --project integration --passWithNoTests",
|
||||
"test:component": "vitest run --project component --passWithNoTests",
|
||||
"with-env": "sh ../../scripts/with-env ${INFISICAL_ENV:-dev} --"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oslojs/crypto": "^1.0.1",
|
||||
@@ -33,14 +36,18 @@
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edge-runtime/vm": "catalog:test",
|
||||
"@gib/eslint-config": "workspace:*",
|
||||
"@gib/prettier-config": "workspace:*",
|
||||
"@gib/tsconfig": "workspace:*",
|
||||
"@gib/vitest-config": "workspace:*",
|
||||
"@types/node": "catalog:",
|
||||
"convex-test": "catalog:test",
|
||||
"eslint": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"react-email": "5.2.10",
|
||||
"typescript": "catalog:"
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:test"
|
||||
},
|
||||
"prettier": "@gib/prettier-config"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { convexTest } from 'convex-test';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import schema from '../../convex/schema';
|
||||
|
||||
const modules = import.meta.glob('../../convex/**/*.*s');
|
||||
|
||||
describe('convex-test harness', () => {
|
||||
test('boots and executes against the project schema', async () => {
|
||||
const t = convexTest(schema, modules);
|
||||
expect(await t.run(() => Promise.resolve(42))).toBe(42);
|
||||
});
|
||||
});
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "@gib/tsconfig/base.json",
|
||||
"compilerOptions": { "lib": ["ES2022", "DOM"], "types": ["node"] },
|
||||
"include": ["tests", "vitest.config.ts"],
|
||||
"exclude": ["node_modules", "convex/_generated"]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
import { convexProject, nodeProject } from '@gib/vitest-config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
projects: [
|
||||
convexProject('unit', ['tests/unit/**/*.test.ts']),
|
||||
convexProject('integration', ['tests/integration/**/*.test.ts']),
|
||||
nodeProject('component', ['tests/component/**/*.test.{ts,tsx}']),
|
||||
],
|
||||
},
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,9 @@
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
||||
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
|
||||
"test:unit": "vitest run --project unit --passWithNoTests",
|
||||
"test:integration": "vitest run --project integration --passWithNoTests",
|
||||
"test:component": "NODE_ENV=test vitest run --project component",
|
||||
"ui-add": "bunx --bun shadcn@latest add && prettier src --write --list-different"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -53,11 +56,15 @@
|
||||
"@gib/eslint-config": "workspace:*",
|
||||
"@gib/prettier-config": "workspace:*",
|
||||
"@gib/tsconfig": "workspace:*",
|
||||
"@gib/vitest-config": "workspace:*",
|
||||
"@testing-library/react": "catalog:test",
|
||||
"@types/react": "catalog:react19",
|
||||
"eslint": "catalog:",
|
||||
"jsdom": "catalog:test",
|
||||
"prettier": "catalog:",
|
||||
"react": "catalog:react19",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:test",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -21,7 +21,7 @@ const BasedProgress = ({
|
||||
value = 0,
|
||||
...props
|
||||
}: BasedProgressProps) => {
|
||||
const [progress, setProgress] = React.useState<number>(value);
|
||||
const [progress, setProgress] = React.useState<number>(value ?? 0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const id = window.setInterval(() => {
|
||||
|
||||
+44
-19
@@ -105,6 +105,15 @@ ${colorConfig
|
||||
|
||||
const ChartTooltip = RechartsPrimitive.Tooltip;
|
||||
|
||||
type ChartPayloadItem = {
|
||||
name?: string | number;
|
||||
value?: number | string;
|
||||
dataKey?: string | number;
|
||||
type?: string;
|
||||
color?: string;
|
||||
payload?: Record<string, unknown> & { fill?: string };
|
||||
};
|
||||
|
||||
const ChartTooltipContent = ({
|
||||
active,
|
||||
payload,
|
||||
@@ -119,14 +128,29 @@ const ChartTooltipContent = ({
|
||||
color,
|
||||
nameKey,
|
||||
labelKey,
|
||||
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
||||
React.ComponentProps<'div'> & {
|
||||
hideLabel?: boolean;
|
||||
hideIndicator?: boolean;
|
||||
indicator?: 'line' | 'dot' | 'dashed';
|
||||
nameKey?: string;
|
||||
labelKey?: string;
|
||||
}) => {
|
||||
}: React.ComponentProps<'div'> & {
|
||||
active?: boolean;
|
||||
payload?: ChartPayloadItem[];
|
||||
label?: React.ReactNode;
|
||||
labelFormatter?: (
|
||||
value: React.ReactNode,
|
||||
payload: ChartPayloadItem[],
|
||||
) => React.ReactNode;
|
||||
formatter?: (
|
||||
value: number | string,
|
||||
name: string | number,
|
||||
item: ChartPayloadItem,
|
||||
index: number,
|
||||
itemPayload: ChartPayloadItem['payload'],
|
||||
) => React.ReactNode;
|
||||
color?: string;
|
||||
labelClassName?: string;
|
||||
hideLabel?: boolean;
|
||||
hideIndicator?: boolean;
|
||||
indicator?: 'line' | 'dot' | 'dashed';
|
||||
nameKey?: string;
|
||||
labelKey?: string;
|
||||
}) => {
|
||||
const { config } = useChart();
|
||||
|
||||
const tooltipLabel = React.useMemo(() => {
|
||||
@@ -139,7 +163,7 @@ const ChartTooltipContent = ({
|
||||
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
||||
const value =
|
||||
!labelKey && typeof label === 'string'
|
||||
? config[label]?.label ?? label
|
||||
? (config[label]?.label ?? label)
|
||||
: itemConfig?.label;
|
||||
|
||||
if (labelFormatter) {
|
||||
@@ -185,17 +209,17 @@ const ChartTooltipContent = ({
|
||||
.map((item, index) => {
|
||||
const key = `${nameKey ?? item.name ?? item.dataKey ?? 'value'}`;
|
||||
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
||||
const indicatorColor = color ?? item.payload.fill ?? item.color;
|
||||
const indicatorColor = color ?? item.payload?.fill ?? item.color;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.dataKey}
|
||||
key={item.dataKey ?? index}
|
||||
className={cn(
|
||||
'[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5',
|
||||
indicator === 'dot' && 'items-center',
|
||||
)}
|
||||
>
|
||||
{formatter && item?.value !== undefined && item.name ? (
|
||||
{formatter && item.value !== undefined && item.name ? (
|
||||
formatter(item.value, item.name, item, index, item.payload)
|
||||
) : (
|
||||
<>
|
||||
@@ -259,11 +283,12 @@ const ChartLegendContent = ({
|
||||
payload,
|
||||
verticalAlign = 'bottom',
|
||||
nameKey,
|
||||
}: React.ComponentProps<'div'> &
|
||||
Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
|
||||
hideIcon?: boolean;
|
||||
nameKey?: string;
|
||||
}) => {
|
||||
}: React.ComponentProps<'div'> & {
|
||||
payload?: ChartPayloadItem[];
|
||||
verticalAlign?: 'top' | 'bottom';
|
||||
hideIcon?: boolean;
|
||||
nameKey?: string;
|
||||
}) => {
|
||||
const { config } = useChart();
|
||||
|
||||
if (!payload?.length) {
|
||||
@@ -280,13 +305,13 @@ const ChartLegendContent = ({
|
||||
>
|
||||
{payload
|
||||
.filter((item) => item.type !== 'none')
|
||||
.map((item) => {
|
||||
.map((item, index) => {
|
||||
const key = `${nameKey ?? item.dataKey ?? 'value'}`;
|
||||
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.value}
|
||||
key={item.value ?? index}
|
||||
className={cn(
|
||||
'[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3',
|
||||
)}
|
||||
|
||||
@@ -68,13 +68,16 @@ const ComboboxInput = ({
|
||||
/>
|
||||
<InputGroupAddon align='inline-end'>
|
||||
{showTrigger && (
|
||||
<InputGroupButton
|
||||
size='icon-xs'
|
||||
variant='ghost'
|
||||
render={<ComboboxTrigger />}
|
||||
data-slot='input-group-button'
|
||||
className='group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent'
|
||||
disabled={disabled}
|
||||
<ComboboxTrigger
|
||||
render={
|
||||
<InputGroupButton
|
||||
size='icon-xs'
|
||||
variant='ghost'
|
||||
data-slot='input-group-button'
|
||||
className='group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent'
|
||||
disabled={disabled}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{showClear && <ComboboxClear disabled={disabled} />}
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
declare module '*.css';
|
||||
@@ -294,19 +294,17 @@ export const ImageCropApply = ({
|
||||
|
||||
if (asChild) {
|
||||
return (
|
||||
<Slot.Root onClick={handleClick} {...(props as any)}>
|
||||
<Slot.Root
|
||||
onClick={handleClick}
|
||||
{...(props as ComponentProps<typeof Slot.Root>)}
|
||||
>
|
||||
{children}
|
||||
</Slot.Root>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
size='icon'
|
||||
variant='ghost'
|
||||
{...(props as any)}
|
||||
>
|
||||
<Button onClick={handleClick} size='icon' variant='ghost' {...props}>
|
||||
{children ?? <CropIcon className='size-4' />}
|
||||
</Button>
|
||||
);
|
||||
@@ -331,19 +329,17 @@ export const ImageCropReset = ({
|
||||
|
||||
if (asChild) {
|
||||
return (
|
||||
<Slot.Root onClick={handleClick} {...(props as any)}>
|
||||
<Slot.Root
|
||||
onClick={handleClick}
|
||||
{...(props as ComponentProps<typeof Slot.Root>)}
|
||||
>
|
||||
{children}
|
||||
</Slot.Root>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
size='icon'
|
||||
variant='ghost'
|
||||
{...(props as any)}
|
||||
>
|
||||
<Button onClick={handleClick} size='icon' variant='ghost' {...props}>
|
||||
{children ?? <RotateCcwIcon className='size-4' />}
|
||||
</Button>
|
||||
);
|
||||
@@ -373,7 +369,15 @@ export const Cropper = ({
|
||||
onChange={onChange}
|
||||
onComplete={onComplete}
|
||||
onCrop={onCrop}
|
||||
{...(props as any)}
|
||||
{...(props as Omit<
|
||||
ImageCropProps,
|
||||
| 'file'
|
||||
| 'maxImageSize'
|
||||
| 'onChange'
|
||||
| 'onComplete'
|
||||
| 'onCrop'
|
||||
| 'children'
|
||||
>)}
|
||||
>
|
||||
<ImageCropContent className={className} style={style} />
|
||||
</ImageCrop>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { Button } from '../../src/button';
|
||||
|
||||
describe('Button', () => {
|
||||
it('renders its children', () => {
|
||||
render(<Button>Click me</Button>);
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Click me' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
@@ -5,6 +5,6 @@
|
||||
"jsx": "preserve",
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["src"],
|
||||
"include": ["src", "tests", "vitest.config.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
import { jsdomProject, nodeProject } from '@gib/vitest-config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
projects: [
|
||||
nodeProject('unit', ['tests/unit/**/*.test.{ts,tsx}']),
|
||||
nodeProject('integration', ['tests/integration/**/*.test.{ts,tsx}']),
|
||||
jsdomProject('component', ['tests/component/**/*.test.{ts,tsx}']),
|
||||
],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user