Browse Source

Refactor, add tests

Extract client components and organize code base.

We also add tests for the form component.
master
TheoryOfNekomata 7 months ago
parent
commit
0dfe9aa6bb
14 changed files with 476 additions and 274 deletions
  1. +6
    -3
      packages/iceform-next/package.json
  2. +0
    -162
      packages/iceform-next/src/client.tsx
  3. +26
    -0
      packages/iceform-next/src/client/common.ts
  4. +94
    -0
      packages/iceform-next/src/client/components/Form.tsx
  5. +89
    -0
      packages/iceform-next/src/client/hooks/useFormFetch.ts
  6. +26
    -0
      packages/iceform-next/src/client/hooks/useResponse.ts
  7. +3
    -0
      packages/iceform-next/src/client/index.ts
  8. +1
    -1
      packages/iceform-next/src/index.ts
  9. +1
    -1
      packages/iceform-next/src/server.ts
  10. +0
    -0
      packages/iceform-next/src/utils/request.ts
  11. +1
    -1
      packages/iceform-next/src/utils/serialization.ts
  12. +61
    -0
      packages/iceform-next/test/client.test.tsx
  13. +0
    -13
      packages/iceform-next/test/index.test.tsx
  14. +168
    -93
      pnpm-lock.yaml

+ 6
- 3
packages/iceform-next/package.json View File

