fix: fix cron builder
This commit is contained in:
parent
6cdd8c27ce
commit
5be5b9f634
@ -12,6 +12,7 @@ const config: CodegenConfig = {
|
||||
},
|
||||
config: {
|
||||
enumsAsConst: true,
|
||||
useTypeImports: true,
|
||||
scalars: {
|
||||
SubscriberTaskType: {
|
||||
input: 'recorder/bindings/SubscriberTaskInput#SubscriberTaskInput',
|
||||
|
@ -1,3 +1,14 @@
|
||||
import { getFutureMatches } from '@datasert/cronjs-matcher';
|
||||
import { Calendar, Clock, Info, Settings, Zap } from 'lucide-react';
|
||||
import {
|
||||
type CSSProperties,
|
||||
type FC,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@ -20,16 +31,6 @@ import { Separator } from '@/components/ui/separator';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import { cn } from '@/presentation/utils';
|
||||
import { getFutureMatches } from '@datasert/cronjs-matcher';
|
||||
import { Calendar, Clock, Info, Settings, Zap } from 'lucide-react';
|
||||
import {
|
||||
type CSSProperties,
|
||||
type FC,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
type CronBuilderProps,
|
||||
CronField,
|
||||
@ -345,7 +346,7 @@ const CronBuilder: FC<CronBuilderProps> = ({
|
||||
<div className={cn(withCard && 'space-y-6', className)}>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={(value) => handlePeriodChange(value as CronPeriod)}
|
||||
onValueChange={(v) => handlePeriodChange(v as CronPeriod)}
|
||||
>
|
||||
<div className="overflow-x-auto">
|
||||
<TabsList
|
||||
@ -516,15 +517,114 @@ const CronFieldEditor: FC<CronFieldEditorProps> = ({
|
||||
const currentValue = fields[field];
|
||||
|
||||
return (
|
||||
<div key={field} className="space-y-2">
|
||||
<CronFieldItemEditor
|
||||
key={field}
|
||||
config={config}
|
||||
field={field}
|
||||
value={currentValue}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CronFieldItemAnyOrSpecificOption = {
|
||||
Any: 'any',
|
||||
Specific: 'specific',
|
||||
} as const;
|
||||
|
||||
type CronFieldItemAnyOrSpecificOption =
|
||||
(typeof CronFieldItemAnyOrSpecificOption)[keyof typeof CronFieldItemAnyOrSpecificOption];
|
||||
|
||||
interface CronFieldItemEditorProps {
|
||||
config: CronFieldConfig;
|
||||
field: CronField;
|
||||
value: string;
|
||||
onChange: (field: CronField, value: string) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
function encodeCronFieldItem(value: string): string {
|
||||
if (value === '') {
|
||||
return '<meta:empty>';
|
||||
}
|
||||
|
||||
if (value.includes(' ')) {
|
||||
return `<meta:contains-space:${encodeURIComponent(value)}>`;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function decodeCronFieldItem(value: string): string {
|
||||
if (value.startsWith('<meta:contains')) {
|
||||
return decodeURIComponent(
|
||||
// biome-ignore lint/performance/useTopLevelRegex: false
|
||||
value.replace(/^<meta:contains-space:([^>]+)>$/, '$1')
|
||||
);
|
||||
}
|
||||
|
||||
if (value === '<meta:empty>') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export const CronFieldItemEditor: FC<CronFieldItemEditorProps> = memo(
|
||||
({ field, value, onChange, config, disabled = false }) => {
|
||||
const [innerValue, _setInnerValue] = useState(() =>
|
||||
decodeCronFieldItem(value)
|
||||
);
|
||||
|
||||
const [anyOrSpecificOption, _setAnyOrSpecificOption] =
|
||||
useState<CronFieldItemAnyOrSpecificOption>(() =>
|
||||
innerValue === '*'
|
||||
? CronFieldItemAnyOrSpecificOption.Any
|
||||
: CronFieldItemAnyOrSpecificOption.Specific
|
||||
);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: false
|
||||
useEffect(() => {
|
||||
const nextValue = decodeCronFieldItem(value);
|
||||
if (nextValue !== innerValue) {
|
||||
_setInnerValue(nextValue);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(v: string) => {
|
||||
_setInnerValue(v);
|
||||
onChange(field, encodeCronFieldItem(v));
|
||||
},
|
||||
[field, onChange]
|
||||
);
|
||||
|
||||
const setAnyOrSpecificOption = useCallback(
|
||||
(v: CronFieldItemAnyOrSpecificOption) => {
|
||||
_setAnyOrSpecificOption(v);
|
||||
if (v === CronFieldItemAnyOrSpecificOption.Any) {
|
||||
handleChange('*');
|
||||
} else if (v === CronFieldItemAnyOrSpecificOption.Specific) {
|
||||
handleChange('0');
|
||||
}
|
||||
},
|
||||
[handleChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm capitalize">
|
||||
{field.replace(/([A-Z])/g, ' $1').toLowerCase()}
|
||||
</Label>
|
||||
|
||||
{field === 'month' || field === 'dayOfWeek' ? (
|
||||
{(field === 'month' || field === 'dayOfWeek') && (
|
||||
<Select
|
||||
value={currentValue}
|
||||
onValueChange={(value) => onChange(field, value)}
|
||||
value={innerValue}
|
||||
onValueChange={handleChange}
|
||||
disabled={disabled}
|
||||
>
|
||||
<SelectTrigger>
|
||||
@ -539,12 +639,12 @@ const CronFieldEditor: FC<CronFieldEditorProps> = ({
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
// biome-ignore lint/nursery/noNestedTernary: <explanation>
|
||||
) : field === 'dayOfMonth' ? (
|
||||
)}
|
||||
{field === 'dayOfMonth' && (
|
||||
<div className="space-y-2">
|
||||
<Select
|
||||
value={currentValue}
|
||||
onValueChange={(value) => onChange(field, value)}
|
||||
value={innerValue}
|
||||
onValueChange={handleChange}
|
||||
disabled={disabled}
|
||||
>
|
||||
<SelectTrigger>
|
||||
@ -564,36 +664,39 @@ const CronFieldEditor: FC<CronFieldEditorProps> = ({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
) : (
|
||||
)}
|
||||
{!(
|
||||
field === 'month' ||
|
||||
field === 'dayOfWeek' ||
|
||||
field === 'dayOfMonth'
|
||||
) && (
|
||||
<div className="space-y-2">
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
value={currentValue === '*' ? '*' : 'specific'}
|
||||
onValueChange={(value) => {
|
||||
if (value === '*') {
|
||||
onChange(field, '*');
|
||||
} else if (value === 'specific' && currentValue === '*') {
|
||||
onChange(field, '0');
|
||||
}
|
||||
}}
|
||||
value={anyOrSpecificOption}
|
||||
onValueChange={setAnyOrSpecificOption}
|
||||
disabled={disabled}
|
||||
>
|
||||
<ToggleGroupItem value="*" className="min-w-fit text-xs">
|
||||
<ToggleGroupItem
|
||||
value={CronFieldItemAnyOrSpecificOption.Any}
|
||||
className="min-w-fit text-xs"
|
||||
>
|
||||
Any
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem
|
||||
value="specific"
|
||||
value={CronFieldItemAnyOrSpecificOption.Specific}
|
||||
className="min-w-fit text-xs"
|
||||
>
|
||||
Specific
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
|
||||
{currentValue !== '*' && (
|
||||
{anyOrSpecificOption ===
|
||||
CronFieldItemAnyOrSpecificOption.Specific && (
|
||||
<Input
|
||||
type="text"
|
||||
value={currentValue}
|
||||
onChange={(e) => onChange(field, e.target.value)}
|
||||
value={innerValue}
|
||||
onChange={(e) => handleChange(e.target.value)}
|
||||
placeholder={`0-${config.max}`}
|
||||
disabled={disabled}
|
||||
className="font-mono text-sm"
|
||||
@ -608,18 +711,15 @@ const CronFieldEditor: FC<CronFieldEditorProps> = ({
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
Supports: *, numbers, ranges (1-5), lists (1,3,5), steps
|
||||
(*/5)
|
||||
Supports: *, numbers, ranges (1-5), lists (1,3,5), steps (*/5)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function parseCronExpression(expression: string): Record<CronField, string> {
|
||||
const parts = expression.split(' ');
|
||||
|
@ -33,7 +33,11 @@ const CronInput = forwardRef<HTMLInputElement, CronInputProps>(
|
||||
|
||||
const validationResult = useMemo((): CronValidationResult => {
|
||||
if (!internalValue.trim()) {
|
||||
return { isValid: false, error: 'Expression is required' };
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Expression is required',
|
||||
isEmpty: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -1,3 +1,14 @@
|
||||
import { parse } from '@datasert/cronjs-parser';
|
||||
import {
|
||||
AlertCircle,
|
||||
Bolt,
|
||||
Check,
|
||||
Code2,
|
||||
Copy,
|
||||
Settings,
|
||||
Type,
|
||||
} from 'lucide-react';
|
||||
import { type FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@ -10,17 +21,6 @@ import {
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { cn } from '@/presentation/utils';
|
||||
import { parse } from '@datasert/cronjs-parser';
|
||||
import {
|
||||
AlertCircle,
|
||||
Bolt,
|
||||
Check,
|
||||
Code2,
|
||||
Copy,
|
||||
Settings,
|
||||
Type,
|
||||
} from 'lucide-react';
|
||||
import { type FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { CronBuilder } from './cron-builder';
|
||||
import { CronDisplay } from './cron-display';
|
||||
import { CronInput } from './cron-input';
|
||||
@ -55,7 +55,7 @@ const Cron: FC<CronProps> = ({
|
||||
showPresets,
|
||||
withCard = true,
|
||||
isFirstSibling = false,
|
||||
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
|
||||
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: false
|
||||
}) => {
|
||||
const [internalValue, setInternalValue] = useState(value || '');
|
||||
const [internalActiveMode, setInternalActiveMode] =
|
||||
@ -106,9 +106,9 @@ const Cron: FC<CronProps> = ({
|
||||
);
|
||||
|
||||
const handleActiveModeChange = useCallback(
|
||||
(mode: CronPrimitiveMode) => {
|
||||
setInternalActiveMode(mode);
|
||||
onActiveModeChange?.(mode);
|
||||
(m: CronPrimitiveMode) => {
|
||||
setInternalActiveMode(m);
|
||||
onActiveModeChange?.(m);
|
||||
},
|
||||
[onActiveModeChange]
|
||||
);
|
||||
@ -122,8 +122,8 @@ const Cron: FC<CronProps> = ({
|
||||
await navigator.clipboard.writeText(internalValue);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (error) {
|
||||
console.warn('Failed to copy to clipboard:', error);
|
||||
} catch (e) {
|
||||
console.warn('Failed to copy to clipboard:', e);
|
||||
}
|
||||
}, [internalValue]);
|
||||
|
||||
@ -241,8 +241,8 @@ const Cron: FC<CronProps> = ({
|
||||
<CardContent className={cn(!withCard && 'px-0')}>
|
||||
<Tabs
|
||||
value={internalActiveMode}
|
||||
onValueChange={(value) =>
|
||||
handleActiveModeChange(value as 'input' | 'builder')
|
||||
onValueChange={(v) =>
|
||||
handleActiveModeChange(v as 'input' | 'builder')
|
||||
}
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
|
@ -1,20 +1,20 @@
|
||||
export { Cron } from './cron';
|
||||
export { CronInput } from './cron-input';
|
||||
export { CronBuilder } from './cron-builder';
|
||||
export { CronDisplay } from './cron-display';
|
||||
export { CronExample } from './cron-example';
|
||||
export { CronInput } from './cron-input';
|
||||
|
||||
export {
|
||||
type CronProps,
|
||||
type CronInputProps,
|
||||
type CronBuilderProps,
|
||||
type CronDisplayProps,
|
||||
type CronExpression,
|
||||
CronField,
|
||||
type CronFieldConfig,
|
||||
type CronInputProps,
|
||||
type CronNextRun,
|
||||
CronPeriod,
|
||||
type CronPreset,
|
||||
type CronProps,
|
||||
type CronValidationResult,
|
||||
type CronNextRun,
|
||||
type CronFieldConfig,
|
||||
CronField,
|
||||
type PeriodConfig,
|
||||
} from './types';
|
||||
|
@ -1,6 +1,5 @@
|
||||
import type { CronPreset } from '@/components/domains/cron';
|
||||
import type { GetCronsQuery } from '@/infra/graphql/gql/graphql';
|
||||
import { gql } from '@apollo/client';
|
||||
import type { GetCronsQuery } from '@/infra/graphql/gql/graphql';
|
||||
|
||||
export const GET_CRONS = gql`
|
||||
query GetCrons($filter: CronFilterInput!, $orderBy: CronOrderInput!, $pagination: PaginationInput!) {
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { gql } from '@apollo/client';
|
||||
import { type } from 'arktype';
|
||||
import { arkValidatorToTypeNarrower } from '@/infra/errors/arktype';
|
||||
import {
|
||||
type GetSubscriptionsQuery,
|
||||
SubscriptionCategoryEnum,
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
import { gql } from '@apollo/client';
|
||||
import { type } from 'arktype';
|
||||
import {
|
||||
extractMikanSubscriptionBangumiSourceUrl,
|
||||
extractMikanSubscriptionSubscriberSourceUrl,
|
||||
MikanSubscriptionBangumiSourceUrlSchema,
|
||||
MikanSubscriptionSeasonSourceUrlSchema,
|
||||
MikanSubscriptionSubscriberSourceUrlSchema,
|
||||
extractMikanSubscriptionBangumiSourceUrl,
|
||||
extractMikanSubscriptionSubscriberSourceUrl,
|
||||
} from './mikan';
|
||||
|
||||
export const GET_SUBSCRIPTIONS = gql`
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { GetTasksQuery } from '@/infra/graphql/gql/graphql';
|
||||
import { gql } from '@apollo/client';
|
||||
import type { GetTasksQuery } from '@/infra/graphql/gql/graphql';
|
||||
|
||||
export const GET_TASKS = gql`
|
||||
query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {
|
||||
|
30
apps/webui/src/infra/forms/compat.ts
Normal file
30
apps/webui/src/infra/forms/compat.ts
Normal file
@ -0,0 +1,30 @@
|
||||
type AllKeys<T> = T extends any ? keyof T : never;
|
||||
|
||||
type ToDefaultable<T> = Exclude<
|
||||
T extends string | undefined
|
||||
? T | ''
|
||||
: T extends number | undefined
|
||||
? T | number
|
||||
: T extends undefined
|
||||
? T | null
|
||||
: T,
|
||||
undefined
|
||||
>;
|
||||
|
||||
type PickFieldFormUnion<T, K extends keyof T> = T extends any
|
||||
? T[keyof T & K]
|
||||
: never;
|
||||
|
||||
// compact more types;
|
||||
export type FormDefaultValues<T> = {
|
||||
-readonly [K in AllKeys<T>]-?: ToDefaultable<PickFieldFormUnion<T, K>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* https://github.com/shadcn-ui/ui/issues/427
|
||||
*/
|
||||
export function compatFormDefaultValues<T, K extends AllKeys<T> = AllKeys<T>>(
|
||||
d: FormDefaultValues<Pick<T, K>>
|
||||
): T {
|
||||
return d as unknown as T;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable */
|
||||
import { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
|
||||
import { FragmentDefinitionNode } from 'graphql';
|
||||
import { Incremental } from './graphql';
|
||||
import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
|
||||
import type { FragmentDefinitionNode } from 'graphql';
|
||||
import type { Incremental } from './graphql';
|
||||
|
||||
|
||||
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable */
|
||||
import * as types from './graphql';
|
||||
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
|
||||
/**
|
||||
* Map of all GraphQL operations in the project.
|
||||
@ -31,7 +31,7 @@ type Documents = {
|
||||
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument,
|
||||
"\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": typeof types.DeleteSubscriptionsDocument,
|
||||
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument,
|
||||
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetTasksDocument,
|
||||
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetTasksDocument,
|
||||
"\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": typeof types.InsertSubscriberTaskDocument,
|
||||
"\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": typeof types.DeleteTasksDocument,
|
||||
"\n mutation RetryTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksRetryOne(filter: $filter) {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n }\n": typeof types.RetryTasksDocument,
|
||||
@ -54,7 +54,7 @@ const documents: Documents = {
|
||||
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument,
|
||||
"\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": types.DeleteSubscriptionsDocument,
|
||||
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument,
|
||||
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetTasksDocument,
|
||||
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetTasksDocument,
|
||||
"\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": types.InsertSubscriberTaskDocument,
|
||||
"\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": types.DeleteTasksDocument,
|
||||
"\n mutation RetryTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksRetryOne(filter: $filter) {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n }\n": types.RetryTasksDocument,
|
||||
@ -145,7 +145,7 @@ export function gql(source: "\nquery GetSubscriptionDetail ($id: Int!) {\n subs
|
||||
/**
|
||||
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function gql(source: "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"): (typeof documents)["\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"];
|
||||
export function gql(source: "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"): (typeof documents)["\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"];
|
||||
/**
|
||||
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
import { AUTH_PROVIDER } from '@/infra/auth/auth.provider';
|
||||
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
|
||||
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
|
||||
import { setContext } from '@apollo/client/link/context';
|
||||
import { Injectable, inject } from '@outposts/injection-js';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { AUTH_PROVIDER } from '@/infra/auth/auth.provider';
|
||||
|
||||
@Injectable()
|
||||
export class GraphQLService {
|
||||
|
@ -1,3 +1,13 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import {
|
||||
createFileRoute,
|
||||
useCanGoBack,
|
||||
useNavigate,
|
||||
useRouter,
|
||||
} from '@tanstack/react-router';
|
||||
import { type } from 'arktype';
|
||||
import { Loader2, Save } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
@ -24,7 +34,9 @@ import {
|
||||
INSERT_CREDENTIAL_3RD,
|
||||
} from '@/domains/recorder/schema/credential3rd';
|
||||
import { useInject } from '@/infra/di/inject';
|
||||
import { compatFormDefaultValues } from '@/infra/forms/compat';
|
||||
import {
|
||||
type Credential3rdInsertInput,
|
||||
Credential3rdTypeEnum,
|
||||
type InsertCredential3rdMutation,
|
||||
type InsertCredential3rdMutationVariables,
|
||||
@ -35,16 +47,6 @@ import {
|
||||
CreateCompleteActionSchema,
|
||||
} from '@/infra/routes/nav';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import {
|
||||
createFileRoute,
|
||||
useCanGoBack,
|
||||
useNavigate,
|
||||
useRouter,
|
||||
} from '@tanstack/react-router';
|
||||
import { type } from 'arktype';
|
||||
import { Loader2, Save } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const RouteSearchSchema = type({
|
||||
completeAction: CreateCompleteActionSchema.optional(),
|
||||
@ -98,21 +100,24 @@ function CredentialCreateRouteComponent() {
|
||||
});
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
defaultValues: compatFormDefaultValues<
|
||||
Credential3rdInsertInput,
|
||||
'credentialType' | 'username' | 'password' | 'userAgent'
|
||||
>({
|
||||
credentialType: Credential3rdTypeEnum.Mikan,
|
||||
username: '',
|
||||
password: '',
|
||||
userAgent: '',
|
||||
},
|
||||
}),
|
||||
validators: {
|
||||
onChangeAsync: Credential3rdInsertSchema,
|
||||
onChangeAsyncDebounceMs: 300,
|
||||
onSubmit: Credential3rdInsertSchema,
|
||||
},
|
||||
onSubmit: async (form) => {
|
||||
onSubmit: async (submittedForm) => {
|
||||
const value = {
|
||||
...form.value,
|
||||
userAgent: form.value.userAgent || platformService.userAgent,
|
||||
...submittedForm.value,
|
||||
userAgent: submittedForm.value.userAgent || platformService.userAgent,
|
||||
};
|
||||
await insertCredential3rd({
|
||||
variables: {
|
||||
@ -183,7 +188,7 @@ function CredentialCreateRouteComponent() {
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
value={field.state.value ?? ''}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
placeholder="Please enter username"
|
||||
@ -207,7 +212,7 @@ function CredentialCreateRouteComponent() {
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
type="password"
|
||||
value={field.state.value}
|
||||
value={field.state.value ?? ''}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
placeholder="Please enter password"
|
||||
|
@ -1,3 +1,8 @@
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Eye, EyeOff, Save } from 'lucide-react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@ -32,18 +37,15 @@ import {
|
||||
apolloErrorToMessage,
|
||||
getApolloQueryError,
|
||||
} from '@/infra/errors/apollo';
|
||||
import { compatFormDefaultValues } from '@/infra/forms/compat';
|
||||
import type {
|
||||
Credential3rdTypeEnum,
|
||||
Credential3rdUpdateInput,
|
||||
GetCredential3rdDetailQuery,
|
||||
UpdateCredential3rdMutation,
|
||||
UpdateCredential3rdMutationVariables,
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Eye, EyeOff, Save } from 'lucide-react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export const Route = createFileRoute('/_app/credential3rd/edit/$id')({
|
||||
component: Credential3rdEditRouteComponent,
|
||||
@ -77,18 +79,21 @@ function FormView({
|
||||
});
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
defaultValues: compatFormDefaultValues<
|
||||
Credential3rdUpdateInput,
|
||||
'credentialType' | 'username' | 'password' | 'userAgent'
|
||||
>({
|
||||
credentialType: credential.credentialType,
|
||||
username: credential.username,
|
||||
password: credential.password,
|
||||
userAgent: credential.userAgent,
|
||||
},
|
||||
username: credential.username ?? '',
|
||||
password: credential.password ?? '',
|
||||
userAgent: credential.userAgent ?? '',
|
||||
}),
|
||||
validators: {
|
||||
onBlur: Credential3rdUpdateSchema,
|
||||
onSubmit: Credential3rdUpdateSchema,
|
||||
},
|
||||
onSubmit: (form) => {
|
||||
const value = form.value;
|
||||
onSubmit: (submittedForm) => {
|
||||
const value = submittedForm.value;
|
||||
updateCredential({
|
||||
variables: {
|
||||
data: value,
|
||||
@ -238,7 +243,7 @@ function Credential3rdEditRouteComponent() {
|
||||
const { loading, error, data, refetch } =
|
||||
useQuery<GetCredential3rdDetailQuery>(GET_CREDENTIAL_3RD_DETAIL, {
|
||||
variables: {
|
||||
id: Number.parseInt(id),
|
||||
id: Number.parseInt(id, 10),
|
||||
},
|
||||
});
|
||||
|
||||
@ -246,10 +251,10 @@ function Credential3rdEditRouteComponent() {
|
||||
|
||||
const onCompleted = useCallback(async () => {
|
||||
const refetchResult = await refetch();
|
||||
const error = getApolloQueryError(refetchResult);
|
||||
if (error) {
|
||||
const _error = getApolloQueryError(refetchResult);
|
||||
if (_error) {
|
||||
toast.error('Update credential failed', {
|
||||
description: apolloErrorToMessage(error),
|
||||
description: apolloErrorToMessage(_error),
|
||||
});
|
||||
} else {
|
||||
toast.success('Update credential successfully');
|
||||
|
@ -1,3 +1,7 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
||||
import { Loader2, Save } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
@ -27,6 +31,7 @@ import {
|
||||
} from '@/domains/recorder/schema/subscriptions';
|
||||
import { SubscriptionService } from '@/domains/recorder/services/subscription.service';
|
||||
import { useInject } from '@/infra/di/inject';
|
||||
import { compatFormDefaultValues } from '@/infra/forms/compat';
|
||||
import {
|
||||
Credential3rdTypeEnum,
|
||||
type InsertSubscriptionMutation,
|
||||
@ -34,11 +39,6 @@ import {
|
||||
SubscriptionCategoryEnum,
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { Loader2, Save } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { Credential3rdSelectContent } from './-credential3rd-select';
|
||||
|
||||
export const Route = createFileRoute('/_app/subscriptions/create')({
|
||||
@ -71,22 +71,24 @@ function SubscriptionCreateRouteComponent() {
|
||||
});
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
defaultValues: compatFormDefaultValues<SubscriptionForm>({
|
||||
displayName: '',
|
||||
category: undefined,
|
||||
category: '',
|
||||
enabled: true,
|
||||
sourceUrl: '',
|
||||
credentialId: '',
|
||||
year: undefined,
|
||||
credentialId: Number.NaN,
|
||||
year: Number.NaN,
|
||||
seasonStr: '',
|
||||
} as unknown as SubscriptionForm,
|
||||
}),
|
||||
validators: {
|
||||
onChangeAsync: SubscriptionFormSchema,
|
||||
onChangeAsyncDebounceMs: 300,
|
||||
onSubmit: SubscriptionFormSchema,
|
||||
},
|
||||
onSubmit: async (form) => {
|
||||
const input = subscriptionService.transformInsertFormToInput(form.value);
|
||||
onSubmit: async (submittedForm) => {
|
||||
const input = subscriptionService.transformInsertFormToInput(
|
||||
submittedForm.value
|
||||
);
|
||||
await insertSubscription({
|
||||
variables: {
|
||||
data: input,
|
||||
@ -119,30 +121,6 @@ function SubscriptionCreateRouteComponent() {
|
||||
}}
|
||||
className="space-y-6"
|
||||
>
|
||||
<form.Field name="displayName">
|
||||
{(field) => (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={field.name}>Display Name *</Label>
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
placeholder="Please enter display name"
|
||||
autoComplete="off"
|
||||
/>
|
||||
{field.state.meta.errors && (
|
||||
<FormFieldErrors
|
||||
errors={field.state.meta.errors}
|
||||
isDirty={field.state.meta.isDirty}
|
||||
submissionAttempts={form.state.submissionAttempts}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="category">
|
||||
{(field) => (
|
||||
<div className="space-y-2">
|
||||
@ -192,7 +170,7 @@ function SubscriptionCreateRouteComponent() {
|
||||
<Select
|
||||
value={field.state.value.toString()}
|
||||
onValueChange={(value) =>
|
||||
field.handleChange(Number.parseInt(value))
|
||||
field.handleChange(Number.parseInt(value, 10))
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
@ -227,7 +205,7 @@ function SubscriptionCreateRouteComponent() {
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) =>
|
||||
field.handleChange(
|
||||
Number.parseInt(e.target.value)
|
||||
Number.parseInt(e.target.value, 10)
|
||||
)
|
||||
}
|
||||
placeholder={`Please enter full year (e.g. ${new Date().getFullYear()})`}
|
||||
@ -315,6 +293,29 @@ function SubscriptionCreateRouteComponent() {
|
||||
);
|
||||
}}
|
||||
</form.Subscribe>
|
||||
<form.Field name="displayName">
|
||||
{(field) => (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={field.name}>Display Name *</Label>
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
placeholder="Please enter display name"
|
||||
autoComplete="off"
|
||||
/>
|
||||
{field.state.meta.errors && (
|
||||
<FormFieldErrors
|
||||
errors={field.state.meta.errors}
|
||||
isDirty={field.state.meta.isDirty}
|
||||
submissionAttempts={form.state.submissionAttempts}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
<form.Field name="enabled">
|
||||
{(field) => (
|
||||
<div className="flex items-center justify-between">
|
||||
|
@ -1,3 +1,8 @@
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Save } from 'lucide-react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@ -36,6 +41,7 @@ import {
|
||||
apolloErrorToMessage,
|
||||
getApolloQueryError,
|
||||
} from '@/infra/errors/apollo';
|
||||
import { compatFormDefaultValues } from '@/infra/forms/compat';
|
||||
import {
|
||||
Credential3rdTypeEnum,
|
||||
type GetSubscriptionDetailQuery,
|
||||
@ -44,11 +50,6 @@ import {
|
||||
type UpdateSubscriptionsMutationVariables,
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Save } from 'lucide-react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Credential3rdSelectContent } from './-credential3rd-select';
|
||||
|
||||
export const Route = createFileRoute('/_app/subscriptions/edit/$id')({
|
||||
@ -100,7 +101,9 @@ function FormView({
|
||||
category: subscription.category,
|
||||
enabled: subscription.enabled,
|
||||
sourceUrl: subscription.sourceUrl,
|
||||
credentialId: subscription.credential3rd?.id || '',
|
||||
credentialId: subscription.credential3rd?.id ?? Number.NaN,
|
||||
year: Number.NaN,
|
||||
seasonStr: '',
|
||||
};
|
||||
|
||||
if (
|
||||
@ -118,14 +121,16 @@ function FormView({
|
||||
}, [subscription, sourceUrlMeta]);
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: defaultValues as unknown as SubscriptionForm,
|
||||
defaultValues: compatFormDefaultValues<SubscriptionForm>(defaultValues),
|
||||
validators: {
|
||||
onChangeAsync: SubscriptionFormSchema,
|
||||
onChangeAsyncDebounceMs: 300,
|
||||
onSubmit: SubscriptionFormSchema,
|
||||
},
|
||||
onSubmit: async (form) => {
|
||||
const input = subscriptionService.transformInsertFormToInput(form.value);
|
||||
onSubmit: async (submittedForm) => {
|
||||
const input = subscriptionService.transformInsertFormToInput(
|
||||
submittedForm.value
|
||||
);
|
||||
|
||||
await updateSubscription({
|
||||
variables: {
|
||||
@ -217,7 +222,7 @@ function FormView({
|
||||
<Select
|
||||
value={field.state.value.toString()}
|
||||
onValueChange={(value) =>
|
||||
field.handleChange(Number.parseInt(value))
|
||||
field.handleChange(Number.parseInt(value, 10))
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
@ -249,7 +254,9 @@ function FormView({
|
||||
min={1970}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) =>
|
||||
field.handleChange(Number.parseInt(e.target.value))
|
||||
field.handleChange(
|
||||
Number.parseInt(e.target.value, 10)
|
||||
)
|
||||
}
|
||||
placeholder={`Please enter full year (e.g. ${new Date().getFullYear()})`}
|
||||
autoComplete="off"
|
||||
@ -359,7 +366,7 @@ function SubscriptionEditRouteComponent() {
|
||||
const { loading, error, data, refetch } =
|
||||
useQuery<GetSubscriptionDetailQuery>(GET_SUBSCRIPTION_DETAIL, {
|
||||
variables: {
|
||||
id: Number.parseInt(id),
|
||||
id: Number.parseInt(id, 10),
|
||||
},
|
||||
});
|
||||
|
||||
@ -367,10 +374,10 @@ function SubscriptionEditRouteComponent() {
|
||||
|
||||
const onCompleted = useCallback(async () => {
|
||||
const refetchResult = await refetch();
|
||||
const error = getApolloQueryError(refetchResult);
|
||||
if (error) {
|
||||
const _error = getApolloQueryError(refetchResult);
|
||||
if (_error) {
|
||||
toast.error('Update subscription failed', {
|
||||
description: apolloErrorToMessage(error),
|
||||
description: apolloErrorToMessage(_error),
|
||||
});
|
||||
} else {
|
||||
toast.success('Update subscription successfully');
|
||||
|
@ -1,9 +1,313 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { format } from 'date-fns';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { CronDisplay } from '@/components/domains/cron';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { ContainerHeader } from '@/components/ui/container-header';
|
||||
import { DetailCardSkeleton } from '@/components/ui/detail-card-skeleton';
|
||||
import { DetailEmptyView } from '@/components/ui/detail-empty-view';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { QueryErrorView } from '@/components/ui/query-error-view';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { GET_CRONS } from '@/domains/recorder/schema/cron';
|
||||
import {
|
||||
CronStatusEnum,
|
||||
type GetCronsQuery,
|
||||
type GetCronsQueryVariables,
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { getStatusBadge } from './-status-badge';
|
||||
|
||||
export const Route = createFileRoute('/_app/tasks/cron/detail/$id')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
component: CronDetailRouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: { label: 'Detail' },
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/_app/tasks/cron/detail/$id"!</div>
|
||||
function CronDetailRouteComponent() {
|
||||
const { id } = Route.useParams();
|
||||
|
||||
const { data, loading, error, refetch } = useQuery<
|
||||
GetCronsQuery,
|
||||
GetCronsQueryVariables
|
||||
>(GET_CRONS, {
|
||||
variables: {
|
||||
filter: {
|
||||
id: {
|
||||
eq: Number.parseInt(id, 10),
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
page: {
|
||||
page: 0,
|
||||
limit: 1,
|
||||
},
|
||||
},
|
||||
orderBy: {},
|
||||
},
|
||||
pollInterval: 5000, // Auto-refresh every 5 seconds for running crons
|
||||
});
|
||||
|
||||
const cron = data?.cron?.nodes?.[0];
|
||||
|
||||
const subscriberTaskCron = useMemo(() => {
|
||||
if (!cron) {
|
||||
return null;
|
||||
}
|
||||
return cron.subscriberTaskCron;
|
||||
}, [cron]);
|
||||
|
||||
if (loading) {
|
||||
return <DetailCardSkeleton />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <QueryErrorView message={error.message} onRetry={refetch} />;
|
||||
}
|
||||
|
||||
if (!cron) {
|
||||
return <DetailEmptyView message="Not found Cron task" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto max-w-4xl py-6">
|
||||
<ContainerHeader
|
||||
title="Cron task detail"
|
||||
description={`View Cron task #${cron.id}`}
|
||||
defaultBackTo="/tasks/cron/manage"
|
||||
actions={
|
||||
<Button variant="outline" size="sm" onClick={() => refetch()}>
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
Refresh
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>Cron task information</CardTitle>
|
||||
<CardDescription className="mt-2">
|
||||
View Cron task execution details
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{getStatusBadge(cron.status)}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-6">
|
||||
{/* Basic Information */}
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Cron ID</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<code className="text-sm">{cron.id}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Priority</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">{cron.priority}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Retry count</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">
|
||||
{cron.attempts} / {cron.maxAttempts}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Enabled</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<Badge variant={cron.enabled ? 'default' : 'secondary'}>
|
||||
{cron.enabled ? 'Enabled' : 'Disabled'}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Next run time</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">
|
||||
{cron.nextRun
|
||||
? format(new Date(cron.nextRun), 'yyyy-MM-dd HH:mm:ss')
|
||||
: '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Last run time</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">
|
||||
{cron.lastRun
|
||||
? format(new Date(cron.lastRun), 'yyyy-MM-dd HH:mm:ss')
|
||||
: '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Locked time</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">
|
||||
{cron.lockedAt
|
||||
? format(new Date(cron.lockedAt), 'yyyy-MM-dd HH:mm:ss')
|
||||
: '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Locked by</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<code className="text-sm">{cron.lockedBy || '-'}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Timeout</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">
|
||||
{cron.timeoutMs ? `${cron.timeoutMs}ms` : 'No limit'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Created at</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">
|
||||
{format(new Date(cron.createdAt), 'yyyy-MM-dd HH:mm:ss')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">Updated at</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<span className="text-sm">
|
||||
{format(new Date(cron.updatedAt), 'yyyy-MM-dd HH:mm:ss')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cron Expression Display */}
|
||||
{cron.cronExpr && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">
|
||||
Cron expression
|
||||
</Label>
|
||||
<CronDisplay
|
||||
expression={cron.cronExpr}
|
||||
timezone="UTC"
|
||||
showDescription={true}
|
||||
showNextRuns={true}
|
||||
withCard={false}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Subscriber Task Details */}
|
||||
{subscriberTaskCron && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">
|
||||
Subscriber task details
|
||||
</Label>
|
||||
<div className="rounded-md bg-muted p-3">
|
||||
<pre className="overflow-x-auto whitespace-pre-wrap text-sm">
|
||||
<code>
|
||||
{JSON.stringify(subscriberTaskCron, null, 2)}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Related Subscriber Tasks */}
|
||||
{cron.subscriberTask?.nodes &&
|
||||
cron.subscriberTask.nodes.length > 0 && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">
|
||||
Associated tasks
|
||||
</Label>
|
||||
<div className="space-y-2">
|
||||
{cron.subscriberTask.nodes.map((task, index) => (
|
||||
<div
|
||||
key={`${task.id}-${index}`}
|
||||
className="rounded-md border bg-muted/50 p-3"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<code className="text-sm">{task.id}</code>
|
||||
<Badge variant="outline">{task.status}</Badge>
|
||||
</div>
|
||||
<div className="mt-2 text-muted-foreground text-sm">
|
||||
Priority: {task.priority} | Retry: {task.attempts}
|
||||
/{task.maxAttempts}
|
||||
</div>
|
||||
{task.subscription && (
|
||||
<div className="mt-1 text-sm">
|
||||
<span className="font-medium">
|
||||
Subscription:
|
||||
</span>{' '}
|
||||
{task.subscription.displayName}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Error Information */}
|
||||
{cron.status === CronStatusEnum.Failed && cron.lastError && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="space-y-2">
|
||||
<Label className="font-medium text-sm">最后错误</Label>
|
||||
<div className="rounded-md bg-destructive/10 p-3">
|
||||
<p className="text-destructive text-sm">
|
||||
{cron.lastError}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,23 @@
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
||||
import {
|
||||
type ColumnDef,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
type PaginationState,
|
||||
type SortingState,
|
||||
useReactTable,
|
||||
type VisibilityState,
|
||||
} from '@tanstack/react-table';
|
||||
import { format } from 'date-fns';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ContainerHeader } from '@/components/ui/container-header';
|
||||
import { DataTablePagination } from '@/components/ui/data-table-pagination';
|
||||
import { DetailEmptyView } from '@/components/ui/detail-empty-view';
|
||||
import { DropdownMenuItem } from '@/components/ui/dropdown-menu';
|
||||
import { DropdownMenuActions } from '@/components/ui/dropdown-menu-actions';
|
||||
import { QueryErrorView } from '@/components/ui/query-error-view';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
@ -25,21 +39,6 @@ import {
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton';
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
||||
import {
|
||||
type ColumnDef,
|
||||
type PaginationState,
|
||||
type SortingState,
|
||||
type VisibilityState,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { format } from 'date-fns';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { getStatusBadge } from './-status-badge';
|
||||
|
||||
export const Route = createFileRoute('/_app/tasks/cron/manage')({
|
||||
@ -88,18 +87,18 @@ function TaskCronManageRouteComponent() {
|
||||
>(DELETE_CRONS, {
|
||||
onCompleted: async () => {
|
||||
const refetchResult = await refetch();
|
||||
const error = getApolloQueryError(refetchResult);
|
||||
if (error) {
|
||||
const errorResult = getApolloQueryError(refetchResult);
|
||||
if (errorResult) {
|
||||
toast.error('Failed to delete tasks', {
|
||||
description: apolloErrorToMessage(error),
|
||||
description: apolloErrorToMessage(errorResult),
|
||||
});
|
||||
return;
|
||||
}
|
||||
toast.success('Tasks deleted');
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: (mutationError) => {
|
||||
toast.error('Failed to delete tasks', {
|
||||
description: error.message,
|
||||
description: mutationError.message,
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -168,16 +167,16 @@ function TaskCronManageRouteComponent() {
|
||||
<div className="space-y-3">
|
||||
{showSkeleton &&
|
||||
Array.from(new Array(10)).map((_, index) => (
|
||||
<Skeleton key={index} className="h-32 w-full" />
|
||||
<Skeleton key={`skeleton-${index}`} className="h-32 w-full" />
|
||||
))}
|
||||
|
||||
{!showSkeleton && table.getRowModel().rows?.length > 0 ? (
|
||||
table.getRowModel().rows.map((row, index) => {
|
||||
table.getRowModel().rows.map((row) => {
|
||||
const cron = row.original;
|
||||
return (
|
||||
<div
|
||||
className="space-y-3 rounded-lg border bg-card p-4"
|
||||
key={`${cron.id}-${index}`}
|
||||
key={cron.id}
|
||||
>
|
||||
{/* Header with status and priority */}
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
@ -215,17 +214,7 @@ function TaskCronManageRouteComponent() {
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
{cron.status === CronStatusEnum.Failed && (
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
// TODO: Retry cron
|
||||
}}
|
||||
>
|
||||
Retry
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuActions>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
41
biome.json
41
biome.json
@ -1,27 +1,32 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.1.1/schema.json",
|
||||
"extends": ["ultracite"],
|
||||
"javascript": {
|
||||
"globals": ["Liveblocks"]
|
||||
},
|
||||
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
"useSortedAttributes": "off"
|
||||
}
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"rules": {
|
||||
"nursery": {
|
||||
"noEnum": "off"
|
||||
},
|
||||
"nursery": {},
|
||||
"style": {
|
||||
"noParameterProperties": "off",
|
||||
"noNonNullAssertion": "off"
|
||||
"noNonNullAssertion": "off",
|
||||
"noEnum": "off"
|
||||
},
|
||||
"security": {
|
||||
"noDangerouslySetInnerHtml": "off"
|
||||
},
|
||||
"suspicious": {
|
||||
"noArrayIndexKey": "off",
|
||||
"noEmptyBlockStatements": "off",
|
||||
"noExplicitAny": "off",
|
||||
"noConsole": "off",
|
||||
"noConsoleLog": "off"
|
||||
"noConsole": "off"
|
||||
},
|
||||
"a11y": {
|
||||
"noSvgWithoutTitle": "off"
|
||||
@ -45,7 +50,7 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"include": ["**/tsconfig.json", "**/tsconfig.*.json"],
|
||||
"includes": ["**/tsconfig.json", "**/tsconfig.*.json"],
|
||||
"json": {
|
||||
"parser": {
|
||||
"allowComments": true
|
||||
@ -53,11 +58,17 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": ["apps/webui/src/infra/graphql/gql/**/*"],
|
||||
"includes": ["**/apps/webui/src/infra/graphql/gql/**/*"],
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "off"
|
||||
}
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useShorthandArrayType": "off",
|
||||
"useConsistentArrayType": "off",
|
||||
"useImportType": "off"
|
||||
}
|
||||
@ -65,7 +76,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": ["apps/webui/src/components/ui/**/*"],
|
||||
"includes": ["**/apps/webui/src/components/ui/**/*"],
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
@ -75,10 +86,10 @@
|
||||
"rules": {
|
||||
"style": {
|
||||
"useBlockStatements": "off",
|
||||
"useImportType": "off"
|
||||
"useImportType": "off",
|
||||
"noNestedTernary": "off"
|
||||
},
|
||||
"nursery": {
|
||||
"noNestedTernary": "off",
|
||||
"useSortedClasses": "off"
|
||||
},
|
||||
"a11y": {
|
||||
@ -94,6 +105,6 @@
|
||||
}
|
||||
],
|
||||
"files": {
|
||||
"ignore": [".vscode/*.json"]
|
||||
"includes": ["**", "!**/.vscode/**/*.json"]
|
||||
}
|
||||
}
|
||||
|
13
package.json
13
package.json
@ -3,10 +3,7 @@
|
||||
"version": "0.0.0",
|
||||
"description": "Kono bangumi?",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"apps/*"
|
||||
],
|
||||
"workspaces": ["packages/*", "apps/*"],
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -18,22 +15,22 @@
|
||||
"bump-deps": "npx --yes npm-check-updates --deep -u && pnpm install",
|
||||
"clean": "git clean -xdf node_modules"
|
||||
},
|
||||
"packageManager": "pnpm@10.12.1",
|
||||
"packageManager": "pnpm@10.12.4",
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
"node": ">=24"
|
||||
},
|
||||
"dependencies": {
|
||||
"es-toolkit": "^1.39.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.1.1",
|
||||
"@types/node": "^24.0.10",
|
||||
"cross-env": "^7.0.3",
|
||||
"kill-port": "^2.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "^5.8.3",
|
||||
"ultracite": "^4.2.13"
|
||||
"ultracite": "^5.0.32"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
304
pnpm-lock.yaml
generated
304
pnpm-lock.yaml
generated
@ -16,8 +16,8 @@ importers:
|
||||
version: 1.39.6
|
||||
devDependencies:
|
||||
'@biomejs/biome':
|
||||
specifier: 1.9.4
|
||||
version: 1.9.4
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
'@types/node':
|
||||
specifier: ^24.0.10
|
||||
version: 24.0.10
|
||||
@ -37,8 +37,8 @@ importers:
|
||||
specifier: ^5.8.3
|
||||
version: 5.8.3
|
||||
ultracite:
|
||||
specifier: ^4.2.13
|
||||
version: 4.2.13
|
||||
specifier: ^5.0.32
|
||||
version: 5.0.32(@types/node@24.0.10)(jsdom@25.0.1(bufferutil@4.0.9)(utf-8-validate@6.0.5))(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1)
|
||||
|
||||
apps/docs: {}
|
||||
|
||||
@ -601,59 +601,65 @@ packages:
|
||||
resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@biomejs/biome@1.9.4':
|
||||
resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==}
|
||||
'@biomejs/biome@2.1.1':
|
||||
resolution: {integrity: sha512-HFGYkxG714KzG+8tvtXCJ1t1qXQMzgWzfvQaUjxN6UeKv+KvMEuliInnbZLJm6DXFXwqVi6446EGI0sGBLIYng==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
hasBin: true
|
||||
|
||||
'@biomejs/cli-darwin-arm64@1.9.4':
|
||||
resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==}
|
||||
'@biomejs/cli-darwin-arm64@2.1.1':
|
||||
resolution: {integrity: sha512-2Muinu5ok4tWxq4nu5l19el48cwCY/vzvI7Vjbkf3CYIQkjxZLyj0Ad37Jv2OtlXYaLvv+Sfu1hFeXt/JwRRXQ==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@biomejs/cli-darwin-x64@1.9.4':
|
||||
resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==}
|
||||
'@biomejs/cli-darwin-x64@2.1.1':
|
||||
resolution: {integrity: sha512-cC8HM5lrgKQXLAK+6Iz2FrYW5A62pAAX6KAnRlEyLb+Q3+Kr6ur/sSuoIacqlp1yvmjHJqjYfZjPvHWnqxoEIA==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@biomejs/cli-linux-arm64-musl@1.9.4':
|
||||
resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==}
|
||||
'@biomejs/cli-linux-arm64-musl@2.1.1':
|
||||
resolution: {integrity: sha512-/7FBLnTswu4jgV9ttI3AMIdDGqVEPIZd8I5u2D4tfCoj8rl9dnjrEQbAIDlWhUXdyWlFSz8JypH3swU9h9P+2A==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-arm64@1.9.4':
|
||||
resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==}
|
||||
'@biomejs/cli-linux-arm64@2.1.1':
|
||||
resolution: {integrity: sha512-tw4BEbhAUkWPe4WBr6IX04DJo+2jz5qpPzpW/SWvqMjb9QuHY8+J0M23V8EPY/zWU4IG8Ui0XESapR1CB49Q7g==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-x64-musl@1.9.4':
|
||||
resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==}
|
||||
'@biomejs/cli-linux-x64-musl@2.1.1':
|
||||
resolution: {integrity: sha512-kUu+loNI3OCD2c12cUt7M5yaaSjDnGIksZwKnueubX6c/HWUyi/0mPbTBHR49Me3F0KKjWiKM+ZOjsmC+lUt9g==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-linux-x64@1.9.4':
|
||||
resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==}
|
||||
'@biomejs/cli-linux-x64@2.1.1':
|
||||
resolution: {integrity: sha512-3WJ1GKjU7NzZb6RTbwLB59v9cTIlzjbiFLDB0z4376TkDqoNYilJaC37IomCr/aXwuU8QKkrYoHrgpSq5ffJ4Q==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@biomejs/cli-win32-arm64@1.9.4':
|
||||
resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==}
|
||||
'@biomejs/cli-win32-arm64@2.1.1':
|
||||
resolution: {integrity: sha512-vEHK0v0oW+E6RUWLoxb2isI3rZo57OX9ZNyyGH701fZPj6Il0Rn1f5DMNyCmyflMwTnIQstEbs7n2BxYSqQx4Q==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@biomejs/cli-win32-x64@1.9.4':
|
||||
resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==}
|
||||
'@biomejs/cli-win32-x64@2.1.1':
|
||||
resolution: {integrity: sha512-i2PKdn70kY++KEF/zkQFvQfX1e8SkA8hq4BgC+yE9dZqyLzB/XStY2MvwI3qswlRgnGpgncgqe0QYKVS1blksg==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@clack/core@0.5.0':
|
||||
resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==}
|
||||
|
||||
'@clack/prompts@0.11.0':
|
||||
resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==}
|
||||
|
||||
'@codemirror/language@6.11.1':
|
||||
resolution: {integrity: sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==}
|
||||
|
||||
@ -3211,6 +3217,9 @@ packages:
|
||||
'@vitest/expect@3.2.3':
|
||||
resolution: {integrity: sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==}
|
||||
|
||||
'@vitest/expect@3.2.4':
|
||||
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
|
||||
|
||||
'@vitest/mocker@3.2.3':
|
||||
resolution: {integrity: sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==}
|
||||
peerDependencies:
|
||||
@ -3222,21 +3231,47 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/mocker@3.2.4':
|
||||
resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@3.2.3':
|
||||
resolution: {integrity: sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==}
|
||||
|
||||
'@vitest/pretty-format@3.2.4':
|
||||
resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
|
||||
|
||||
'@vitest/runner@3.2.3':
|
||||
resolution: {integrity: sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==}
|
||||
|
||||
'@vitest/runner@3.2.4':
|
||||
resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
|
||||
|
||||
'@vitest/snapshot@3.2.3':
|
||||
resolution: {integrity: sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==}
|
||||
|
||||
'@vitest/snapshot@3.2.4':
|
||||
resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
|
||||
|
||||
'@vitest/spy@3.2.3':
|
||||
resolution: {integrity: sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==}
|
||||
|
||||
'@vitest/spy@3.2.4':
|
||||
resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
|
||||
|
||||
'@vitest/utils@3.2.3':
|
||||
resolution: {integrity: sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==}
|
||||
|
||||
'@vitest/utils@3.2.4':
|
||||
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
|
||||
|
||||
'@webassemblyjs/ast@1.14.1':
|
||||
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
|
||||
|
||||
@ -4876,6 +4911,9 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
jsonc-parser@3.3.1:
|
||||
resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
|
||||
|
||||
jsonfile@2.4.0:
|
||||
resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==}
|
||||
|
||||
@ -5002,6 +5040,9 @@ packages:
|
||||
loupe@3.1.3:
|
||||
resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
|
||||
|
||||
loupe@3.1.4:
|
||||
resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==}
|
||||
|
||||
lower-case-first@2.0.2:
|
||||
resolution: {integrity: sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==}
|
||||
|
||||
@ -5952,6 +5993,9 @@ packages:
|
||||
simple-swizzle@0.2.2:
|
||||
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
|
||||
|
||||
sisteransi@1.0.5:
|
||||
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||
|
||||
slash@3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
@ -6260,6 +6304,10 @@ packages:
|
||||
resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
||||
tinypool@1.1.1:
|
||||
resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
||||
tinyrainbow@2.0.0:
|
||||
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -6384,8 +6432,8 @@ packages:
|
||||
uc.micro@2.1.0:
|
||||
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
|
||||
|
||||
ultracite@4.2.13:
|
||||
resolution: {integrity: sha512-j49R1z3xXIPhdvU19x0z0Z4hNewJYn4F1h42ULeaCOylBuxwGVE401piPxe3aVapwue7+Ec3J6wnL/+mW4zwww==}
|
||||
ultracite@5.0.32:
|
||||
resolution: {integrity: sha512-JjVNswL1mkIaOkPVh1nuEGnbEaCa94+ftqJ9hpRX2Y+jt72pcv32JeWg3Dqhkz/e3l449f7KAzMHO4x6IbxFZQ==}
|
||||
hasBin: true
|
||||
|
||||
unbox-primitive@1.1.0:
|
||||
@ -6492,6 +6540,11 @@ packages:
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
|
||||
vite-node@3.2.4:
|
||||
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
|
||||
vite@5.4.11:
|
||||
resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@ -6551,6 +6604,34 @@ packages:
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
vitest@3.2.4:
|
||||
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/debug': ^4.1.12
|
||||
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||
'@vitest/browser': 3.2.4
|
||||
'@vitest/ui': 3.2.4
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@types/debug':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
vscode-languageserver-types@3.17.5:
|
||||
resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
|
||||
|
||||
@ -7133,41 +7214,52 @@ snapshots:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
|
||||
'@biomejs/biome@1.9.4':
|
||||
'@biomejs/biome@2.1.1':
|
||||
optionalDependencies:
|
||||
'@biomejs/cli-darwin-arm64': 1.9.4
|
||||
'@biomejs/cli-darwin-x64': 1.9.4
|
||||
'@biomejs/cli-linux-arm64': 1.9.4
|
||||
'@biomejs/cli-linux-arm64-musl': 1.9.4
|
||||
'@biomejs/cli-linux-x64': 1.9.4
|
||||
'@biomejs/cli-linux-x64-musl': 1.9.4
|
||||
'@biomejs/cli-win32-arm64': 1.9.4
|
||||
'@biomejs/cli-win32-x64': 1.9.4
|
||||
'@biomejs/cli-darwin-arm64': 2.1.1
|
||||
'@biomejs/cli-darwin-x64': 2.1.1
|
||||
'@biomejs/cli-linux-arm64': 2.1.1
|
||||
'@biomejs/cli-linux-arm64-musl': 2.1.1
|
||||
'@biomejs/cli-linux-x64': 2.1.1
|
||||
'@biomejs/cli-linux-x64-musl': 2.1.1
|
||||
'@biomejs/cli-win32-arm64': 2.1.1
|
||||
'@biomejs/cli-win32-x64': 2.1.1
|
||||
|
||||
'@biomejs/cli-darwin-arm64@1.9.4':
|
||||
'@biomejs/cli-darwin-arm64@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-darwin-x64@1.9.4':
|
||||
'@biomejs/cli-darwin-x64@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-arm64-musl@1.9.4':
|
||||
'@biomejs/cli-linux-arm64-musl@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-arm64@1.9.4':
|
||||
'@biomejs/cli-linux-arm64@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-x64-musl@1.9.4':
|
||||
'@biomejs/cli-linux-x64-musl@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-linux-x64@1.9.4':
|
||||
'@biomejs/cli-linux-x64@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-win32-arm64@1.9.4':
|
||||
'@biomejs/cli-win32-arm64@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@biomejs/cli-win32-x64@1.9.4':
|
||||
'@biomejs/cli-win32-x64@2.1.1':
|
||||
optional: true
|
||||
|
||||
'@clack/core@0.5.0':
|
||||
dependencies:
|
||||
picocolors: 1.1.1
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@clack/prompts@0.11.0':
|
||||
dependencies:
|
||||
'@clack/core': 0.5.0
|
||||
picocolors: 1.1.1
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@codemirror/language@6.11.1':
|
||||
dependencies:
|
||||
'@codemirror/state': 6.5.2
|
||||
@ -9898,6 +9990,14 @@ snapshots:
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/expect@3.2.4':
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/spy': 3.2.4
|
||||
'@vitest/utils': 3.2.4
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.2.3(vite@5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.3
|
||||
@ -9906,32 +10006,66 @@ snapshots:
|
||||
optionalDependencies:
|
||||
vite: 5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1)
|
||||
|
||||
'@vitest/mocker@3.2.4(vite@5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.2.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
optionalDependencies:
|
||||
vite: 5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1)
|
||||
|
||||
'@vitest/pretty-format@3.2.3':
|
||||
dependencies:
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/pretty-format@3.2.4':
|
||||
dependencies:
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/runner@3.2.3':
|
||||
dependencies:
|
||||
'@vitest/utils': 3.2.3
|
||||
pathe: 2.0.3
|
||||
strip-literal: 3.0.0
|
||||
|
||||
'@vitest/runner@3.2.4':
|
||||
dependencies:
|
||||
'@vitest/utils': 3.2.4
|
||||
pathe: 2.0.3
|
||||
strip-literal: 3.0.0
|
||||
|
||||
'@vitest/snapshot@3.2.3':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.2.3
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/snapshot@3.2.4':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/spy@3.2.3':
|
||||
dependencies:
|
||||
tinyspy: 4.0.3
|
||||
|
||||
'@vitest/spy@3.2.4':
|
||||
dependencies:
|
||||
tinyspy: 4.0.3
|
||||
|
||||
'@vitest/utils@3.2.3':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.2.3
|
||||
loupe: 3.1.3
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/utils@3.2.4':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
loupe: 3.1.4
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@webassemblyjs/ast@1.14.1':
|
||||
dependencies:
|
||||
'@webassemblyjs/helper-numbers': 1.13.2
|
||||
@ -11790,6 +11924,8 @@ snapshots:
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
jsonc-parser@3.3.1: {}
|
||||
|
||||
jsonfile@2.4.0:
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
@ -11907,6 +12043,8 @@ snapshots:
|
||||
|
||||
loupe@3.1.3: {}
|
||||
|
||||
loupe@3.1.4: {}
|
||||
|
||||
lower-case-first@2.0.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
@ -12943,6 +13081,8 @@ snapshots:
|
||||
is-arrayish: 0.3.2
|
||||
optional: true
|
||||
|
||||
sisteransi@1.0.5: {}
|
||||
|
||||
slash@3.0.0: {}
|
||||
|
||||
slice-ansi@3.0.0:
|
||||
@ -13264,6 +13404,8 @@ snapshots:
|
||||
|
||||
tinypool@1.1.0: {}
|
||||
|
||||
tinypool@1.1.1: {}
|
||||
|
||||
tinyrainbow@2.0.0: {}
|
||||
|
||||
tinyspy@4.0.3: {}
|
||||
@ -13390,9 +13532,30 @@ snapshots:
|
||||
|
||||
uc.micro@2.1.0: {}
|
||||
|
||||
ultracite@4.2.13:
|
||||
ultracite@5.0.32(@types/node@24.0.10)(jsdom@25.0.1(bufferutil@4.0.9)(utf-8-validate@6.0.5))(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1):
|
||||
dependencies:
|
||||
'@clack/prompts': 0.11.0
|
||||
commander: 14.0.0
|
||||
deepmerge: 4.3.1
|
||||
jsonc-parser: 3.3.1
|
||||
vitest: 3.2.4(@types/node@24.0.10)(jsdom@25.0.1(bufferutil@4.0.9)(utf-8-validate@6.0.5))(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1)
|
||||
transitivePeerDependencies:
|
||||
- '@edge-runtime/vm'
|
||||
- '@types/debug'
|
||||
- '@types/node'
|
||||
- '@vitest/browser'
|
||||
- '@vitest/ui'
|
||||
- happy-dom
|
||||
- jsdom
|
||||
- less
|
||||
- lightningcss
|
||||
- msw
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
unbox-primitive@1.1.0:
|
||||
dependencies:
|
||||
@ -13527,6 +13690,24 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-node@3.2.4(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.1
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
- lightningcss
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite@5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
@ -13578,6 +13759,45 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vitest@3.2.4(@types/node@24.0.10)(jsdom@25.0.1(bufferutil@4.0.9)(utf-8-validate@6.0.5))(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1):
|
||||
dependencies:
|
||||
'@types/chai': 5.2.2
|
||||
'@vitest/expect': 3.2.4
|
||||
'@vitest/mocker': 3.2.4(vite@5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1))
|
||||
'@vitest/pretty-format': 3.2.4
|
||||
'@vitest/runner': 3.2.4
|
||||
'@vitest/snapshot': 3.2.4
|
||||
'@vitest/spy': 3.2.4
|
||||
'@vitest/utils': 3.2.4
|
||||
chai: 5.2.0
|
||||
debug: 4.4.1
|
||||
expect-type: 1.2.1
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.2
|
||||
std-env: 3.9.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 0.3.2
|
||||
tinyglobby: 0.2.14
|
||||
tinypool: 1.1.1
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1)
|
||||
vite-node: 3.2.4(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 24.0.10
|
||||
jsdom: 25.0.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
- lightningcss
|
||||
- msw
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vscode-languageserver-types@3.17.5: {}
|
||||
|
||||
w3c-keyname@2.2.8: {}
|
||||
|
Loading…
Reference in New Issue
Block a user