@@ -13,13 +13,16 @@
"pridepack"
],
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@types/testing-library__jest-dom": "^5.14.9",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"@types/busboy": "^1.5.1",
"@types/cookie": "^0.5.2",
"@types/express": "^4.17.17",
"@types/node": "^18.14.1",
"@types/react": "^18.0.27",
"@vitest/coverage-v8": "^0.33.0",
"eslint": "^8.35.0",
"eslint-config-lxsmnsyc": "^0.5.0",
"express": "^4.18.2",
@@ -31,7 +34,7 @@
"react-test-renderer": "^18.2.0",
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vitest": "^0.28.1"
"vitest": "^0.34.1"
},
"peerDependencies": {
"next": "13.4.19",


+ 0
- 162
packages/iceform-next/src/client.tsx View File

@@ -1,162 +0,0 @@
import * as React from 'react';
import fetchPonyfill from 'fetch-ponyfill';
import { NextPage as DefaultNextPage } from 'next';
import {
NextApiResponse,
NextApiRequest,
ENCTYPE_APPLICATION_JSON,
ENCTYPE_MULTIPART_FORM_DATA,
} from './common';
import {
DEFAULT_ENCTYPE_SERIALIZERS,
EncTypeSerializerMap, serializeBody,
SerializerOptions,
} from './utils/serialization';

const FormDerivedElementComponent = 'form' as const;

type FormDerivedElement = HTMLElementTagNameMap[typeof FormDerivedElementComponent];

const ALLOWED_SERVER_METHODS = ['get', 'post'] as const;

type AllowedServerMethod = typeof ALLOWED_SERVER_METHODS[number];

const ALLOWED_CLIENT_METHODS = [
...ALLOWED_SERVER_METHODS,
'put',
'patch',
'delete',
] as const;

type AllowedClientMethod = typeof ALLOWED_CLIENT_METHODS[number];

export interface FormProps extends Omit<React.HTMLProps<FormDerivedElement>, 'action' | 'method'> {
action?: string;
method?: AllowedServerMethod;
clientAction?: string;
clientHeaders?: HeadersInit;
clientMethod?: AllowedClientMethod;
invalidate?: (...args: unknown[]) => unknown;
refresh?: (response: Response) => void;
encTypeSerializers?: EncTypeSerializerMap;
responseEncType?: string;
serializerOptions?: SerializerOptions;
}

export const useResponse = (res: NextApiResponse) => {
const [response, setResponse] = React.useState<Response | undefined>(
res.body ? new Response(res.body as unknown as BodyInit) : undefined,
);

const invalidate = React.useCallback(() => {
setResponse(undefined);
}, []);

const refresh = React.useCallback((newResponse: Response) => {
setResponse(newResponse);
}, []);

return React.useMemo(() => ({
response,
refresh,
invalidate,
}), [
response,
refresh,
invalidate,
]);
};

export const Form = React.forwardRef<FormDerivedElement, FormProps>(({
children,
onSubmit,
action,
method = 'get',
clientAction = action,
clientMethod = method,
clientHeaders,
encType = ENCTYPE_MULTIPART_FORM_DATA,
invalidate,
refresh,
encTypeSerializers = DEFAULT_ENCTYPE_SERIALIZERS,
responseEncType = ENCTYPE_APPLICATION_JSON,
serializerOptions,
...etcProps
}, forwardedRef) => {
const handleSubmit: React.FormEventHandler<FormDerivedElement> = async (event) => {
event.preventDefault();
const nativeEvent = event.nativeEvent as unknown as { submitter?: HTMLElement };

if (clientAction) {
invalidate?.();
const { fetch } = fetchPonyfill();
const headers: HeadersInit = {
...(clientHeaders ?? {}),
Accept: responseEncType,
};
if (encType !== ENCTYPE_MULTIPART_FORM_DATA) {
// browser automatically generates content-type header for multipart/form-data
(headers as unknown as Record<string, string>)['Content-Type'] = encType;
}

const fetchInit: RequestInit = {
method: clientMethod.toUpperCase(),
headers,
};

if (!['GET', 'HEAD'].includes(clientMethod.toUpperCase())) {
fetchInit.body = serializeBody({
form: event.currentTarget,
encType,
serializers: encTypeSerializers,
options: {
...serializerOptions,
submitter: nativeEvent.submitter,
},
});
}
const response = await fetch(clientAction, fetchInit);
refresh?.(response);
}

onSubmit?.(event);
};

const serverMethodRaw = method.toLowerCase();
const serverMethod = serverMethodRaw === 'get' ? 'get' : 'post';

return (
<FormDerivedElementComponent
{...etcProps}
ref={forwardedRef}
onSubmit={handleSubmit}
action={action}
method={serverMethod}
encType={ENCTYPE_MULTIPART_FORM_DATA}
>
{children}
</FormDerivedElementComponent>
);
});

Form.displayName = 'Form' as const;

Form.defaultProps = {
action: undefined,
clientAction: undefined,
clientHeaders: undefined,
clientMethod: undefined,
encTypeSerializers: DEFAULT_ENCTYPE_SERIALIZERS,
invalidate: undefined,
method: 'get' as const,
refresh: undefined,
responseEncType: ENCTYPE_APPLICATION_JSON,
serializerOptions: undefined,
};

export type NextPage<T = NonNullable<unknown>, U = T> = DefaultNextPage<
T & {
res: NextApiResponse;
req: NextApiRequest;
}, U
>

+ 26
- 0
packages/iceform-next/src/client/common.ts View File

@@ -0,0 +1,26 @@
import { NextPage as DefaultNextPage } from 'next/types';
import { NextApiRequest, NextApiResponse } from '../common';

export const FormDerivedElementComponent = 'form' as const;

export type FormDerivedElement = HTMLElementTagNameMap[typeof FormDerivedElementComponent];

export const ALLOWED_SERVER_METHODS = ['get', 'post'] as const;

export type AllowedServerMethod = typeof ALLOWED_SERVER_METHODS[number];

export const ALLOWED_CLIENT_METHODS = [
...ALLOWED_SERVER_METHODS,
'put',
'patch',
'delete',
] as const;

export type AllowedClientMethod = typeof ALLOWED_CLIENT_METHODS[number];

export type NextPage<T = NonNullable<unknown>, U = T> = DefaultNextPage<
T & {
res: NextApiResponse;
req: NextApiRequest;
}, U
>

+ 94
- 0
packages/iceform-next/src/client/components/Form.tsx View File

@@ -0,0 +1,94 @@
import * as React from 'react';
import {
ENCTYPE_APPLICATION_JSON,
ENCTYPE_MULTIPART_FORM_DATA,
} from '../../common';
import {
DEFAULT_ENCTYPE_SERIALIZERS,
EncTypeSerializerMap,
SerializerOptions,
} from '../../utils/serialization';
import {
AllowedClientMethod,
AllowedServerMethod,
FormDerivedElement,
FormDerivedElementComponent,
} from '../common';
import { useFormFetch } from '../hooks/useFormFetch';

export interface FormProps extends Omit<React.HTMLProps<FormDerivedElement>, 'action' | 'method'> {
action?: string;
method?: AllowedServerMethod;
clientAction?: string;
clientHeaders?: HeadersInit;
clientMethod?: AllowedClientMethod;
invalidate?: (...args: unknown[]) => unknown;
refresh?: (response: Response) => void;
encTypeSerializers?: EncTypeSerializerMap;
responseEncType?: string;
serializerOptions?: SerializerOptions;
disableFetch?: boolean;
}

export const Form = React.forwardRef<FormDerivedElement, FormProps>(({
children,
onSubmit,
action,
method = 'get' as const,
clientAction = action,
clientMethod = method,
clientHeaders,
encType = ENCTYPE_MULTIPART_FORM_DATA,
invalidate,
refresh,
encTypeSerializers = DEFAULT_ENCTYPE_SERIALIZERS,
responseEncType = ENCTYPE_APPLICATION_JSON,
serializerOptions,
disableFetch = false,
...etcProps
}, forwardedRef) => {
const { handleSubmit } = useFormFetch({
clientAction,
onSubmit,
clientMethod,
clientHeaders,
encType,
invalidate,
refresh,
encTypeSerializers,
responseEncType,
serializerOptions,
});

const serverMethodRaw = method.toLowerCase();
const serverMethod = serverMethodRaw === 'get' ? 'get' : 'post';

return (
<FormDerivedElementComponent
{...etcProps}
ref={forwardedRef}
onSubmit={disableFetch ? onSubmit : handleSubmit}
action={action}
method={serverMethod}
encType={ENCTYPE_MULTIPART_FORM_DATA}
>
{children}
</FormDerivedElementComponent>
);
});

Form.displayName = 'Form' as const;

Form.defaultProps = {
action: undefined,
clientAction: undefined,
clientHeaders: undefined,
clientMethod: undefined,
encTypeSerializers: DEFAULT_ENCTYPE_SERIALIZERS,
disableFetch: false as const,
invalidate: undefined,
method: 'get' as const,
refresh: undefined,
responseEncType: ENCTYPE_APPLICATION_JSON,
serializerOptions: undefined,
};

+ 89
- 0
packages/iceform-next/src/client/hooks/useFormFetch.ts View File

@@ -0,0 +1,89 @@
import * as React from 'react';
import fetchPonyfill from 'fetch-ponyfill';
import { EncTypeSerializerMap, serializeBody, SerializerOptions } from '../../utils/serialization';
import { ENCTYPE_MULTIPART_FORM_DATA } from '../../common';
import { AllowedClientMethod, FormDerivedElement } from '../common';

export interface UseFormFetchParams {
clientAction?: string;
clientHeaders?: HeadersInit;
clientMethod: AllowedClientMethod;
encType: string;
invalidate?: (...args: unknown[]) => unknown;
refresh?: (response: Response) => void;
encTypeSerializers?: EncTypeSerializerMap;
responseEncType: string;
serializerOptions?: SerializerOptions;
onSubmit?: React.FormEventHandler<FormDerivedElement>;
}

export const useFormFetch = ({
clientAction,
invalidate,
clientHeaders,
responseEncType,
encType,
clientMethod,
encTypeSerializers,
serializerOptions,
refresh,
onSubmit,
}: UseFormFetchParams) => {
const handleSubmit: React.FormEventHandler<
FormDerivedElement
> = React.useCallback(async (event) => {
event.preventDefault();
const { submitter } = event.nativeEvent as unknown as { submitter?: HTMLElement };

if (clientAction) {
invalidate?.();
const { fetch } = fetchPonyfill();
const headers: HeadersInit = {
...(clientHeaders ?? {}),
Accept: responseEncType,
};
if (encType !== ENCTYPE_MULTIPART_FORM_DATA) {
// browser automatically generates content-type header for multipart/form-data
(headers as unknown as Record<string, string>)['Content-Type'] = encType;
}

const fetchInit: RequestInit = {
method: clientMethod.toUpperCase(),
headers,
};

if (!['GET', 'HEAD'].includes(clientMethod.toUpperCase())) {
fetchInit.body = serializeBody({
form: event.currentTarget,
encType,
serializers: encTypeSerializers,
options: {
...serializerOptions,
submitter,
},
});
}
const response = await fetch(clientAction, fetchInit);
refresh?.(response);
}

onSubmit?.(event);
}, [
clientAction,
invalidate,
clientHeaders,
responseEncType,
encType,
clientMethod,
encTypeSerializers,
serializerOptions,
refresh,
onSubmit,
]);

return React.useMemo(() => ({
handleSubmit,
}), [
handleSubmit,
]);
};

+ 26
- 0
packages/iceform-next/src/client/hooks/useResponse.ts View File

@@ -0,0 +1,26 @@
import * as React from 'react';
import { NextApiResponse } from '../../common';

export const useResponse = (res: NextApiResponse) => {
const [response, setResponse] = React.useState<Response | undefined>(
res.body ? new Response(res.body as unknown as BodyInit) : undefined,
);

const invalidate = React.useCallback(() => {
setResponse(undefined);
}, []);

const refresh = React.useCallback((newResponse: Response) => {
setResponse(newResponse);
}, []);

return React.useMemo(() => ({
response,
refresh,
invalidate,
}), [
response,
refresh,
invalidate,
]);
};

+ 3
- 0
packages/iceform-next/src/client/index.ts View File

@@ -0,0 +1,3 @@
export * from './hooks/useResponse';
export * from './components/Form';
export * from './common';

+ 1
- 1
packages/iceform-next/src/index.ts View File

@@ -1,3 +1,3 @@
export * from './common';
export type * from './common';
export * from './client';
export * from './server';

+ 1
- 1
packages/iceform-next/src/server.ts View File

@@ -12,7 +12,7 @@ import {
ENCTYPE_APPLICATION_JSON,
ENCTYPE_APPLICATION_OCTET_STREAM,
} from './common';
import { getBody } from './utils/body';
import { getBody } from './utils/request';
import {
CookieManager,
BODY_COOKIE_KEY,


packages/iceform-next/src/utils/body.ts → packages/iceform-next/src/utils/request.ts View File


+ 1
- 1
packages/iceform-next/src/utils/serialization.ts View File

@@ -5,7 +5,7 @@ import {
ENCTYPE_MULTIPART_FORM_DATA,
ENCTYPE_X_WWW_FORM_URLENCODED,
} from '../common';
import { getBody, parseMultipartFormData } from './body';
import { getBody, parseMultipartFormData } from './request';

export type EncTypeSerializer = (data: unknown) => string;



+ 61
- 0
packages/iceform-next/test/client.test.tsx View File

@@ -0,0 +1,61 @@
import * as React from 'react';
import {
vi,
describe,
it,
expect,
afterEach,
} from 'vitest';
import userEvent from '@testing-library/user-event';
import { render, screen, cleanup } from '@testing-library/react';
import matchers from '@testing-library/jest-dom/matchers';
import { Form } from '../src';

expect.extend(matchers);

describe('Form', () => {
afterEach(() => {
cleanup();
});

it('renders a form', () => {
render(<Form aria-label="Form" />);

const form = screen.getByRole('form');

expect(form).toBeInTheDocument();
});

it('calls the submit handler on submit', async () => {
const onSubmit = vi.fn();

render(
<Form onSubmit={onSubmit} aria-label="Form">
<button type="submit">Submit</button>
</Form>,
);

const button = screen.getByRole('button', { name: 'Submit' });

await userEvent.click(button);

expect(onSubmit).toHaveBeenCalled();
});

it.skip('calls the submit handler on submit with a custom component', async () => {
// TODO
const onSubmit = vi.fn();

render(
<Form onSubmit={onSubmit} aria-label="Form">
<button type="submit">Submit</button>
</Form>,
);

const button = screen.getByRole('button', { name: 'Submit' });

await userEvent.click(button);

expect(onSubmit).toHaveBeenCalled();
});
});

+ 0
- 13
packages/iceform-next/test/index.test.tsx View File

@@ -1,13 +0,0 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import Hello from '../src';

describe('Example', () => {
it('should have the expected content', () => {
const greeting = 'World';
render(<Hello greeting={greeting} />);

expect(screen.getByText(`Hello ${greeting}`)).toBeInTheDocument();
});
});

+ 168
- 93
pnpm-lock.yaml View File

@@ -30,6 +30,9 @@ importers:
'@testing-library/react':
specifier: ^13.4.0
version: 13.4.0(react-dom@18.2.0)(react@18.2.0)
'@testing-library/user-event':
specifier: ^14.4.3
version: 14.4.3(@testing-library/dom@8.20.1)
'@types/busboy':
specifier: ^1.5.1
version: 1.5.1
@@ -45,6 +48,12 @@ importers:
'@types/react':
specifier: ^18.0.27
version: 18.2.21
'@types/testing-library__jest-dom':
specifier: ^5.14.9
version: 5.14.9
'@vitest/coverage-v8':
specifier: ^0.33.0
version: 0.33.0(vitest@0.34.1)
eslint:
specifier: ^8.35.0
version: 8.49.0
@@ -79,8 +88,8 @@ importers:
specifier: ^4.9.5
version: 4.9.5
vitest:
specifier: ^0.28.1
version: 0.28.1(jsdom@21.1.0)
specifier: ^0.34.1
version: 0.34.1(jsdom@21.1.0)

packages/iceform-next-sandbox:
dependencies:
@@ -373,6 +382,10 @@ packages:
'@babel/helper-validator-identifier': 7.22.19
to-fast-properties: 2.0.0

/@bcoe/v8-coverage@0.2.3:
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
dev: true

/@esbuild/android-arm64@0.17.19:
resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
engines: {node: '>=12'}
@@ -819,6 +832,11 @@ packages:
/@humanwhocodes/object-schema@1.2.1:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}

/@istanbuljs/schema@0.1.3:
resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
engines: {node: '>=8'}
dev: true

/@jest/expect-utils@29.7.0:
resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -1039,6 +1057,15 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true

/@testing-library/user-event@14.4.3(@testing-library/dom@8.20.1):
resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==}
engines: {node: '>=12', npm: '>=6'}
peerDependencies:
'@testing-library/dom': '>=7.21.4'
dependencies:
'@testing-library/dom': 8.20.1
dev: true

/@theoryofnekomata/formxtra@1.0.3:
resolution: {integrity: sha512-xOzE07Slttpx7vbOWqXfatJ+k44TN4zUjI57A5/sNqUDtHzp3pz94A+AVPGVoBY0QXiwzMjeN4DPMp6U1qlkyg==}
engines: {node: '>=10'}
@@ -1425,36 +1452,63 @@ packages:
eslint-visitor-keys: 3.4.3
dev: false

/@vitest/expect@0.28.1:
resolution: {integrity: sha512-BOvWjBoocKrrTTTC0opIvzOEa7WR/Ovx4++QYlbjYKjnQJfWRSEQkTpAIEfOURtZ/ICcaLk5jvsRshXvjarZew==}
/@vitest/coverage-v8@0.33.0(vitest@0.34.1):
resolution: {integrity: sha512-Rj5IzoLF7FLj6yR7TmqsfRDSeaFki6NAJ/cQexqhbWkHEV2htlVGrmuOde3xzvFsCbLCagf4omhcIaVmfU8Okg==}
peerDependencies:
vitest: '>=0.32.0 <1'
dependencies:
'@vitest/spy': 0.28.1
'@vitest/utils': 0.28.1
'@ampproject/remapping': 2.2.1
'@bcoe/v8-coverage': 0.2.3
istanbul-lib-coverage: 3.2.0
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 4.0.1
istanbul-reports: 3.1.6
magic-string: 0.30.3
picocolors: 1.0.0
std-env: 3.4.3
test-exclude: 6.0.0
v8-to-istanbul: 9.1.0
vitest: 0.34.1(jsdom@21.1.0)
transitivePeerDependencies:
- supports-color
dev: true

/@vitest/expect@0.34.1:
resolution: {integrity: sha512-q2CD8+XIsQ+tHwypnoCk8Mnv5e6afLFvinVGCq3/BOT4kQdVQmY6rRfyKkwcg635lbliLPqbunXZr+L1ssUWiQ==}
dependencies:
'@vitest/spy': 0.34.1
'@vitest/utils': 0.34.1
chai: 4.3.8
dev: true

/@vitest/runner@0.28.1:
resolution: {integrity: sha512-kOdmgiNe+mAxZhvj2eUTqKnjfvzzknmrcS+SZXV7j6VgJuWPFAMCv3TWOe03nF9dkqDfVLCDRw/hwFuCzmzlQg==}
/@vitest/runner@0.34.1:
resolution: {integrity: sha512-YfQMpYzDsYB7yqgmlxZ06NI4LurHWfrH7Wy3Pvf/z/vwUSgq1zLAb1lWcItCzQG+NVox+VvzlKQrYEXb47645g==}
dependencies:
'@vitest/utils': 0.28.1
'@vitest/utils': 0.34.1
p-limit: 4.0.0
pathe: 1.1.1
dev: true

/@vitest/spy@0.28.1:
resolution: {integrity: sha512-XGlD78cG3IxXNnGwEF121l0MfTNlHSdI25gS2ik0z6f/D9wWUOru849QkJbuNl4CMlZCtNkx3b5IS6MRwKGKuA==}
/@vitest/snapshot@0.34.1:
resolution: {integrity: sha512-0O9LfLU0114OqdF8lENlrLsnn024Tb1CsS9UwG0YMWY2oGTQfPtkW+B/7ieyv0X9R2Oijhi3caB1xgGgEgclSQ==}
dependencies:
tinyspy: 1.1.1
magic-string: 0.30.3
pathe: 1.1.1
pretty-format: 29.7.0
dev: true

/@vitest/utils@0.28.1:
resolution: {integrity: sha512-a7cV1fs5MeU+W+8sn8gM9gV+q7V/wYz3/4y016w/icyJEKm9AMdSHnrzxTWaElJ07X40pwU6m5353Jlw6Rbd8w==}
/@vitest/spy@0.34.1:
resolution: {integrity: sha512-UT4WcI3EAPUNO8n6y9QoEqynGGEPmmRxC+cLzneFFXpmacivjHZsNbiKD88KUScv5DCHVDgdBsLD7O7s1enFcQ==}
dependencies:
cli-truncate: 3.1.0
diff: 5.1.0
tinyspy: 2.1.1
dev: true

/@vitest/utils@0.34.1:
resolution: {integrity: sha512-/ql9dsFi4iuEbiNcjNHQWXBum7aL8pyhxvfnD9gNtbjR9fUKAjxhj4AA3yfLXg6gJpMGGecvtF8Au2G9y3q47Q==}
dependencies:
diff-sequences: 29.6.3
loupe: 2.3.6
picocolors: 1.0.0
pretty-format: 27.5.1
pretty-format: 29.7.0
dev: true

/abab@2.0.6:
@@ -1536,11 +1590,6 @@ packages:
engines: {node: '>=10'}
dev: true

/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
dev: true

/any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
dev: false
@@ -1761,10 +1810,6 @@ packages:
node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.10)

/buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: true

/buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
dependencies:
@@ -1888,14 +1933,6 @@ packages:
engines: {node: '>=6'}
dev: true

/cli-truncate@3.1.0:
resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
slice-ansi: 5.0.0
string-width: 5.1.2
dev: true

/client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}

@@ -2185,11 +2222,6 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true

/diff@5.1.0:
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
engines: {node: '>=0.3.1'}
dev: true

/dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
@@ -2235,10 +2267,6 @@ packages:
engines: {node: '>=12'}
dev: true

/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true

/ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: true
@@ -3355,6 +3383,10 @@ packages:
whatwg-encoding: 2.0.0
dev: true

/html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
dev: true

/http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
@@ -3525,11 +3557,6 @@ packages:
engines: {node: '>=8'}
dev: true

/is-fullwidth-code-point@4.0.0:
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
engines: {node: '>=12'}
dev: true

/is-generator-function@1.0.10:
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
engines: {node: '>= 0.4'}
@@ -3644,6 +3671,39 @@ packages:
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}

/istanbul-lib-coverage@3.2.0:
resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==}
engines: {node: '>=8'}
dev: true

/istanbul-lib-report@3.0.1:
resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
engines: {node: '>=10'}
dependencies:
istanbul-lib-coverage: 3.2.0
make-dir: 4.0.0
supports-color: 7.2.0
dev: true

/istanbul-lib-source-maps@4.0.1:
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
engines: {node: '>=10'}
dependencies:
debug: 4.3.4
istanbul-lib-coverage: 3.2.0
source-map: 0.6.1
transitivePeerDependencies:
- supports-color
dev: true

/istanbul-reports@3.1.6:
resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==}
engines: {node: '>=8'}
dependencies:
html-escaper: 2.0.2
istanbul-lib-report: 3.0.1
dev: true

/iterator.prototype@1.1.2:
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
dependencies:
@@ -3912,6 +3972,13 @@ packages:
hasBin: true
dev: true

/magic-string@0.30.3:
resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true

/make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
@@ -3919,6 +3986,13 @@ packages:
semver: 6.3.1
dev: true

/make-dir@4.0.0:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
dependencies:
semver: 7.5.4
dev: true

/media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
@@ -4847,25 +4921,10 @@ packages:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}

/slice-ansi@5.0.0:
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
engines: {node: '>=12'}
dependencies:
ansi-styles: 6.2.1
is-fullwidth-code-point: 4.0.0
dev: true

/source-map-js@1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}

/source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
dev: true

/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
@@ -4918,15 +4977,6 @@ packages:
strip-ansi: 6.0.1
dev: true

/string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.1.0
dev: true

/string.prototype.matchall@4.0.10:
resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==}
dependencies:
@@ -5093,6 +5143,15 @@ packages:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}

/test-exclude@6.0.0:
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
engines: {node: '>=8'}
dependencies:
'@istanbuljs/schema': 0.1.3
glob: 7.2.3
minimatch: 3.1.2
dev: true

/text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}

@@ -5113,13 +5172,13 @@ packages:
resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==}
dev: true

/tinypool@0.3.1:
resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==}
/tinypool@0.7.0:
resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
engines: {node: '>=14.0.0'}
dev: true

/tinyspy@1.1.1:
resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==}
/tinyspy@2.1.1:
resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==}
engines: {node: '>=14.0.0'}
dev: true

@@ -5331,14 +5390,23 @@ packages:
engines: {node: '>= 0.4.0'}
dev: true

/v8-to-istanbul@9.1.0:
resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==}
engines: {node: '>=10.12.0'}
dependencies:
'@jridgewell/trace-mapping': 0.3.19
'@types/istanbul-lib-coverage': 2.0.4
convert-source-map: 1.9.0
dev: true

/vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
dev: true

/vite-node@0.28.1(@types/node@18.14.1):
resolution: {integrity: sha512-Mmab+cIeElkVn4noScCRjy8nnQdh5LDIR4QCH/pVWtY15zv5Z1J7u6/471B9JZ2r8CEIs42vTbngaamOVkhPLA==}
engines: {node: '>=v14.16.0'}
/vite-node@0.34.1(@types/node@18.14.1):
resolution: {integrity: sha512-odAZAL9xFMuAg8aWd7nSPT+hU8u2r9gU3LRm9QKjxBEF2rRdWpMuqkrkjvyVQEdNFiBctqr2Gg4uJYizm5Le6w==}
engines: {node: '>=v14.18.0'}
hasBin: true
dependencies:
cac: 6.7.14
@@ -5346,8 +5414,6 @@ packages:
mlly: 1.4.2
pathe: 1.1.1
picocolors: 1.0.0
source-map: 0.6.1
source-map-support: 0.5.21
vite: 4.4.9(@types/node@18.14.1)
transitivePeerDependencies:
- '@types/node'
@@ -5396,9 +5462,9 @@ packages:
fsevents: 2.3.3
dev: true

/vitest@0.28.1(jsdom@21.1.0):
resolution: {integrity: sha512-F6wAO3K5+UqJCCGt0YAl3Ila2f+fpBrJhl9n7qWEhREwfzQeXlMkkCqGqGtzBxCSa8kv5QHrkshX8AaPTXYACQ==}
engines: {node: '>=v14.16.0'}
/vitest@0.34.1(jsdom@21.1.0):
resolution: {integrity: sha512-G1PzuBEq9A75XSU88yO5G4vPT20UovbC/2osB2KEuV/FisSIIsw7m5y2xMdB7RsAGHAfg2lPmp2qKr3KWliVlQ==}
engines: {node: '>=v14.18.0'}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
@@ -5406,6 +5472,9 @@ packages:
'@vitest/ui': '*'
happy-dom: '*'
jsdom: '*'
playwright: '*'
safaridriver: '*'
webdriverio: '*'
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
@@ -5417,14 +5486,21 @@ packages:
optional: true
jsdom:
optional: true
playwright:
optional: true
safaridriver:
optional: true
webdriverio:
optional: true
dependencies:
'@types/chai': 4.3.6
'@types/chai-subset': 1.3.3
'@types/node': 18.14.1
'@vitest/expect': 0.28.1
'@vitest/runner': 0.28.1
'@vitest/spy': 0.28.1
'@vitest/utils': 0.28.1
'@vitest/expect': 0.34.1
'@vitest/runner': 0.34.1
'@vitest/snapshot': 0.34.1
'@vitest/spy': 0.34.1
'@vitest/utils': 0.34.1
acorn: 8.10.0
acorn-walk: 8.2.0
cac: 6.7.14
@@ -5432,16 +5508,15 @@ packages:
debug: 4.3.4
jsdom: 21.1.0
local-pkg: 0.4.3
magic-string: 0.30.3
pathe: 1.1.1
picocolors: 1.0.0
source-map: 0.6.1
std-env: 3.4.3
strip-literal: 1.3.0
tinybench: 2.5.1
tinypool: 0.3.1
tinyspy: 1.1.1
tinypool: 0.7.0
vite: 4.4.9(@types/node@18.14.1)
vite-node: 0.28.1(@types/node@18.14.1)
vite-node: 0.34.1(@types/node@18.14.1)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less


Loading…
Cancel
Save