diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ee7297f4..650b6fd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,7 +62,7 @@ To add a new package, you must add a new corresponding repository inside the `packages` folder. You must also define multiple files -(like inside [`packages/core`](packages/core)): +(like inside [`packages/core`](https://github.com/foscia-dev/foscia/tree/main/packages/core)): - `src/index.ts`: contains all exported modules - `.release-it.json` manage release and dependencies update. diff --git a/Dockerfile b/Dockerfile index dfc564c3..d4f10ea4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM node:20-alpine as dependencies RUN apk update && apk add --no-cache zip git curl -RUN corepack enable +RUN npm install -g corepack@latest RUN corepack use pnpm@latest COPY entrypoint.sh /usr/local/bin/docker-entrypoint.sh diff --git a/package.json b/package.json index 22d5503d..4d744a02 100644 --- a/package.json +++ b/package.json @@ -17,37 +17,37 @@ "prepare": "husky" }, "devDependencies": { - "@commitlint/cli": "^19.3.0", - "@commitlint/config-conventional": "^19.2.2", + "@commitlint/cli": "^19.6.1", + "@commitlint/config-conventional": "^19.6.0", "@release-it/bumper": "^6.0.1", - "@release-it/conventional-changelog": "^8.0.1", - "@rollup/plugin-commonjs": "^26.0.1", + "@release-it/conventional-changelog": "^8.0.2", + "@rollup/plugin-commonjs": "^26.0.3", "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-node-resolve": "^15.3.1", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", - "@types/node": "^18.19.39", - "@typescript-eslint/eslint-plugin": "^7.14.1", - "@typescript-eslint/parser": "^7.14.1", + "@types/node": "^18.19.70", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", "@vitest/coverage-istanbul": "^1.6.0", "ansi-colors": "^4.1.3", "concurrently": "^8.2.2", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-import": "^2.29.1", - "execa": "^9.3.0", - "husky": "^9.0.11", - "jsdom": "^24.1.0", + "eslint-import-resolver-typescript": "^3.7.0", + "eslint-plugin-import": "^2.31.0", + "execa": "^9.5.2", + "husky": "^9.1.7", + "jsdom": "^24.1.3", "minimist": "^1.2.8", - "ora": "^8.0.1", - "release-it": "^17.4.0", - "rimraf": "^5.0.7", - "rollup": "^4.18.0", + "ora": "^8.1.1", + "release-it": "^17.11.0", + "rimraf": "^5.0.10", + "rollup": "^4.30.1", "tsc-alias": "^1.8.10", - "tslib": "^2.6.3", - "typescript": "^5.5.2", + "tslib": "^2.8.1", + "typescript": "^5.7.3", "vitest": "^1.6.0" }, "engines": { diff --git a/packages/cli/src/commands/initCommand.ts b/packages/cli/src/commands/initCommand.ts index a4f26ab5..9f906779 100644 --- a/packages/cli/src/commands/initCommand.ts +++ b/packages/cli/src/commands/initCommand.ts @@ -13,6 +13,7 @@ import makeCommander from '@foscia/cli/utils/cli/makeCommander'; import makeUsageExamples from '@foscia/cli/utils/cli/makeUsageExamples'; import output from '@foscia/cli/utils/cli/output'; import { + AppFramework, AppLanguage, AppModules, AppPackageManager, @@ -141,7 +142,7 @@ async function resolveEnvironment(options: InitCommandOptions) { return { packageManager, language, modules }; } -async function guessPathAndFramework(): Promise<[string] | [string, 'nuxt']> { +async function guessPathAndFramework(): Promise<[string, undefined] | [string, 'nuxt']> { if ( await pathExists(resolve('nuxt.config.ts')) || await pathExists(resolve('nuxt.config.js')) @@ -159,6 +160,7 @@ async function guessPathAndFramework(): Promise<[string] | [string, 'nuxt']> { await pathExists(resolve('resources/ts')) ? 'resources/ts/data' : 'resources/js/data', + undefined, ]; } @@ -167,10 +169,11 @@ async function guessPathAndFramework(): Promise<[string] | [string, 'nuxt']> { await pathExists(resolve('assets/ts')) ? 'assets/ts/data' : 'assets/js/data', + undefined, ]; } - return ['src/data']; + return ['src/data', undefined]; } function guessAlias(path: string) { @@ -240,9 +243,23 @@ export async function runInitCommand( ], })); - const [defaultPath, framework] = await guessPathAndFramework(); - if (framework) { - output.success(`${c.bold('detected framework:')} ${c.cyan(framework)}`); + let framework: AppFramework | undefined; + + const [defaultPath, detectedFramework] = await guessPathAndFramework(); + if (detectedFramework) { + output.success(`${c.bold('detected framework:')} ${c.cyan(detectedFramework)}`); + + const frameworkIntegrate = await promptConfirm({ + name: 'integrate', + message: 'would you like to integrate it?', + initial: true, + }); + if (frameworkIntegrate) { + framework = detectedFramework; + } else { + output.info('ok, you integrate it later using the following command, but it might requires additional configuration:'); + output.instruct(`foscia integrate ${detectedFramework}\n`); + } } const filesPath = normalize(path?.length ? path : await promptText({ @@ -259,6 +276,7 @@ export async function runInitCommand( packageManager, language, modules, + framework, path: filesPath, alias: alias || undefined, tabSize: 2, @@ -266,7 +284,7 @@ export async function runInitCommand( setLifecycleConfig(config); - const missingDependencies = await checkMissingDependencies(config.usage); + const missingDependencies = await checkMissingDependencies(config.usage, config.framework); await installDependencies(config, missingDependencies, { show }); const configContent = `${JSON.stringify(config, null, 2)}\n`; @@ -305,20 +323,10 @@ export async function runInitCommand( output.instruct('foscia make action\n'); } - if (framework) { - output.step(`${framework} integration`); + if (config.framework) { + output.step(`${config.framework} integration`); - const frameworkIntegrate = await promptConfirm({ - name: 'integrate', - message: `we detected you use ${framework}, would you like to integrate it?`, - initial: true, - }); - if (frameworkIntegrate) { - await runNuxtCommand({ payloadPlugin: 'fosciaPayloadPlugin', show, force }); - } else { - output.info('ok, integrate it later using:'); - output.instruct(`foscia integrate ${framework}\n`); - } + await runNuxtCommand({ payloadPlugin: 'fosciaPayloadPlugin', show, force }); } } diff --git a/packages/cli/src/commands/integrate/nuxtCommand.ts b/packages/cli/src/commands/integrate/nuxtCommand.ts index fbe22e5d..c772d611 100644 --- a/packages/cli/src/commands/integrate/nuxtCommand.ts +++ b/packages/cli/src/commands/integrate/nuxtCommand.ts @@ -3,8 +3,9 @@ import payloadPluginCommand, { } from '@foscia/cli/commands/integrate/nuxt/payloadPluginCommand'; import { ConfigurableOptions, useConfigOption } from '@foscia/cli/composables/useConfig'; import { ForceableOptions, useForceOption } from '@foscia/cli/composables/useForce'; -import { ShowableOptions, useShowOption } from '@foscia/cli/composables/useShow'; +import { ShowableOptions, useShow, useShowOption } from '@foscia/cli/composables/useShow'; import makeCommander from '@foscia/cli/utils/cli/makeCommander'; +import output from '@foscia/cli/utils/cli/output'; export type NuxtCommandOptions = & { payloadPlugin: string; pluginsDirectory?: string; } @@ -13,12 +14,18 @@ export type NuxtCommandOptions = & ForceableOptions; export async function runNuxtCommand(options: NuxtCommandOptions) { + const show = useShow(options); + await runPayloadPluginCommand(options.payloadPlugin, { directory: options.pluginsDirectory, show: options.show, force: options.force, config: options.config, }); + + if (!show) { + output.info('integration with Nuxt is ready, you should now ensure that you adapter configuration contains `fetch: ofetch.native` option.'); + } } export default function nuxtCommand() { diff --git a/packages/cli/src/commands/make/makeActionCommand.ts b/packages/cli/src/commands/make/makeActionCommand.ts index f0f643f4..e11590b1 100644 --- a/packages/cli/src/commands/make/makeActionCommand.ts +++ b/packages/cli/src/commands/make/makeActionCommand.ts @@ -30,6 +30,7 @@ export async function runMakeActionCommand( const show = useShow(options); const force = useForce(options); const usage = await useUsage(options, () => config.usage); + const { framework } = config; await warnMissingDependencies(config); @@ -41,8 +42,11 @@ export async function runMakeActionCommand( return renderAction({ config, imports, - usage, - options: await promptForActionFactoryOptions(config, imports, { usage, show, force }), + options: await promptForActionFactoryOptions( + config, + imports, + { usage, framework, show, force }, + ), }); }, { show, force }); } diff --git a/packages/cli/src/templates/concerns/renderImportsList.ts b/packages/cli/src/templates/concerns/renderImportsList.ts index 62749440..e7bc3e0b 100644 --- a/packages/cli/src/templates/concerns/renderImportsList.ts +++ b/packages/cli/src/templates/concerns/renderImportsList.ts @@ -60,7 +60,7 @@ function renderEsmImports(from: string, imports: ImportItem[], isTypeOnly: boole } function renderCommonJSImports(from: string, imports: ImportItem[]) { - return `const ${renderImportsNames(imports)} = require('${from})';`; + return `const ${renderImportsNames(imports)} = require('${from}');`; } function renderGroupedImports( diff --git a/packages/cli/src/templates/make/renderAction.ts b/packages/cli/src/templates/make/renderAction.ts index 7690de98..6d99448d 100644 --- a/packages/cli/src/templates/make/renderAction.ts +++ b/packages/cli/src/templates/make/renderAction.ts @@ -11,7 +11,6 @@ import toIndent from '@foscia/cli/utils/output/toIndent'; type ActionFactoryTemplateData = { config: CLIConfig; imports: ImportsList; - usage: CLIConfig['usage']; options: ActionFactoryOptions; }; @@ -22,6 +21,7 @@ function renderFactoryOptions(config: CLIConfig, options?: { [K: string]: unknow } return JSON.stringify(options, null, (config.tabSize ?? 2)) + .replace(/: "!raw!([^"]+)"/g, ': $1') .replace(/"([^"]+)":/g, '$1:') .replace(/\\"/g, '\\\'') .replace(/"/g, '\''); diff --git a/packages/cli/src/templates/make/renderEnhancer.ts b/packages/cli/src/templates/make/renderEnhancer.ts index 855a6bc1..d091c186 100644 --- a/packages/cli/src/templates/make/renderEnhancer.ts +++ b/packages/cli/src/templates/make/renderEnhancer.ts @@ -13,7 +13,7 @@ type EnhancerTemplateData = { export default function renderEnhancer( { config, imports, functionName }: EnhancerTemplateData, ) { - imports.add('context', '@foscia/core'); + imports.add('makeEnhancer', '@foscia/core'); const functionGeneric = config.language === 'ts' ? '' : ''; const paramTyping = config.language === 'ts' ? ': Action' : ''; @@ -22,12 +22,11 @@ export default function renderEnhancer( } const enhancer = ` -function ${functionName}${functionGeneric}() { -${toIndent(config, `return (action${paramTyping}) => {`)} -${toIndent(config, '// TODO Write enhancer logic.', 2)} -${toIndent(config, 'return action.use(context({}))', 2)} -${toIndent(config, '};')} -} +makeEnhancer('${functionName}', ${functionGeneric}( +${toIndent(config, '// TODO Add enhancer parameters here.', 1)} +) => async (action${paramTyping}) => action.use( +${toIndent(config, '// TODO Use other enhancers here, such as `context`.', 1)} +)) `.trim(); return ` diff --git a/packages/cli/src/templates/make/renderRunner.ts b/packages/cli/src/templates/make/renderRunner.ts index 2a32f040..7155b4a5 100644 --- a/packages/cli/src/templates/make/renderRunner.ts +++ b/packages/cli/src/templates/make/renderRunner.ts @@ -13,6 +13,8 @@ type RunnerTemplateData = { export default function renderRunner( { config, imports, functionName }: RunnerTemplateData, ) { + imports.add('makeRunner', '@foscia/core'); + const functionGeneric = config.language === 'ts' ? '' : ''; const paramTyping = config.language === 'ts' ? ': Action' : ''; if (config.language === 'ts') { @@ -20,11 +22,11 @@ export default function renderRunner( } const runner = ` -function ${functionName}${functionGeneric}() { -${toIndent(config, `return (action${paramTyping}) => {`)} -${toIndent(config, '// TODO Write runner logic.', 2)} -${toIndent(config, '};')} -} +makeRunner('${functionName}', ${functionGeneric}( +${toIndent(config, '// TODO Add runner parameters here.', 1)} +) => async (action${paramTyping}) => action.run( +${toIndent(config, '// TODO Use other enhancers and one runner here, such as `one`.', 1)} +)) `.trim(); return ` diff --git a/packages/cli/src/utils/config/config.ts b/packages/cli/src/utils/config/config.ts index f9bf7236..0246bc0b 100644 --- a/packages/cli/src/utils/config/config.ts +++ b/packages/cli/src/utils/config/config.ts @@ -1,12 +1,14 @@ export type AppPackageManager = typeof CONFIG_PACKAGE_MANAGERS[number]['value']; export type AppLanguage = typeof CONFIG_LANGUAGES[number]['value']; export type AppModules = typeof CONFIG_MODULES[number]['value']; +export type AppFramework = typeof CONFIG_FRAMEWORKS[number]['value']; export type AppUsage = typeof CONFIG_USAGES[number]['value']; export type CLIConfig = { path: string; alias?: string; tabSize?: number; + framework?: AppFramework; packageManager: AppPackageManager; language: AppLanguage; modules: AppModules; @@ -56,6 +58,14 @@ export const CONFIG_MODULES = [ }, ] as const; +export const CONFIG_FRAMEWORKS = [ + { + name: 'Nuxt', + value: 'nuxt', + packages: ['ofetch'], + }, +] as const; + export const CONFIG_USAGES = [ { name: 'consuming a JSON:API', diff --git a/packages/cli/src/utils/config/validateConfig.ts b/packages/cli/src/utils/config/validateConfig.ts index 6fdc47bd..e8089627 100644 --- a/packages/cli/src/utils/config/validateConfig.ts +++ b/packages/cli/src/utils/config/validateConfig.ts @@ -1,5 +1,5 @@ import { - CLIConfig, + CLIConfig, CONFIG_FRAMEWORKS, CONFIG_LANGUAGES, CONFIG_MODULES, CONFIG_PACKAGE_MANAGERS, @@ -20,17 +20,39 @@ export default function validateConfig(config: object) { validateRequired(value) !== true || (typeof value === 'number' && value >= 0) || 'value must be a string' ); const validateIn = (values: T) => (value: unknown) => ( - values.some((v) => value === v) || `value must match one of: ${values.join(', ')}.` + validateRequired(value) !== true || values.some((v) => value === v) || `value must match one of: ${values.join(', ')}.` ); const errors = Object.entries({ - usage: [validateIn(CONFIG_USAGES.map(({ value }) => value))], - packageManager: [validateIn(CONFIG_PACKAGE_MANAGERS.map(({ value }) => value))], - language: [validateIn(CONFIG_LANGUAGES.map(({ value }) => value))], - modules: [validateIn(CONFIG_MODULES.map(({ value }) => value))], - path: [validateRequired, validateString], - alias: [validateString], - tabSize: [validateUnsignedInt], + usage: [ + validateRequired, + validateIn(CONFIG_USAGES.map(({ value }) => value)), + ], + packageManager: [ + validateRequired, + validateIn(CONFIG_PACKAGE_MANAGERS.map(({ value }) => value)), + ], + language: [ + validateRequired, + validateIn(CONFIG_LANGUAGES.map(({ value }) => value)), + ], + modules: [ + validateRequired, + validateIn(CONFIG_MODULES.map(({ value }) => value)), + ], + framework: [ + validateIn(CONFIG_FRAMEWORKS.map(({ value }) => value)), + ], + path: [ + validateRequired, + validateString, + ], + alias: [ + validateString, + ], + tabSize: [ + validateUnsignedInt, + ], }).reduce((messages, [key, rules]) => { const value = config[key as keyof typeof config]; let message = true as true | string; diff --git a/packages/cli/src/utils/dependencies/checkMissingDependencies.ts b/packages/cli/src/utils/dependencies/checkMissingDependencies.ts index 206be449..85512d77 100644 --- a/packages/cli/src/utils/dependencies/checkMissingDependencies.ts +++ b/packages/cli/src/utils/dependencies/checkMissingDependencies.ts @@ -1,10 +1,22 @@ -import { AppUsage, CONFIG_USAGES } from '@foscia/cli/utils/config/config'; +import { + AppFramework, + AppUsage, + CONFIG_FRAMEWORKS, + CONFIG_USAGES, +} from '@foscia/cli/utils/config/config'; import usePkg from '@foscia/cli/utils/dependencies/usePkg'; import findChoice from '@foscia/cli/utils/prompts/findChoice'; -export default async function checkMissingDependencies(usage: AppUsage) { +export default async function checkMissingDependencies( + usage: AppUsage, + framework?: AppFramework, +) { const pkg = await usePkg(); - const { packages } = findChoice(CONFIG_USAGES, usage); + const packages = [...findChoice(CONFIG_USAGES, usage).packages] as string[]; + + if (framework) { + packages.push(...findChoice(CONFIG_FRAMEWORKS, framework).packages); + } return packages.filter((p) => pkg.findDependency(p) === null); } diff --git a/packages/cli/src/utils/dependencies/warnMissingDependencies.ts b/packages/cli/src/utils/dependencies/warnMissingDependencies.ts index a29508b9..1fe9a1d0 100644 --- a/packages/cli/src/utils/dependencies/warnMissingDependencies.ts +++ b/packages/cli/src/utils/dependencies/warnMissingDependencies.ts @@ -12,7 +12,7 @@ export default async function warnMissingDependencies(config: CLIConfig, otherUs if (!lifecycleWarned) { warnedMissingDependencies(); const usage = otherUsage ?? config.usage; - const missingPackages = await checkMissingDependencies(usage); + const missingPackages = await checkMissingDependencies(usage, config.framework); if (missingPackages.length) { output.warn(`missing dependencies for "${usage}", install them with:`); output.instruct(`${config.packageManager} add ${missingPackages.join(' ')}`); diff --git a/packages/cli/src/utils/prompts/promptForActionFactoryOptions.ts b/packages/cli/src/utils/prompts/promptForActionFactoryOptions.ts index 976c20a8..814fea4a 100644 --- a/packages/cli/src/utils/prompts/promptForActionFactoryOptions.ts +++ b/packages/cli/src/utils/prompts/promptForActionFactoryOptions.ts @@ -1,5 +1,5 @@ import { runMakeModelsCommand } from '@foscia/cli/commands/make/makeModelsCommand'; -import { AppUsage, CLIConfig, CONFIG_USAGES } from '@foscia/cli/utils/config/config'; +import { AppFramework, AppUsage, CLIConfig, CONFIG_USAGES } from '@foscia/cli/utils/config/config'; import hasModelsList from '@foscia/cli/utils/context/hasModelsList'; import installDependencies from '@foscia/cli/utils/dependencies/installDependencies'; import usePkg from '@foscia/cli/utils/dependencies/usePkg'; @@ -9,6 +9,7 @@ import promptText from '@foscia/cli/utils/prompts/promptText'; export type ActionFactoryPromptOptions = { usage: AppUsage; + framework?: AppFramework; show: boolean; force: boolean; }; @@ -27,7 +28,11 @@ export type ActionFactoryOptions = { adapter?: ActionFactoryDependency; }; -async function promptForHttpAdapterConfig(usage: AppUsage) { +async function promptForHttpAdapterConfig( + imports: ImportsList, + usage: AppUsage, + framework?: AppFramework, +) { const defaultBaseURL = { jsonapi: '/api/v1', jsonrest: '/api', @@ -41,7 +46,14 @@ async function promptForHttpAdapterConfig(usage: AppUsage) { default: defaultBaseURL, }); - return { baseURL }; + if (framework === 'nuxt') { + imports.add('ofetch', 'ofetch', { isDefault: true }); + } + + return { + fetch: framework === 'nuxt' ? '!raw!ofetch.native' : undefined, + baseURL, + }; } async function promptForRegistry( @@ -94,7 +106,7 @@ export default async function promptForActionFactoryOptions( test, adapter: { name: 'makeHttpAdapter', - options: await promptForHttpAdapterConfig(options.usage), + options: await promptForHttpAdapterConfig(imports, options.usage, options.framework), }, }; } @@ -113,7 +125,7 @@ export default async function promptForActionFactoryOptions( serializer: { name: 'makeJsonApiSerializer' }, adapter: { name: 'makeJsonApiAdapter', - options: await promptForHttpAdapterConfig(options.usage), + options: await promptForHttpAdapterConfig(imports, options.usage, options.framework), }, }; } @@ -132,7 +144,7 @@ export default async function promptForActionFactoryOptions( serializer: { name: 'makeJsonRestSerializer' }, adapter: { name: 'makeJsonRestAdapter', - options: await promptForHttpAdapterConfig(options.usage), + options: await promptForHttpAdapterConfig(imports, options.usage, options.framework), }, }; } diff --git a/packages/cli/tests/unit/templates.test.ts b/packages/cli/tests/unit/templates.test.ts new file mode 100644 index 00000000..149ec0c7 --- /dev/null +++ b/packages/cli/tests/unit/templates.test.ts @@ -0,0 +1,83 @@ +import renderEnhancer from '@foscia/cli/templates/make/renderEnhancer'; +import renderRunner from '@foscia/cli/templates/make/renderRunner'; +import makeImportsList from '@foscia/cli/utils/imports/makeImportsList'; +import { describe, expect, it } from 'vitest'; + +describe.concurrent('unit: templates', () => { + const jsProps = () => ({ + config: { + packageManager: 'npm', + modules: 'commonjs', + language: 'js', + path: '/tmp', + usage: 'jsonapi', + tabSize: 4, + }, + imports: makeImportsList(), + } as const); + + const tsProps = () => ({ + config: { + packageManager: 'npm', + modules: 'esm', + language: 'ts', + path: '/tmp', + alias: '@/data', + usage: 'jsonapi', + tabSize: 2, + }, + imports: makeImportsList(), + } as const); + + it('should render enhancer (JS)', async () => { + expect(renderEnhancer({ ...jsProps(), functionName: 'first' })) + .toStrictEqual(` +const { makeEnhancer } = require('@foscia/core'); + +modules.export = makeEnhancer('first', ( + // TODO Add enhancer parameters here. +) => async (action) => action.use( + // TODO Use other enhancers here, such as \`context\`. +)); +`.trim()); + }); + + it('should render enhancer (TS)', async () => { + expect(renderEnhancer({ ...tsProps(), functionName: 'first' })) + .toStrictEqual(` +import { Action, makeEnhancer } from '@foscia/core'; + +export default makeEnhancer('first', ( + // TODO Add enhancer parameters here. +) => async (action: Action) => action.use( + // TODO Use other enhancers here, such as \`context\`. +)); +`.trim()); + }); + + it('should render runner (JS)', async () => { + expect(renderRunner({ ...jsProps(), functionName: 'first' })) + .toStrictEqual(` +const { makeRunner } = require('@foscia/core'); + +modules.export = makeRunner('first', ( + // TODO Add runner parameters here. +) => async (action) => action.run( + // TODO Use other enhancers and one runner here, such as \`one\`. +)); +`.trim()); + }); + + it('should render runner (TS)', async () => { + expect(renderRunner({ ...tsProps(), functionName: 'first' })) + .toStrictEqual(` +import { Action, makeRunner } from '@foscia/core'; + +export default makeRunner('first', ( + // TODO Add runner parameters here. +) => async (action: Action) => action.run( + // TODO Use other enhancers and one runner here, such as \`one\`. +)); +`.trim()); + }); +}); diff --git a/packages/cli/typedoc.json b/packages/cli/typedoc.json deleted file mode 100644 index df6539ab..00000000 --- a/packages/cli/typedoc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": [ - "../../typedoc.json" - ], - "entryPoints": [ - "./src/index.ts" - ] -} diff --git a/packages/core/src/actions/actionName.ts b/packages/core/src/actions/actionName.ts index 2b6c58c8..fe6a7137 100644 --- a/packages/core/src/actions/actionName.ts +++ b/packages/core/src/actions/actionName.ts @@ -1,3 +1,8 @@ +/** + * Common and standardized actions that can be run using Foscia. + * + * @internal + */ enum ActionName { READ = 'read', CREATE = 'create', diff --git a/packages/core/src/actions/actionVariadicRun.ts b/packages/core/src/actions/actionVariadicRun.ts deleted file mode 100644 index 43543f8d..00000000 --- a/packages/core/src/actions/actionVariadicRun.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable max-len */ -import type { ContextEnhancer, ContextRunner } from '@foscia/core/actions/types'; - -// Do not edit this file, it is generated by `pnpm generate-files`. - -export type ActionVariadicRun = { - run(enhancer1: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, runner: ContextRunner): Promise>; - run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, enhancer15: ContextEnhancer, runner: ContextRunner): Promise>; -}; diff --git a/packages/core/src/actions/actionVariadicUse.ts b/packages/core/src/actions/actionVariadicUse.ts deleted file mode 100644 index 80471ebc..00000000 --- a/packages/core/src/actions/actionVariadicUse.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable max-len */ -import type { Action, ContextEnhancer } from '@foscia/core/actions/types'; - -// Do not edit this file, it is generated by `pnpm generate-files`. - -export type ActionVariadicUse = { - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, enhancer15: ContextEnhancer): Action; - use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, enhancer15: ContextEnhancer, enhancer16: ContextEnhancer): Action; -}; diff --git a/packages/core/src/actions/checks/isContextFunction.ts b/packages/core/src/actions/checks/isContextFunction.ts new file mode 100644 index 00000000..79d89572 --- /dev/null +++ b/packages/core/src/actions/checks/isContextFunction.ts @@ -0,0 +1,20 @@ +import isWhenContextFunction from '@foscia/core/actions/checks/isWhenContextFunction'; +import { ContextFunction } from '@foscia/core/actions/types'; +import { + SYMBOL_ACTION_CONTEXT_ENHANCER, + SYMBOL_ACTION_CONTEXT_RUNNER, +} from '@foscia/core/symbols'; +import { isFosciaType } from '@foscia/shared'; + +/** + * Check if given value is a context function with name and arguments. + * + * @param value + * + * @internal + */ +export default (value: unknown): value is ContextFunction => ( + isWhenContextFunction(value) + || isFosciaType(value, SYMBOL_ACTION_CONTEXT_ENHANCER) + || isFosciaType(value, SYMBOL_ACTION_CONTEXT_RUNNER) +); diff --git a/packages/core/src/actions/checks/isWhenContextFunction.ts b/packages/core/src/actions/checks/isWhenContextFunction.ts new file mode 100644 index 00000000..9503f978 --- /dev/null +++ b/packages/core/src/actions/checks/isWhenContextFunction.ts @@ -0,0 +1,14 @@ +import { ContextWhenFunction } from '@foscia/core/actions/types'; +import { SYMBOL_ACTION_CONTEXT_WHEN } from '@foscia/core/symbols'; +import { isFosciaType } from '@foscia/shared'; + +/** + * Check if given value is a `when` context function. + * + * @param value + * + * @internal + */ +export default (value: unknown): value is ContextWhenFunction => ( + isFosciaType(value, SYMBOL_ACTION_CONTEXT_WHEN) +); diff --git a/packages/core/src/actions/context/consumers/consumeAction.ts b/packages/core/src/actions/context/consumers/consumeAction.ts index 1fd24a60..595e9fa9 100644 --- a/packages/core/src/actions/context/consumers/consumeAction.ts +++ b/packages/core/src/actions/context/consumers/consumeAction.ts @@ -1,6 +1,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; import { ConsumeAction } from '@foscia/core/actions/types'; +/** + * Retrieve the action name from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeActionMiddlewares.ts b/packages/core/src/actions/context/consumers/consumeActionMiddlewares.ts new file mode 100644 index 00000000..18129a7b --- /dev/null +++ b/packages/core/src/actions/context/consumers/consumeActionMiddlewares.ts @@ -0,0 +1,19 @@ +import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; +import { ActionMiddleware, ConsumeMiddlewares } from '@foscia/core/actions/types'; + +/** + * Retrieve the action's middlewares from a context. + * + * @param context + * @param defaultValue + * + * @internal + */ +export default ( + context: C & Partial>, + defaultValue?: D, +) => consumeContext(context, 'middlewares', [ + 'appendActionMiddlewares', + 'prependActionMiddlewares', + 'replaceActionMiddlewares', +], defaultValue) as ActionMiddleware[] | D; diff --git a/packages/core/src/actions/context/consumers/consumeAdapter.ts b/packages/core/src/actions/context/consumers/consumeAdapter.ts index 7a805d15..e2893d34 100644 --- a/packages/core/src/actions/context/consumers/consumeAdapter.ts +++ b/packages/core/src/actions/context/consumers/consumeAdapter.ts @@ -2,6 +2,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContex import { ConsumeAdapter } from '@foscia/core/actions/types'; import { value } from '@foscia/shared'; +/** + * Retrieve the adapter from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial>, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeCache.ts b/packages/core/src/actions/context/consumers/consumeCache.ts index 29de5a1f..d235533f 100644 --- a/packages/core/src/actions/context/consumers/consumeCache.ts +++ b/packages/core/src/actions/context/consumers/consumeCache.ts @@ -2,6 +2,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContex import { ConsumeCache } from '@foscia/core/actions/types'; import { value } from '@foscia/shared'; +/** + * Retrieve the cache from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeContext.ts b/packages/core/src/actions/context/consumers/consumeContext.ts index 1ca45543..ae541ef5 100644 --- a/packages/core/src/actions/context/consumers/consumeContext.ts +++ b/packages/core/src/actions/context/consumers/consumeContext.ts @@ -1,6 +1,18 @@ import InvalidContextError from '@foscia/core/errors/invalidContextError'; import { isNil } from '@foscia/shared'; +/** + * Consume a context property. If the property is null or undefined, it will + * return the default value (if set), or throw and error listing the enhancers + * that could be used to provide the context property. + * + * @param context + * @param key + * @param enhancers + * @param defaultValue + * + * @internal + */ export default < Context extends {}, Key extends keyof Context, diff --git a/packages/core/src/actions/context/consumers/consumeData.ts b/packages/core/src/actions/context/consumers/consumeData.ts index f3622903..12c38b4d 100644 --- a/packages/core/src/actions/context/consumers/consumeData.ts +++ b/packages/core/src/actions/context/consumers/consumeData.ts @@ -1,6 +1,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; import { ConsumeData } from '@foscia/core/actions/types'; +/** + * Retrieve the data from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeDeserializer.ts b/packages/core/src/actions/context/consumers/consumeDeserializer.ts index 0ec4985f..aca47b1f 100644 --- a/packages/core/src/actions/context/consumers/consumeDeserializer.ts +++ b/packages/core/src/actions/context/consumers/consumeDeserializer.ts @@ -3,6 +3,12 @@ import { ConsumeDeserializer } from '@foscia/core/actions/types'; import { DeserializedData } from '@foscia/core/types'; import { value } from '@foscia/shared'; +/** + * Retrieve the deserializer from a context. + * + * @param context + * @param defaultValue + */ export default < C extends {}, Data, diff --git a/packages/core/src/actions/context/consumers/consumeId.ts b/packages/core/src/actions/context/consumers/consumeId.ts index b129f55c..f6f81728 100644 --- a/packages/core/src/actions/context/consumers/consumeId.ts +++ b/packages/core/src/actions/context/consumers/consumeId.ts @@ -1,6 +1,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; import { ConsumeId } from '@foscia/core/actions/types'; +/** + * Retrieve the record ID from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeInclude.ts b/packages/core/src/actions/context/consumers/consumeInclude.ts index 5ad38895..63bdd44b 100644 --- a/packages/core/src/actions/context/consumers/consumeInclude.ts +++ b/packages/core/src/actions/context/consumers/consumeInclude.ts @@ -1,6 +1,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; import { ConsumeInclude } from '@foscia/core/actions/types'; +/** + * Retrieve the included relations from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeInstance.ts b/packages/core/src/actions/context/consumers/consumeInstance.ts index 4718e9ce..f8a16807 100644 --- a/packages/core/src/actions/context/consumers/consumeInstance.ts +++ b/packages/core/src/actions/context/consumers/consumeInstance.ts @@ -2,6 +2,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContex import { ConsumeInstance } from '@foscia/core/actions/types'; import { ModelInstance } from '@foscia/core/model/types'; +/** + * Retrieve the instance from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial>, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeModel.ts b/packages/core/src/actions/context/consumers/consumeModel.ts index 9f23883a..edd191cb 100644 --- a/packages/core/src/actions/context/consumers/consumeModel.ts +++ b/packages/core/src/actions/context/consumers/consumeModel.ts @@ -2,6 +2,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContex import { ConsumeModel } from '@foscia/core/actions/types'; import { Model } from '@foscia/core/model/types'; +/** + * Retrieve the model from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial>, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeQueryAs.ts b/packages/core/src/actions/context/consumers/consumeQueryAs.ts new file mode 100644 index 00000000..24167c90 --- /dev/null +++ b/packages/core/src/actions/context/consumers/consumeQueryAs.ts @@ -0,0 +1,14 @@ +import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; +import { ConsumeQueryAs } from '@foscia/core/actions/types'; +import { Model } from '@foscia/core/model/types'; + +/** + * Retrieve the "query as" models from a context. + * + * @param context + * @param defaultValue + */ +export default ( + context: C & Partial>, + defaultValue?: D, +) => consumeContext(context, 'queryAs', ['queryAs'], defaultValue); diff --git a/packages/core/src/actions/context/consumers/consumeRegistry.ts b/packages/core/src/actions/context/consumers/consumeRegistry.ts index 9f2daaf3..cfe2f707 100644 --- a/packages/core/src/actions/context/consumers/consumeRegistry.ts +++ b/packages/core/src/actions/context/consumers/consumeRegistry.ts @@ -2,6 +2,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContex import { ConsumeRegistry } from '@foscia/core/actions/types'; import { value } from '@foscia/shared'; +/** + * Retrieve the registry from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeRelation.ts b/packages/core/src/actions/context/consumers/consumeRelation.ts index 948fb6c2..d4eb3710 100644 --- a/packages/core/src/actions/context/consumers/consumeRelation.ts +++ b/packages/core/src/actions/context/consumers/consumeRelation.ts @@ -1,6 +1,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; import { ConsumeRelation } from '@foscia/core/actions/types'; +/** + * Retrieve the relation from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/core/src/actions/context/consumers/consumeSerializer.ts b/packages/core/src/actions/context/consumers/consumeSerializer.ts index e10e4bdb..b753eaba 100644 --- a/packages/core/src/actions/context/consumers/consumeSerializer.ts +++ b/packages/core/src/actions/context/consumers/consumeSerializer.ts @@ -2,6 +2,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContex import { ConsumeSerializer } from '@foscia/core/actions/types'; import { value } from '@foscia/shared'; +/** + * Retrieve the serializer from a context. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial>, defaultValue?: D, diff --git a/packages/core/src/actions/context/enhancers/context.ts b/packages/core/src/actions/context/enhancers/context.ts index 9e12d839..fc61fefc 100644 --- a/packages/core/src/actions/context/enhancers/context.ts +++ b/packages/core/src/actions/context/enhancers/context.ts @@ -1,5 +1,5 @@ -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, WithParsedExtension } from '@foscia/core/actions/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action } from '@foscia/core/actions/types'; /** * Merge the given context into the action's current context. @@ -8,21 +8,21 @@ import { Action, WithParsedExtension } from '@foscia/core/actions/types'; * @param contextToMerge * * @category Enhancers + * + * @example + * ```typescript + * import { context } from '@foscia/core'; + * + * action().use(context({ /* ...additional context... *\/ })); + * ``` + * + * @remarks + * This is the most basic context enhancer. + * It is used by a lot of Foscia enhancers. */ -const context = ( - contextToMerge: NC, -) => async (action: Action) => action.updateContext({ +export default /* @__PURE__ */ makeEnhancer('context', ( + contextToMerge: NewContext, +) => async (action: Action) => action.updateContext({ ...await action.useContext(), ...contextToMerge, -}); - -export default /* @__PURE__ */ appendExtension( - 'context', - context, - 'use', -) as WithParsedExtension( - this: Action, - context: NC, - ): Action; -}>; +})); diff --git a/packages/core/src/actions/context/enhancers/crud/associate.ts b/packages/core/src/actions/context/enhancers/crud/associate.ts index ba6f44fc..20f70110 100644 --- a/packages/core/src/actions/context/enhancers/crud/associate.ts +++ b/packages/core/src/actions/context/enhancers/crud/associate.ts @@ -1,66 +1,51 @@ import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; -import syncRelationValue from '@foscia/core/actions/context/enhancers/crud/utils/syncRelationValue'; import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; +import fill from '@foscia/core/model/fill'; +import markSynced from '@foscia/core/model/snapshots/markSynced'; import { - Action, - ConsumeId, - ConsumeModel, - ConsumeRelation, - ConsumeSerializer, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { - Model, - ModelClassInstance, - ModelInferPropValue, ModelInstance, - ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; -const associate = < +/** + * Prepare context for a singular relation's update operation. + * This will replace the previous relation's value. + * + * @param instance + * @param relation + * @param value + * + * @category Enhancers + * @provideContext model, instance, id, relation + * @requireContext serializer + * + * @example + * ```typescript + * import { associate, none } from '@foscia/core'; + * + * await action().run(associate(post, 'author', user), none()); + * ``` + */ + +export default /* @__PURE__ */ makeEnhancer('associate', < C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, + I extends ModelInstance, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue, -) => (action: Action, E>) => action.use( + instance: I, + relation: K, + value: ModelValues[K], +) => (action: Action>) => action.use( updateRelation(instance, relation, value), - onSuccess( - () => syncRelationValue(instance, instance.$model.$schema[relation] as ModelRelation, value), - ), -); - -export default /* @__PURE__ */ appendExtension( - 'associate', - associate, - 'use', -) as WithParsedExtension, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue, - ): Action> & ConsumeRelation & ConsumeId, E>; -}>; + onSuccess(() => { + fill(instance, { [relation]: value } as unknown as Partial>); + markSynced(instance, relation); + }), +)); diff --git a/packages/core/src/actions/context/enhancers/crud/attach.ts b/packages/core/src/actions/context/enhancers/crud/attach.ts index 0de8708a..4b47bd5e 100644 --- a/packages/core/src/actions/context/enhancers/crud/attach.ts +++ b/packages/core/src/actions/context/enhancers/crud/attach.ts @@ -1,64 +1,47 @@ import ActionName from '@foscia/core/actions/actionName'; import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - Action, - ConsumeId, - ConsumeModel, - ConsumeRelation, - ConsumeSerializer, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { - Model, - ModelClassInstance, - ModelInferPropValue, ModelInstance, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; +import { Itemable, wrap } from '@foscia/shared'; -const attach = < +/** + * Prepare context for a plural relation's update operation. + * This will add instances to the previous relation's value. + * + * @param instance + * @param relation + * @param value + * + * @category Enhancers + * @provideContext model, instance, id, relation + * @requireContext serializer + * + * @example + * ```typescript + * import { attach, none } from '@foscia/core'; + * + * await action().run(attach(post, 'tags', [tag1, tag2]), none()); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('attach', < C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, + I extends ModelInstance, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], -) => updateRelation( + instance: I, + relation: K, + value: Itemable[K]>, +) => updateRelation( instance, relation, - value, + wrap(value) as ModelValues[K], ActionName.ATTACH_RELATION, -); - -export default /* @__PURE__ */ appendExtension( - 'attach', - attach, - 'use', -) as WithParsedExtension, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], - ): Action> & ConsumeRelation & ConsumeId, E>; -}>; +)); diff --git a/packages/core/src/actions/context/enhancers/crud/create.ts b/packages/core/src/actions/context/enhancers/crud/create.ts index 769625f7..894aaa1e 100644 --- a/packages/core/src/actions/context/enhancers/crud/create.ts +++ b/packages/core/src/actions/context/enhancers/crud/create.ts @@ -1,10 +1,10 @@ import ActionName from '@foscia/core/actions/actionName'; import context from '@foscia/core/actions/context/enhancers/context'; import instanceData from '@foscia/core/actions/context/enhancers/crud/instanceData'; -import onRunning from '@foscia/core/actions/context/enhancers/hooks/onRunning'; -import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; import query from '@foscia/core/actions/context/enhancers/query'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import registerWriteActionHooks + from '@foscia/core/actions/context/utilities/registerWriteActionHooks'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, ConsumeId, @@ -13,117 +13,87 @@ import { ConsumeRelation, ConsumeSerializer, ContextEnhancer, - InferConsumedInstance, - WithParsedExtension, + InferQueryInstance, } from '@foscia/core/actions/types'; -import runHooks from '@foscia/core/hooks/runHooks'; -import markSynced from '@foscia/core/model/snapshots/markSynced'; import { - Model, - ModelClassInstance, + InferModelSchemaProp, ModelInstance, + ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, } from '@foscia/core/model/types'; -/** - * Prepare context for an instance creation. - * - * @param instance - * @param throughInstance - * @param throughRelation - * - * @category Enhancers - */ -const create: { - , Record, Related, Data>( - instance: ModelClassInstance & I, - // eslint-disable-next-line max-len - ): ContextEnhancer, E, C & ConsumeModel> & ConsumeInstance>; - < - C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - RI extends InferConsumedInstance>, - Record, - Related, - Data, - >( - instance: RI, - throughInstance: ModelClassInstance & I, - throughRelation: ModelRelationKey & K, - // eslint-disable-next-line max-len - ): ContextEnhancer, E, C & ConsumeModel> & ConsumeRelation & ConsumeInstance & ConsumeId>; -} = < +export default /* @__PURE__ */ makeEnhancer('create', (< C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - RI extends InferConsumedInstance>, + I extends ModelInstance, + K extends string, + R extends InferModelSchemaProp, + RI extends InferQueryInstance>, Record, Related, Data, >( - instance: (ModelClassInstance & I) | RI, - throughInstance?: ModelClassInstance & I, - throughRelation?: ModelRelationKey & K, -) => (action: Action, E>) => action.use( + instance: I | RI, + throughInstance?: I, + throughRelation?: K & ModelRelationKey, +) => ( + action: Action>, +) => registerWriteActionHooks(action.use( query(throughInstance ?? instance, throughRelation as any), context({ action: ActionName.CREATE, instance, // Rewrite ID when creating through another record. - id: throughInstance ? (throughInstance as ModelInstance).id : undefined, + id: throughInstance ? throughInstance.id : undefined, }), instanceData(instance), - onRunning(() => runHooks(instance.$model, ['creating', 'saving'], instance)), - onSuccess(async () => { - // eslint-disable-next-line no-param-reassign - instance.$exists = true; - markSynced(instance); - await runHooks(instance.$model, ['created', 'saved'], instance); - }), -); - -export default /* @__PURE__ */ appendExtension( - 'create', - create, - 'use', -) as WithParsedExtension, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - ): Action> & ConsumeInstance, E>; - create< +), instance, ['creating', 'saving'], ['created', 'saved'], true)) as { + /** + * Prepare context for an instance creation. + * + * @param instance + * + * @category Enhancers + * @provideContext model, instance + * @requireContext serializer + * + * @example + * ```typescript + * import { create, none } from '@foscia/core'; + * + * await action().run(create(post), none()); + * ``` + */( + instance: I, + // eslint-disable-next-line max-len + ): ContextEnhancer, C & ConsumeModel & ConsumeInstance>; + /** + * Prepare context for an instance creation through another instance relation. + * + * @param instance + * @param throughInstance + * @param throughRelation + * + * @category Enhancers + * + * @example + * ```typescript + * import { create, none } from '@foscia/core'; + * + * await action().run(create(comment, post, 'comments'), none()); + * ``` + */< C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - RI extends InferConsumedInstance>, + I extends ModelInstance, + K extends string, + R extends InferModelSchemaProp, + RI extends InferQueryInstance>, Record, Related, Data, >( - this: Action, E>, instance: RI, - throughInstance: ModelClassInstance & I, - throughRelation: ModelRelationKey & K, + throughInstance: I, + throughRelation: K & ModelRelationKey, // eslint-disable-next-line max-len - ): Action> & ConsumeRelation & ConsumeInstance & ConsumeId, E>; -}>; + ): ContextEnhancer, C & ConsumeModel & ConsumeRelation & ConsumeInstance & ConsumeId>; +}); diff --git a/packages/core/src/actions/context/enhancers/crud/destroy.ts b/packages/core/src/actions/context/enhancers/crud/destroy.ts index 21607b9c..696335ca 100644 --- a/packages/core/src/actions/context/enhancers/crud/destroy.ts +++ b/packages/core/src/actions/context/enhancers/crud/destroy.ts @@ -1,56 +1,72 @@ import ActionName from '@foscia/core/actions/actionName'; import context from '@foscia/core/actions/context/enhancers/context'; -import onRunning from '@foscia/core/actions/context/enhancers/hooks/onRunning'; -import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; import query from '@foscia/core/actions/context/enhancers/query'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import registerWriteActionHooks + from '@foscia/core/actions/context/utilities/registerWriteActionHooks'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, ConsumeId, ConsumeInstance, ConsumeModel, - WithParsedExtension, + ContextEnhancer, } from '@foscia/core/actions/types'; -import runHooks from '@foscia/core/hooks/runHooks'; -import markSynced from '@foscia/core/model/snapshots/markSynced'; -import { Model, ModelClassInstance, ModelInstance } from '@foscia/core/model/types'; +import isModel from '@foscia/core/model/checks/isModel'; +import forceFill from '@foscia/core/model/forceFill'; +import { Model, ModelIdType, ModelInstance } from '@foscia/core/model/types'; +import { using } from '@foscia/shared'; -/** - * Prepare context for an instance deletion. - * - * @param instance - * - * @category Enhancers - */ -const destroy = < - C extends {}, - E extends {}, - D extends {}, - I extends ModelInstance, ->(instance: ModelClassInstance & I) => (action: Action) => action.use( - query(instance), - context({ - action: ActionName.DESTROY, - // Rewrite ID to ensure destroy targets the record termination point - // even if $exists is false. - id: (instance as ModelInstance).id, - }), - onRunning(() => runHooks(instance.$model, 'destroying', instance)), - onSuccess(async () => { - // eslint-disable-next-line no-param-reassign - instance.$exists = false; - markSynced(instance); - await runHooks(instance.$model, 'destroyed', instance); - }), -); - -export default /* @__PURE__ */ appendExtension( - 'destroy', - destroy, - 'use', -) as WithParsedExtension>( - this: Action, - instance: ModelClassInstance & I, - ): Action> & ConsumeInstance & ConsumeId, E>; -}>; +export default /* @__PURE__ */ makeEnhancer('destroy', (( + modelOrInstance: Model | ModelInstance, + id?: ModelIdType, +) => using( + // eslint-disable-next-line new-cap + isModel(modelOrInstance) ? forceFill(new modelOrInstance(), { id }) : modelOrInstance, + (instance) => (action: Action) => registerWriteActionHooks(action.use( + query(modelOrInstance as any, id as any), + context({ + action: ActionName.DESTROY, + // Rewrite ID to ensure destroy targets the record termination point + // even if $exists is false. + id: instance.id, + }), + ), instance, 'destroying', 'destroyed', false), +)) as { + /** + * Prepare context for an instance deletion. + * + * @param instance + * + * @category Enhancers + * @provideContext model, instance, id + * + * @example + * ```typescript + * import { destroy, none } from '@foscia/core'; + * + * await action().run(destroy(post), none()); + * ``` + */( + instance: I, + ): ContextEnhancer & ConsumeInstance & ConsumeId>; + /** + * Prepare context for a record deletion using model and ID. + * + * @param model + * @param id + * + * @category Enhancers + * @since 0.13.0 + * @provideContext model, id + * + * @example + * ```typescript + * import { destroy, none } from '@foscia/core'; + * + * await action().run(destroy(Post, '123'), none()); + * ``` + */( + model: M, + id: ModelIdType, + ): ContextEnhancer & ConsumeId>; +}); diff --git a/packages/core/src/actions/context/enhancers/crud/detach.ts b/packages/core/src/actions/context/enhancers/crud/detach.ts index cea0b810..4af8e163 100644 --- a/packages/core/src/actions/context/enhancers/crud/detach.ts +++ b/packages/core/src/actions/context/enhancers/crud/detach.ts @@ -1,64 +1,47 @@ import ActionName from '@foscia/core/actions/actionName'; import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - Action, - ConsumeId, - ConsumeModel, - ConsumeRelation, - ConsumeSerializer, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { - Model, - ModelClassInstance, - ModelInferPropValue, ModelInstance, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; +import { Itemable, wrap } from '@foscia/shared'; -const detach = < +/** + * Prepare context for a plural relation's update operation. + * This will remove instances from the previous relation's value. + * + * @param instance + * @param relation + * @param value + * + * @category Enhancers + * @provideContext model, instance, id, relation + * @requireContext serializer + * + * @example + * ```typescript + * import { detach, none } from '@foscia/core'; + * + * await action().run(detach(post, 'tags', [tag1, tag2]), none()); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('detach', < C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, + I extends ModelInstance, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], -) => updateRelation( + instance: I, + relation: K, + value: Itemable[K]>, +) => updateRelation( instance, relation, - value, + wrap(value) as ModelValues[K], ActionName.DETACH_RELATION, -); - -export default /* @__PURE__ */ appendExtension( - 'detach', - detach, - 'use', -) as WithParsedExtension, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], - ): Action> & ConsumeRelation & ConsumeId, E>; -}>; +)); diff --git a/packages/core/src/actions/context/enhancers/crud/dissociate.ts b/packages/core/src/actions/context/enhancers/crud/dissociate.ts index 7662bc64..065bf429 100644 --- a/packages/core/src/actions/context/enhancers/crud/dissociate.ts +++ b/packages/core/src/actions/context/enhancers/crud/dissociate.ts @@ -1,55 +1,37 @@ import associate from '@foscia/core/actions/context/enhancers/crud/associate'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { - Action, - ConsumeId, - ConsumeModel, - ConsumeRelation, - ConsumeSerializer, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { - Model, - ModelClassInstance, - ModelInstance, - ModelRelationKey, - ModelSchema, - ModelSchemaRelations, -} from '@foscia/core/model/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { ModelInstance, ModelRelationKey, ModelWritableKey } from '@foscia/core/model/types'; -const dissociate = < +/** + * Prepare context for a singular relation's update operation. + * This will remove the previous relation's value. + * + * @param instance + * @param relation + * + * @category Enhancers + * @provideContext model, instance, id, relation + * @requireContext serializer + * + * @example + * ```typescript + * import { dissociate, none } from '@foscia/core'; + * + * await action().run(dissociate(post, 'author'), none()); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('dissociate', < C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, + I extends ModelInstance, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, -) => associate(instance, relation, null as any); - -export default /* @__PURE__ */ appendExtension( - 'dissociate', - dissociate, - 'use', -) as WithParsedExtension, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - ): Action> & ConsumeRelation & ConsumeId, E>; -}>; + instance: I, + relation: K, +) => associate( + instance, + relation, + null as any, +)); diff --git a/packages/core/src/actions/context/enhancers/crud/instanceData.ts b/packages/core/src/actions/context/enhancers/crud/instanceData.ts index ad5ce9d3..e4046990 100644 --- a/packages/core/src/actions/context/enhancers/crud/instanceData.ts +++ b/packages/core/src/actions/context/enhancers/crud/instanceData.ts @@ -1,7 +1,7 @@ import context from '@foscia/core/actions/context/enhancers/context'; -import serializeInstance from '@foscia/core/actions/context/utils/serializeInstance'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, ConsumeSerializer, WithParsedExtension } from '@foscia/core/actions/types'; +import serializeInstance from '@foscia/core/actions/context/utilities/serializeInstance'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; import { ModelInstance } from '@foscia/core/model/types'; /** @@ -10,22 +10,12 @@ import { ModelInstance } from '@foscia/core/model/types'; * @param instance * * @category Enhancers + * @requireContext serializer */ -const instanceData = ( +export default /* @__PURE__ */ makeEnhancer('instanceData', ( instance: ModelInstance, ) => async ( action: Action>, ) => action.use(context({ - data: await serializeInstance(action, instance), -})); - -export default /* @__PURE__ */ appendExtension( - 'instanceData', - instanceData, - 'use', -) as WithParsedExtension( - this: Action, E>, - instance: ModelInstance, - ): Action; -}>; + data: await serializeInstance(await action.useContext(), instance), +}))); diff --git a/packages/core/src/actions/context/enhancers/crud/relationData.ts b/packages/core/src/actions/context/enhancers/crud/relationData.ts index dfd81bff..75f0be20 100644 --- a/packages/core/src/actions/context/enhancers/crud/relationData.ts +++ b/packages/core/src/actions/context/enhancers/crud/relationData.ts @@ -1,7 +1,7 @@ import context from '@foscia/core/actions/context/enhancers/context'; -import serializeRelation from '@foscia/core/actions/context/utils/serializeRelation'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, ConsumeSerializer, WithParsedExtension } from '@foscia/core/actions/types'; +import serializeRelation from '@foscia/core/actions/context/utilities/serializeRelation'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; import { ModelInstance, ModelRelationKey } from '@foscia/core/model/types'; /** @@ -11,24 +11,24 @@ import { ModelInstance, ModelRelationKey } from '@foscia/core/model/types'; * @param key * * @category Enhancers + * @requireContext serializer */ -const relationData = ( +export default /* @__PURE__ */ makeEnhancer('relationData', < + C extends {}, + I extends ModelInstance, + Record, + Related, + Data, +>( instance: I, key: ModelRelationKey, ) => async ( action: Action>, ) => action.use(context({ - data: await serializeRelation(action, instance, key, instance[key]), -})); - -export default /* @__PURE__ */ appendExtension( - 'relationData', - relationData, - 'use', -) as WithParsedExtension( - this: Action, E>, - instance: I, - key: ModelRelationKey, - ): Action; -}>; + data: await serializeRelation( + await action.useContext(), + instance, + key, + instance[key], + ), +}))); diff --git a/packages/core/src/actions/context/enhancers/crud/save.ts b/packages/core/src/actions/context/enhancers/crud/save.ts index b353a9a2..8fe231ec 100644 --- a/packages/core/src/actions/context/enhancers/crud/save.ts +++ b/packages/core/src/actions/context/enhancers/crud/save.ts @@ -1,56 +1,37 @@ import create from '@foscia/core/actions/context/enhancers/crud/create'; import update from '@foscia/core/actions/context/enhancers/crud/update'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - Action, - ConsumeId, ConsumeInstance, ConsumeModel, ConsumeSerializer, - WithParsedExtension, + ContextEnhancer, } from '@foscia/core/actions/types'; -import { Model, ModelClassInstance, ModelInstance } from '@foscia/core/model/types'; +import { ModelInstance } from '@foscia/core/model/types'; /** * Prepare context for an instance creation or update depending on its existence - * state. Calls "update" if the instance exists, otherwise call "create". + * state (calls {@link update | `update`} if the instance `$exists`, + * otherwise call {@link create | `create`}. * * @param instance * * @category Enhancers + * @provideContext model, instance, id + * @requireContext serializer + * + * @example + * ```typescript + * import { save, none } from '@foscia/core'; + * + * await action().run(save(post), none()); + * ``` */ -const save = < - C extends {}, - D extends {}, - I extends ModelInstance, - Record, - Related, - Data, ->( - instance: ModelClassInstance & I, -) => ( - action: Action>, -) => ( - instance.$exists - ? action.use(update(instance)) - : action.use(create(instance)) -) as Action> & ConsumeInstance & ConsumeId>; - -export default /* @__PURE__ */ appendExtension( - 'save', - save, - 'use', -) as WithParsedExtension, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - ): Action> & ConsumeInstance & ConsumeId, E>; -}>; +export default /* @__PURE__ */ makeEnhancer('save', ((instance: I) => ( + instance.$exists ? update(instance) : create(instance) +)) as { + ( + instance: I, + // eslint-disable-next-line max-len + ): ContextEnhancer, C & ConsumeModel & ConsumeInstance>; +}); diff --git a/packages/core/src/actions/context/enhancers/crud/update.ts b/packages/core/src/actions/context/enhancers/crud/update.ts index 2fedb915..b236b64a 100644 --- a/packages/core/src/actions/context/enhancers/crud/update.ts +++ b/packages/core/src/actions/context/enhancers/crud/update.ts @@ -1,21 +1,12 @@ import ActionName from '@foscia/core/actions/actionName'; import context from '@foscia/core/actions/context/enhancers/context'; import instanceData from '@foscia/core/actions/context/enhancers/crud/instanceData'; -import onRunning from '@foscia/core/actions/context/enhancers/hooks/onRunning'; -import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; import query from '@foscia/core/actions/context/enhancers/query'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { - Action, - ConsumeId, - ConsumeInstance, - ConsumeModel, - ConsumeSerializer, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import runHooks from '@foscia/core/hooks/runHooks'; -import markSynced from '@foscia/core/model/snapshots/markSynced'; -import { Model, ModelClassInstance, ModelInstance } from '@foscia/core/model/types'; +import registerWriteActionHooks + from '@foscia/core/actions/context/utilities/registerWriteActionHooks'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; +import { ModelInstance } from '@foscia/core/model/types'; /** * Prepare context for an instance update. @@ -23,21 +14,28 @@ import { Model, ModelClassInstance, ModelInstance } from '@foscia/core/model/typ * @param instance * * @category Enhancers + * @provideContext model, instance, id + * @requireContext serializer + * + * @example + * ```typescript + * import { update, none } from '@foscia/core'; + * + * await action().run(update(post), none()); + * ``` */ -const update = < +export default /* @__PURE__ */ makeEnhancer('update', < C extends {}, - E extends {}, - D extends {}, - I extends ModelInstance, + I extends ModelInstance, Record, Related, Data, >( - instance: ModelClassInstance & I, + instance: I, ) => ( - action: Action, E>, -) => action.use( - query, E, D, I>(instance), + action: Action>, +) => registerWriteActionHooks(action.use( + query(instance), context({ action: ActionName.UPDATE, // Rewrite ID to ensure update targets the record termination point @@ -45,30 +43,4 @@ const update = < id: (instance as ModelInstance).id, }), instanceData(instance), - onRunning(() => runHooks(instance.$model, ['updating', 'saving'], instance)), - onSuccess(async () => { - // eslint-disable-next-line no-param-reassign - instance.$exists = true; - markSynced(instance); - await runHooks(instance.$model, ['updated', 'saved'], instance); - }), -); - -export default /* @__PURE__ */ appendExtension( - 'update', - update, - 'use', -) as WithParsedExtension, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - ): Action> & ConsumeInstance & ConsumeId, E>; -}>; +), instance, ['updating', 'saving'], ['updated', 'saved'], true)); diff --git a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts index f0efc960..808dd774 100644 --- a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts +++ b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts @@ -1,81 +1,74 @@ import ActionName from '@foscia/core/actions/actionName'; import context from '@foscia/core/actions/context/enhancers/context'; import query from '@foscia/core/actions/context/enhancers/query'; -import serializeRelation from '@foscia/core/actions/context/utils/serializeRelation'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import serializeRelation from '@foscia/core/actions/context/utilities/serializeRelation'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, ConsumeId, ConsumeModel, ConsumeRelation, ConsumeSerializer, - WithParsedExtension, } from '@foscia/core/actions/types'; -import isSingularRelationDef from '@foscia/core/model/checks/isSingularRelationDef'; import { - Model, - ModelClassInstance, - ModelInferPropValue, + InferModelSchemaProp, ModelInstance, + ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; -import { wrap } from '@foscia/shared'; -type UpdateRelationActionName = - | ActionName.UPDATE_RELATION - | ActionName.ATTACH_RELATION - | ActionName.DETACH_RELATION; +/** + * Infer the relation update possible values. + * + * @internal + */ +export type InferRelationUpdateValue = R extends ModelRelation + ? NonNullable extends any[] ? T | NonNullable[number] : T : never; -const updateRelation = < +/** + * Prepare context for a singular or plural relation's update operation. + * This will replace the previous relation's value. + * + * @param instance + * @param relation + * @param value + * @param actionName + * + * @category Enhancers + * @provideContext model, instance, id, relation + * @requireContext serializer + * + * @example + * ```typescript + * import { updateRelation, none } from '@foscia/core'; + * + * await action().run(updateRelation(post, 'tags', [tag1, tag2]), none()); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('updateRelation', < C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, + I extends ModelInstance, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], - actionName: UpdateRelationActionName = ActionName.UPDATE_RELATION, -) => async (action: Action, E>) => { - const wrappedValue = isSingularRelationDef(instance.$model.$schema[relation] as RD[K]) - ? value : wrap(value); - - return action.use( - query(instance, relation), - context({ - action: actionName, - data: await serializeRelation(action, instance, relation, wrappedValue), - }), - ) as unknown as Action> & ConsumeRelation & ConsumeId, E>; -}; - -export default /* @__PURE__ */ appendExtension( - 'updateRelation', - updateRelation, - 'use', -) as WithParsedExtension, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - Record, - Related, - Data, - >( - this: Action, E>, - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], - action?: UpdateRelationActionName, - ): Action> & ConsumeRelation & ConsumeId, E>; -}>; + instance: I, + relation: K, + value: ModelValues[K], + // eslint-disable-next-line max-len + actionName: ActionName.UPDATE_RELATION | ActionName.ATTACH_RELATION | ActionName.DETACH_RELATION = ActionName.UPDATE_RELATION, +) => async (action: Action>) => action.use( + query(instance, relation), + context({ + action: actionName, + data: await serializeRelation( + await action.useContext(), + instance, + relation, + value, + ), + }), +) as unknown as Action & ConsumeRelation> & ConsumeId>); diff --git a/packages/core/src/actions/context/enhancers/crud/utils/syncRelationValue.ts b/packages/core/src/actions/context/enhancers/crud/utils/syncRelationValue.ts deleted file mode 100644 index 44a06a4e..00000000 --- a/packages/core/src/actions/context/enhancers/crud/utils/syncRelationValue.ts +++ /dev/null @@ -1,15 +0,0 @@ -import markSynced from '@foscia/core/model/snapshots/markSynced'; -import { ModelInferPropValue, ModelInstance, ModelRelation } from '@foscia/core/model/types'; - -export default < - I extends ModelInstance, - R extends ModelRelation, ->( - instance: I, - relation: R, - value: ModelInferPropValue, -) => { - // eslint-disable-next-line no-param-reassign - instance[relation.key as keyof I] = value; - markSynced(instance, relation.key as any); -}; diff --git a/packages/core/src/actions/context/enhancers/hooks/onError.ts b/packages/core/src/actions/context/enhancers/hooks/onError.ts index 07923b0b..e7aa5ecd 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onError.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onError.ts @@ -1,5 +1,5 @@ -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, WithParsedExtension } from '@foscia/core/actions/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action } from '@foscia/core/actions/types'; import registerHook from '@foscia/core/hooks/registerHook'; import { Awaitable } from '@foscia/shared'; @@ -10,20 +10,19 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks + * + * @example + * ```typescript + * import { onError } from '@foscia/core'; + * + * action().use(onError((event) => { + * console.log(event.action, event.error); + * })); + * ``` */ -const onError = ( - callback: (event: { context: C; error: unknown; }) => Awaitable, +export default /* @__PURE__ */ makeEnhancer('onError', ( + callback: (event: { action: Action; error: unknown; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'error', callback as any); -}; - -export default /* @__PURE__ */ appendExtension( - 'onError', - onError, - 'use', -) as WithParsedExtension( - this: Action, - callback: (event: { context: C; error: unknown; }) => Awaitable, - ): Action; -}>; +}); diff --git a/packages/core/src/actions/context/enhancers/hooks/onFinally.ts b/packages/core/src/actions/context/enhancers/hooks/onFinally.ts index 788e1ee7..bb55bc7e 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onFinally.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onFinally.ts @@ -1,5 +1,5 @@ -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, WithParsedExtension } from '@foscia/core/actions/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action } from '@foscia/core/actions/types'; import registerHook from '@foscia/core/hooks/registerHook'; import { Awaitable } from '@foscia/shared'; @@ -10,20 +10,19 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks + * + * @example + * ```typescript + * import { onFinally } from '@foscia/core'; + * + * action().use(onFinally((event) => { + * console.log(event.action); + * })); + * ``` */ -const onFinally = ( - callback: (event: { context: C; }) => Awaitable, +export default /* @__PURE__ */ makeEnhancer('onFinally', ( + callback: (event: { action: Action; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'finally', callback as any); -}; - -export default /* @__PURE__ */ appendExtension( - 'onFinally', - onFinally, - 'use', -) as WithParsedExtension( - this: Action, - callback: (event: { context: C; }) => Awaitable, - ): Action; -}>; +}); diff --git a/packages/core/src/actions/context/enhancers/hooks/onRunning.ts b/packages/core/src/actions/context/enhancers/hooks/onRunning.ts index 6829ea7c..695783c0 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onRunning.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onRunning.ts @@ -1,5 +1,5 @@ -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, WithParsedExtension } from '@foscia/core/actions/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action } from '@foscia/core/actions/types'; import registerHook from '@foscia/core/hooks/registerHook'; import { Awaitable } from '@foscia/shared'; @@ -10,20 +10,19 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks + * + * @example + * ```typescript + * import { onRunning } from '@foscia/core'; + * + * action().use(onRunning((event) => { + * console.log(event.action, event.runner); + * })); + * ``` */ -const onRunning = ( - callback: (event: { context: C; runner: Function; }) => Awaitable, +export default /* @__PURE__ */ makeEnhancer('onRunning', ( + callback: (event: { action: Action; runner: Function; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'running', callback as any); -}; - -export default /* @__PURE__ */ appendExtension( - 'onRunning', - onRunning, - 'use', -) as WithParsedExtension( - this: Action, - callback: (event: { context: C; runner: Function; }) => Awaitable, - ): Action; -}>; +}); diff --git a/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts b/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts index 47c440cd..bbd7ad89 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts @@ -1,5 +1,5 @@ -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, WithParsedExtension } from '@foscia/core/actions/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action } from '@foscia/core/actions/types'; import registerHook from '@foscia/core/hooks/registerHook'; import { Awaitable } from '@foscia/shared'; @@ -10,20 +10,19 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks + * + * @example + * ```typescript + * import { onSuccess } from '@foscia/core'; + * + * action().use(onSuccess((event) => { + * console.log(event.action, event.result); + * })); + * ``` */ -const onSuccess = ( - callback: (event: { context: C; result: unknown; }) => Awaitable, +export default /* @__PURE__ */ makeEnhancer('onSuccess', ( + callback: (event: { action: Action; result: unknown; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'success', callback as any); -}; - -export default /* @__PURE__ */ appendExtension( - 'onSuccess', - onSuccess, - 'use', -) as WithParsedExtension( - this: Action, - callback: (event: { context: C; result: unknown; }) => Awaitable, - ): Action; -}>; +}); diff --git a/packages/core/src/actions/context/enhancers/include.ts b/packages/core/src/actions/context/enhancers/include.ts index eff0cf5c..6d359ef7 100644 --- a/packages/core/src/actions/context/enhancers/include.ts +++ b/packages/core/src/actions/context/enhancers/include.ts @@ -1,11 +1,7 @@ +import consumeInclude from '@foscia/core/actions/context/consumers/consumeInclude'; import context from '@foscia/core/actions/context/enhancers/context'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { - Action, - ConsumeInclude, - InferConsumedModelOrInstance, - WithParsedExtension, -} from '@foscia/core/actions/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action, InferQueryModelOrInstance } from '@foscia/core/actions/types'; import { ModelRelationDotKey } from '@foscia/core/model/types'; import { ArrayableVariadic, uniqueValues, wrapVariadic } from '@foscia/shared'; @@ -17,25 +13,23 @@ import { ArrayableVariadic, uniqueValues, wrapVariadic } from '@foscia/shared'; * @param relations * * @category Enhancers + * @requireContext model + * + * @example + * ```typescript + * import { query, include } from '@foscia/core'; + * + * action().use(query(Post), include('comments')); + * action().use(query(Post), include('author', 'comments.author')); + * ``` */ -const include = ( - ...relations: ArrayableVariadic>> +export default /* @__PURE__ */ makeEnhancer('include', ( + ...relations: ArrayableVariadic>> ) => async ( - action: Action, + action: Action, ) => action.use(context({ include: uniqueValues([ - ...((await action.useContext()).include ?? []), + ...(consumeInclude(await action.useContext(), null) ?? []), ...wrapVariadic(...relations), ]), -})); - -export default /* @__PURE__ */ appendExtension( - 'include', - include, - 'use', -) as WithParsedExtension( - this: Action, - ...relations: ArrayableVariadic>> - ): Action; -}>; +}))); diff --git a/packages/core/src/actions/context/enhancers/middlewares/appendActionMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/appendActionMiddlewares.ts new file mode 100644 index 00000000..1aecb59a --- /dev/null +++ b/packages/core/src/actions/context/enhancers/middlewares/appendActionMiddlewares.ts @@ -0,0 +1,30 @@ +import replaceActionMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/replaceActionMiddlewares'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { ActionMiddleware } from '@foscia/core/actions/types'; +import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; + +/** + * Append one or many middlewares. + * + * @param middlewares + * + * @category Enhancers + * @since 0.13.0 + * + * @example + * ```typescript + * import { appendActionMiddlewares } from '@foscia/core'; + * + * const posts = await action().use(appendActionMiddlewares( + * (action, next) => { + * // Do something... + * + * return next(); + * }, + * )); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('appendActionMiddlewares', (( + ...middlewares: ArrayableVariadic> +) => replaceActionMiddlewares((prev) => [...prev, ...wrapVariadic(...middlewares)]))); diff --git a/packages/core/src/actions/context/enhancers/middlewares/prependActionMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/prependActionMiddlewares.ts new file mode 100644 index 00000000..c6de2e99 --- /dev/null +++ b/packages/core/src/actions/context/enhancers/middlewares/prependActionMiddlewares.ts @@ -0,0 +1,30 @@ +import replaceActionMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/replaceActionMiddlewares'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { ActionMiddleware } from '@foscia/core/actions/types'; +import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; + +/** + * Prepend one or many middlewares. + * + * @param middlewares + * + * @category Enhancers + * @since 0.13.0 + * + * @example + * ```typescript + * import { prependActionMiddlewares } from '@foscia/core'; + * + * const posts = await action().use(prependActionMiddlewares( + * (action, next) => { + * // Do something... + * + * return next(); + * }, + * )); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('prependActionMiddlewares', (( + ...middlewares: ArrayableVariadic> +) => replaceActionMiddlewares((prev) => [...wrapVariadic(...middlewares), ...prev]))); diff --git a/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts new file mode 100644 index 00000000..7c186c24 --- /dev/null +++ b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts @@ -0,0 +1,36 @@ +import consumeActionMiddlewares + from '@foscia/core/actions/context/consumers/consumeActionMiddlewares'; +import context from '@foscia/core/actions/context/enhancers/context'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action, ActionMiddleware } from '@foscia/core/actions/types'; +import { Awaitable } from '@foscia/shared'; + +/** + * Replace existing middlewares with the given middlewares array factory. + * + * @param middlewares + * + * @category Enhancers + * @since 0.13.0 + * + * @example + * ```typescript + * import { replaceActionMiddlewares } from '@foscia/core'; + * + * const posts = await action().use(replaceActionMiddlewares((previous) => [ + * ...previous, + * (action, next) => { + * // Do something... + * + * return next(); + * }, + * ])); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('replaceActionMiddlewares', (( + middlewares: (prev: ActionMiddleware[]) => Awaitable[]>, +) => async (action: Action) => action.use(context({ + middlewares: await middlewares( + consumeActionMiddlewares(await action.useContext(), []) as ActionMiddleware[], + ), +})))); diff --git a/packages/core/src/actions/context/enhancers/query.ts b/packages/core/src/actions/context/enhancers/query.ts index 179b5eef..fbf6e28a 100644 --- a/packages/core/src/actions/context/enhancers/query.ts +++ b/packages/core/src/actions/context/enhancers/query.ts @@ -1,58 +1,25 @@ import context from '@foscia/core/actions/context/enhancers/context'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - Action, ConsumeId, ConsumeInstance, ConsumeModel, ConsumeRelation, ContextEnhancer, - WithParsedExtension, } from '@foscia/core/actions/types'; import isModel from '@foscia/core/model/checks/isModel'; import { + InferModelSchemaProp, Model, - ModelClassInstance, ModelIdType, ModelInstance, + ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, } from '@foscia/core/model/types'; -/** - * Query the given model, instance or relation. - * - * @param modelOrInstance - * @param idOrRelation - * - * @category Enhancers - */ -const query: { - ( - model: M, - ): ContextEnhancer>; - ( - model: M, - id: ModelIdType, - ): ContextEnhancer & ConsumeId>; - >( - instance: ModelClassInstance & I, - ): ContextEnhancer> & ConsumeInstance & ConsumeId>; - < - C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, - >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - ): ContextEnhancer> & ConsumeRelation & ConsumeId>; -} = ( +export default /* @__PURE__ */ makeEnhancer('query', (( modelOrInstance: Model | ModelInstance, - idOrRelation?: ModelIdType | ModelRelationKey, + idOrRelation?: ModelIdType | string, ) => ( isModel(modelOrInstance) ? context({ model: modelOrInstance, id: idOrRelation }) @@ -62,37 +29,86 @@ const query: { id: modelOrInstance.$exists ? modelOrInstance.id : undefined, relation: idOrRelation && modelOrInstance.$model.$schema[idOrRelation], }) -); - -export default /* @__PURE__ */ appendExtension( - 'query', - query, - 'use', -) as WithParsedExtension( - this: Action, +)) as { + /** + * Query a model. + * + * @param model + * + * @category Enhancers + * @since 0.6.3 + * @provideContext model + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * + * const posts = await action().run(query(Post), all()); + * ``` + */( model: M, - ): Action, E>; - query( - this: Action, + ): ContextEnhancer>; + /** + * Query a model record by ID. + * + * @param model + * @param id + * + * @category Enhancers + * @since 0.6.3 + * @provideContext model, id + * + * @example + * ```typescript + * import { query, oneOrFail } from '@foscia/core'; + * + * const post = await action().run(query(Post, '123'), oneOrFail()); + * ``` + */( model: M, id: ModelIdType, - ): Action & ConsumeId, E>; - query>( - this: Action, - instance: ModelClassInstance & I, - ): Action> & ConsumeInstance & ConsumeId, E>; - query< + ): ContextEnhancer & ConsumeId>; + /** + * Query a model instance. + * + * @param instance + * + * @category Enhancers + * @since 0.6.3 + * @provideContext model, instance, id + * + * @example + * ```typescript + * import { query, oneOrFail } from '@foscia/core'; + * + * const refreshedPost = await action().run(query(post), oneOrFail()); + * ``` + */( + instance: I, + ): ContextEnhancer & ConsumeInstance & ConsumeId>; + /** + * Query a model instance relation. + * + * @param instance + * @param relation + * + * @category Enhancers + * @since 0.6.3 + * @provideContext model, instance, id, relation + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * + * const comments = await action().run(query(myPost, 'comments'), all()); + * ``` + */< C extends {}, - E extends {}, - D extends {}, - RD extends ModelSchemaRelations, - I extends ModelInstance, - K extends keyof ModelSchema & keyof RD & string, + I extends ModelInstance, + K extends string, + R extends InferModelSchemaProp, >( - this: Action, - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - // eslint-disable-next-line max-len - ): Action> & ConsumeRelation & ConsumeInstance & ConsumeId, E>; -}>; + instance: I, + relation: K & ModelRelationKey, + ): ContextEnhancer & ConsumeRelation & ConsumeId>; +}); diff --git a/packages/core/src/actions/context/enhancers/queryAs.ts b/packages/core/src/actions/context/enhancers/queryAs.ts new file mode 100644 index 00000000..914a0ee6 --- /dev/null +++ b/packages/core/src/actions/context/enhancers/queryAs.ts @@ -0,0 +1,39 @@ +import context from '@foscia/core/actions/context/enhancers/context'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action } from '@foscia/core/actions/types'; +import { Model } from '@foscia/core/model/types'; +import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; + +/** + * Define models targeted by the query without affecting its execution context. + * + * @param models + * + * @category Enhancers + * @since 0.13.0 + * @provideContext queryAs + * + * @example + * ```typescript + * import { queryAs } from '@foscia/core'; + * + * action().use(queryAs(Post)); + * action().use(queryAs(Post, Comment)); + * action().use(queryAs([Post, Comment])); + * ``` + * + * @remarks + * This will keep the context state when executing the query, but will take + * priority when normalizing included relations or deserializing models, + * allowing to deserialize models from any request (even non-standard ones). + * The query won't request the model like with `query`, but results will be + * deserialized as given models (e.g. `all` and `one`) and other functions will + * use the given models for contextual params (e.g. relations through `include`). + * When using a registry, `queryAs` can also be called with a type parameter + * and without the `models` parameters. + */ +export default /* @__PURE__ */ makeEnhancer('queryAs', ( + ...models: ArrayableVariadic +) => async ( + action: Action, +) => action.use(context({ queryAs: wrapVariadic(...models) }))); diff --git a/packages/core/src/actions/context/guessers/guessContextModel.ts b/packages/core/src/actions/context/guessers/guessContextModel.ts index c5934816..648a94e0 100644 --- a/packages/core/src/actions/context/guessers/guessContextModel.ts +++ b/packages/core/src/actions/context/guessers/guessContextModel.ts @@ -1,12 +1,18 @@ import guessRelationType from '@foscia/core/model/relations/utilities/guessRelationType'; import { Model, ModelRelation } from '@foscia/core/model/types'; -import { RegistryI } from '@foscia/core/types'; +import { ModelsRegistry } from '@foscia/core/types'; import { isNil, Optional, wrap } from '@foscia/shared'; -type GuessContextModelContext = { +/** + * Context used to guess a queried model. + * + * @internal + */ +export type GuessContextModelContext = { + queryAs?: Optional; model?: Optional; relation?: Optional; - registry?: Optional; + registry?: Optional; ensureType?: Optional; }; @@ -35,6 +41,10 @@ export default (async ( context: GuessContextModelContext, multiple: boolean = false, ): Promise => { + if (context.queryAs) { + return guessModelIn(context.queryAs, context.ensureType, multiple); + } + if (context.relation) { if (context.relation.model) { return guessModelIn(await context.relation.model(), context.ensureType, multiple); @@ -62,6 +72,18 @@ export default (async ( return guessModelIn(context.model, context.ensureType, multiple); }) as { + /** + * Guess the model targeted by the given context. + * + * @param context + * @param multiple + */ (context: GuessContextModelContext, multiple?: false): Promise; + /** + * Guess the models targeted by the given context. + * + * @param context + * @param multiple + */ (context: GuessContextModelContext, multiple: true): Promise; }; diff --git a/packages/core/src/actions/context/runners/all.ts b/packages/core/src/actions/context/runners/all.ts index 25bc617a..26462d9e 100644 --- a/packages/core/src/actions/context/runners/all.ts +++ b/packages/core/src/actions/context/runners/all.ts @@ -1,20 +1,23 @@ import deserializeInstances, { - DeserializedDataOf, -} from '@foscia/core/actions/context/utils/deserializeInstances'; + RetypedDeserializedData, +} from '@foscia/core/actions/context/utilities/deserializeInstances'; import executeContextThroughAdapter - from '@foscia/core/actions/context/utils/executeContextThroughAdapter'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; + from '@foscia/core/actions/context/utilities/executeContextThroughAdapter'; +import makeRunner from '@foscia/core/actions/makeRunner'; import { Action, ConsumeAdapter, ConsumeDeserializer, - InferConsumedInstance, - WithParsedExtension, + InferQueryInstance, } from '@foscia/core/actions/types'; import { ModelInstance } from '@foscia/core/model/types'; import { DeserializedData } from '@foscia/core/types'; import { Awaitable } from '@foscia/shared'; +/** + * Data retrieved with {@link all | `all`} which can be transformed + * to another return value than an instances array. + */ export type AllData< Data, Deserialized extends DeserializedData, @@ -31,53 +34,39 @@ export type AllData< * @param transform * * @category Runners + * @requireContext adapter, deserializer, model + * + * @example + * ```typescript + * import { all, query } from '@foscia/core'; + * + * const posts = await action().run(query(Post), all()); + * ``` */ -const all = < +export default /* @__PURE__ */ makeRunner('all', < C extends {}, - I extends InferConsumedInstance, + I extends InferQueryInstance, RawData, Data, Deserialized extends DeserializedData, Next = I[], >(transform?: ( - data: AllData, I>, + data: AllData, I>, ) => Awaitable) => async ( // eslint-disable-next-line max-len action: Action & ConsumeDeserializer, Deserialized>>, ) => { - const response = await executeContextThroughAdapter( - await action.useContext(), - ); + const context = await action.useContext(); + const response = await executeContextThroughAdapter(context); const data = await response.read(); const deserialized = await deserializeInstances( - action, + context, data, - ) as DeserializedDataOf; + ) as RetypedDeserializedData; return ( transform ? transform({ data, deserialized, instances: deserialized.instances }) : deserialized.instances ) as Awaitable; -}; - -export default /* @__PURE__ */ appendExtension( - 'all', - all, - 'run', -) as WithParsedExtension, - RawData, - Data, - Deserialized extends DeserializedData, - NextData = I[], - >( - // eslint-disable-next-line max-len - this: Action & ConsumeDeserializer, Deserialized>>, - transform?: ( - data: AllData, I>, - ) => Awaitable, - ): Promise>; -}>; +}); diff --git a/packages/core/src/actions/context/runners/cached.ts b/packages/core/src/actions/context/runners/cached.ts index aab286cc..27983adb 100644 --- a/packages/core/src/actions/context/runners/cached.ts +++ b/packages/core/src/actions/context/runners/cached.ts @@ -1,14 +1,6 @@ import cachedOr, { CachedData } from '@foscia/core/actions/context/runners/cachedOr'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { - Action, - ConsumeCache, - ConsumeId, - ConsumeInclude, - ConsumeModel, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { Model } from '@foscia/core/model/types'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { InferQueryInstance } from '@foscia/core/actions/types'; import { Awaitable } from '@foscia/shared'; /** @@ -17,28 +9,19 @@ import { Awaitable } from '@foscia/shared'; * returns null. * * @category Runners + * @requireContext cache, model, id + * + * @example + * ```typescript + * import { cached, query } from '@foscia/core'; + * + * const post = await action().run(query(Post, '123'), cached()); + * ``` */ -const cached = < +export default /* @__PURE__ */ makeRunner('cached', < C extends {}, - M extends Model, - I extends InstanceType, + I extends InferQueryInstance, ND = I, >( transform?: (data: CachedData) => Awaitable, -) => cachedOr(() => null, transform); - -export default /* @__PURE__ */ appendExtension( - 'cached', - cached, - 'run', -) as WithParsedExtension, - ND = I, - >( - this: Action & ConsumeInclude & ConsumeId>, - transform?: (data: CachedData) => Awaitable, - ): Promise; -}>; +) => cachedOr(() => null, transform)); diff --git a/packages/core/src/actions/context/runners/cachedOr.ts b/packages/core/src/actions/context/runners/cachedOr.ts index 09e5a809..2c29927d 100644 --- a/packages/core/src/actions/context/runners/cachedOr.ts +++ b/packages/core/src/actions/context/runners/cachedOr.ts @@ -1,7 +1,7 @@ import consumeCache from '@foscia/core/actions/context/consumers/consumeCache'; import consumeId from '@foscia/core/actions/context/consumers/consumeId'; import consumeModel from '@foscia/core/actions/context/consumers/consumeModel'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import makeRunner from '@foscia/core/actions/makeRunner'; import { Action, ConsumeCache, @@ -9,14 +9,18 @@ import { ConsumeInclude, ConsumeModel, ContextRunner, - WithParsedExtension, + InferQueryInstance, } from '@foscia/core/actions/types'; import logger from '@foscia/core/logger/logger'; import filled from '@foscia/core/model/filled'; import loaded from '@foscia/core/model/relations/loaded'; -import { Model, ModelInstance } from '@foscia/core/model/types'; +import { ModelInstance } from '@foscia/core/model/types'; import { Awaitable, isNil } from '@foscia/shared'; +/** + * Data retrieved with {@link cachedOr | `cachedOr`} which can be transformed + * to another return value than an instance. + */ export type CachedData = { instance: I; }; @@ -24,25 +28,31 @@ export type CachedData = { /** * Retrieve an instance from the cache. * If the instance is not in cache or if the included relations are not loaded, - * run the given runner. + * run the given anonymous runner. * * @param nilRunner * @param transform * * @category Runners + * @requireContext cache, model, id + * + * @example + * ```typescript + * import { cachedOr, query } from '@foscia/core'; + * + * const post = await action().run(query(Post, '123'), cachedOr(() => null)); + * ``` */ -const cachedOr = < +export default /* @__PURE__ */ makeRunner('cachedOr', < C extends {}, - E extends {}, - M extends Model, - I extends InstanceType, + I extends InferQueryInstance, RD, ND = I, >( - nilRunner: ContextRunner, E, Awaitable>, + nilRunner: ContextRunner>, transform?: (data: CachedData) => Awaitable, ) => async ( - action: Action & ConsumeInclude & ConsumeId, E>, + action: Action, ) => { const context = await action.useContext(); const model = consumeModel(context); @@ -50,7 +60,7 @@ const cachedOr = < const cache = await consumeCache(context); if (!isNil(id)) { const instance = await cache.find(model.$type, id); - if (!isNil(instance)) { + if (instance) { if (filled(instance) && loaded(instance, context.include ?? [])) { return (transform ? transform({ instance: instance as I }) : instance) as ND; } @@ -66,23 +76,4 @@ const cachedOr = < } return action.run(nilRunner); -}; - -export default /* @__PURE__ */ appendExtension( - 'cachedOr', - cachedOr, - 'run', -) as WithParsedExtension, - RD, - ND = I, - >( - this: Action & ConsumeInclude & ConsumeId, E>, - nilRunner: ContextRunner, E, Awaitable>, - transform?: (data: CachedData) => Awaitable, - ): Promise | Awaited>; -}>; +}); diff --git a/packages/core/src/actions/context/runners/cachedOrFail.ts b/packages/core/src/actions/context/runners/cachedOrFail.ts index 96c5349a..37f7cc20 100644 --- a/packages/core/src/actions/context/runners/cachedOrFail.ts +++ b/packages/core/src/actions/context/runners/cachedOrFail.ts @@ -1,49 +1,32 @@ import cachedOr, { CachedData } from '@foscia/core/actions/context/runners/cachedOr'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { - Action, - ConsumeCache, - ConsumeId, - ConsumeInclude, - ConsumeModel, - WithParsedExtension, -} from '@foscia/core/actions/types'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { InferQueryInstance } from '@foscia/core/actions/types'; import ExpectedRunFailureError from '@foscia/core/errors/expectedRunFailureError'; -import { Model } from '@foscia/core/model/types'; import { Awaitable } from '@foscia/shared'; /** * Retrieve an instance from the cache. * If the instance is not in cache or if the included relations are not loaded, - * throws an "ExpectedRunFailureError". + * throws an {@link ExpectedRunFailureError | `ExpectedRunFailureError`}. * * @category Runners + * @requireContext cache, model, id + * + * @example + * ```typescript + * import { cachedOrFail, query } from '@foscia/core'; + * + * const post = await action().run(query(Post, '123'), cachedOrFail()); + * ``` */ -const cachedOrFail = < +export default /* @__PURE__ */ makeRunner('cachedOrFail', < C extends {}, - M extends Model, - I extends InstanceType, + I extends InferQueryInstance, ND = I, >( transform?: (data: CachedData) => Awaitable, -) => cachedOr(() => { +) => cachedOr(() => { throw new ExpectedRunFailureError( '`cachedOrFail` failed. You may handle this error globally as a "not found" record error.', ); -}, transform); - -export default /* @__PURE__ */ appendExtension( - 'cachedOrFail', - cachedOrFail, - 'run', -) as WithParsedExtension, - ND = I, - >( - this: Action & ConsumeInclude & ConsumeId>, - transform?: (data: CachedData) => Awaitable, - ): Promise; -}>; +}, transform)); diff --git a/packages/core/src/actions/context/runners/catchIf.ts b/packages/core/src/actions/context/runners/catchIf.ts index 39a32d59..b456558c 100644 --- a/packages/core/src/actions/context/runners/catchIf.ts +++ b/packages/core/src/actions/context/runners/catchIf.ts @@ -1,26 +1,11 @@ -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, ContextRunner, WithParsedExtension } from '@foscia/core/actions/types'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { Action, ContextRunner } from '@foscia/core/actions/types'; import { Awaitable } from '@foscia/shared'; -export type CatchCallback = ( - error: unknown, -) => Awaitable> | boolean>; - -/** - * Run given runner and catch errors using catchCallback. - * If catchCallback is omitted, will return null on error. - * If catchCallback returns a function, will run it as an action's runner. - * Else, will ignore error and return null only if callback for error is truthy. - * - * @param runner - * @param catchCallback - * - * @category Runners - */ -const catchIf = ( - runner: ContextRunner>, - catchCallback?: CatchCallback, -) => async (action: Action): Promise => { +export default makeRunner('catchIf', (( + runner: ContextRunner>, + catchCallback: (error: unknown) => Awaitable> | boolean>, +) => async (action: Action): Promise => { try { return await action.run(runner); } catch (error) { @@ -35,16 +20,46 @@ const catchIf = ( return null as any; } -}; - -export default /* @__PURE__ */ appendExtension( - 'catchIf', - catchIf, - 'run', -) as WithParsedExtension( - this: Action, - runner: ContextRunner>, - catchCallback?: CatchCallback, - ): Promise | Awaited>; -}>; +}) as { + /** + * Run given runner and catch all errors to return null. + * + * @param runner + * + * @category Runners + * + * @example + * ```typescript + * import { catchIf, oneOrFail, query } from '@foscia/core'; + * + * const postOrNull = await action().run( + * query(Post, '123'), + * catchIf(oneOrFail()), + * ); + * ``` + */( + runner: ContextRunner>, + ): ContextRunner; + /** + * Run given runner and catch errors to `null` if catch callback returns a truthy value. + * If the catch callback returns another action runner, it will run it. + * + * @param runner + * @param catchCallback + * + * @category Runners + * + * @example + * ```typescript + * import { catchIf, oneOrFail, query } from '@foscia/core'; + * + * const postOrNull = await action().run( + * query(Post, '123'), + * catchIf(oneOrFail(), (error) => error instanceof ErrorToCatch), + * ); + * ``` + */( + runner: ContextRunner>, + catchCallback: (error: unknown) => Awaitable> | boolean>, + ): ContextRunner; +}); diff --git a/packages/core/src/actions/context/runners/none.ts b/packages/core/src/actions/context/runners/none.ts index 7dc5370f..83746c8d 100644 --- a/packages/core/src/actions/context/runners/none.ts +++ b/packages/core/src/actions/context/runners/none.ts @@ -1,23 +1,23 @@ import raw from '@foscia/core/actions/context/runners/raw'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, ConsumeAdapter, WithParsedExtension } from '@foscia/core/actions/types'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { Action, ConsumeAdapter } from '@foscia/core/actions/types'; /** * Run the action and ignore the content of the result. * Adapter errors are not caught and so may be thrown. * * @category Runners + * @requireContext adapter + * + * @example + * ```typescript + * import { destroy, none } from '@foscia/core'; + * + * await action().run(destroy(post), none()); + * ``` */ -const none = () => async (action: Action) => { +export default makeRunner('none', () => async ( + action: Action, +) => { await action.run(raw()); -}; - -export default /* @__PURE__ */ appendExtension( - 'none', - none, - 'run', -) as WithParsedExtension( - this: Action, - ): Promise; -}>; +}); diff --git a/packages/core/src/actions/context/runners/one.ts b/packages/core/src/actions/context/runners/one.ts index 862bc495..03455afb 100644 --- a/packages/core/src/actions/context/runners/one.ts +++ b/packages/core/src/actions/context/runners/one.ts @@ -1,13 +1,7 @@ import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utils/deserializeInstances'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { - Action, - ConsumeAdapter, - ConsumeDeserializer, - InferConsumedInstance, - WithParsedExtension, -} from '@foscia/core/actions/types'; +import { RetypedDeserializedData } from '@foscia/core/actions/context/utilities/deserializeInstances'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { InferQueryInstance } from '@foscia/core/actions/types'; import { DeserializedData } from '@foscia/core/types'; import { Awaitable } from '@foscia/shared'; @@ -16,33 +10,22 @@ import { Awaitable } from '@foscia/shared'; * Returns null when not found or empty result. * * @category Runners + * @requireContext adapter, deserializer, model + * + * @example + * ```typescript + * import { query, one } from '@foscia/core'; + * + * const post = await action().run(query(post, '123'), one()); + * ``` */ -const one = < +export default makeRunner('one', < C extends {}, - I extends InferConsumedInstance, + I extends InferQueryInstance, RawData, Data, Deserialized extends DeserializedData, Next = I, >( - transform?: (data: OneData, I>) => Awaitable, -) => oneOr(() => null, transform); - -export default /* @__PURE__ */ appendExtension( - 'one', - one, - 'run', -) as WithParsedExtension, - RawData, - Data, - Deserialized extends DeserializedData, - Next = I, - >( - // eslint-disable-next-line max-len - this: Action & ConsumeDeserializer, Deserialized>>, - transform?: (data: OneData, I>) => Awaitable, - ): Promise; -}>; + transform?: (data: OneData, I>) => Awaitable, +) => oneOr(() => null, transform)); diff --git a/packages/core/src/actions/context/runners/oneOr.ts b/packages/core/src/actions/context/runners/oneOr.ts index 18742dca..2305d373 100644 --- a/packages/core/src/actions/context/runners/oneOr.ts +++ b/packages/core/src/actions/context/runners/oneOr.ts @@ -1,19 +1,24 @@ import all, { AllData } from '@foscia/core/actions/context/runners/all'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utils/deserializeInstances'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import { + RetypedDeserializedData, +} from '@foscia/core/actions/context/utilities/deserializeInstances'; +import makeRunner from '@foscia/core/actions/makeRunner'; import { Action, ConsumeAdapter, ConsumeDeserializer, ContextRunner, - InferConsumedInstance, - WithParsedExtension, + InferQueryInstance, } from '@foscia/core/actions/types'; -import isNotFoundError from '@foscia/core/errors/flags/isNotFoundError'; +import { FLAG_ERROR_NOT_FOUND } from '@foscia/core/flags'; import { ModelInstance } from '@foscia/core/model/types'; import { DeserializedData } from '@foscia/core/types'; -import { Awaitable } from '@foscia/shared'; +import { Awaitable, isFosciaFlag } from '@foscia/shared'; +/** + * Data retrieved with {@link oneOr | `oneOr`} which can be transformed + * to another return value than an instance. + */ export type OneData< Data, Deserialized extends DeserializedData, @@ -29,11 +34,18 @@ export type OneData< * @param transform * * @category Runners + * @requireContext adapter, deserializer, model + * + * @example + * ```typescript + * import { query, oneOr } from '@foscia/core'; + * + * const post = await action().run(query(post, '123'), oneOr(() => null)); + * ``` */ -const oneOr = < +export default makeRunner('oneOr', < C extends {}, - E extends {}, - I extends InferConsumedInstance, + I extends InferQueryInstance, RawData, Data, Deserialized extends DeserializedData, @@ -41,11 +53,11 @@ const oneOr = < Next = I, >( // eslint-disable-next-line max-len - nilRunner: ContextRunner & ConsumeDeserializer, Deserialized>, E, Awaitable>, - transform?: (data: OneData, I>) => Awaitable, + nilRunner: ContextRunner & ConsumeDeserializer, Deserialized>, Awaitable>, + transform?: (data: OneData, I>) => Awaitable, ) => async ( // eslint-disable-next-line max-len - action: Action & ConsumeDeserializer, Deserialized>, E>, + action: Action & ConsumeDeserializer, Deserialized>>, ) => { try { const result = await action.run(all((data) => { @@ -60,33 +72,10 @@ const oneOr = < return result as Next; } } catch (error) { - if (!isNotFoundError(error)) { + if (!isFosciaFlag(error, FLAG_ERROR_NOT_FOUND)) { throw error; } } return action.run(nilRunner); -}; - -export default /* @__PURE__ */ appendExtension( - 'oneOr', - oneOr, - 'run', -) as WithParsedExtension, - RawData, - Data, - Deserialized extends DeserializedData, - NilData, - Next = I, - >( - // eslint-disable-next-line max-len - this: Action & ConsumeDeserializer, Deserialized>, E>, - // eslint-disable-next-line max-len - nilRunner: ContextRunner & ConsumeDeserializer, Deserialized>, E, NilData>, - transform?: (data: OneData, I>) => Awaitable, - ): Promise | Awaited>; -}>; +}); diff --git a/packages/core/src/actions/context/runners/oneOrCurrent.ts b/packages/core/src/actions/context/runners/oneOrCurrent.ts index 2c9a0683..cca8f831 100644 --- a/packages/core/src/actions/context/runners/oneOrCurrent.ts +++ b/packages/core/src/actions/context/runners/oneOrCurrent.ts @@ -1,15 +1,10 @@ import consumeInstance from '@foscia/core/actions/context/consumers/consumeInstance'; import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utils/deserializeInstances'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; import { - Action, - ConsumeAdapter, - ConsumeDeserializer, - ConsumeInstance, - InferConsumedInstance, - WithParsedExtension, -} from '@foscia/core/actions/types'; + RetypedDeserializedData, +} from '@foscia/core/actions/context/utilities/deserializeInstances'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { ConsumeInstance, InferQueryInstance } from '@foscia/core/actions/types'; import { ModelInstance } from '@foscia/core/model/types'; import { DeserializedData } from '@foscia/core/types'; import { Awaitable } from '@foscia/shared'; @@ -19,38 +14,26 @@ import { Awaitable } from '@foscia/shared'; * Returns current instance when not found or empty result. * * @category Runners + * @requireContext adapter, deserializer, model + * + * @example + * ```typescript + * import { save, oneOrCurrent } from '@foscia/core'; + * + * const savedPost = await action().run(save(post), oneOrCurrent()); + * ``` */ -const oneOrCurrent = < +export default makeRunner('oneOrCurrent', < C extends ConsumeInstance, - I extends InferConsumedInstance, + I extends InferQueryInstance, CI extends ModelInstance, RawData, Data, Deserialized extends DeserializedData, Next = CI, >( - transform?: (data: OneData, I>) => Awaitable, -) => oneOr, any, I, RawData, Data, Deserialized, CI, Next>( + transform?: (data: OneData, I>) => Awaitable, +) => oneOr, I, RawData, Data, Deserialized, CI, Next>( async (action) => consumeInstance(await action.useContext()) as Promise, transform, -); - -export default /* @__PURE__ */ appendExtension( - 'oneOrCurrent', - oneOrCurrent, - 'run', -) as WithParsedExtension, - I extends InferConsumedInstance, - CI extends ModelInstance, - RawData, - Data, - Deserialized extends DeserializedData, - Next = CI, - >( - // eslint-disable-next-line max-len - this: Action & ConsumeDeserializer, Deserialized> & ConsumeInstance>, - transform?: (data: OneData, I>) => Awaitable, - ): Promise; -}>; +)); diff --git a/packages/core/src/actions/context/runners/oneOrFail.ts b/packages/core/src/actions/context/runners/oneOrFail.ts index 5d88afe2..2e930b09 100644 --- a/packages/core/src/actions/context/runners/oneOrFail.ts +++ b/packages/core/src/actions/context/runners/oneOrFail.ts @@ -1,13 +1,9 @@ import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utils/deserializeInstances'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; import { - Action, - ConsumeAdapter, - ConsumeDeserializer, - InferConsumedInstance, - WithParsedExtension, -} from '@foscia/core/actions/types'; + RetypedDeserializedData, +} from '@foscia/core/actions/context/utilities/deserializeInstances'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { InferQueryInstance } from '@foscia/core/actions/types'; import ExpectedRunFailureError from '@foscia/core/errors/expectedRunFailureError'; import { DeserializedData } from '@foscia/core/types'; import { Awaitable } from '@foscia/shared'; @@ -17,37 +13,26 @@ import { Awaitable } from '@foscia/shared'; * Throw an "ExpectedRunFailureError" when not found or empty result. * * @category Runners + * @requireContext adapter, deserializer, model + * + * @example + * ```typescript + * import { query, oneOrFail } from '@foscia/core'; + * + * const post = await action().run(query(post, '123'), oneOrFail()); + * ``` */ -const oneOrFail = < +export default makeRunner('oneOrFail', < C extends {}, - I extends InferConsumedInstance, + I extends InferQueryInstance, RawData, Data, Deserialized extends DeserializedData, Next = I, >( - transform?: (data: OneData, I>) => Awaitable, -) => oneOr(() => { + transform?: (data: OneData, I>) => Awaitable, +) => oneOr(() => { throw new ExpectedRunFailureError( '`oneOrFail` failed. You may handle this error globally as a "not found" record error.', ); -}, transform); - -export default /* @__PURE__ */ appendExtension( - 'oneOrFail', - oneOrFail, - 'run', -) as WithParsedExtension, - RawData, - Data, - Deserialized extends DeserializedData, - Next = I, - >( - // eslint-disable-next-line max-len - this: Action & ConsumeDeserializer, Deserialized>>, - transform?: (data: OneData, I>) => Awaitable, - ): Promise; -}>; +}, transform)); diff --git a/packages/core/src/actions/context/runners/raw.ts b/packages/core/src/actions/context/runners/raw.ts index 555fc887..49d46749 100644 --- a/packages/core/src/actions/context/runners/raw.ts +++ b/packages/core/src/actions/context/runners/raw.ts @@ -1,31 +1,25 @@ import executeContextThroughAdapter - from '@foscia/core/actions/context/utils/executeContextThroughAdapter'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, ConsumeAdapter, WithParsedExtension } from '@foscia/core/actions/types'; -import { Awaitable } from '@foscia/shared'; + from '@foscia/core/actions/context/utilities/executeContextThroughAdapter'; +import makeRunner from '@foscia/core/actions/makeRunner'; +import { Action, ConsumeAdapter } from '@foscia/core/actions/types'; +import { Awaitable, using } from '@foscia/shared'; /** * Run the action and retrieve the raw adapter's data. * * @category Runners + * @requireContext adapter + * + * @example + * ```typescript + * import { query, raw } from '@foscia/core'; + * + * const response = await action().run(query(post, '123'), raw()); + * ``` */ -const raw = ( +export default makeRunner('raw', ( transform?: (data: RawData) => Awaitable, -) => async (action: Action>) => { - const response = await executeContextThroughAdapter( - await action.useContext(), - ); - - return (transform ? transform(response.raw) : response.raw) as Awaitable; -}; - -export default /* @__PURE__ */ appendExtension( - 'raw', - raw, - 'run', -) as WithParsedExtension( - this: Action>, - transform?: (data: RawData) => Awaitable, - ): Promise; -}>; +) => async (action: Action>) => using( + await executeContextThroughAdapter(await action.useContext()), + (response) => (transform ? transform(response.raw) : response.raw) as Awaitable, +)); diff --git a/packages/core/src/actions/context/utilities/deserializeInstances.ts b/packages/core/src/actions/context/utilities/deserializeInstances.ts new file mode 100644 index 00000000..ddaa8ea1 --- /dev/null +++ b/packages/core/src/actions/context/utilities/deserializeInstances.ts @@ -0,0 +1,34 @@ +import consumeDeserializer from '@foscia/core/actions/context/consumers/consumeDeserializer'; +import { ConsumeDeserializer } from '@foscia/core/actions/types'; +import { ModelInstance } from '@foscia/core/model/types'; +import { DeserializedData } from '@foscia/core/types'; +import { isNil, using } from '@foscia/shared'; + +/** + * Deserialized data with a strongly retyped instances array. + * + * @internal + */ +export type RetypedDeserializedData
= { + instances: I[]; +} & Omit; + +/** + * Deserialize the instances from the given data. + * + * @param context + * @param data + * + * @internal + */ +export default async ( + context: ConsumeDeserializer, Deserialized>, + data: Data, +) => ( + isNil(data) + ? { instances: [] } as unknown as Deserialized + : using( + await consumeDeserializer(context), + (deserializer) => deserializer.deserialize(data!, context), + ) +); diff --git a/packages/core/src/actions/context/utils/executeContextThroughAdapter.ts b/packages/core/src/actions/context/utilities/executeContextThroughAdapter.ts similarity index 74% rename from packages/core/src/actions/context/utils/executeContextThroughAdapter.ts rename to packages/core/src/actions/context/utilities/executeContextThroughAdapter.ts index a81030c0..1214aa5e 100644 --- a/packages/core/src/actions/context/utils/executeContextThroughAdapter.ts +++ b/packages/core/src/actions/context/utilities/executeContextThroughAdapter.ts @@ -1,11 +1,18 @@ import consumeAction from '@foscia/core/actions/context/consumers/consumeAction'; import consumeAdapter from '@foscia/core/actions/context/consumers/consumeAdapter'; import { ConsumeAdapter } from '@foscia/core/actions/types'; -import { AdapterResponseI } from '@foscia/core/types'; +import { AdapterResponse } from '@foscia/core/types'; +/** + * Execute the given context through the adapter. + * + * @param context + * + * @internal + */ export default async ( context: ConsumeAdapter, -): Promise> => { +): Promise> => { const adapter = await consumeAdapter(context); const action = consumeAction(context, null); if (action && action in adapter && typeof (adapter as any)[action] === 'function') { diff --git a/packages/core/src/actions/context/utilities/normalizeInclude.ts b/packages/core/src/actions/context/utilities/normalizeInclude.ts new file mode 100644 index 00000000..12215cea --- /dev/null +++ b/packages/core/src/actions/context/utilities/normalizeInclude.ts @@ -0,0 +1,27 @@ +import consumeRegistry from '@foscia/core/actions/context/consumers/consumeRegistry'; +import guessContextModel from '@foscia/core/actions/context/guessers/guessContextModel'; +import logger from '@foscia/core/logger/logger'; +import normalizeDotRelations from '@foscia/core/normalization/normalizeDotRelations'; +import { tap, using } from '@foscia/shared'; + +/** + * Normalize the included dot relations. + * + * @param context + * @param include + * + * @internal + */ +export default async ( + context: {}, + include: string[], +) => using(await guessContextModel(context), async (model) => ( + model + ? using( + await consumeRegistry(context, null), + (registry) => normalizeDotRelations(model, include, registry), + ) + : tap(include, () => logger.warn( + 'Could not detect model for context. Skipping include normalization.', + )) +)); diff --git a/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts b/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts new file mode 100644 index 00000000..3bd5a01d --- /dev/null +++ b/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts @@ -0,0 +1,39 @@ +import onRunning from '@foscia/core/actions/context/enhancers/hooks/onRunning'; +import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; +import { Action } from '@foscia/core/actions/types'; +import runHooks from '@foscia/core/hooks/runHooks'; +import markSynced from '@foscia/core/model/snapshots/markSynced'; +import { ModelHooksDefinitionForInstance, ModelInstance } from '@foscia/core/model/types'; +import { Arrayable, using } from '@foscia/shared'; + +/** + * Register hooks for a write action (create, update or destroy). + * + * @param action + * @param instance + * @param runningHooks + * @param successHooks + * @param exists + * + * @internal + */ +export default ( + action: Action, + instance: ModelInstance, + runningHooks: Arrayable, + successHooks: Arrayable, + exists: boolean, +): Action => using(instance.$original, (snapshot) => action.use( + onRunning(() => runHooks(instance.$model, runningHooks, instance)), + onSuccess(async () => { + // When the original snapshot didn't change, this means the instance + // haven't been deserialized, so we must mark it synced manually. + if (instance.$original === snapshot) { + // eslint-disable-next-line no-param-reassign + instance.$exists = exists; + markSynced(instance); + } + + await runHooks(instance.$model, successHooks, instance); + }), +)); diff --git a/packages/core/src/actions/context/utilities/serializeInstance.ts b/packages/core/src/actions/context/utilities/serializeInstance.ts new file mode 100644 index 00000000..c0a74ff6 --- /dev/null +++ b/packages/core/src/actions/context/utilities/serializeInstance.ts @@ -0,0 +1,21 @@ +import consumeSerializer from '@foscia/core/actions/context/consumers/consumeSerializer'; +import { ConsumeSerializer } from '@foscia/core/actions/types'; +import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; +import { ModelInstance } from '@foscia/core/model/types'; +import { using } from '@foscia/shared'; + +/** + * Serialize the given instance to a serialized dataset. + * + * @param context + * @param instance + * + * @internal + */ +export default async ( + context: ConsumeSerializer, + instance: ModelInstance, +) => using(await consumeSerializer(context), async (serializer) => serializer.serializeToData( + await serializer.serializeToRecords(takeSnapshot(instance), context), + context, +)); diff --git a/packages/core/src/actions/context/utilities/serializeRelation.ts b/packages/core/src/actions/context/utilities/serializeRelation.ts new file mode 100644 index 00000000..aa3edafa --- /dev/null +++ b/packages/core/src/actions/context/utilities/serializeRelation.ts @@ -0,0 +1,44 @@ +import consumeSerializer from '@foscia/core/actions/context/consumers/consumeSerializer'; +import { ConsumeSerializer } from '@foscia/core/actions/types'; +import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; +import { + ModelInstance, + ModelRelation, + ModelRelationKey, + ModelValues, +} from '@foscia/core/model/types'; +import { mapArrayable, using } from '@foscia/shared'; + +/** + * Serialize the given relation's value to a serialized dataset. + * + * @param context + * @param instance + * @param relation + * @param value + * + * @internal + */ +export default async < + I extends ModelInstance, + K extends ModelRelationKey, + Record, + Related, + Data, +>( + context: ConsumeSerializer, + instance: I, + relation: K, + value: ModelValues[K], +) => using(await consumeSerializer(context), async (serializer) => serializer.serializeToData( + await serializer.serializeToRelatedRecords( + takeSnapshot(instance), + instance.$model.$schema[relation] as ModelRelation, + await mapArrayable( + value, + (related) => takeSnapshot(related as unknown as ModelInstance), + ), + context, + ), + context, +)); diff --git a/packages/core/src/actions/context/utils/deserializeInstances.ts b/packages/core/src/actions/context/utils/deserializeInstances.ts deleted file mode 100644 index 8e429387..00000000 --- a/packages/core/src/actions/context/utils/deserializeInstances.ts +++ /dev/null @@ -1,22 +0,0 @@ -import consumeDeserializer from '@foscia/core/actions/context/consumers/consumeDeserializer'; -import { Action, ConsumeDeserializer } from '@foscia/core/actions/types'; -import { ModelInstance } from '@foscia/core/model/types'; -import { DeserializedData } from '@foscia/core/types'; -import { isNil } from '@foscia/shared'; - -export type DeserializedDataOf = { - instances: I[]; -} & Omit; - -export default async < - C extends {}, Data, Deserialized extends DeserializedData = DeserializedData, ->(action: Action, Deserialized>>, data: Data) => { - if (isNil(data)) { - return { instances: [] }; - } - - const context = await action.useContext(); - const deserializer = await consumeDeserializer(context); - - return deserializer.deserialize(data!, context); -}; diff --git a/packages/core/src/actions/context/utils/normalizeInclude.ts b/packages/core/src/actions/context/utils/normalizeInclude.ts deleted file mode 100644 index 9b7a0609..00000000 --- a/packages/core/src/actions/context/utils/normalizeInclude.ts +++ /dev/null @@ -1,22 +0,0 @@ -import consumeRegistry from '@foscia/core/actions/context/consumers/consumeRegistry'; -import guessContextModel from '@foscia/core/actions/context/guessers/guessContextModel'; -import logger from '@foscia/core/logger/logger'; -import normalizeDotRelations from '@foscia/core/normalization/normalizeDotRelations'; - -export default async ( - context: {}, - include: string[], -) => { - const model = await guessContextModel(context); - if (model) { - const registry = await consumeRegistry(context, null); - - return normalizeDotRelations(model, include, registry); - } - - logger.warn( - 'Could not detect model for context. Skipping include normalization.', - ); - - return include; -}; diff --git a/packages/core/src/actions/context/utils/serializeInstance.ts b/packages/core/src/actions/context/utils/serializeInstance.ts deleted file mode 100644 index b6e09a70..00000000 --- a/packages/core/src/actions/context/utils/serializeInstance.ts +++ /dev/null @@ -1,11 +0,0 @@ -import serializeWith from '@foscia/core/actions/context/utils/serializeWith'; -import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; -import { ModelInstance } from '@foscia/core/model/types'; - -export default ( - action: Action>, - instance: ModelInstance, -) => serializeWith(action, async (serializer, context) => serializer.serialize( - await serializer.serializeInstance(instance, context), - context, -)); diff --git a/packages/core/src/actions/context/utils/serializeRelation.ts b/packages/core/src/actions/context/utils/serializeRelation.ts deleted file mode 100644 index 5c3a54c6..00000000 --- a/packages/core/src/actions/context/utils/serializeRelation.ts +++ /dev/null @@ -1,19 +0,0 @@ -import serializeWith from '@foscia/core/actions/context/utils/serializeWith'; -import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; -import { ModelInstance } from '@foscia/core/model/types'; -import { Arrayable } from '@foscia/shared'; - -export default ( - action: Action>, - instance: ModelInstance, - relation: string, - value: Arrayable | null, -) => serializeWith(action, async (serializer, context) => serializer.serialize( - await serializer.serializeRelation( - instance, - instance.$model.$schema[relation], - value, - context, - ), - context, -)); diff --git a/packages/core/src/actions/context/utils/serializeWith.ts b/packages/core/src/actions/context/utils/serializeWith.ts deleted file mode 100644 index 01121768..00000000 --- a/packages/core/src/actions/context/utils/serializeWith.ts +++ /dev/null @@ -1,12 +0,0 @@ -import consumeSerializer from '@foscia/core/actions/context/consumers/consumeSerializer'; -import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; -import { SerializerI } from '@foscia/core/types'; - -export default async ( - action: Action>, - callback: (serializer: SerializerI, context: C) => Return, -) => { - const context = await action.useContext(); - - return callback(await consumeSerializer(context), context); -}; diff --git a/packages/core/src/actions/extensions/appendExtension.ts b/packages/core/src/actions/extensions/appendExtension.ts deleted file mode 100644 index 2c5a0ebe..00000000 --- a/packages/core/src/actions/extensions/appendExtension.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - Action, - ContextEnhancer, - ContextRunner, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { makeDescriptorHolder } from '@foscia/shared'; - -/** - * Append extension to a context enhancer or runner. - * - * @param name - * @param factory - * @param method - * - * @since 0.10.0 - * - * @todo Improve generic typing. - */ -const appendExtension: { - < - C extends {}, - R extends {}, - K extends string, - // eslint-disable-next-line max-len - V extends ((...args: any[]) => ContextEnhancer) | ((...args: never[]) => ContextEnhancer), - >(name: K, factory: V, method: 'use'): WithParsedExtension(this: Action, ...args: Parameters) => Action; - }>; - < - C extends {}, - R extends any, - K extends string, - // eslint-disable-next-line max-len - V extends ((...args: any[]) => ContextRunner) | ((...args: never[]) => ContextRunner), - >(name: K, factory: V, method: 'run'): WithParsedExtension(this: Action, ...args: Parameters) => R; - }> -} = < - K extends string, - V extends (...args: any[]) => ContextEnhancer | ContextRunner, ->(name: K, factory: V, method: 'use' | 'run'): V => { - // eslint-disable-next-line no-param-reassign - (factory as any).extension = () => ({ - [name]: makeDescriptorHolder(Object.getOwnPropertyDescriptor({ - [name](this: Action, ...args: any[]) { - return (this[method] as any)(factory(...args)); - }, - }, name)!), - }); - - return factory; -}; - -export default appendExtension; diff --git a/packages/core/src/actions/extensions/coreExtensions.ts b/packages/core/src/actions/extensions/coreExtensions.ts deleted file mode 100644 index f75bc178..00000000 --- a/packages/core/src/actions/extensions/coreExtensions.ts +++ /dev/null @@ -1,13 +0,0 @@ -import context from '@foscia/core/actions/context/enhancers/context'; -import include from '@foscia/core/actions/context/enhancers/include'; -import query from '@foscia/core/actions/context/enhancers/query'; -import catchIf from '@foscia/core/actions/context/runners/catchIf'; -import when from '@foscia/core/actions/when'; - -export default () => ({ - ...query.extension(), - ...include.extension(), - ...context.extension(), - ...when.extension(), - ...catchIf.extension(), -}); diff --git a/packages/core/src/actions/extensions/crudExtensions.ts b/packages/core/src/actions/extensions/crudExtensions.ts deleted file mode 100644 index 5038f0d4..00000000 --- a/packages/core/src/actions/extensions/crudExtensions.ts +++ /dev/null @@ -1,7 +0,0 @@ -import readExtensions from '@foscia/core/actions/extensions/readExtensions'; -import writeExtensions from '@foscia/core/actions/extensions/writeExtensions'; - -export default () => ({ - ...readExtensions(), - ...writeExtensions(), -}); diff --git a/packages/core/src/actions/extensions/hooksExtensions.ts b/packages/core/src/actions/extensions/hooksExtensions.ts deleted file mode 100644 index 5851fcbc..00000000 --- a/packages/core/src/actions/extensions/hooksExtensions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import onError from '@foscia/core/actions/context/enhancers/hooks/onError'; -import onFinally from '@foscia/core/actions/context/enhancers/hooks/onFinally'; -import onRunning from '@foscia/core/actions/context/enhancers/hooks/onRunning'; -import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; - -export default () => ({ - ...onRunning.extension(), - ...onSuccess.extension(), - ...onError.extension(), - ...onFinally.extension(), -}); diff --git a/packages/core/src/actions/extensions/readExtensions.ts b/packages/core/src/actions/extensions/readExtensions.ts deleted file mode 100644 index 194a60ce..00000000 --- a/packages/core/src/actions/extensions/readExtensions.ts +++ /dev/null @@ -1,23 +0,0 @@ -import all from '@foscia/core/actions/context/runners/all'; -import cached from '@foscia/core/actions/context/runners/cached'; -import cachedOr from '@foscia/core/actions/context/runners/cachedOr'; -import cachedOrFail from '@foscia/core/actions/context/runners/cachedOrFail'; -import none from '@foscia/core/actions/context/runners/none'; -import one from '@foscia/core/actions/context/runners/one'; -import oneOr from '@foscia/core/actions/context/runners/oneOr'; -import oneOrCurrent from '@foscia/core/actions/context/runners/oneOrCurrent'; -import oneOrFail from '@foscia/core/actions/context/runners/oneOrFail'; -import raw from '@foscia/core/actions/context/runners/raw'; - -export default () => ({ - ...all.extension(), - ...one.extension(), - ...oneOrFail.extension(), - ...oneOrCurrent.extension(), - ...oneOr.extension(), - ...none.extension(), - ...raw.extension(), - ...cached.extension(), - ...cachedOrFail.extension(), - ...cachedOr.extension(), -}); diff --git a/packages/core/src/actions/extensions/writeExtensions.ts b/packages/core/src/actions/extensions/writeExtensions.ts deleted file mode 100644 index f8d820f8..00000000 --- a/packages/core/src/actions/extensions/writeExtensions.ts +++ /dev/null @@ -1,25 +0,0 @@ -import associate from '@foscia/core/actions/context/enhancers/crud/associate'; -import attach from '@foscia/core/actions/context/enhancers/crud/attach'; -import create from '@foscia/core/actions/context/enhancers/crud/create'; -import destroy from '@foscia/core/actions/context/enhancers/crud/destroy'; -import detach from '@foscia/core/actions/context/enhancers/crud/detach'; -import dissociate from '@foscia/core/actions/context/enhancers/crud/dissociate'; -import instanceData from '@foscia/core/actions/context/enhancers/crud/instanceData'; -import relationData from '@foscia/core/actions/context/enhancers/crud/relationData'; -import save from '@foscia/core/actions/context/enhancers/crud/save'; -import update from '@foscia/core/actions/context/enhancers/crud/update'; -import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; - -export default () => ({ - ...create.extension(), - ...update.extension(), - ...save.extension(), - ...destroy.extension(), - ...instanceData.extension(), - ...attach.extension(), - ...detach.extension(), - ...associate.extension(), - ...dissociate.extension(), - ...updateRelation.extension(), - ...relationData.extension(), -}); diff --git a/packages/core/src/actions/index.ts b/packages/core/src/actions/index.ts index ea557771..a10a9eb0 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -1,4 +1,6 @@ import ActionName from '@foscia/core/actions/actionName'; +import isContextFunction from '@foscia/core/actions/checks/isContextFunction'; +import isWhenContextFunction from '@foscia/core/actions/checks/isWhenContextFunction'; import consumeAction from '@foscia/core/actions/context/consumers/consumeAction'; import consumeAdapter from '@foscia/core/actions/context/consumers/consumeAdapter'; import consumeCache from '@foscia/core/actions/context/consumers/consumeCache'; @@ -9,6 +11,7 @@ import consumeId from '@foscia/core/actions/context/consumers/consumeId'; import consumeInclude from '@foscia/core/actions/context/consumers/consumeInclude'; import consumeInstance from '@foscia/core/actions/context/consumers/consumeInstance'; import consumeModel from '@foscia/core/actions/context/consumers/consumeModel'; +import consumeQueryAs from '@foscia/core/actions/context/consumers/consumeQueryAs'; import consumeRegistry from '@foscia/core/actions/context/consumers/consumeRegistry'; import consumeRelation from '@foscia/core/actions/context/consumers/consumeRelation'; import consumeSerializer from '@foscia/core/actions/context/consumers/consumeSerializer'; @@ -29,36 +32,31 @@ import onFinally from '@foscia/core/actions/context/enhancers/hooks/onFinally'; import onRunning from '@foscia/core/actions/context/enhancers/hooks/onRunning'; import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; import include from '@foscia/core/actions/context/enhancers/include'; +import appendActionMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/appendActionMiddlewares'; +import prependActionMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/prependActionMiddlewares'; +import replaceActionMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/replaceActionMiddlewares'; import query from '@foscia/core/actions/context/enhancers/query'; +import queryAs from '@foscia/core/actions/context/enhancers/queryAs'; import guessContextModel from '@foscia/core/actions/context/guessers/guessContextModel'; -import all, { AllData } from '@foscia/core/actions/context/runners/all'; +import all from '@foscia/core/actions/context/runners/all'; import cached from '@foscia/core/actions/context/runners/cached'; -import cachedOr, { CachedData } from '@foscia/core/actions/context/runners/cachedOr'; +import cachedOr from '@foscia/core/actions/context/runners/cachedOr'; import cachedOrFail from '@foscia/core/actions/context/runners/cachedOrFail'; -import catchIf, { CatchCallback } from '@foscia/core/actions/context/runners/catchIf'; +import catchIf from '@foscia/core/actions/context/runners/catchIf'; import none from '@foscia/core/actions/context/runners/none'; import one from '@foscia/core/actions/context/runners/one'; -import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; +import oneOr from '@foscia/core/actions/context/runners/oneOr'; import oneOrCurrent from '@foscia/core/actions/context/runners/oneOrCurrent'; import oneOrFail from '@foscia/core/actions/context/runners/oneOrFail'; import raw from '@foscia/core/actions/context/runners/raw'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import coreExtensions from '@foscia/core/actions/extensions/coreExtensions'; -import crudExtensions from '@foscia/core/actions/extensions/crudExtensions'; -import hooksExtensions from '@foscia/core/actions/extensions/hooksExtensions'; -import readExtensions from '@foscia/core/actions/extensions/readExtensions'; -import writeExtensions from '@foscia/core/actions/extensions/writeExtensions'; -import makeActionClass from '@foscia/core/actions/makeActionClass'; import makeActionFactory from '@foscia/core/actions/makeActionFactory'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import makeRunner from '@foscia/core/actions/makeRunner'; import when from '@foscia/core/actions/when'; -export type { - AllData, - OneData, - CachedData, - CatchCallback, -}; - export { none, all, @@ -83,6 +81,7 @@ export { catchIf, context, query, + queryAs, include, instanceData, relationData, @@ -90,6 +89,9 @@ export { onSuccess, onError, onFinally, + appendActionMiddlewares, + prependActionMiddlewares, + replaceActionMiddlewares, consumeAction, consumeAdapter, consumeCache, @@ -100,17 +102,15 @@ export { consumeInclude, consumeInstance, consumeModel, + consumeQueryAs, consumeRegistry, consumeRelation, consumeSerializer, guessContextModel, - appendExtension, - makeActionClass, makeActionFactory, - coreExtensions, - crudExtensions, - hooksExtensions, - readExtensions, - writeExtensions, + makeEnhancer, + makeRunner, ActionName, + isContextFunction, + isWhenContextFunction, }; diff --git a/packages/core/src/actions/makeActionClass.ts b/packages/core/src/actions/makeActionClass.ts deleted file mode 100644 index 7a7347bb..00000000 --- a/packages/core/src/actions/makeActionClass.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { - Action, - ActionClass, - ActionHooksDefinition, - ContextEnhancer, - ContextRunner, -} from '@foscia/core/actions/types'; -import registerHook from '@foscia/core/hooks/registerHook'; -import runHooks from '@foscia/core/hooks/runHooks'; -import { HooksRegistrar } from '@foscia/core/hooks/types'; -import withoutHooks from '@foscia/core/hooks/withoutHooks'; -import logger from '@foscia/core/logger/logger'; -import { Dictionary, eachDescriptors, sequentialTransform } from '@foscia/shared'; - -export default ( - extensions?: Extension & ThisType>, -) => { - class CustomActionClass { - public $hooks: HooksRegistrar | null; - - private $enhancementsQueue: ContextEnhancer[]; - - private $context: Dictionary; - - public static extend(newExtensions?: Dictionary) { - eachDescriptors(newExtensions ?? {}, (key, descriptor) => { - Object.defineProperty(this.prototype, key, descriptor); - }); - - return this; - } - - public constructor() { - this.$enhancementsQueue = []; - this.$context = {}; - this.$hooks = {}; - - registerHook(this, 'running', (event) => logger.debug('Action running.', [event])); - registerHook(this, 'success', (event) => logger.debug('Action success.', [event])); - registerHook(this, 'error', (event) => logger.debug('Action error.', [event])); - } - - public async useContext() { - await this.dequeueEnhancements(); - - return this.$context; - } - - public updateContext(newContext: Dictionary) { - this.$context = newContext; - - return this; - } - - public use(...enhancers: ContextEnhancer[]) { - this.$enhancementsQueue.push(...enhancers); - - return this; - } - - public async run( - ...enhancers: (ContextEnhancer | ContextRunner)[] - ) { - const runner = enhancers.pop() as ContextRunner; - - this.use(...enhancers); - - const context = await this.useContext(); - - await runHooks(this, 'running', { context, runner }); - - try { - // Context runner might use other context runners, so we must disable - // hooks at this point to avoid duplicated hooks runs. - const result = await withoutHooks(this, async () => runner(this as any)); - - await runHooks(this, 'success', { context, result }); - - return result; - } catch (error) { - await runHooks(this, 'error', { context, error }); - - throw error; - } finally { - await runHooks(this, 'finally', { context }); - } - } - - private async dequeueEnhancements() { - const enhancements = this.$enhancementsQueue.map((e) => async () => { - await e(this as any); - - // Any enhancement might push other enhancement in the queue, - // so we must process those too. - await this.dequeueEnhancements(); - }); - - this.$enhancementsQueue = []; - - await sequentialTransform(enhancements); - } - } - - return CustomActionClass.extend(extensions) as ActionClass<{}, Extension>; -}; diff --git a/packages/core/src/actions/makeActionFactory.ts b/packages/core/src/actions/makeActionFactory.ts index 328f1c96..2229cd1b 100644 --- a/packages/core/src/actions/makeActionFactory.ts +++ b/packages/core/src/actions/makeActionFactory.ts @@ -1,12 +1,130 @@ -import context from '@foscia/core/actions/context/enhancers/context'; -import makeActionClass from '@foscia/core/actions/makeActionClass'; -import { Action } from '@foscia/core/actions/types'; +import { + Action, + ActionCall, + ActionFactory, + ContextEnhancer, + ContextRunner, +} from '@foscia/core/actions/types'; +import FosciaError from '@foscia/core/errors/fosciaError'; +import registerHook from '@foscia/core/hooks/registerHook'; +import runHooks from '@foscia/core/hooks/runHooks'; +import withoutHooks from '@foscia/core/hooks/withoutHooks'; +import logger from '@foscia/core/logger/logger'; +import { + Dictionary, + Middleware, + sequentialTransform, + throughMiddlewares, + value, +} from '@foscia/shared'; -export default ( - initialContext?: Context, - extensions?: Extensions, +/** + * Create an action factory. + * + * @param initialContext + * + * @category Factories + */ +export default ( + initialContext?: Context | (() => Context), +): ActionFactory => ( + ...immediateEnhancers: (ContextEnhancer | ContextRunner)[] ) => { - const ActionClass = makeActionClass(extensions); + const currentCalls: ActionCall[] = []; + let currentCall: ActionCall | null = null; - return () => new ActionClass().use(context(initialContext ?? {})) as Action; + let currentQueue: ContextEnhancer[] = []; + let currentContext: Dictionary = value(initialContext) ?? {}; + + const dequeueEnhancers = async (action: Action) => { + const enhancements = currentQueue.map((enhancer) => async () => { + await action.track(enhancer); + }); + + currentQueue = []; + + await sequentialTransform(enhancements); + }; + + const action = { + $hooks: {}, + async useContext() { + await dequeueEnhancers(this); + + return currentContext; + }, + updateContext(newContext: Dictionary) { + currentContext = newContext; + + return this; + }, + use(...enhancers: ContextEnhancer[]) { + currentQueue.push(...enhancers); + + return this; + }, + async run(...enhancers: (ContextEnhancer | ContextRunner)[]) { + if (enhancers.length === 0) { + throw new FosciaError('`run` must be called with at least one runner function.'); + } + + const runner = enhancers.pop() as ContextRunner; + + this.use(...enhancers); + + const { middlewares, ...context } = await this.useContext() as Dictionary; + this.updateContext(context); + + await runHooks(this, 'running', { action: this, runner }); + + try { + // Context runner might use other context enhancers and runners, + // so we must disable hooks at this point to avoid duplicated hooks runs. + const result = await throughMiddlewares( + (middlewares ?? []) as Middleware[], + async (a) => withoutHooks(a, async () => a.track(runner)), + )(this); + + if (result === this) { + logger.warn('Action run result is the action itself, did you forget to pass a runner when calling `run`?'); + } + + await runHooks(this, 'success', { action: this, result }); + + return result; + } catch (error) { + await runHooks(this, 'error', { action: this, error }); + + throw error; + } finally { + await runHooks(this, 'finally', { action: this }); + } + }, + async track( + callback: (action: Action) => unknown, + call?: ContextEnhancer | ContextRunner, + ) { + const parentCall = currentCall; + currentCall = { call: call ?? callback, calls: [] }; + (parentCall ? parentCall.calls : currentCalls).push(currentCall); + + const result = await callback(this); + await dequeueEnhancers(this); + + currentCall = parentCall; + + return result; + }, + calls() { + return currentCalls; + }, + } as any; + + registerHook(action, 'running', (event) => logger.debug('Action running.', [event])); + registerHook(action, 'success', (event) => logger.debug('Action success.', [event])); + registerHook(action, 'error', (event) => logger.debug('Action error.', [event])); + + return immediateEnhancers.length + ? action.run(...immediateEnhancers) + : action; }; diff --git a/packages/core/src/actions/makeContextFunctionFactory.ts b/packages/core/src/actions/makeContextFunctionFactory.ts new file mode 100644 index 00000000..c4c396b6 --- /dev/null +++ b/packages/core/src/actions/makeContextFunctionFactory.ts @@ -0,0 +1,37 @@ +import { ContextEnhancer, ContextFunctionType, ContextRunner } from '@foscia/core/actions/types'; +import { SYMBOL_ACTION_CONTEXT_FUNCTION_FACTORY } from '@foscia/core/symbols'; + +type ContextFunctionFactory = + | ((...args: any[]) => ContextEnhancer) + | ((...args: never[]) => ContextEnhancer) + | ((...args: any[]) => ContextRunner) + | ((...args: never[]) => ContextRunner); + +/** + * Make a context function factory with metadata to track its usage. + * + * @param type + * @param name + * @param originalFactory + * + * @internal + */ +export default ( + type: ContextFunctionType, + name: string, + originalFactory: F, +) => { + const factory = (...args: any[]) => { + const functionWithMetadata = (originalFactory as any)(...args); + + functionWithMetadata.$FOSCIA_TYPE = type; + functionWithMetadata.meta = { factory, args }; + + return functionWithMetadata; + }; + + factory.$FOSCIA_TYPE = SYMBOL_ACTION_CONTEXT_FUNCTION_FACTORY; + factory.meta = { name }; + + return factory as unknown as F; +}; diff --git a/packages/core/src/actions/makeEnhancer.ts b/packages/core/src/actions/makeEnhancer.ts new file mode 100644 index 00000000..068a56aa --- /dev/null +++ b/packages/core/src/actions/makeEnhancer.ts @@ -0,0 +1,17 @@ +import makeContextFunctionFactory from '@foscia/core/actions/makeContextFunctionFactory'; +import { ContextEnhancer } from '@foscia/core/actions/types'; +import { SYMBOL_ACTION_CONTEXT_ENHANCER } from '@foscia/core/symbols'; + +/** + * Make a context enhancer factory with incorporated metadata (name, arguments). + * + * @param name + * @param factory + * + * @category Factories + */ +// eslint-disable-next-line max-len +export default ( ContextEnhancer) | ((...args: never[]) => ContextEnhancer)>( + name: string, + factory: F, +) => makeContextFunctionFactory(SYMBOL_ACTION_CONTEXT_ENHANCER, name, factory)); diff --git a/packages/core/src/actions/makeRunner.ts b/packages/core/src/actions/makeRunner.ts new file mode 100644 index 00000000..ef337470 --- /dev/null +++ b/packages/core/src/actions/makeRunner.ts @@ -0,0 +1,17 @@ +import makeContextFunctionFactory from '@foscia/core/actions/makeContextFunctionFactory'; +import { ContextRunner } from '@foscia/core/actions/types'; +import { SYMBOL_ACTION_CONTEXT_RUNNER } from '@foscia/core/symbols'; + +/** + * Make a context runner factory with incorporated metadata (name, arguments). + * + * @param name + * @param factory + * + * @category Factories + */ +// eslint-disable-next-line max-len +export default ContextRunner) | ((...args: never[]) => ContextRunner)>( + name: string, + factory: F, +) => makeContextFunctionFactory(SYMBOL_ACTION_CONTEXT_RUNNER, name, factory); diff --git a/packages/core/src/actions/types.ts b/packages/core/src/actions/types.ts index 199c762f..44e3e9f9 100644 --- a/packages/core/src/actions/types.ts +++ b/packages/core/src/actions/types.ts @@ -1,142 +1,384 @@ import ActionName from '@foscia/core/actions/actionName'; -import { ActionVariadicRun } from '@foscia/core/actions/actionVariadicRun'; -import { ActionVariadicUse } from '@foscia/core/actions/actionVariadicUse'; +import type { + InferRelationUpdateValue, +} from '@foscia/core/actions/context/enhancers/crud/updateRelation'; +import type { + GuessContextModelContext, +} from '@foscia/core/actions/context/guessers/guessContextModel'; +import type { AllData } from '@foscia/core/actions/context/runners/all'; +import type { CachedData } from '@foscia/core/actions/context/runners/cachedOr'; +import type { OneData } from '@foscia/core/actions/context/runners/oneOr'; +import type { + RetypedDeserializedData, +} from '@foscia/core/actions/context/utilities/deserializeInstances'; +import { + ActionFactoryVariadicRun, + ActionVariadicRun, + ActionVariadicUse, +} from '@foscia/core/actions/variadic'; import { Hookable, HookCallback } from '@foscia/core/hooks/types'; +import { Model, ModelIdType, ModelInstance, ModelRelation } from '@foscia/core/model/types'; import { - Model, - ModelIdType, - ModelInstance, - ModelRelation, - RawModelRelation, -} from '@foscia/core/model/types'; + SYMBOL_ACTION_CONTEXT_ENHANCER, + SYMBOL_ACTION_CONTEXT_FUNCTION_FACTORY, + SYMBOL_ACTION_CONTEXT_RUNNER, + SYMBOL_ACTION_CONTEXT_WHEN, +} from '@foscia/core/symbols'; import { - AdapterI, - CacheI, + Adapter, DeserializedData, - DeserializerI, - RegistryI, - SerializerI, + Deserializer, + InstancesCache, + ModelsRegistry, + Serializer, } from '@foscia/core/types'; -import { Awaitable, Constructor, DescriptorHolder } from '@foscia/shared'; +import { Awaitable, Constructor, FosciaObject } from '@foscia/shared'; + +export type { + AllData, + OneData, + CachedData, + RetypedDeserializedData, + GuessContextModelContext, + InferRelationUpdateValue, +}; -export * from '@foscia/core/actions/actionVariadicUse'; +export * from '@foscia/core/actions/variadic'; +/** + * Action hooks definition. + * + * @internal + */ export type ActionHooksDefinition = { - running: HookCallback<{ context: {}; runner: Function; }>; - success: HookCallback<{ context: {}; result: unknown; }>; - error: HookCallback<{ context: {}; error: unknown; }>; - finally: HookCallback<{ context: {}; }>; + running: HookCallback<{ action: Action; runner: ContextRunner; }>; + success: HookCallback<{ action: Action; result: unknown; }>; + error: HookCallback<{ action: Action; error: unknown; }>; + finally: HookCallback<{ action: Action; }>; }; -export type Action = +/** + * Action. + * + * @typeParam Context The current context of the action. + * + * @interface + */ +export type Action = & { + /** + * Retrieve the current context after applying all queued enhancers. + */ useContext(): Promise; + /** + * Update the current context. + * + * @param newContext + */ updateContext( newContext: NewContext, - ): Action; + ): Action; + /** + * Queue an enhancer which will update the current context. + * Enhancer will apply on next context compute, run or immediate apply. + * + * @param enhancer + */ use( - enhancer: ContextEnhancer, - ): Action; + enhancer: ContextEnhancer, + ): Action; + /** + * Run the action. + * + * @param runner + */ run( - runner: ContextRunner, + runner: ContextRunner, ): Promise>; + /** + * Run the given callback on action and keep track for call stack. + * It will also dequeue enhancements after the callback, to keep + * track of call stack depth. + * + * @param callback + * @param call + * + * @internal + */ + track( + callback: (action: Action) => Result, + call?: ContextEnhancer | ContextRunner, + ): Promise>; + /** + * Get the tree of action calls (enhancers or runners). + * + * @internal + */ + calls(): ActionCall[]; } - & ActionVariadicUse - & ActionVariadicRun - & Hookable - & ExtendedAction; + & ActionVariadicUse + & ActionVariadicRun + & Hookable; -export type ActionClass = { - extend( - newExtensions?: NewExtension & ThisType>, - ): ActionClass; -} & Constructor>; +/** + * Action factory. + * + * @internal + */ +export type ActionFactory = + & (() => Action) + & ActionFactoryVariadicRun; -export type ActionFactory = ( - ...args: Args -) => Action; +/** + * Middleware to impact an action result or behavior. + * + * @internal + */ +export type ActionMiddleware = ( + value: Action, + next: (value: Action) => Promise, +) => Awaitable; -export type ActionParsedExtension = { - [K in keyof E]: E[K] extends DescriptorHolder ? E[K] : DescriptorHolder; +/** + * Action call (enhancer or runner) tree node. + * + * @internal + */ +export type ActionCall = { + call: ContextEnhancer | ContextRunner; + calls: ActionCall[]; }; -export type WithParsedExtension< - // eslint-disable-next-line max-len - V extends ((...args: never[]) => ContextEnhancer | ContextRunner) | ((...args: any[]) => ContextEnhancer | ContextRunner), - E extends {}, -> = V & { extension: () => ActionParsedExtension; }; +/** + * Function to enhance the action context. + */ +export type ContextEnhancer = ( + action: Action, +) => Awaitable | void>; -export type ExtendedAction = { - [K in keyof E]: E[K] extends DescriptorHolder ? T : E[K]; -}; +/** + * Function to create a result from a contextualized action. + */ +export type ContextRunner = ( + action: Action, +) => R; -export type ContextEnhancer = ( - action: Action, -) => Awaitable | Action | void>; +/** + * Available types for a context function. + * + * @internal + */ +export type ContextFunctionType = + | typeof SYMBOL_ACTION_CONTEXT_WHEN + | typeof SYMBOL_ACTION_CONTEXT_ENHANCER + | typeof SYMBOL_ACTION_CONTEXT_RUNNER; -export type ContextRunner = ( - action: Action, -) => R; +/** + * Conditional context function. + * + * @internal + */ +export type ContextWhenFunction = { + (): ContextEnhancer | ContextRunner; + readonly meta: { readonly factory: ContextFunctionFactory; readonly args: any[]; }; +} & FosciaObject; -export type InferConsumedInstance = - C extends { relation: RawModelRelation> } ? I extends ModelInstance ? I : never - : C extends { relation: RawModelRelation } ? I extends ModelInstance ? I : never - : C extends { instance: infer I } ? I extends ModelInstance ? I : never - : C extends { model: Constructor } ? I extends ModelInstance ? I : never - : never; +/** + * Enhancer context function. + * + * @internal + */ +export type ContextEnhancerFunction = { + (): ContextEnhancer; + readonly meta: { readonly factory: ContextFunctionFactory; readonly args: any[]; }; +} & FosciaObject; -export type InferConsumedModelOrInstance = - C extends { relation: RawModelRelation> } ? I extends ModelInstance ? I : never - : C extends { relation: RawModelRelation } ? I extends ModelInstance ? I : never - : C extends { model: infer M } ? M - : InferConsumedInstance; +/** + * Runner context function. + * + * @internal + */ +export type ContextRunnerFunction = { + (): ContextRunner; + readonly meta: { readonly factory: ContextFunctionFactory; readonly args: any[]; }; +} & FosciaObject; +/** + * Available context functions. + * + * @internal + */ +export type ContextFunction = + | ContextWhenFunction + | ContextEnhancerFunction + | ContextRunnerFunction; + +/** + * Factory to produce a context function. + * + * @internal + */ +export type ContextFunctionFactory = + & { + (...args: any[]): ContextFunction; + readonly meta: { readonly name: string; }; + } + & FosciaObject; + +/** + * Infer the query instance from context. + * + * This type should be used to infer the instance returned by an action runner. + * + * @internal + */ +export type InferQueryInstance = + C extends { queryAs: Constructor[] } ? I extends ModelInstance ? I : never + : C extends { relation: ModelRelation> } + ? I extends ModelInstance ? I : never + : C extends { relation: ModelRelation } ? I extends ModelInstance ? I : never + : C extends { instance: infer I } ? I extends ModelInstance ? I : never + : C extends { model: Constructor } ? I extends ModelInstance ? I : never + : never; + +/** + * Infer the query model or instance from context. + * + * This type should be used to infer the model/instance properties to strict + * type context enhancers and runners. + * + * @internal + */ +export type InferQueryModelOrInstance = + C extends { queryAs: Constructor[] } ? I extends ModelInstance ? I : never + : C extends { relation: ModelRelation> } + ? I extends ModelInstance ? I : never + : C extends { relation: ModelRelation } ? I extends ModelInstance ? I : never + : C extends { instance: infer I } ? I + : C extends { model: infer M } ? M + : InferQueryInstance; + +/** + * Define action to run on data source. + * + * @internal + */ export type ConsumeAction = { action: ActionName | string; }; +/** + * Define data to send to data source. + * + * @internal + */ export type ConsumeData = { data: unknown; }; +/** + * Define the query model without affecting data source request. + * + * @internal + */ +export type ConsumeQueryAs = { + queryAs: M[]; +}; + +/** + * Define the model to query. + */ export type ConsumeModel = { model: M; }; +/** + * Define the instance to query. + */ export type ConsumeInstance = { instance: I; }; +/** + * Define the relation to query. + */ export type ConsumeRelation = { relation: R; }; +/** + * Define the ID to query. + */ export type ConsumeId = { id?: ModelIdType; }; +/** + * Define the relations to include. + * + * @internal + */ export type ConsumeInclude = { include?: string[]; }; +/** + * Define the middlewares to run. + * + * @internal + */ +export type ConsumeMiddlewares = { + middlewares?: ActionMiddleware[]; +}; + +/** + * Context dependency which can be lazy-loaded. + * + * @internal + */ export type ResolvableContextDependency = T | (() => Awaitable); +/** + * Define the cache implementation to use. + * + * @internal + */ export type ConsumeCache = { - cache: ResolvableContextDependency; + cache: ResolvableContextDependency; }; +/** + * Define the registry implementation to use. + * + * @internal + */ export type ConsumeRegistry = { - registry: ResolvableContextDependency; + registry: ResolvableContextDependency; }; +/** + * Define the adapter implementation to use. + * + * @internal + */ export type ConsumeAdapter = { - adapter: ResolvableContextDependency>; + adapter: ResolvableContextDependency>; }; +/** + * Define the deserializer implementation to use. + * + * @internal + */ export type ConsumeDeserializer = { - deserializer: ResolvableContextDependency>; + deserializer: ResolvableContextDependency>; }; +/** + * Define the serializer implementation to use. + * + * @internal + */ export type ConsumeSerializer = { - serializer: ResolvableContextDependency>; + serializer: ResolvableContextDependency>; }; diff --git a/packages/core/src/actions/variadic.ts b/packages/core/src/actions/variadic.ts new file mode 100644 index 00000000..5dfad131 --- /dev/null +++ b/packages/core/src/actions/variadic.ts @@ -0,0 +1,74 @@ +/* eslint-disable max-len */ +import type { Action, ContextEnhancer, ContextRunner } from '@foscia/core/actions/types'; + +// Do not edit this file, it is generated by `pnpm generate-files`. + +/** + * Variadic action use function signatures. + * + * @internal + */ +export type ActionVariadicUse = { + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, enhancer15: ContextEnhancer): Action; + use(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, enhancer15: ContextEnhancer, enhancer16: ContextEnhancer): Action; +}; + +/** + * Variadic action run function signatures. + * + * @internal + */ +export type ActionVariadicRun = { + run(enhancer1: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, runner: ContextRunner): Promise>; + run(enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, enhancer15: ContextEnhancer, runner: ContextRunner): Promise>; +}; + +/** + * Variadic action factory run function signatures. + * + * @internal + */ +export type ActionFactoryVariadicRun = { + (runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, runner: ContextRunner): Promise>; + (enhancer1: ContextEnhancer, enhancer2: ContextEnhancer, enhancer3: ContextEnhancer, enhancer4: ContextEnhancer, enhancer5: ContextEnhancer, enhancer6: ContextEnhancer, enhancer7: ContextEnhancer, enhancer8: ContextEnhancer, enhancer9: ContextEnhancer, enhancer10: ContextEnhancer, enhancer11: ContextEnhancer, enhancer12: ContextEnhancer, enhancer13: ContextEnhancer, enhancer14: ContextEnhancer, enhancer15: ContextEnhancer, runner: ContextRunner): Promise>; +}; diff --git a/packages/core/src/actions/when.ts b/packages/core/src/actions/when.ts index ebac22d0..5d01351f 100644 --- a/packages/core/src/actions/when.ts +++ b/packages/core/src/actions/when.ts @@ -1,103 +1,124 @@ -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { - Action, - ContextEnhancer, - ContextRunner, - WithParsedExtension, -} from '@foscia/core/actions/types'; +import makeContextFunctionFactory from '@foscia/core/actions/makeContextFunctionFactory'; +import { Action, ContextEnhancer, ContextRunner } from '@foscia/core/actions/types'; +import { SYMBOL_ACTION_CONTEXT_WHEN } from '@foscia/core/symbols'; import { Awaitable, OnlyFalsy, OnlyTruthy, Value, value } from '@foscia/shared'; -/** - * Create a new enhancer or runner from a conditional expression and given - * enhancer/runner factories. - * When the expression if a function, it will call the function and take its - * result as the evaluated expression. Expression may also be a promise - * or a promise provider function, which will also be evaluated. - * Evaluated expression will be passed to both truthy and falsy callbacks. - * - * @param expression - * @param truthyCallback - * @param falsyCallback - */ -const when: { - ( - expression: V, - truthyCallback: ( - action: Action, - value: OnlyTruthy>>, - ) => Awaitable | void>, - ): ContextEnhancer; - ( - expression: V, - truthyCallback: ( - action: Action, - value: OnlyTruthy>>, - ) => Awaitable | void>, - falsyCallback: ( - action: Action, - value: OnlyFalsy>>, - ) => Awaitable | void>, - ): ContextEnhancer; - ( - expression: V, - truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, - ): ContextRunner; - ( +export default /* @__PURE__ */ makeContextFunctionFactory( + SYMBOL_ACTION_CONTEXT_WHEN, + 'when', + (( expression: V, - truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, - falsyCallback: (action: Action, value: OnlyFalsy>>) => FR, - ): ContextRunner; -} = ( - expression: V, - truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, - falsyCallback?: (action: Action, value: OnlyFalsy>>) => FR, -) => async (action: Action) => { - const exprValue = await value(expression as Function); - if (exprValue) { - return truthyCallback(action, exprValue as OnlyTruthy>>); - } + truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, + falsyCallback?: (action: Action, value: OnlyFalsy>>) => FR, + ) => async (action: Action) => { + const exprValue = await value(expression as Function); - if (falsyCallback !== undefined) { - return falsyCallback(action, exprValue as OnlyFalsy>>); - } + if (exprValue) { + return action.track( + (a) => truthyCallback(a, exprValue as OnlyTruthy>>), + truthyCallback as any, + ); + } - return undefined as any; -}; + if (falsyCallback !== undefined) { + return action.track( + (a) => falsyCallback(a, exprValue as OnlyFalsy>>), + falsyCallback as any, + ); + } -export default /* @__PURE__ */ appendExtension( - 'when', - when, - 'use', -) as WithParsedExtension( - this: Action, - expression: V, - truthyCallback: ( - action: Action, - value: OnlyTruthy>>, - ) => Awaitable | void>, - ): Action; - when( - this: Action, - expression: V, - truthyCallback: ( - action: Action, - value: OnlyTruthy>>, - ) => Awaitable | void>, - falsyCallback: ( - action: Action, - value: OnlyFalsy>>, - ) => Awaitable | void>, - ): Action; - when( - this: Action, - expression: V, - truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, - ): Promise; - when( - this: Action, - expression: V, - truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, - falsyCallback?: (action: Action, value: OnlyFalsy>>) => FR, - ): Promise; -}>; + return undefined as any; + }) as { + /** + * Create an enhancer that runs if `expression` is truthy. + * [Get more details on actions conditionals](/docs/core-concepts/actions#conditionals). + * + * @param expression + * @param truthyCallback + * + * @category Enhancers + * + * @example + * ```typescript + * import { when } from '@foscia/core'; + * import { filterBy } from '@foscia/jsonapi'; + * + * action().use(when(foo, filterBy('foo', foo))); + * ``` + */( + expression: V, + truthyCallback: ( + action: Action, + value: OnlyTruthy>>, + ) => Awaitable | void>, + ): ContextEnhancer; + /** + * Create two enhancers that run depending on `expression` truthiness. + * [Get more details on actions conditionals](/docs/core-concepts/actions#conditionals). + * + * @param expression + * @param truthyCallback + * @param falsyCallback + * + * @category Enhancers + * + * @example + * ```typescript + * import { when } from '@foscia/core'; + * import { filterBy } from '@foscia/jsonapi'; + * + * action().use(when(foo, filterBy('foo', foo), filterBy('bar', 'bar'))); + * ``` + */( + expression: V, + truthyCallback: ( + action: Action, + value: OnlyTruthy>>, + ) => Awaitable | void>, + falsyCallback: ( + action: Action, + value: OnlyFalsy>>, + ) => Awaitable | void>, + ): ContextEnhancer; + /** + * Create a runner that runs if `expression` is truthy. + * [Get more details on actions conditionals](/docs/core-concepts/actions#conditionals). + * + * @param expression + * @param truthyCallback + * + * @category Runners + * + * @example + * ```typescript + * import { when, changed, oneOrFail } from '@foscia/core'; + * + * action().run(when(changed(post), oneOrFail())); + * ``` + */( + expression: V, + truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, + ): ContextRunner; + /** + * Create two runners that run depending on `expression` truthiness. + * [Get more details on actions conditionals](/docs/core-concepts/actions#conditionals). + * + * @param expression + * @param truthyCallback + * @param falsyCallback + * + * @category Runners + * + * @example + * ```typescript + * import { when, changed, oneOrFail } from '@foscia/core'; + * + * action().run(when(changed(post), oneOrFail(), () => post)); + * ``` + */( + expression: V, + truthyCallback: (action: Action, value: OnlyTruthy>>) => TR, + falsyCallback: (action: Action, value: OnlyFalsy>>) => FR, + ): ContextRunner; + }, +); diff --git a/packages/core/src/blueprints/makeCache.ts b/packages/core/src/blueprints/makeCache.ts deleted file mode 100644 index e6c15c09..00000000 --- a/packages/core/src/blueprints/makeCache.ts +++ /dev/null @@ -1,13 +0,0 @@ -import makeRefsCacheWith from '@foscia/core/cache/makeRefsCacheWith'; -import { RefsCacheConfig } from '@foscia/core/cache/types'; -import weakRefManager from '@foscia/core/cache/weakRefManager'; - -type CacheConfig = Partial; - -export default (config: CacheConfig = {}) => ({ - cache: makeRefsCacheWith({ - manager: weakRefManager, - normalizeId: (id) => String(id), - ...config, - }), -}); diff --git a/packages/core/src/blueprints/makeRegistry.ts b/packages/core/src/blueprints/makeRegistry.ts deleted file mode 100644 index df9d7291..00000000 --- a/packages/core/src/blueprints/makeRegistry.ts +++ /dev/null @@ -1,15 +0,0 @@ -import makeMapRegistryWith from '@foscia/core/registry/makeMapRegistryWith'; -import { MapRegistryConfig, MapRegistryModelsRegistration } from '@foscia/core/registry/types'; - -type RegistryConfig = Partial; - -export default ( - models: MapRegistryModelsRegistration, - config: RegistryConfig = {}, -) => { - const registry = makeMapRegistryWith(config); - - registry.register(models); - - return { registry }; -}; diff --git a/packages/core/src/cache/makeCache.ts b/packages/core/src/cache/makeCache.ts new file mode 100644 index 00000000..e992a351 --- /dev/null +++ b/packages/core/src/cache/makeCache.ts @@ -0,0 +1,15 @@ +import makeRefsCache from '@foscia/core/cache/makeRefsCache'; +import makeWeakRefFactory from '@foscia/core/cache/makeWeakRefFactory'; +import { InstancesCache } from '@foscia/core/types'; +import { kebabCase } from '@foscia/shared'; + +/** + * Make a default {@link InstancesCache | `InstancesCache`} implementation. + * + * @category Factories + */ +export default (): { cache: InstancesCache; } => makeRefsCache({ + makeRef: makeWeakRefFactory(), + normalizeType: kebabCase, + normalizeId: (id) => String(id), +}); diff --git a/packages/core/src/cache/makeRefsCache.ts b/packages/core/src/cache/makeRefsCache.ts new file mode 100644 index 00000000..a2b6267a --- /dev/null +++ b/packages/core/src/cache/makeRefsCache.ts @@ -0,0 +1,49 @@ +import { RefsCache, RefsCacheConfig, RefValue } from '@foscia/core/cache/types'; +import { ModelIdType, ModelInstance } from '@foscia/core/model/types'; +import { makeIdentifiersMap } from '@foscia/shared'; + +/** + * Make a cache using a {@link RefFactory | `RefFactory`} to store cached + * instances inside {@link RefValue | `RefValue`}. + * + * @param config + * + * @category Factories + */ +export default (config: RefsCacheConfig) => { + const instances = makeIdentifiersMap>(); + + const normalizeType = config.normalizeType ?? ((t) => t); + const normalizeId = config.normalizeId ?? ((v) => v); + + const forget = async (type: string, id: ModelIdType) => instances.forget( + normalizeType(type), + normalizeId(id), + ); + + return { + cache: { + forget, + forgetAll: async (type: string) => instances.forgetAll(normalizeType(type)), + clear: async () => instances.clear(), + find: async (type: string, id: ModelIdType) => { + const ref = instances.find(normalizeType(type), normalizeId(id)); + if (ref) { + const instance = await ref(); + if (instance) { + return instance; + } + + await forget(normalizeType(type), normalizeId(id)); + } + + return null; + }, + put: async (type: string, id: ModelIdType, instance: ModelInstance) => instances.put( + normalizeType(type), + normalizeId(id), + await config.makeRef(instance), + ), + } as RefsCache, + }; +}; diff --git a/packages/core/src/cache/makeRefsCacheWith.ts b/packages/core/src/cache/makeRefsCacheWith.ts deleted file mode 100644 index 84fad7e7..00000000 --- a/packages/core/src/cache/makeRefsCacheWith.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { RefsCache, RefsCacheConfig } from '@foscia/core/cache/types'; -import { ModelIdType, ModelInstance } from '@foscia/core/model/types'; -import { makeIdentifiersMap } from '@foscia/shared'; - -/** - * Make a {@link CacheI} implementation interacting with instance references - * through configured {@link RefsManager}. - * - * @param config - */ -export default (config: RefsCacheConfig): RefsCache => { - const instances = makeIdentifiersMap(); - - const normalizeId = config.normalizeId ?? ((v) => v); - - const forget = async (type: string, id: ModelIdType) => instances.forget(type, normalizeId(id)); - - const forgetAll = async (type: string) => instances.forgetAll(type); - - const clear = async () => instances.clear(); - - const find = async (type: string, id: ModelIdType) => { - const ref = instances.find(type, normalizeId(id)); - if (!ref) { - return null; - } - - const instance = await config.manager.value(ref); - if (!instance) { - await forget(type, normalizeId(id)); - - return null; - } - - return instance; - }; - - const put = async (type: string, id: ModelIdType, instance: ModelInstance) => { - instances.put(type, normalizeId(id), await config.manager.ref(instance)); - }; - - return { - forget, - forgetAll, - clear, - find, - put, - }; -}; diff --git a/packages/core/src/cache/makeTimedRefFactory.ts b/packages/core/src/cache/makeTimedRefFactory.ts new file mode 100644 index 00000000..daf7fb06 --- /dev/null +++ b/packages/core/src/cache/makeTimedRefFactory.ts @@ -0,0 +1,48 @@ +import { tap } from '@foscia/shared'; + +/** + * Config for the timed ref factory. + * + * @interface + * + * @internal + */ +export type TimedRefFactoryConfig = { + /** + * Lifetime of a reference before expiration in seconds. + * Defaults to `300` (5 minutes). + */ + lifetime?: number; + /** + * When enabled, access to a reference will reset the expiration. + * Defaults to `true`. + */ + postpone?: boolean; +}; + +/** + * Make a {@link RefFactory | `RefFactory`} using {@link !setTimeout | `setTimeout`} + * to manage expiration. + * + * @category Factories + * @since 0.13.0 + */ +export default (config?: TimedRefFactoryConfig) => (value: V) => { + let expirationTimeout: ReturnType | undefined; + const scheduleExpiration = () => { + clearTimeout(expirationTimeout); + expirationTimeout = setTimeout(() => { + expirationTimeout = undefined; + }, (config?.lifetime ?? (5 * 60)) * 1000); + }; + + scheduleExpiration(); + + return () => ( + expirationTimeout === undefined ? null : tap(value, () => { + if (config?.postpone ?? true) { + scheduleExpiration(); + } + }) + ); +}; diff --git a/packages/core/src/cache/makeWeakRefFactory.ts b/packages/core/src/cache/makeWeakRefFactory.ts new file mode 100644 index 00000000..25b23ddd --- /dev/null +++ b/packages/core/src/cache/makeWeakRefFactory.ts @@ -0,0 +1,13 @@ +import { using } from '@foscia/shared'; + +/** + * Make a {@link RefFactory | `RefFactory`} using {@link !WeakRef | `WeakRef`} + * to manage expiration. + * + * @category Factories + * @since 0.13.0 + */ +export default () => (value: V) => using( + new WeakRef(value), + (ref) => () => (ref.deref() ?? null), +); diff --git a/packages/core/src/cache/types.ts b/packages/core/src/cache/types.ts index 957b0c4e..1014f155 100644 --- a/packages/core/src/cache/types.ts +++ b/packages/core/src/cache/types.ts @@ -1,16 +1,55 @@ +import type { TimedRefFactoryConfig } from '@foscia/core/cache/makeTimedRefFactory'; import { ModelIdType, ModelInstance } from '@foscia/core/model/types'; -import { CacheI } from '@foscia/core/types'; +import { InstancesCache } from '@foscia/core/types'; import { Awaitable, Optional, Transformer } from '@foscia/shared'; -export type RefManager = { - ref(instance: ModelInstance): Awaitable; - value(ref: R): Awaitable; +export type { + TimedRefFactoryConfig, }; -export type RefsCacheConfig = { - manager: RefManager; +/** + * Function which stores a reference to a value. + * Calling the function retrieves the value, or `null` if the reference + * expired. + * + * @internal + */ +export type RefValue = () => Awaitable; + +/** + * Factory to create a reference to a value. + * + * @internal + */ +export type RefFactory = (value: V) => Awaitable>; + +/** + * Config for refs cache implementation. + * + * @interface + * + * @internal + */ +export type RefsCacheConfig = { + /** + * Create a reference to a model instance. + */ + makeRef: RefFactory; + /** + * Normalize the type before storing and resolving instances. + */ + normalizeType?: Optional>; + /** + * Normalize the ID before storing and resolving instances. + */ normalizeId?: Optional>; }; -export interface RefsCache extends CacheI { -} +/** + * Cache implementation using references. + * + * @interface + * + * @internal + */ +export type RefsCache = InstancesCache; diff --git a/packages/core/src/cache/weakRefManager.ts b/packages/core/src/cache/weakRefManager.ts deleted file mode 100644 index ac24961e..00000000 --- a/packages/core/src/cache/weakRefManager.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { RefManager } from '@foscia/core/cache/types'; -import { ModelInstance } from '@foscia/core/model/types'; - -export default { - ref: (instance: ModelInstance) => new WeakRef(instance), - value: (ref: WeakRef) => ref.deref(), -} as RefManager>; diff --git a/packages/core/src/errors/adapterError.ts b/packages/core/src/errors/adapterError.ts index fa73c7a6..21d991e6 100644 --- a/packages/core/src/errors/adapterError.ts +++ b/packages/core/src/errors/adapterError.ts @@ -1,4 +1,12 @@ import FosciaError from '@foscia/core/errors/fosciaError'; +/** + * Error which occurs during adapter context execution. + * + * It should be thrown when encountering ever on a client request + * misconfiguration or a data source error. + * + * @group Errors + */ export default class AdapterError extends FosciaError { } diff --git a/packages/core/src/errors/deserializerError.ts b/packages/core/src/errors/deserializerError.ts index 7a3ffe35..b615b64e 100644 --- a/packages/core/src/errors/deserializerError.ts +++ b/packages/core/src/errors/deserializerError.ts @@ -5,6 +5,8 @@ import FosciaError from '@foscia/core/errors/fosciaError'; * * It should be thrown when encountering a deserializer configuration error * or a data source's data format mismatch. + * + * @group Errors */ export default class DeserializerError extends FosciaError { } diff --git a/packages/core/src/errors/expectedRunFailureError.ts b/packages/core/src/errors/expectedRunFailureError.ts index 687bc5bc..b1be8414 100644 --- a/packages/core/src/errors/expectedRunFailureError.ts +++ b/packages/core/src/errors/expectedRunFailureError.ts @@ -5,6 +5,8 @@ import FosciaError from '@foscia/core/errors/fosciaError'; * * It can be handled globally by the underlying application * (e.g. to display a 404 Not Found page). + * + * @group Errors */ export default class ExpectedRunFailureError extends FosciaError { } diff --git a/packages/core/src/errors/flags/isNotFoundError.ts b/packages/core/src/errors/flags/isNotFoundError.ts deleted file mode 100644 index 65122fb0..00000000 --- a/packages/core/src/errors/flags/isNotFoundError.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { NotFoundErrorI } from '@foscia/core/errors/flags/types'; - -export default (error: unknown): error is NotFoundErrorI => !!error - && typeof error === 'object' - && '$FOSCIA_ERROR_NOT_FOUND' in error - && error.$FOSCIA_ERROR_NOT_FOUND === true; diff --git a/packages/core/src/errors/flags/types.ts b/packages/core/src/errors/flags/types.ts deleted file mode 100644 index fe547e9b..00000000 --- a/packages/core/src/errors/flags/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Describe an error that should be treated as "not found" by Foscia. - */ -export type NotFoundErrorI = { - readonly $FOSCIA_ERROR_NOT_FOUND: true; -}; diff --git a/packages/core/src/errors/fosciaError.ts b/packages/core/src/errors/fosciaError.ts index 54f9fdd8..80b8be1c 100644 --- a/packages/core/src/errors/fosciaError.ts +++ b/packages/core/src/errors/fosciaError.ts @@ -1,5 +1,7 @@ /** * Extendable error class used inside Foscia. + * + * @group Errors */ export default class FosciaError extends Error { /** diff --git a/packages/core/src/errors/invalidContextError.ts b/packages/core/src/errors/invalidContextError.ts index a6ddeb80..e8df7bb2 100644 --- a/packages/core/src/errors/invalidContextError.ts +++ b/packages/core/src/errors/invalidContextError.ts @@ -5,6 +5,8 @@ import FosciaError from '@foscia/core/errors/fosciaError'; * * It should be thrown when trying to access an action context which isn't * available when it should be and is required by in-use enhancer/runner. + * + * @group Errors */ export default class InvalidContextError extends FosciaError { } diff --git a/packages/core/src/errors/serializerError.ts b/packages/core/src/errors/serializerError.ts index 084ebb10..901bae7e 100644 --- a/packages/core/src/errors/serializerError.ts +++ b/packages/core/src/errors/serializerError.ts @@ -5,6 +5,8 @@ import FosciaError from '@foscia/core/errors/fosciaError'; * * It should be thrown when encountering a serializer configuration error * or a data source's data format mismatch. + * + * @group Errors */ export default class SerializerError extends FosciaError { } diff --git a/packages/core/src/flags.ts b/packages/core/src/flags.ts new file mode 100644 index 00000000..7303cc2e --- /dev/null +++ b/packages/core/src/flags.ts @@ -0,0 +1,8 @@ +/* eslint-disable import/prefer-default-export */ + +/** + * Bit flag to use when it is a not found error. + * + * @internal + */ +export const FLAG_ERROR_NOT_FOUND = 1; diff --git a/packages/core/src/hooks/mergeHooks.ts b/packages/core/src/hooks/mergeHooks.ts index a8f4873f..a6acfb2a 100644 --- a/packages/core/src/hooks/mergeHooks.ts +++ b/packages/core/src/hooks/mergeHooks.ts @@ -1,6 +1,14 @@ import { HooksDefinition, HooksRawRegistrar, HooksRegistrar } from '@foscia/core/hooks/types'; import { mapWithKeys, wrap } from '@foscia/shared'; +/** + * Merge two hooks registrar objects. + * + * @param prevHooks + * @param nextHooks + * + * @internal + */ export default ( prevHooks?: HooksRawRegistrar, nextHooks?: HooksRawRegistrar, diff --git a/packages/core/src/hooks/registerHook.ts b/packages/core/src/hooks/registerHook.ts index 741c93a9..ac4e61ed 100644 --- a/packages/core/src/hooks/registerHook.ts +++ b/packages/core/src/hooks/registerHook.ts @@ -1,15 +1,24 @@ /* eslint-disable no-param-reassign */ import { Hookable, HooksDefinition } from '@foscia/core/hooks/types'; import unregisterHook from '@foscia/core/hooks/unregisterHook'; +import { tap } from '@foscia/shared'; +/** + * Register a hook on a hookable object. + * Return value is a function which unregister the hook. + * + * @param hookable + * @param key + * @param callback + * + * @internal + */ export default ( hookable: Hookable, key: K, callback: D[K], -) => { +) => tap(() => unregisterHook(hookable, key, callback), () => { if (hookable.$hooks !== null) { hookable.$hooks[key] = [...(hookable.$hooks[key] ?? []), callback] as D[K][]; } - - return () => unregisterHook(hookable, key, callback); -}; +}); diff --git a/packages/core/src/hooks/runHooks.ts b/packages/core/src/hooks/runHooks.ts index 2be2ea39..3d740be8 100644 --- a/packages/core/src/hooks/runHooks.ts +++ b/packages/core/src/hooks/runHooks.ts @@ -7,6 +7,8 @@ import { Arrayable, sequentialTransform, wrap } from '@foscia/shared'; * @param hookable * @param hooks * @param event + * + * @internal */ export default ( hookable: Hookable, diff --git a/packages/core/src/hooks/runHooksSync.ts b/packages/core/src/hooks/runHooksSync.ts index 311d5565..7b8a196b 100644 --- a/packages/core/src/hooks/runHooksSync.ts +++ b/packages/core/src/hooks/runHooksSync.ts @@ -7,6 +7,8 @@ import { Arrayable, wrap } from '@foscia/shared'; * @param hookable * @param hooks * @param event + * + * @internal */ export default ( hookable: Hookable, diff --git a/packages/core/src/hooks/types.ts b/packages/core/src/hooks/types.ts index f5eea8b7..c1a91ac2 100644 --- a/packages/core/src/hooks/types.ts +++ b/packages/core/src/hooks/types.ts @@ -1,19 +1,54 @@ import { Arrayable, Awaitable, Dictionary } from '@foscia/shared'; +/** + * Synchronous hook callback. + * + * @internal + */ export type SyncHookCallback = (event: E) => void; +/** + * Asynchronous hook callback. + * + * @internal + */ export type HookCallback = (event: E) => Awaitable; +/** + * Hooks definition object. + * + * @internal + */ export type HooksDefinition = Dictionary>; +/** + * Hooks unparsed registrar for a definition. + * + * @internal + */ export type HooksRawRegistrar = { [K in keyof D]?: Arrayable; }; +/** + * Hooks parsed registrar for a definition. + * + * @internal + */ export type HooksRegistrar = { [K in keyof D]?: D[K][]; }; +/** + * Object on which hooks can be bound and triggered. + * + * @internal + */ export type Hookable = { + /** + * Currently registered hooks. + * + * @internal + */ $hooks: HooksRegistrar | null; }; diff --git a/packages/core/src/hooks/unregisterHook.ts b/packages/core/src/hooks/unregisterHook.ts index 61233b46..cdf31037 100644 --- a/packages/core/src/hooks/unregisterHook.ts +++ b/packages/core/src/hooks/unregisterHook.ts @@ -1,5 +1,14 @@ import { Hookable, HooksDefinition } from '@foscia/core/hooks/types'; +/** + * Unregister a hook from a hookable object. + * + * @param hookable + * @param key + * @param callback + * + * @category Hooks + */ export default ( hookable: Hookable, key: K, diff --git a/packages/core/src/hooks/withoutHooks.ts b/packages/core/src/hooks/withoutHooks.ts index bf64bef1..8ab20e6f 100644 --- a/packages/core/src/hooks/withoutHooks.ts +++ b/packages/core/src/hooks/withoutHooks.ts @@ -1,13 +1,25 @@ -/* eslint-disable no-param-reassign */ import { Hookable } from '@foscia/core/hooks/types'; +/** + * Temporary disable the hooks of the given object and run callback. + * + * @param hookable + * @param callback + * + * @category Hooks + */ export default , R>( hookable: T, callback: (hookable: T) => R, -): R extends Promise ? Promise : R => { +): R => { const hooksBackup = hookable.$hooks; - let restoreHooksImmediately = true; + const restoreHooks = () => { + // eslint-disable-next-line no-param-reassign + hookable.$hooks = hooksBackup; + }; + let restoreHooksImmediately = true; + // eslint-disable-next-line no-param-reassign hookable.$hooks = null; try { @@ -18,11 +30,11 @@ export default , R>( return new Promise((resolve, reject) => { value .then((v) => { - hookable.$hooks = hooksBackup; + restoreHooks(); resolve(v); }) .catch((e) => { - hookable.$hooks = hooksBackup; + restoreHooks(); reject(e); }); }) as any; @@ -31,7 +43,7 @@ export default , R>( return value as any; } finally { if (restoreHooksImmediately) { - hookable.$hooks = hooksBackup; + restoreHooks(); } } }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 42dee1d4..d235eaf1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,14 +1,14 @@ -import normalizeInclude from '@foscia/core/actions/context/utils/normalizeInclude'; -import makeCache from '@foscia/core/blueprints/makeCache'; -import makeRegistry from '@foscia/core/blueprints/makeRegistry'; -import makeRefsCacheWith from '@foscia/core/cache/makeRefsCacheWith'; -import weakRefManager from '@foscia/core/cache/weakRefManager'; +import normalizeInclude from '@foscia/core/actions/context/utilities/normalizeInclude'; +import makeCache from '@foscia/core/cache/makeCache'; +import makeRefsCache from '@foscia/core/cache/makeRefsCache'; +import makeTimedRefFactory from '@foscia/core/cache/makeTimedRefFactory'; +import makeWeakRefFactory from '@foscia/core/cache/makeWeakRefFactory'; import AdapterError from '@foscia/core/errors/adapterError'; import DeserializerError from '@foscia/core/errors/deserializerError'; import ExpectedRunFailureError from '@foscia/core/errors/expectedRunFailureError'; -import isNotFoundError from '@foscia/core/errors/flags/isNotFoundError'; import FosciaError from '@foscia/core/errors/fosciaError'; import SerializerError from '@foscia/core/errors/serializerError'; +import { FLAG_ERROR_NOT_FOUND } from '@foscia/core/flags'; import registerHook from '@foscia/core/hooks/registerHook'; import runHooks from '@foscia/core/hooks/runHooks'; import unregisterHook from '@foscia/core/hooks/unregisterHook'; @@ -20,12 +20,16 @@ import isInstance from '@foscia/core/model/checks/isInstance'; import isInstanceUsing from '@foscia/core/model/checks/isInstanceUsing'; import isModel from '@foscia/core/model/checks/isModel'; import isModelUsing from '@foscia/core/model/checks/isModelUsing'; -import isPendingPropDef from '@foscia/core/model/checks/isPendingPropDef'; import isPluralRelationDef from '@foscia/core/model/checks/isPluralRelationDef'; -import isPropDef from '@foscia/core/model/checks/isPropDef'; import isRelationDef from '@foscia/core/model/checks/isRelationDef'; import isSingularRelationDef from '@foscia/core/model/checks/isSingularRelationDef'; +import isSnapshot from '@foscia/core/model/checks/isSnapshot'; +import applyDefinition from '@foscia/core/model/composition/applyDefinition'; +import makeComposable from '@foscia/core/model/composition/makeComposable'; +import makeComposableFactory from '@foscia/core/model/composition/makeComposableFactory'; +import makeDefinition from '@foscia/core/model/composition/makeDefinition'; import fill from '@foscia/core/model/fill'; +import filled from '@foscia/core/model/filled'; import forceFill from '@foscia/core/model/forceFill'; import onBoot from '@foscia/core/model/hooks/onBoot'; import onCreated from '@foscia/core/model/hooks/onCreated'; @@ -42,48 +46,66 @@ import onPropertyRead from '@foscia/core/model/hooks/properties/onPropertyRead'; import onPropertyReading from '@foscia/core/model/hooks/properties/onPropertyReading'; import onPropertyWrite from '@foscia/core/model/hooks/properties/onPropertyWrite'; import onPropertyWriting from '@foscia/core/model/hooks/properties/onPropertyWriting'; -import filled from '@foscia/core/model/filled'; import isSame from '@foscia/core/model/isSame'; -import makeComposable from '@foscia/core/model/makeComposable'; import makeModel from '@foscia/core/model/makeModel'; import makeModelFactory from '@foscia/core/model/makeModelFactory'; +import assembled from '@foscia/core/model/props/builders/assembled'; import attr from '@foscia/core/model/props/builders/attr'; import hasMany from '@foscia/core/model/props/builders/hasMany'; import hasOne from '@foscia/core/model/props/builders/hasOne'; import id from '@foscia/core/model/props/builders/id'; import mapAttributes from '@foscia/core/model/props/mappers/mapAttributes'; -import mapIds from '@foscia/core/model/props/mappers/mapIds'; -import mapProps from '@foscia/core/model/props/mappers/mapProps'; import mapRelations from '@foscia/core/model/props/mappers/mapRelations'; import shouldSync from '@foscia/core/model/props/shouldSync'; import loaded from '@foscia/core/model/relations/loaded'; -import makeQueryModelLoader from '@foscia/core/model/relations/makeQueryModelLoader'; +import makeQueryModelLoader, { + QueryModelLoaderOptions, +} from '@foscia/core/model/relations/makeQueryModelLoader'; import makeQueryModelLoaderExtractor from '@foscia/core/model/relations/makeQueryModelLoaderExtractor'; -import makeQueryRelationLoader from '@foscia/core/model/relations/makeQueryRelationLoader'; -import makeRefreshIncludeLoader from '@foscia/core/model/relations/makeRefreshIncludeLoader'; +import makeQueryRelationLoader, { + QueryRelationLoaderOptions, +} from '@foscia/core/model/relations/makeQueryRelationLoader'; +import makeRefreshIncludeLoader, { + RefreshIncludeLoaderOptions, +} from '@foscia/core/model/relations/makeRefreshIncludeLoader'; +import attachRelationInverse from '@foscia/core/model/relations/utilities/attachRelationInverse'; +import guessRelationInverses from '@foscia/core/model/relations/utilities/guessRelationInverses'; import guessRelationType from '@foscia/core/model/relations/utilities/guessRelationType'; import makeModelsReducer from '@foscia/core/model/revivers/makeModelsReducer'; import makeModelsReviver from '@foscia/core/model/revivers/makeModelsReviver'; import changed from '@foscia/core/model/snapshots/changed'; -import compareSnapshots from '@foscia/core/model/snapshots/compareSnapshots'; +import isSameSnapshot from '@foscia/core/model/snapshots/isSameSnapshot'; import markSynced from '@foscia/core/model/snapshots/markSynced'; import restore from '@foscia/core/model/snapshots/restore'; import restoreSnapshot from '@foscia/core/model/snapshots/restoreSnapshot'; import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; +import cloneModelValue from '@foscia/core/model/utilities/cloneModelValue'; +import compareModelValues from '@foscia/core/model/utilities/compareModelValues'; import normalizeDotRelations from '@foscia/core/normalization/normalizeDotRelations'; import normalizeKey from '@foscia/core/normalization/normalizeKey'; -import makeMapRegistryWith from '@foscia/core/registry/makeMapRegistryWith'; +import makeMapRegistry from '@foscia/core/registry/makeMapRegistry'; +import makeRegistry from '@foscia/core/registry/makeRegistry'; import { + SYMBOL_ACTION_CONTEXT_ENHANCER, + SYMBOL_ACTION_CONTEXT_FUNCTION_FACTORY, + SYMBOL_ACTION_CONTEXT_RUNNER, + SYMBOL_ACTION_CONTEXT_WHEN, SYMBOL_MODEL_CLASS, + SYMBOL_MODEL_COMPOSABLE, SYMBOL_MODEL_INSTANCE, - SYMBOL_MODEL_PROP_ATTRIBUTE, - SYMBOL_MODEL_PROP_ID, - SYMBOL_MODEL_PROP_PENDING, - SYMBOL_MODEL_PROP_RELATION, + SYMBOL_MODEL_PROP, + SYMBOL_MODEL_PROP_FACTORY, + SYMBOL_MODEL_PROP_KIND_ATTRIBUTE, + SYMBOL_MODEL_PROP_KIND_ID, + SYMBOL_MODEL_PROP_KIND_RELATION, + SYMBOL_MODEL_PROP_TRANSFORMER, SYMBOL_MODEL_RELATION_HAS_MANY, SYMBOL_MODEL_RELATION_HAS_ONE, + SYMBOL_MODEL_SNAPSHOT, } from '@foscia/core/symbols'; +import isTransformer from '@foscia/core/transformers/isTransformer'; +import makeCustomTransformer from '@foscia/core/transformers/makeCustomTransformer'; import makeTransformer from '@foscia/core/transformers/makeTransformer'; import toArrayOf from '@foscia/core/transformers/toArrayOf'; import toBoolean from '@foscia/core/transformers/toBoolean'; @@ -94,8 +116,9 @@ import toString from '@foscia/core/transformers/toString'; export * from '@foscia/core/actions/types'; export * from '@foscia/core/cache/types'; -export * from '@foscia/core/errors/flags/types'; export * from '@foscia/core/hooks/types'; +export * from '@foscia/core/logger/types'; +export * from '@foscia/core/model/composition/types'; export * from '@foscia/core/model/props/builders/types'; export * from '@foscia/core/model/revivers/types'; export * from '@foscia/core/model/types'; @@ -111,16 +134,17 @@ export { DeserializerError, SerializerError, ExpectedRunFailureError, - isNotFoundError, makeRegistry, - makeMapRegistryWith, + makeMapRegistry, makeCache, - makeRefsCacheWith, - weakRefManager, + makeRefsCache, + makeWeakRefFactory, + makeTimedRefFactory, attr, hasMany, hasOne, id, + assembled, loaded, fill, forceFill, @@ -129,12 +153,18 @@ export { changed, restore, markSynced, + applyDefinition, + makeDefinition, makeComposable, + makeComposableFactory, makeModel, makeModelFactory, makeQueryModelLoader, + QueryModelLoaderOptions, makeQueryModelLoaderExtractor, + QueryRelationLoaderOptions, makeQueryRelationLoader, + RefreshIncludeLoaderOptions, makeRefreshIncludeLoader, toArrayOf, toBoolean, @@ -143,6 +173,8 @@ export { toNumber, toString, makeTransformer, + makeCustomTransformer, + isTransformer, onBoot, onInit, onRetrieved, @@ -158,14 +190,14 @@ export { onPropertyReading, onPropertyWrite, onPropertyWriting, - compareSnapshots, + isSnapshot, + isSameSnapshot, restoreSnapshot, takeSnapshot, runHooks, registerHook, unregisterHook, withoutHooks, - isPropDef, isAttributeDef, isRelationDef, isIdDef, @@ -175,25 +207,35 @@ export { isInstance, isModelUsing, isInstanceUsing, - isPendingPropDef, - mapIds, mapAttributes, mapRelations, - mapProps, shouldSync, guessRelationType, + guessRelationInverses, + attachRelationInverse, normalizeDotRelations, normalizeInclude, normalizeKey, makeModelsReducer, makeModelsReviver, + cloneModelValue, + compareModelValues, logger, - SYMBOL_MODEL_PROP_PENDING, - SYMBOL_MODEL_PROP_ID, - SYMBOL_MODEL_PROP_ATTRIBUTE, - SYMBOL_MODEL_PROP_RELATION, + FLAG_ERROR_NOT_FOUND, + SYMBOL_MODEL_PROP_FACTORY, + SYMBOL_MODEL_PROP_TRANSFORMER, + SYMBOL_MODEL_PROP, + SYMBOL_MODEL_PROP_KIND_ID, + SYMBOL_MODEL_PROP_KIND_ATTRIBUTE, + SYMBOL_MODEL_PROP_KIND_RELATION, SYMBOL_MODEL_RELATION_HAS_ONE, SYMBOL_MODEL_RELATION_HAS_MANY, SYMBOL_MODEL_CLASS, SYMBOL_MODEL_INSTANCE, + SYMBOL_MODEL_COMPOSABLE, + SYMBOL_MODEL_SNAPSHOT, + SYMBOL_ACTION_CONTEXT_WHEN, + SYMBOL_ACTION_CONTEXT_ENHANCER, + SYMBOL_ACTION_CONTEXT_RUNNER, + SYMBOL_ACTION_CONTEXT_FUNCTION_FACTORY, }; diff --git a/packages/core/src/logger/logger.ts b/packages/core/src/logger/logger.ts index f8819a58..ba5a57fc 100644 --- a/packages/core/src/logger/logger.ts +++ b/packages/core/src/logger/logger.ts @@ -1,22 +1,14 @@ +import { Logger, LoggerLevel, LoggerOutput } from '@foscia/core/logger/types'; import { IS_DEV, IS_TEST } from '@foscia/shared'; -const LOGGER_LEVELS = { - error: 'error', - warn: 'warn', - info: 'info', - debug: 'debug', +const LOGGER_LEVELS_WEIGHTS: Record = { + debug: 0, + info: 10, + warn: 100, + error: 1000, }; -const LOGGER_LEVELS_WEIGHTS = { - [LOGGER_LEVELS.debug]: 0, - [LOGGER_LEVELS.info]: 10, - [LOGGER_LEVELS.warn]: 100, - [LOGGER_LEVELS.error]: 1000, -}; - -type LoggerLevel = keyof typeof LOGGER_LEVELS; - -const defaultLoggerLevel = (): LoggerLevel | null => { +const makeDefaultLevel = (): LoggerLevel | null => { if (IS_TEST) { return null; } @@ -24,34 +16,24 @@ const defaultLoggerLevel = (): LoggerLevel | null => { return IS_DEV ? 'warn' : 'error'; }; -class Logger { - public level: LoggerLevel | null = defaultLoggerLevel(); - - public error(message: string, args: unknown[] = []) { - this.message('error', message, args); +const makeMessageLog = (level: LoggerLevel) => function log( + this: { level: LoggerLevel | null; output: LoggerOutput | null; }, + message: string, + args: unknown[] = [], +) { + if (this.level + && LOGGER_LEVELS_WEIGHTS[level] >= LOGGER_LEVELS_WEIGHTS[this.level] + && this.output + ) { + this.output[level](`[foscia] ${level}: ${message}`, ...args); } +}; - public warn(message: string, args: unknown[] = []) { - this.message('warn', message, args); - } - - public info(message: string, args: unknown[] = []) { - this.message('info', message, args); - } - - public debug(message: string, args: unknown[] = []) { - this.message('debug', message, args); - } - - private message(level: LoggerLevel, message: string, args: unknown[]) { - if (this.level - && LOGGER_LEVELS_WEIGHTS[level] >= LOGGER_LEVELS_WEIGHTS[this.level] - && typeof console !== 'undefined' - && typeof console[level] === 'function' - ) { - console[level](`[foscia] ${level}: ${message}`, ...args); - } - } -} - -export default /* @__PURE__ */ new Logger(); +export default { + level: makeDefaultLevel(), + output: (typeof console !== 'undefined' ? console : null) as LoggerOutput | null, + error: makeMessageLog('error'), + warn: makeMessageLog('warn'), + info: makeMessageLog('info'), + debug: makeMessageLog('debug'), +} as Logger; diff --git a/packages/core/src/logger/types.ts b/packages/core/src/logger/types.ts new file mode 100644 index 00000000..c2783125 --- /dev/null +++ b/packages/core/src/logger/types.ts @@ -0,0 +1,62 @@ +/** + * Available logger levels. + * + * @internal + */ +export type LoggerLevel = 'error' | 'warn' | 'info' | 'debug'; + +/** + * Output to use on a logger. + * + * @internal + */ +export type LoggerOutput = { + /** + * Log an error message. + * + * @param message + * @param args + */ + error: (message: string, ...args: unknown[]) => void; + /** + * Log a warning message. + * + * @param message + * @param args + */ + warn: (message: string, ...args: unknown[]) => void; + /** + * Log an info message. + * + * @param message + * @param args + */ + info: (message: string, ...args: unknown[]) => void; + /** + * Log a debug message. + * + * @param message + * @param args + */ + debug: (message: string, ...args: unknown[]) => void; +}; + +/** + * Logger used for all Foscia messages. + * + * @internal + */ +export type Logger = + & { + /** + * The minimum level of logged messages. + * Defaults to `error` in PROD env, `warn` in DEV env, and `null` in TEST env. + */ + level: LoggerLevel | null; + /** + * The output to use for logged messages. + * Defaults to global `console` if available. + */ + output: LoggerOutput | null; + } + & Readonly; diff --git a/packages/core/src/model/checks/isAttributeDef.ts b/packages/core/src/model/checks/isAttributeDef.ts index 21590cb8..e31c85f8 100644 --- a/packages/core/src/model/checks/isAttributeDef.ts +++ b/packages/core/src/model/checks/isAttributeDef.ts @@ -1,7 +1,14 @@ +import isPropDefOfType from '@foscia/core/model/checks/isPropDefOfType'; import { ModelAttribute } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_ATTRIBUTE } from '@foscia/core/symbols'; -import { isFosciaType } from '@foscia/shared'; +import { SYMBOL_MODEL_PROP_KIND_ATTRIBUTE } from '@foscia/core/symbols'; +/** + * Check if value is an attribute definition. + * + * @param value + * + * @internal + */ export default ( value: unknown, -): value is ModelAttribute => isFosciaType(value, SYMBOL_MODEL_PROP_ATTRIBUTE); +): value is ModelAttribute => isPropDefOfType(value, SYMBOL_MODEL_PROP_KIND_ATTRIBUTE); diff --git a/packages/core/src/model/checks/isComposable.ts b/packages/core/src/model/checks/isComposable.ts index e0f1da30..01e3500a 100644 --- a/packages/core/src/model/checks/isComposable.ts +++ b/packages/core/src/model/checks/isComposable.ts @@ -1,7 +1,14 @@ -import { ModelComposable } from '@foscia/core/model/types'; +import { ModelComposableFactory } from '@foscia/core/model/types'; import { SYMBOL_MODEL_COMPOSABLE } from '@foscia/core/symbols'; import { isFosciaType } from '@foscia/shared'; +/** + * Check if value is a composable. + * + * @param value + * + * @category Utilities + */ export default ( value: unknown, -): value is ModelComposable => isFosciaType(value, SYMBOL_MODEL_COMPOSABLE); +): value is ModelComposableFactory => isFosciaType(value, SYMBOL_MODEL_COMPOSABLE); diff --git a/packages/core/src/model/checks/isIdDef.ts b/packages/core/src/model/checks/isIdDef.ts index 1dae5f89..3f92a18c 100644 --- a/packages/core/src/model/checks/isIdDef.ts +++ b/packages/core/src/model/checks/isIdDef.ts @@ -1,7 +1,14 @@ +import isPropDefOfType from '@foscia/core/model/checks/isPropDefOfType'; import { ModelId } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_ID } from '@foscia/core/symbols'; -import { isFosciaType } from '@foscia/shared'; +import { SYMBOL_MODEL_PROP_KIND_ID } from '@foscia/core/symbols'; +/** + * Check if value is an ID definition. + * + * @param value + * + * @internal + */ export default ( value: unknown, -): value is ModelId => isFosciaType(value, SYMBOL_MODEL_PROP_ID); +): value is ModelId => isPropDefOfType(value, SYMBOL_MODEL_PROP_KIND_ID); diff --git a/packages/core/src/model/checks/isInstance.ts b/packages/core/src/model/checks/isInstance.ts index 72828ed7..7c0dd01e 100644 --- a/packages/core/src/model/checks/isInstance.ts +++ b/packages/core/src/model/checks/isInstance.ts @@ -2,6 +2,13 @@ import { ModelInstance } from '@foscia/core/model/types'; import { SYMBOL_MODEL_INSTANCE } from '@foscia/core/symbols'; import { isFosciaType } from '@foscia/shared'; +/** + * Check if value is a model instance. + * + * @param value + * + * @category Utilities + */ export default ( value: unknown, ): value is I => isFosciaType(value, SYMBOL_MODEL_INSTANCE); diff --git a/packages/core/src/model/checks/isInstanceUsing.ts b/packages/core/src/model/checks/isInstanceUsing.ts index 46064954..096fdd72 100644 --- a/packages/core/src/model/checks/isInstanceUsing.ts +++ b/packages/core/src/model/checks/isInstanceUsing.ts @@ -1,8 +1,29 @@ -import isInstance from '@foscia/core/model/checks/isInstance'; import isModelUsing from '@foscia/core/model/checks/isModelUsing'; -import { ModelComposable, ModelInstanceUsing } from '@foscia/core/model/types'; +import { + ModelComposableFactory, + ModelInstance, + ModelInstanceUsing, + ModelComposable, +} from '@foscia/core/model/types'; +/** + * Check if value is a model instance using given composable. + * + * @param value + * @param composable + * + * @category Utilities + * + * @example + * ```typescript + * import { isInstanceUsing } from '@foscia/core'; + * + * if (isInstanceUsing(myPost, publishable)) { + * // `myPost` is strictly typed with `publishable` definition. + * } + * ``` + */ export default ( - value: unknown, - composable: C, -): value is ModelInstanceUsing => isInstance(value) && isModelUsing(value.$model, composable); + value: ModelInstance, + composable: ModelComposableFactory, +): value is ModelInstanceUsing => isModelUsing(value.$model, composable); diff --git a/packages/core/src/model/checks/isModel.ts b/packages/core/src/model/checks/isModel.ts index 8814f138..219a24f6 100644 --- a/packages/core/src/model/checks/isModel.ts +++ b/packages/core/src/model/checks/isModel.ts @@ -1,7 +1,14 @@ -import { Model, ModelClass } from '@foscia/core/model/types'; +import { Model } from '@foscia/core/model/types'; import { SYMBOL_MODEL_CLASS } from '@foscia/core/symbols'; import { isFosciaType } from '@foscia/shared'; -export default ( +/** + * Check if value is a model. + * + * @param value + * + * @category Utilities + */ +export default ( value: unknown, ): value is M => isFosciaType(value, SYMBOL_MODEL_CLASS); diff --git a/packages/core/src/model/checks/isModelUsing.ts b/packages/core/src/model/checks/isModelUsing.ts index 3f3eee3e..eb689c70 100644 --- a/packages/core/src/model/checks/isModelUsing.ts +++ b/packages/core/src/model/checks/isModelUsing.ts @@ -1,7 +1,28 @@ -import isModel from '@foscia/core/model/checks/isModel'; -import { ModelComposable, ModelUsing } from '@foscia/core/model/types'; +import { + Model, + ModelComposableFactory, + ModelUsing, + ModelComposable, +} from '@foscia/core/model/types'; +/** + * Check if value is a model using given composable. + * + * @param value + * @param composable + * + * @category Utilities + * + * @example + * ```typescript + * import { isModelUsing } from '@foscia/core'; + * + * if (isModelUsing(Post, publishable)) { + * // `Post` is strictly typed with `publishable` definition. + * } + * ``` + */ export default ( - value: unknown, - composable: C, -): value is ModelUsing => isModel(value) && value.$composables.indexOf(composable) !== -1; + value: Model, + composable: ModelComposableFactory, +): value is ModelUsing => value.$composables.some((c) => c.factory === composable); diff --git a/packages/core/src/model/checks/isPendingPropDef.ts b/packages/core/src/model/checks/isPendingPropDef.ts deleted file mode 100644 index 0f286e67..00000000 --- a/packages/core/src/model/checks/isPendingPropDef.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PendingModelProp } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_PENDING } from '@foscia/core/symbols'; -import { isFosciaType } from '@foscia/shared'; - -export default ( - value: unknown, -): value is PendingModelProp => isFosciaType(value, SYMBOL_MODEL_PROP_PENDING); diff --git a/packages/core/src/model/checks/isPluralRelationDef.ts b/packages/core/src/model/checks/isPluralRelationDef.ts index a506402d..27b4f6de 100644 --- a/packages/core/src/model/checks/isPluralRelationDef.ts +++ b/packages/core/src/model/checks/isPluralRelationDef.ts @@ -1,6 +1,13 @@ import { ModelRelation } from '@foscia/core/model/types'; import { SYMBOL_MODEL_RELATION_HAS_MANY } from '@foscia/core/symbols'; +/** + * Check if relation definition is a plural relation. + * + * @param def + * + * @internal + */ export default ( def: ModelRelation, ): boolean => def.$RELATION_TYPE === SYMBOL_MODEL_RELATION_HAS_MANY; diff --git a/packages/core/src/model/checks/isPropDef.ts b/packages/core/src/model/checks/isPropDef.ts index 999b4c4a..4777fe89 100644 --- a/packages/core/src/model/checks/isPropDef.ts +++ b/packages/core/src/model/checks/isPropDef.ts @@ -3,8 +3,15 @@ import isIdDef from '@foscia/core/model/checks/isIdDef'; import isRelationDef from '@foscia/core/model/checks/isRelationDef'; import { ModelAttribute, ModelId, ModelRelation } from '@foscia/core/model/types'; +/** + * Check if value is a property definition. + * + * @param value + * + * @internal + */ export default ( - def: unknown, -): def is ModelId | ModelAttribute | ModelRelation => isIdDef(def) - || isAttributeDef(def) - || isRelationDef(def); + value: unknown, +): value is ModelId | ModelAttribute | ModelRelation => isIdDef(value) + || isAttributeDef(value) + || isRelationDef(value); diff --git a/packages/core/src/model/checks/isPropDefOfType.ts b/packages/core/src/model/checks/isPropDefOfType.ts new file mode 100644 index 00000000..776ab699 --- /dev/null +++ b/packages/core/src/model/checks/isPropDefOfType.ts @@ -0,0 +1,18 @@ +import { ModelAttribute, ModelId, ModelRelation } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP } from '@foscia/core/symbols'; +import { isFosciaType } from '@foscia/shared'; + +/** + * Check if value is a property definition of the given type. + * + * @param value + * @param propType + * + * @internal + */ +export default

( + value: unknown, + propType: P['$VALUE_PROP_TYPE'], +): value is P => isFosciaType(value, SYMBOL_MODEL_PROP) + && '$VALUE_PROP_TYPE' in value + && value.$VALUE_PROP_TYPE === propType; diff --git a/packages/core/src/model/checks/isRelationDef.ts b/packages/core/src/model/checks/isRelationDef.ts index 736d0e33..bea01f95 100644 --- a/packages/core/src/model/checks/isRelationDef.ts +++ b/packages/core/src/model/checks/isRelationDef.ts @@ -1,7 +1,14 @@ +import isPropDefOfType from '@foscia/core/model/checks/isPropDefOfType'; import { ModelRelation } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_RELATION } from '@foscia/core/symbols'; -import { isFosciaType } from '@foscia/shared'; +import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; +/** + * Check if value is a relation definition. + * + * @param value + * + * @internal + */ export default ( value: unknown, -): value is ModelRelation => isFosciaType(value, SYMBOL_MODEL_PROP_RELATION); +): value is ModelRelation => isPropDefOfType(value, SYMBOL_MODEL_PROP_KIND_RELATION); diff --git a/packages/core/src/model/checks/isSingularRelationDef.ts b/packages/core/src/model/checks/isSingularRelationDef.ts index f45ef820..abf152de 100644 --- a/packages/core/src/model/checks/isSingularRelationDef.ts +++ b/packages/core/src/model/checks/isSingularRelationDef.ts @@ -1,6 +1,13 @@ +import isPluralRelationDef from '@foscia/core/model/checks/isPluralRelationDef'; import { ModelRelation } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_RELATION_HAS_ONE } from '@foscia/core/symbols'; +/** + * Check if relation definition is a singular relation. + * + * @param def + * + * @internal + */ export default ( def: ModelRelation, -): boolean => def.$RELATION_TYPE === SYMBOL_MODEL_RELATION_HAS_ONE; +) => !isPluralRelationDef(def); diff --git a/packages/core/src/model/checks/isSnapshot.ts b/packages/core/src/model/checks/isSnapshot.ts new file mode 100644 index 00000000..1b24b1cb --- /dev/null +++ b/packages/core/src/model/checks/isSnapshot.ts @@ -0,0 +1,14 @@ +import { ModelLimitedSnapshot, ModelSnapshot } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_SNAPSHOT } from '@foscia/core/symbols'; +import { isFosciaType } from '@foscia/shared'; + +/** + * Check if value is a snapshot. + * + * @param value + * + * @category Utilities + */ +export default ( + value: unknown, +): value is ModelSnapshot | ModelLimitedSnapshot => isFosciaType(value, SYMBOL_MODEL_SNAPSHOT); diff --git a/packages/core/src/model/composition/applyDefinition.ts b/packages/core/src/model/composition/applyDefinition.ts new file mode 100644 index 00000000..565ba24b --- /dev/null +++ b/packages/core/src/model/composition/applyDefinition.ts @@ -0,0 +1,35 @@ +import FosciaError from '@foscia/core/errors/fosciaError'; +import isComposable from '@foscia/core/model/checks/isComposable'; +import isIdDef from '@foscia/core/model/checks/isIdDef'; +import { Model, ModelComposable } from '@foscia/core/model/types'; +import { eachDescriptors } from '@foscia/shared'; + +/** + * Apply the definition to the model. + * + * @param parent + * @param definition + * + * @internal + */ +export default ( + parent: Model, + definition: object, +) => eachDescriptors(definition, (key, descriptor) => { + let composable: ModelComposable | undefined; + if (isComposable(descriptor.value)) { + composable = descriptor.value.bind({ parent, key }); + } + + if ((key === 'id' || key === 'lid') && !isIdDef(composable)) { + throw new FosciaError( + `\`id\` and \`lid\` must be defined with \`id()\` factory (found \`${key}\`).`, + ); + } + + if (composable) { + parent.$composables.push(composable); + } else { + Object.defineProperty(parent.prototype, key, descriptor); + } +}); diff --git a/packages/core/src/model/composition/makeComposable.ts b/packages/core/src/model/composition/makeComposable.ts new file mode 100644 index 00000000..42b4a8f8 --- /dev/null +++ b/packages/core/src/model/composition/makeComposable.ts @@ -0,0 +1,89 @@ +import mergeHooks from '@foscia/core/hooks/mergeHooks'; +import { Hookable } from '@foscia/core/hooks/types'; +import applyDefinition from '@foscia/core/model/composition/applyDefinition'; +import makeComposableFactory from '@foscia/core/model/composition/makeComposableFactory'; +import makeDefinition from '@foscia/core/model/composition/makeDefinition'; +import { + ModelComposable, + ModelComposableFactory, + ModelHooksDefinition, + ModelInstance, + ModelParsedFlattenDefinition, +} from '@foscia/core/model/types'; + +const makeComposable: { + /** + * Create a composable with a static definition. + * + * @param rawDefinition + * + * @category Factories + * + * @example + * ```typescript + * import { makeComposable } from '@foscia/core'; + * + * const taggable = makeComposable({ + * tags: hasMany(() => Tag), + * }); + * + * export default class Post extends makeModel('posts', { + * // Produces a "to many" `tags` relation. + * taggable, + * }) {} + * ``` + */( + rawDefinition: D & ThisType>>, + ): ModelComposableFactory; + }> & Hookable; + /** + * Create a composable with a dynamic definition. + * + * @param rawDefinitionFactory + * + * @category Factories + * + * @experimental + * + * @example + * ```typescript + * import { makeComposable } from '@foscia/core'; + * + * type ImageableDefinition = + * & Record> + * & Record<`${K}URL`, ModelAttributeFactory>; + * + * interface Imageable extends ModelComposable { + * readonly _type: ImageableDefinition; + * } + * + * const imageable = makeComposable(({ key }) => ({ + * [key]: hasOne(() => Image), + * [`${key}URL`]: attr(() => '', { readOnly: true }), + * })); + * + * export default class Post extends makeModel('posts', { + * // Produces a "to one" `image` relation and a string `imageURL` attribute. + * image: imgeable, + * }) {} + * ``` + */( + rawDefinitionFactory: (composable: C) => {}, + ): ModelComposableFactory & Hookable; +} = ( + rawDefinition: {} | ((composable: ModelComposable) => {}), +) => makeComposableFactory>({ + bind: (composable) => { + applyDefinition(composable.parent, makeDefinition( + typeof rawDefinition === 'function' ? rawDefinition(composable) : rawDefinition, + )); + // eslint-disable-next-line no-param-reassign + composable.parent.$hooks = mergeHooks(composable.parent.$hooks!, composable.factory.$hooks!); + }, + properties: { + $hooks: {}, + }, +}); + +export default makeComposable; diff --git a/packages/core/src/model/composition/makeComposableFactory.ts b/packages/core/src/model/composition/makeComposableFactory.ts new file mode 100644 index 00000000..4ff2cd07 --- /dev/null +++ b/packages/core/src/model/composition/makeComposableFactory.ts @@ -0,0 +1,37 @@ +import { ModelPendingComposable } from '@foscia/core/model/composition/types'; +import { ModelComposable, ModelComposableFactory } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_COMPOSABLE } from '@foscia/core/symbols'; + +/** + * Create a fully customizable composable factory. + * + * @param config + * + * @internal + */ +export default ( + config: { + bind: (composable: C & { factory: ModelComposableFactory & U; }) => void, + composable?: ModelPendingComposable, + properties?: U, + }, +) => { + const factory = { + ...config.properties, + $FOSCIA_TYPE: SYMBOL_MODEL_COMPOSABLE, + bind: ({ parent, key }) => { + const composable: any = { + factory, + parent, + key, + ...config.composable, + }; + + config.bind(composable); + + return composable; + }, + } as ModelComposableFactory & U; + + return factory; +}; diff --git a/packages/core/src/model/composition/makeDefinition.ts b/packages/core/src/model/composition/makeDefinition.ts new file mode 100644 index 00000000..fbae8b4f --- /dev/null +++ b/packages/core/src/model/composition/makeDefinition.ts @@ -0,0 +1,21 @@ +import isComposable from '@foscia/core/model/checks/isComposable'; +import { ModelParsedDefinition } from '@foscia/core/model/types'; +import { Dictionary, eachDescriptors, makeDescriptorHolder, tap } from '@foscia/shared'; + +/** + * Parse each descriptor of a raw definition. + * + * @param definition + * + * @internal + */ +export default (definition?: D) => tap({} as Dictionary, (parsedDefinition) => { + eachDescriptors(definition ?? {}, (key, descriptor) => { + // eslint-disable-next-line no-param-reassign + parsedDefinition[key] = ( + descriptor.value && isComposable(descriptor.value) + ? descriptor.value + : makeDescriptorHolder(descriptor) + ); + }); +}) as ModelParsedDefinition; diff --git a/packages/core/src/model/composition/types.ts b/packages/core/src/model/composition/types.ts new file mode 100644 index 00000000..541eb7bb --- /dev/null +++ b/packages/core/src/model/composition/types.ts @@ -0,0 +1,9 @@ +import { ModelComposable } from '@foscia/core/model/types'; + +/** + * Model composable factory typing. + * + * @internal + */ +export type ModelPendingComposable = + Omit & ThisType; diff --git a/packages/core/src/model/fill.ts b/packages/core/src/model/fill.ts index 207370fc..aa5780b7 100644 --- a/packages/core/src/model/fill.ts +++ b/packages/core/src/model/fill.ts @@ -1,13 +1,27 @@ -/* eslint-disable no-param-reassign */ -import { ModelInstance, ModelKey, ModelWritableValues } from '@foscia/core/model/types'; +import { ModelInstance, ModelKey, ModelValues, ModelWritableKey } from '@foscia/core/model/types'; +import { tap } from '@foscia/shared'; +/** + * Fill the instance with given values. + * + * @param instance + * @param values + * + * @category Utilities + * + * @example + * ```typescript + * import { fill } from '@foscia/core'; + * + * const post = fill(new Post(), { title: 'Hello', description: 'World' }); + * ``` + */ export default ( instance: I, - values: Partial>, -) => { + values: Partial, ModelWritableKey>>, +) => tap(instance, () => { Object.entries(values).forEach(([key, value]) => { + // eslint-disable-next-line no-param-reassign instance[key as ModelKey] = value as any; }); - - return instance; -}; +}); diff --git a/packages/core/src/model/filled.ts b/packages/core/src/model/filled.ts index d4f3bbd3..aa6c2f8b 100644 --- a/packages/core/src/model/filled.ts +++ b/packages/core/src/model/filled.ts @@ -1,13 +1,25 @@ import { ModelInstance } from '@foscia/core/model/types'; /** - * Check if instance contains any values, even defined as null. It excludes ID - * and LID from checked values. + * Check if instance contains any values, even defined as null. + * * This can be useful to check if any data has been loaded on an instance from - * the store. If no attributes/relations are declared on model, it will always - * return true. + * the store. If no attributes or relations are declared on model, it will + * always return true. Notice that it excludes ID and LID from checked values. * * @param instance + * + * @category Utilities + * @since 0.9.3 + * + * @example + * ```typescript + * import { filled } from '@foscia/core'; + * + * if (filled(myPost)) { + * /* myPost contains at least one filled value *\/ + * } + * ``` */ export default (instance: ModelInstance) => Object.keys(instance.$model.$schema).length <= 2 || Object.keys(instance.$values).some( diff --git a/packages/core/src/model/forceFill.ts b/packages/core/src/model/forceFill.ts index c0afe2b9..867d4cc4 100644 --- a/packages/core/src/model/forceFill.ts +++ b/packages/core/src/model/forceFill.ts @@ -2,17 +2,33 @@ import fill from '@foscia/core/model/fill'; import { ModelInstance, ModelValues } from '@foscia/core/model/types'; +/** + * Fill the instance with given values even if values are read-only. + * + * @param instance + * @param values + * + * @category Utilities + * @since 0.6.1 + * + * @example + * ```typescript + * import { forceFill } from '@foscia/core'; + * + * const post = forceFill(new Post(), { author: user }); + * ``` + */ export default ( instance: I, values: Partial>, ) => { - const { strictReadonly } = instance.$model.$config; + const { strictReadOnly } = instance.$model.$config; try { - instance.$model.$config.strictReadonly = false; + instance.$model.$config.strictReadOnly = false; - return fill(instance, values); + return fill(instance, values as any); } finally { - instance.$model.$config.strictReadonly = strictReadonly; + instance.$model.$config.strictReadOnly = strictReadOnly; } }; diff --git a/packages/core/src/model/hooks/makeInstanceHook.ts b/packages/core/src/model/hooks/makeInstanceHook.ts index 3a0a9d63..26c141cd 100644 --- a/packages/core/src/model/hooks/makeInstanceHook.ts +++ b/packages/core/src/model/hooks/makeInstanceHook.ts @@ -2,19 +2,26 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { HookCallback, SyncHookCallback } from '@foscia/core/hooks/types'; import { Model, - ModelComposable, + ModelComposableFactory, ModelFactory, ModelHooksDefinition, ModelInstance, ModelInstanceUsing, } from '@foscia/core/model/types'; +/** + * Create an instance hook registration function. + * + * @param hook + * + * @internal + */ export default (hook: keyof ModelHooksDefinition): { ( model: Model, callback: (instance: I) => unknown, ): () => void; - ( + ( composable: C, callback: (instance: ModelInstanceUsing) => unknown, ): () => void; diff --git a/packages/core/src/model/hooks/makeModelHook.ts b/packages/core/src/model/hooks/makeModelHook.ts index ec9c57c2..264c2354 100644 --- a/packages/core/src/model/hooks/makeModelHook.ts +++ b/packages/core/src/model/hooks/makeModelHook.ts @@ -2,19 +2,26 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { SyncHookCallback } from '@foscia/core/hooks/types'; import { Model, - ModelComposable, + ModelComposableFactory, ModelFactory, ModelHooksDefinition, ModelInstance, ModelUsing, } from '@foscia/core/model/types'; +/** + * Create a model hook registration function. + * + * @param hook + * + * @internal + */ export default (hook: keyof ModelHooksDefinition): { ( model: M, callback: (model: M) => unknown, ): () => void; - ( + ( composable: C, callback: (model: ModelUsing) => unknown, ): () => void; diff --git a/packages/core/src/model/hooks/onBoot.ts b/packages/core/src/model/hooks/onBoot.ts index 4a783793..dcb444ad 100644 --- a/packages/core/src/model/hooks/onBoot.ts +++ b/packages/core/src/model/hooks/onBoot.ts @@ -1,3 +1,8 @@ import makeModelHook from '@foscia/core/model/hooks/makeModelHook'; -export default makeModelHook('boot'); +/** + * Register a "boot" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeModelHook('boot'); diff --git a/packages/core/src/model/hooks/onCreated.ts b/packages/core/src/model/hooks/onCreated.ts index 6a64a6c3..3dd70ced 100644 --- a/packages/core/src/model/hooks/onCreated.ts +++ b/packages/core/src/model/hooks/onCreated.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('created'); +/** + * Register a "created" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('created'); diff --git a/packages/core/src/model/hooks/onCreating.ts b/packages/core/src/model/hooks/onCreating.ts index 1a7e137f..6496dd4e 100644 --- a/packages/core/src/model/hooks/onCreating.ts +++ b/packages/core/src/model/hooks/onCreating.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('creating'); +/** + * Register a "creating" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('creating'); diff --git a/packages/core/src/model/hooks/onDestroyed.ts b/packages/core/src/model/hooks/onDestroyed.ts index e860c4a3..5226d50d 100644 --- a/packages/core/src/model/hooks/onDestroyed.ts +++ b/packages/core/src/model/hooks/onDestroyed.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('destroyed'); +/** + * Register a "destroyed" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('destroyed'); diff --git a/packages/core/src/model/hooks/onDestroying.ts b/packages/core/src/model/hooks/onDestroying.ts index 2cd8bbd3..4598c46b 100644 --- a/packages/core/src/model/hooks/onDestroying.ts +++ b/packages/core/src/model/hooks/onDestroying.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('destroying'); +/** + * Register a "destroying" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('destroying'); diff --git a/packages/core/src/model/hooks/onInit.ts b/packages/core/src/model/hooks/onInit.ts index 409a9f59..f970f8dd 100644 --- a/packages/core/src/model/hooks/onInit.ts +++ b/packages/core/src/model/hooks/onInit.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('init'); +/** + * Register a "init" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('init'); diff --git a/packages/core/src/model/hooks/onRetrieved.ts b/packages/core/src/model/hooks/onRetrieved.ts index c42865f1..a22ca09f 100644 --- a/packages/core/src/model/hooks/onRetrieved.ts +++ b/packages/core/src/model/hooks/onRetrieved.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('retrieved'); +/** + * Register a "retrieved" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('retrieved'); diff --git a/packages/core/src/model/hooks/onSaved.ts b/packages/core/src/model/hooks/onSaved.ts index b2a9f846..12ed7fb4 100644 --- a/packages/core/src/model/hooks/onSaved.ts +++ b/packages/core/src/model/hooks/onSaved.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('saved'); +/** + * Register a "saved" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('saved'); diff --git a/packages/core/src/model/hooks/onSaving.ts b/packages/core/src/model/hooks/onSaving.ts index 08f5759f..55ab0656 100644 --- a/packages/core/src/model/hooks/onSaving.ts +++ b/packages/core/src/model/hooks/onSaving.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('saving'); +/** + * Register a "saving" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('saving'); diff --git a/packages/core/src/model/hooks/onUpdated.ts b/packages/core/src/model/hooks/onUpdated.ts index 2134fd51..2a2fb919 100644 --- a/packages/core/src/model/hooks/onUpdated.ts +++ b/packages/core/src/model/hooks/onUpdated.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('updated'); +/** + * Register a "updated" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('updated'); diff --git a/packages/core/src/model/hooks/onUpdating.ts b/packages/core/src/model/hooks/onUpdating.ts index 04b49e13..7d24c7cc 100644 --- a/packages/core/src/model/hooks/onUpdating.ts +++ b/packages/core/src/model/hooks/onUpdating.ts @@ -1,3 +1,8 @@ import makeInstanceHook from '@foscia/core/model/hooks/makeInstanceHook'; -export default makeInstanceHook('updating'); +/** + * Register a "updating" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makeInstanceHook('updating'); diff --git a/packages/core/src/model/hooks/properties/makePropertyReadHook.ts b/packages/core/src/model/hooks/properties/makePropertyReadHook.ts index 0e1017a8..2e558d67 100644 --- a/packages/core/src/model/hooks/properties/makePropertyReadHook.ts +++ b/packages/core/src/model/hooks/properties/makePropertyReadHook.ts @@ -1,40 +1,82 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { + InferModelSchemaProp, Model, ModelComposable, + ModelComposableFactory, ModelFactory, ModelInstance, ModelInstancePropertyReadHookCallback, + ModelInstanceUsing, ModelValues, } from '@foscia/core/model/types'; +/** + * Create a property read/reading hook registration function. + * + * @param hook + * + * @internal + */ export default (hook: 'read' | 'reading'): { - , V extends ModelValues, K extends keyof D & keyof V>( + < + D extends {}, + I extends ModelInstance, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( model: Model, - callback: (event: { instance: I; def: D[K]; value?: V[K] }) => unknown, + callback: (event: { instance: I; def: P; value?: V[K] }) => unknown, ): () => void; - , V extends ModelValues, K extends keyof D & keyof V>( + < + D extends {}, + I extends ModelInstance, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( model: Model, key: K, - callback: (event: { instance: I; def: D[K]; value?: V[K] }) => unknown, + callback: (event: { instance: I; def: P; value?: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelComposable, - callback: (event: { instance: ModelInstance; def: D[K]; value?: V[K] }) => unknown, + < + C extends ModelComposable, + V extends ModelValues>, + K extends keyof V, + P extends InferModelSchemaProp, K>, + >( + composable: ModelComposableFactory, + callback: (event: { instance: ModelInstanceUsing; def: P; value?: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelComposable, + < + C extends ModelComposable, + V extends ModelValues>, + K extends keyof V, + P extends InferModelSchemaProp, K>, + >( + composable: ModelComposableFactory, key: K, - callback: (event: { instance: ModelInstance; def: D[K]; value?: V[K] }) => unknown, + callback: (event: { instance: ModelInstanceUsing; def: P; value?: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelFactory, - callback: (event: { instance: ModelInstance; def: D[K]; value?: V[K] }) => unknown, + < + D extends {}, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( + factory: ModelFactory, + callback: (event: { instance: ModelInstance; def: P; value?: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelFactory, + < + D extends {}, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( + factory: ModelFactory, key: K, - callback: (event: { instance: ModelInstance; def: D[K]; value?: V[K] }) => unknown, + callback: (event: { instance: ModelInstance; def: P; value?: V[K] }) => unknown, ): () => void; } => ( model: any, diff --git a/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts b/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts index 1146ee63..c3b8412f 100644 --- a/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts +++ b/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts @@ -1,57 +1,99 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { + InferModelSchemaProp, Model, ModelComposable, + ModelComposableFactory, ModelFactory, ModelInstance, ModelInstancePropertyWriteHookCallback, + ModelInstanceUsing, ModelValues, } from '@foscia/core/model/types'; +/** + * Create a property write/writing hook registration function. + * + * @param hook + * + * @internal + */ export default (hook: 'write' | 'writing'): { - , V extends ModelValues, K extends keyof D & keyof V>( + < + D extends {}, + I extends ModelInstance, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( model: Model, - callback: (event: { instance: I; def: D[K]; prev?: V[K]; next: V[K] }) => unknown, + callback: (event: { instance: I; def: P; prev?: V[K]; next: V[K] }) => unknown, ): () => void; - , V extends ModelValues, K extends keyof D & keyof V>( + < + D extends {}, + I extends ModelInstance, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( model: Model, key: K, - callback: (event: { instance: I; def: D[K]; prev?: V[K]; next: V[K] }) => unknown, + callback: (event: { instance: I; def: P; prev?: V[K]; next: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelComposable, + < + C extends ModelComposable, + V extends ModelValues>, + K extends keyof V, + P extends InferModelSchemaProp, K>, + >( + composable: ModelComposableFactory, callback: (event: { - instance: ModelInstance; - def: D[K]; + instance: ModelInstanceUsing; + def: P; prev?: V[K]; next: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelComposable, + < + C extends ModelComposable, + V extends ModelValues>, + K extends keyof V, + P extends InferModelSchemaProp, K>, + >( + composable: ModelComposableFactory, key: K, callback: (event: { - instance: ModelInstance; - def: D[K]; + instance: ModelInstanceUsing; + def: P; prev?: V[K]; next: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelFactory, + < + D extends {}, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( + factory: ModelFactory, callback: (event: { instance: ModelInstance; - def: D[K]; + def: P; prev?: V[K]; next: V[K] }) => unknown, ): () => void; - , K extends keyof D & keyof V>( - model: ModelFactory, + < + D extends {}, + V extends ModelValues, + K extends keyof V, + P extends InferModelSchemaProp, + >( + factory: ModelFactory, key: K, callback: (event: { instance: ModelInstance; - def: D[K]; + def: P; prev?: V[K]; next: V[K] }) => unknown, diff --git a/packages/core/src/model/hooks/properties/onPropertyRead.ts b/packages/core/src/model/hooks/properties/onPropertyRead.ts index 5ee9051e..3accb2e0 100644 --- a/packages/core/src/model/hooks/properties/onPropertyRead.ts +++ b/packages/core/src/model/hooks/properties/onPropertyRead.ts @@ -1,3 +1,8 @@ import makePropertyReadHook from '@foscia/core/model/hooks/properties/makePropertyReadHook'; -export default makePropertyReadHook('read'); +/** + * Register a "property read" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makePropertyReadHook('read'); diff --git a/packages/core/src/model/hooks/properties/onPropertyReading.ts b/packages/core/src/model/hooks/properties/onPropertyReading.ts index cf294c4f..247e7260 100644 --- a/packages/core/src/model/hooks/properties/onPropertyReading.ts +++ b/packages/core/src/model/hooks/properties/onPropertyReading.ts @@ -1,3 +1,8 @@ import makePropertyReadHook from '@foscia/core/model/hooks/properties/makePropertyReadHook'; -export default makePropertyReadHook('reading'); +/** + * Register a "property reading" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makePropertyReadHook('reading'); diff --git a/packages/core/src/model/hooks/properties/onPropertyWrite.ts b/packages/core/src/model/hooks/properties/onPropertyWrite.ts index 61b7cc64..0c8d9a89 100644 --- a/packages/core/src/model/hooks/properties/onPropertyWrite.ts +++ b/packages/core/src/model/hooks/properties/onPropertyWrite.ts @@ -1,3 +1,8 @@ import makePropertyWriteHook from '@foscia/core/model/hooks/properties/makePropertyWriteHook'; -export default makePropertyWriteHook('write'); +/** + * Register a "property write" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makePropertyWriteHook('write'); diff --git a/packages/core/src/model/hooks/properties/onPropertyWriting.ts b/packages/core/src/model/hooks/properties/onPropertyWriting.ts index 84ef6289..373d1397 100644 --- a/packages/core/src/model/hooks/properties/onPropertyWriting.ts +++ b/packages/core/src/model/hooks/properties/onPropertyWriting.ts @@ -1,3 +1,8 @@ import makePropertyWriteHook from '@foscia/core/model/hooks/properties/makePropertyWriteHook'; -export default makePropertyWriteHook('writing'); +/** + * Register a "property writing" hook on model. + * + * @category Hooks + */ +export default /* @__PURE__ */ makePropertyWriteHook('writing'); diff --git a/packages/core/src/model/isSame.ts b/packages/core/src/model/isSame.ts index cc65e05d..71aa2eaa 100644 --- a/packages/core/src/model/isSame.ts +++ b/packages/core/src/model/isSame.ts @@ -1,6 +1,25 @@ import isInstance from '@foscia/core/model/checks/isInstance'; import { isNil } from '@foscia/shared'; +/** + * Check if given value are the same instance of model. + * + * @param value + * @param otherValue + * + * @category Utilities + * + * @example + * ```typescript + * import { isSame } from '@foscia/core'; + * + * if (isSame(fooPost, barPost)) { + * } + * ``` + * + * @remarks + * Instances values are not checked, only the model type and the ID. + */ export default ( value: unknown, otherValue: unknown, diff --git a/packages/core/src/model/makeComposable.ts b/packages/core/src/model/makeComposable.ts deleted file mode 100644 index fac0ef3c..00000000 --- a/packages/core/src/model/makeComposable.ts +++ /dev/null @@ -1,21 +0,0 @@ -import makeDefinition from '@foscia/core/model/makeDefinition'; -import { - ModelComposable, - ModelFlattenDefinition, - ModelInstance, - ModelParsedDefinition, -} from '@foscia/core/model/types'; -import { SYMBOL_MODEL_COMPOSABLE } from '@foscia/core/symbols'; - -/** - * Create a composable definition which will be used by a model factory. - * - * @param rawDefinition - */ -export default ( - rawDefinition?: D & ThisType>>>, -) => ({ - $FOSCIA_TYPE: SYMBOL_MODEL_COMPOSABLE, - $definition: makeDefinition(rawDefinition), - $hooks: {}, -} as ModelComposable>); diff --git a/packages/core/src/model/makeDefinition.ts b/packages/core/src/model/makeDefinition.ts deleted file mode 100644 index baead404..00000000 --- a/packages/core/src/model/makeDefinition.ts +++ /dev/null @@ -1,33 +0,0 @@ -import isComposable from '@foscia/core/model/checks/isComposable'; -import isPendingPropDef from '@foscia/core/model/checks/isPendingPropDef'; -import isPropDef from '@foscia/core/model/checks/isPropDef'; -import { ModelParsedDefinition } from '@foscia/core/model/types'; -import { Dictionary, eachDescriptors, makeDescriptorHolder } from '@foscia/shared'; - -const parseDescriptor = (key: string, descriptor: PropertyDescriptor) => { - if (descriptor.value) { - if (isComposable(descriptor.value)) { - return descriptor.value; - } - - if (isPropDef(descriptor.value)) { - return { ...descriptor.value, key }; - } - - if (isPendingPropDef(descriptor.value)) { - return { ...descriptor.value.definition, key }; - } - } - - return makeDescriptorHolder(descriptor); -}; - -export default (definition?: D) => { - const parsedDefinition: Dictionary = {}; - - eachDescriptors(definition ?? {}, (key, descriptor) => { - parsedDefinition[key] = parseDescriptor(key, descriptor); - }); - - return parsedDefinition as ModelParsedDefinition; -}; diff --git a/packages/core/src/model/makeModel.ts b/packages/core/src/model/makeModel.ts index fff8a928..cc002a75 100644 --- a/packages/core/src/model/makeModel.ts +++ b/packages/core/src/model/makeModel.ts @@ -1,3 +1,18 @@ import makeModelFactory from '@foscia/core/model/makeModelFactory'; +/** + * Create a model. + * + * @category Factories + * + * @example + * ```typescript + * import { makeModel } from '@foscia/core'; + * + * export default class Post extends makeModel('posts', { + * // Definition... + * }) { + * } + * ``` + */ export default /* @__PURE__ */ makeModelFactory(); diff --git a/packages/core/src/model/makeModelClass.ts b/packages/core/src/model/makeModelClass.ts index ab0285a2..6d9beb8b 100644 --- a/packages/core/src/model/makeModelClass.ts +++ b/packages/core/src/model/makeModelClass.ts @@ -2,120 +2,116 @@ import FosciaError from '@foscia/core/errors/fosciaError'; import mergeHooks from '@foscia/core/hooks/mergeHooks'; import runHooksSync from '@foscia/core/hooks/runHooksSync'; import { HooksRegistrar } from '@foscia/core/hooks/types'; -import logger from '@foscia/core/logger/logger'; -import isComposable from '@foscia/core/model/checks/isComposable'; -import isIdDef from '@foscia/core/model/checks/isIdDef'; -import isPropDef from '@foscia/core/model/checks/isPropDef'; -import forceFill from '@foscia/core/model/forceFill'; -import makeDefinition from '@foscia/core/model/makeDefinition'; -import id from '@foscia/core/model/props/builders/id'; -import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; +import applyDefinition from '@foscia/core/model/composition/applyDefinition'; +import makeDefinition from '@foscia/core/model/composition/makeDefinition'; import { ExtendableModel, Model, ModelConfig, ModelHooksDefinition, ModelInstance, + ModelSnapshot, } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_CLASS, SYMBOL_MODEL_INSTANCE } from '@foscia/core/symbols'; -import { eachDescriptors, mergeConfig, value } from '@foscia/shared'; - -const createModelClass = ( +import { + SYMBOL_MODEL_CLASS, + SYMBOL_MODEL_INSTANCE, + SYMBOL_MODEL_SNAPSHOT, +} from '@foscia/core/symbols'; +import { mergeConfig } from '@foscia/shared'; + +const { defineProperty } = Object; + +/** + * Create a model class. + * + * @param type + * @param config + * @param hooks + * @param definition + * @param parentModel + * + * @internal + */ +const makeModelClass = ( type: string, config: ModelConfig, hooks: HooksRegistrar, definition: object, - PrevModelClass?: ExtendableModel, + parentModel?: ExtendableModel, ) => { if (type.length === 0) { throw new FosciaError('Model type cannot be an empty string.'); } - const ModelClass = PrevModelClass ? class extends PrevModelClass { + const parseModel = (currentModel: Model) => { + if (!currentModel.$parsed) { + defineProperty(currentModel, '$composables', { value: [] }); + defineProperty(currentModel, '$schema', { value: {} }); + defineProperty(currentModel, '$hooks', { writable: true, value: {} }); + + // eslint-disable-next-line no-param-reassign + currentModel.$hooks = mergeHooks(currentModel.$hooks!, hooks); + applyDefinition(currentModel, definition); + + // eslint-disable-next-line no-param-reassign + currentModel.$parsed = true; + } + }; + + const model = parentModel ? class extends parentModel { } : function ModelConstructor(this: ModelInstance) { - Object.defineProperty(this, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_INSTANCE }); - Object.defineProperty(this, '$model', { value: this.constructor }); - Object.defineProperty(this, '$exists', { writable: true, value: false }); - Object.defineProperty(this, '$raw', { writable: true, value: null }); - Object.defineProperty(this, '$loaded', { writable: true, value: {} }); - Object.defineProperty(this, '$values', { writable: true, value: {} }); - Object.defineProperty(this, '$original', { writable: true, value: takeSnapshot(this) }); - - Object.values(this.$model.$schema).forEach((def) => { - Object.defineProperty(this, def.key, { - enumerable: true, - get: () => { - const readingHookEvent = { instance: this, def, value: this.$values[def.key] }; - runHooksSync(this.$model, `property:reading:${def.key}`, readingHookEvent); - runHooksSync(this.$model, 'property:reading', readingHookEvent); - - const currentValue = this.$values[def.key]; - if ( - currentValue === undefined - && (this.$model.$config.strictProperties ?? this.$model.$config.strict ?? false) - ) { - throw new FosciaError( - `\`${this.$model.$type}.${def.key}\` value was not retrieved from the data source and model uses strict properties.`, - ); - } - - const readHookEvent = { instance: this, def, value: currentValue }; - runHooksSync(this.$model, `property:read:${def.key}`, readHookEvent); - runHooksSync(this.$model, 'property:read', readHookEvent); - - return currentValue; - }, - set: (next) => { - const writeHookEvent = { instance: this, def, prev: this.$values[def.key], next }; - - runHooksSync(this.$model, `property:writing:${def.key}`, writeHookEvent); - runHooksSync(this.$model, 'property:writing', writeHookEvent); - - if ( - def.readOnly - && (this.$model.$config.strictReadOnly ?? this.$model.$config.strict ?? true) - ) { - throw new FosciaError( - `\`${this.$model.$type}.${def.key}\` cannot be set because it is read-only.`, - ); - } - - this.$values[def.key] = next; - - runHooksSync(this.$model, `property:write:${def.key}`, writeHookEvent); - runHooksSync(this.$model, 'property:write', writeHookEvent); - }, - }); - - if (def.default !== undefined) { - if (def.default && typeof def.default === 'object') { - logger.warn( - `Default \`${this.$model.$type}.${def.key}\` object attribute's values must be defined using a factory function.`, - ); - } - - forceFill(this, { [def.key]: value(def.default) }); - } + defineProperty(this, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_INSTANCE }); + defineProperty(this, '$model', { value: this.constructor }); + defineProperty(this, '$exists', { writable: true, value: false }); + defineProperty(this, '$raw', { writable: true, value: null }); + defineProperty(this, '$loaded', { writable: true, value: {} }); + defineProperty(this, '$values', { writable: true, value: {} }); + defineProperty(this, '$original', { + writable: true, + value: { + $FOSCIA_TYPE: SYMBOL_MODEL_SNAPSHOT, + $original: null, + $instance: this, + $exists: false, + $raw: null, + $loaded: {}, + $values: {}, + } satisfies ModelSnapshot, }); + parseModel(this.$model); + + this.$model.$composables.forEach((composable) => composable.init?.(this)); + if (!this.$model.$booted) { + runHooksSync(this.$model, 'boot', this.$model); this.$model.$booted = true; - runHooksSync(this.$model, 'boot', this.$model as Model); } runHooksSync(this.$model, 'init', this); } as unknown as ExtendableModel; - Object.defineProperty(ModelClass, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_CLASS }); - Object.defineProperty(ModelClass, '$type', { value: type }); - Object.defineProperty(ModelClass, '$config', { value: { ...config } }); - Object.defineProperty(ModelClass, '$schema', { value: {} }); - Object.defineProperty(ModelClass, '$composables', { value: [] }); - Object.defineProperty(ModelClass, '$hooks', { writable: true, value: {} }); - Object.defineProperty(ModelClass, '$booted', { writable: true, value: false }); + defineProperty(model, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_CLASS }); + defineProperty(model, '$type', { value: type }); + defineProperty(model, '$config', { value: { ...config } }); + defineProperty(model, '$parsed', { writable: true, value: false }); + defineProperty(model, '$booted', { writable: true, value: false }); - ModelClass.configure = function configureModel(newConfig?: ModelConfig, override = true) { - return createModelClass( + const defineOverwrittenProperty = (property: string) => defineProperty(model, property, { + configurable: true, + get() { + parseModel(this); + + return this[property]; + }, + }); + + defineOverwrittenProperty('$composables'); + defineOverwrittenProperty('$schema'); + defineOverwrittenProperty('$hooks'); + + model.configure = function configureModel(newConfig?: Partial, override = true) { + return makeModelClass( this.$type, mergeConfig(this.$config, newConfig ?? {}, override), mergeHooks(this.$hooks!), @@ -124,54 +120,17 @@ const createModelClass = ( ); }; - ModelClass.extend = function extendModel(rawDefinition?: object) { - return createModelClass( + model.extend = function extendModel(rawDefinition?: object) { + return makeModelClass( this.$type, this.$config, mergeHooks(this.$hooks!), - { ...definition, ...(rawDefinition ?? {}) }, + { ...definition, ...makeDefinition(rawDefinition ?? {}) }, this, ) as any; }; - const applyDefinition = ( - currentDefinition: object, - ) => eachDescriptors(currentDefinition, (key, descriptor) => { - if (key === 'type') { - throw new FosciaError( - '`type` is forbidden as a definition key because it may be used with some implementations.', - ); - } - - if ((key === 'id' || key === 'lid') && !isIdDef(descriptor.value)) { - throw new FosciaError( - `\`id\` and \`lid\` must be defined with \`id()\` factory (found \`${key}\`).`, - ); - } - - if (isComposable(descriptor.value)) { - ModelClass.$composables.push(descriptor.value); - - applyDefinition(descriptor.value.$definition); - - ModelClass.$hooks = mergeHooks(ModelClass.$hooks!, descriptor.value.$hooks!); - } else if (isPropDef(descriptor.value)) { - ModelClass.$schema[key] = descriptor.value; - } else { - Object.defineProperty(ModelClass.prototype, key, descriptor); - } - }); - - applyDefinition(makeDefinition(definition)); - - ModelClass.$hooks = mergeHooks(ModelClass.$hooks!, hooks); - - return ModelClass; + return model; }; -export default ( - type: string, - config: ModelConfig, - hooks: HooksRegistrar, - definition: object, -) => createModelClass(type, config, hooks, { id: id(), lid: id(), ...definition }); +export default makeModelClass; diff --git a/packages/core/src/model/makeModelFactory.ts b/packages/core/src/model/makeModelFactory.ts index a1499031..121fded9 100644 --- a/packages/core/src/model/makeModelFactory.ts +++ b/packages/core/src/model/makeModelFactory.ts @@ -1,34 +1,58 @@ +import makeDefinition from '@foscia/core/model/composition/makeDefinition'; import makeModelClass from '@foscia/core/model/makeModelClass'; +import id from '@foscia/core/model/props/builders/id'; import { - ModelConfig, ModelFactory, - ModelFlattenDefinition, + ModelConfig, + ModelFactory, ModelInstance, - ModelParsedDefinition, + ModelParsedFlattenDefinition, } from '@foscia/core/model/types'; +import cloneModelValue from '@foscia/core/model/utilities/cloneModelValue'; +import compareModelValues from '@foscia/core/model/utilities/compareModelValues'; +import { using } from '@foscia/shared'; +/** + * Create a model factory. + * + * @param baseConfig + * @param baseRawDefinition + * + * @category Factories + * + * @example + * ```typescript + * import { makeModelFactory } from '@foscia/core'; + * + * export default makeModelFactory({ + * // Common configuration... + * }, { + * // Common definition... + * }); + * ``` + */ export default ( - baseConfig?: ModelConfig, - // eslint-disable-next-line max-len - baseRawDefinition?: D & ThisType>>>, + baseConfig?: Partial, + baseRawDefinition?: D & ThisType>>, ) => { const factory = ( - rawConfig: string | (ModelConfig & { type: string; }), + rawConfig: string | (Partial & { type: string; }), rawDefinition?: object, - ) => { - const { type, ...config } = typeof rawConfig === 'string' - ? { type: rawConfig } - : rawConfig; - - return makeModelClass(type, { + ) => using( + typeof rawConfig === 'string' ? { type: rawConfig } : rawConfig, + ({ type, ...config }) => makeModelClass(type, { + compareSnapshotValues: compareModelValues, + cloneSnapshotValue: cloneModelValue, ...baseConfig, ...config, }, factory.$hooks, { - ...baseRawDefinition, - ...rawDefinition, - }); - }; + id: id(), + lid: id(), + ...makeDefinition(baseRawDefinition), + ...makeDefinition(rawDefinition), + }), + ); factory.$hooks = {}; - return factory as ModelFactory>>; + return factory as unknown as ModelFactory>; }; diff --git a/packages/core/src/model/props/builders/assembled.ts b/packages/core/src/model/props/builders/assembled.ts new file mode 100644 index 00000000..2a94fdb8 --- /dev/null +++ b/packages/core/src/model/props/builders/assembled.ts @@ -0,0 +1,132 @@ +import makePropChainableFactory from '@foscia/core/model/props/builders/makePropChainableFactory'; +import { + ModelAssembledFactory, + ModelAssembledFactoryConfig, +} from '@foscia/core/model/props/builders/types'; +import { ModelPropSync } from '@foscia/core/model/types'; +import { tap } from '@foscia/shared'; + +const assembled: { + /** + * Create an assembled property factory. + * + * @category Factories + * + * @since 0.13.0 + * @experimental + * + * @example + * ```typescript + * import { assembled } from '@foscia/core'; + * + * assembled((user) => `${user.firstName} ${user.lastName}`); + * ``` + */( + get: (instance: any) => T, + config?: ModelAssembledFactoryConfig, + ): ModelAssembledFactory; + /** + * Create an assembled property factory. + * + * @category Factories + * + * @since 0.13.0 + * @experimental + * + * @example + * ```typescript + * import { assembled } from '@foscia/core'; + * + * assembled({ + * get: (user) => `${user.firstName} ${user.lastName}`, + * set: (user, fullName) => { + * [user.firstName, user.lastName] = fullName.split(' '); + * }, + * }); + * ``` + */( + config: ModelAssembledFactoryConfig & { + get?: (instance: any) => T; + set: (instance: any, value: T) => void; + }, + ): ModelAssembledFactory; + /** + * Create an assembled property factory. + * + * @category Factories + * + * @since 0.13.0 + * @experimental + * + * @example + * ```typescript + * import { assembled } from '@foscia/core'; + * + * assembled({ + * get: (user) => `${user.firstName} ${user.lastName}`, + * set: (user, fullName) => { + * [user.firstName, user.lastName] = fullName.split(' '); + * }, + * }); + * ``` + */( + config: ModelAssembledFactoryConfig & { + get?: (instance: any) => T; + }, + ): ModelAssembledFactory; +} = ( + config: ((instance: any) => T) | (ModelAssembledFactoryConfig & { + get?: (instance: any) => T; + set?: (instance: any, value: T) => void; + }), + otherConfig?: ModelAssembledFactoryConfig, +) => { + const { get, set, ...props } = ( + typeof config === 'function' ? { get: config, ...otherConfig } : config + ); + + return makePropChainableFactory({ + readOnly: !set, + sync: false, + memo: true, + ...props, + init(instance) { + const noMemoSymbol = Symbol(''); + let memoValue = noMemoSymbol as unknown; + + const previousValues = new Map(); + + const shouldCompute = () => ( + !this.memo || memoValue === noMemoSymbol || [...previousValues.entries()].some( + ([key, value]) => !instance.$model.$config + .compareSnapshotValues(value, Reflect.get(instance, key)), + ) + ); + + const compute = () => { + previousValues.clear(); + + memoValue = get!(new Proxy(instance, { + get: (...params) => tap( + Reflect.get(...params), + (value) => previousValues.set(params[1], value), + ), + })); + + return memoValue; + }; + + Object.defineProperty(instance, this.key, { + enumerable: true, + get: get ? () => (shouldCompute() ? compute() : memoValue) : undefined, + set: set ? (value) => set(instance, value) : undefined, + }); + }, + }, { + alias: (alias: string) => ({ alias }), + sync: (sync?: boolean | ModelPropSync) => ({ sync: sync ?? true }), + memo: (memo?: boolean) => ({ memo: memo ?? true }), + }) as ModelAssembledFactory; +}; + +export default assembled; diff --git a/packages/core/src/model/props/builders/attr.ts b/packages/core/src/model/props/builders/attr.ts index 4f2bb576..8bf6c150 100644 --- a/packages/core/src/model/props/builders/attr.ts +++ b/packages/core/src/model/props/builders/attr.ts @@ -1,15 +1,55 @@ -import makePendingProp, { PROP_MODIFIERS } from '@foscia/core/model/props/builders/makePendingProp'; -import { PendingModelAttribute } from '@foscia/core/model/props/builders/types'; -import { SYMBOL_MODEL_PROP_ATTRIBUTE } from '@foscia/core/symbols'; +import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; +import parseValuePropConfig from '@foscia/core/model/props/builders/parseValuePropConfig'; +import { + ModelAttributeFactory, + ModelAttributeFactoryConfig, + ModelPendingProp, +} from '@foscia/core/model/props/builders/types'; +import { ModelAttribute } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP_KIND_ATTRIBUTE } from '@foscia/core/symbols'; import { ObjectTransformer } from '@foscia/core/transformers/types'; -export default ( - config?: ObjectTransformer | T | (() => T), -) => makePendingProp({ - ...PROP_MODIFIERS, +const attr: { + /** + * Create an attribute property factory. + * + * @category Factories + * + * @example + * ```typescript + * import { attr } from '@foscia/core'; + * + * attr(); + * attr('', { readOnly: true }); + * ``` + */( + defaultValue?: (T extends object ? never : T) | (() => T), + config?: Omit, 'default'>, + ): ModelAttributeFactory; + /** + * Create an attribute property factory. + * + * @category Factories + * + * @example + * ```typescript + * import { attr, toString, toDateTime } from '@foscia/core'; + * + * attr(toString()); + * attr(toDateTime(), { readOnly: true }); + * ``` + */( + transformer: ObjectTransformer, + config?: Omit, 'transformer'>, + ): ModelAttributeFactory; +} = ( + config?: ObjectTransformer | T | (() => T), + otherConfig?: ModelAttributeFactoryConfig, +) => makeValuePropFactory({ + $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_ATTRIBUTE, + ...parseValuePropConfig(config, otherConfig), +} as ModelPendingProp, { transform: (transformer: ObjectTransformer) => ({ transformer }), -})({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_ATTRIBUTE, - default: typeof config !== 'object' ? config : undefined, - transformer: typeof config === 'object' ? config : undefined, -}) as unknown as PendingModelAttribute; +}) as ModelAttributeFactory; + +export default attr; diff --git a/packages/core/src/model/props/builders/hasMany.ts b/packages/core/src/model/props/builders/hasMany.ts index d22e04ce..19ae1f82 100644 --- a/packages/core/src/model/props/builders/hasMany.ts +++ b/packages/core/src/model/props/builders/hasMany.ts @@ -1,22 +1,56 @@ import relation from '@foscia/core/model/props/builders/relation'; import { - PendingModelRelation, - PendingModelRelationConfig, - PendingModelRelationInstance, + InferModelRelationFactoryInstance, + ModelRelationFactory, + ModelRelationFactoryConfig, } from '@foscia/core/model/props/builders/types'; -import { ModelInstance, ModelRelationConfig } from '@foscia/core/model/types'; +import { ModelInstance } from '@foscia/core/model/types'; import { SYMBOL_MODEL_RELATION_HAS_MANY } from '@foscia/core/symbols'; import { Awaitable } from '@foscia/shared'; -const hasMany: { - ( - config?: string | string[] | ModelRelationConfig, - ): PendingModelRelation; - ( +export default /* @__PURE__ */ relation(SYMBOL_MODEL_RELATION_HAS_MANY) as { + /** + * Create a has many relation property factory. + * Recommended when having circular relations. + * + * @param type + * + * @category Factories + * + * @example + * ```typescript + * import { hasMany } from '@foscia/core'; + * + * hasMany(); + * hasMany('posts'); + * hasMany<(Post | Comment)[]>(['posts', 'comments']); + * ``` + */( + type?: string | string[] | ModelRelationFactoryConfig, + ): ModelRelationFactory; + /** + * Create a has many relation property factory. + * Recommended in most circumstances. + * + * @param resolver + * @param config + * + * @category Factories + * + * @example + * ```typescript + * import { hasMany } from '@foscia/core'; + * + * hasMany(() => Post); + * hasMany(() => [Post, Comment]); + * hasMany(() => Post, { readOnly: true }); + * ``` + */< + M extends object | readonly object[], + T extends InferModelRelationFactoryInstance[] = InferModelRelationFactoryInstance[], + R extends boolean = false, + >( resolver: () => Awaitable, - ): PendingModelRelation[], false>; -} = ( - config?: PendingModelRelationConfig, -) => relation(SYMBOL_MODEL_RELATION_HAS_MANY, config) as any; - -export default hasMany; + config?: ModelRelationFactoryConfig, + ): ModelRelationFactory; +}; diff --git a/packages/core/src/model/props/builders/hasOne.ts b/packages/core/src/model/props/builders/hasOne.ts index 122a3c9b..0cc9c21d 100644 --- a/packages/core/src/model/props/builders/hasOne.ts +++ b/packages/core/src/model/props/builders/hasOne.ts @@ -1,22 +1,56 @@ import relation from '@foscia/core/model/props/builders/relation'; import { - PendingModelRelation, - PendingModelRelationConfig, - PendingModelRelationInstance, + InferModelRelationFactoryInstance, + ModelRelationFactory, + ModelRelationFactoryConfig, } from '@foscia/core/model/props/builders/types'; -import { ModelInstance, ModelRelationConfig } from '@foscia/core/model/types'; +import { ModelInstance } from '@foscia/core/model/types'; import { SYMBOL_MODEL_RELATION_HAS_ONE } from '@foscia/core/symbols'; import { Awaitable } from '@foscia/shared'; -const hasOne: { - ( - config?: string | string[] | ModelRelationConfig, - ): PendingModelRelation; - ( +export default /* @__PURE__ */ relation(SYMBOL_MODEL_RELATION_HAS_ONE) as { + /** + * Create a has one relation property factory. + * Recommended when having circular relations. + * + * @param type + * + * @category Factories + * + * @example + * ```typescript + * import { hasOne } from '@foscia/core'; + * + * hasOne(); + * hasOne('posts'); + * hasOne(['posts', 'comments']); + * ``` + */( + type?: string | string[] | ModelRelationFactoryConfig, + ): ModelRelationFactory; + /** + * Create a has one relation property factory. + * Recommended in most circumstances. + * + * @param resolver + * @param config + * + * @category Factories + * + * @example + * ```typescript + * import { hasOne } from '@foscia/core'; + * + * hasOne(() => Post); + * hasOne(() => [Post, Comment]); + * hasOne(() => Post, { readOnly: true }); + * ``` + */< + M extends object | readonly object[], + T extends InferModelRelationFactoryInstance | null = InferModelRelationFactoryInstance, + R extends boolean = false, + >( resolver: () => Awaitable, - ): PendingModelRelation, false>; -} = ( - config?: PendingModelRelationConfig, -) => relation(SYMBOL_MODEL_RELATION_HAS_ONE, config) as any; - -export default hasOne; + config?: ModelRelationFactoryConfig, + ): ModelRelationFactory; +}; diff --git a/packages/core/src/model/props/builders/id.ts b/packages/core/src/model/props/builders/id.ts index 9cc0dd7f..0fe4b967 100644 --- a/packages/core/src/model/props/builders/id.ts +++ b/packages/core/src/model/props/builders/id.ts @@ -1,15 +1,55 @@ -import makePendingProp, { PROP_MODIFIERS } from '@foscia/core/model/props/builders/makePendingProp'; -import { PendingModelId } from '@foscia/core/model/props/builders/types'; -import { ModelIdType } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_ID } from '@foscia/core/symbols'; +import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; +import parseValuePropConfig from '@foscia/core/model/props/builders/parseValuePropConfig'; +import { + ModelIdFactory, + ModelIdFactoryConfig, + ModelPendingProp, +} from '@foscia/core/model/props/builders/types'; +import { ModelId, ModelIdType } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP_KIND_ID } from '@foscia/core/symbols'; import { ObjectTransformer } from '@foscia/core/transformers/types'; -export default ( - config?: ObjectTransformer | T | (() => T), -) => makePendingProp({ - ...PROP_MODIFIERS, +const id: { + /** + * Create an ID property factory. + * + * @category Factories + * + * @example + * ```typescript + * import { id } from '@foscia/core'; + * + * id(); + * id('', { readOnly: true }); + * ``` + */( + defaultValue?: (T extends object ? never : T) | (() => T), + config?: Omit, 'default'>, + ): ModelIdFactory; + /** + * Create an ID property factory. + * + * @category Factories + * + * @example + * ```typescript + * import { id, toString } from '@foscia/core'; + * + * id(toString()); + * id(toString(), { readOnly: true }); + * ``` + */( + transformer: ObjectTransformer, + config?: Omit, 'transformer'>, + ): ModelIdFactory; +} = ( + config?: ObjectTransformer | T | (() => T), + otherConfig?: ModelIdFactoryConfig, +) => makeValuePropFactory({ + $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_ID, + ...parseValuePropConfig(config, otherConfig), +} as ModelPendingProp, { transform: (transformer: ObjectTransformer) => ({ transformer }), -})({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_ID, - transformer: config, -}) as unknown as PendingModelId; +}) as ModelIdFactory; + +export default id; diff --git a/packages/core/src/model/props/builders/makePendingProp.ts b/packages/core/src/model/props/builders/makePendingProp.ts deleted file mode 100644 index e6f40da3..00000000 --- a/packages/core/src/model/props/builders/makePendingProp.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - PendingDefinition, - PendingDefinitionModifiers, -} from '@foscia/core/model/props/builders/types'; -import type { ModelPropSync } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_PENDING } from '@foscia/core/symbols'; -import { Dictionary, mapWithKeys } from '@foscia/shared'; - -export const PROP_MODIFIERS = { - default: (value: unknown | (() => unknown)) => ({ default: value }), - alias: (alias: string) => ({ alias }), - readOnly: (readOnly?: boolean) => ({ readOnly }), - sync: (sync: boolean | ModelPropSync) => ({ sync }), - nullable: () => ({}), -}; - -export default ( - modifiers: Dictionary<(...args: any[]) => Dictionary>, -) => { - const makePendingPropBuilder = (definition: Dictionary): PendingDefinition => ({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_PENDING, - definition, - ...mapWithKeys(modifiers, (modifier, key) => ({ - [key]: (...args: any[]) => makePendingPropBuilder({ - ...definition, - ...modifier(...args), - }), - } as PendingDefinitionModifiers)), - } as PendingDefinition); - - return makePendingPropBuilder; -}; diff --git a/packages/core/src/model/props/builders/makePropChainableFactory.ts b/packages/core/src/model/props/builders/makePropChainableFactory.ts new file mode 100644 index 00000000..3994ae41 --- /dev/null +++ b/packages/core/src/model/props/builders/makePropChainableFactory.ts @@ -0,0 +1,34 @@ +import makePropFactory from '@foscia/core/model/props/builders/makePropFactory'; +import { + ModelPendingProp, + ModelPropChainableFactory, +} from '@foscia/core/model/props/builders/types'; +import { ModelProp } from '@foscia/core/model/types'; +import { Dictionary, mapWithKeys } from '@foscia/shared'; + +/** + * Make a property definition chainable factory supporting + * given definition modifiers. + * + * @param pendingProp + * @param modifiers + * + * @internal + */ +const makePropChainableFactory = < + P extends ModelProp, + M extends Dictionary<(...args: any[]) => Partial

>, +>( + pendingProp: ModelPendingProp

, + modifiers: M, +): ModelPropChainableFactory => makePropFactory( + pendingProp, + mapWithKeys(modifiers, (modifier, key) => ({ + [key]: (...args: Parameters) => makePropChainableFactory({ + ...pendingProp, + ...modifier(...args), + }, modifiers), + })), +) as ModelPropChainableFactory; + +export default makePropChainableFactory; diff --git a/packages/core/src/model/props/builders/makePropFactory.ts b/packages/core/src/model/props/builders/makePropFactory.ts new file mode 100644 index 00000000..aa2dd40a --- /dev/null +++ b/packages/core/src/model/props/builders/makePropFactory.ts @@ -0,0 +1,28 @@ +import makeComposableFactory from '@foscia/core/model/composition/makeComposableFactory'; +import { ModelPendingComposable } from '@foscia/core/model/composition/types'; +import { ModelPendingProp } from '@foscia/core/model/props/builders/types'; +import { ModelProp } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP } from '@foscia/core/symbols'; + +/** + * Make a property factory. + * + * @param pendingProp + * @param properties + * + * @internal + */ +export default

( + pendingProp: ModelPendingProp

, + properties?: U, +) => makeComposableFactory({ + composable: { + $FOSCIA_TYPE: SYMBOL_MODEL_PROP, + ...pendingProp, + } as ModelPendingComposable

, + bind: (prop) => { + // eslint-disable-next-line no-param-reassign + (prop.parent.$schema[prop.key] as any) = prop; + }, + properties, +}); diff --git a/packages/core/src/model/props/builders/makeValuePropFactory.ts b/packages/core/src/model/props/builders/makeValuePropFactory.ts new file mode 100644 index 00000000..954b6f41 --- /dev/null +++ b/packages/core/src/model/props/builders/makeValuePropFactory.ts @@ -0,0 +1,89 @@ +import FosciaError from '@foscia/core/errors/fosciaError'; +import runHooksSync from '@foscia/core/hooks/runHooksSync'; +import logger from '@foscia/core/logger/logger'; +import forceFill from '@foscia/core/model/forceFill'; +import makePropChainableFactory from '@foscia/core/model/props/builders/makePropChainableFactory'; +import { ModelPendingProp } from '@foscia/core/model/props/builders/types'; +import { ModelPropSync, ModelValueProp } from '@foscia/core/model/types'; +import { Dictionary, value } from '@foscia/shared'; + +/** + * Make a value property definition factory. + * + * @param pendingProp + * @param modifiers + * + * @internal + */ +export default < + P extends ModelValueProp, + M extends Dictionary<(...args: any[]) => Partial

>, +>( + pendingProp: ModelPendingProp

, + modifiers: M, +) => makePropChainableFactory({ + init(instance) { + Object.defineProperty(instance, this.key, { + enumerable: true, + get: () => { + const readingHookEvent = { instance, def: this, value: instance.$values[this.key] }; + + runHooksSync(instance.$model, `property:reading:${this.key}`, readingHookEvent); + runHooksSync(instance.$model, 'property:reading', readingHookEvent); + + const current = instance.$values[this.key]; + if (current === undefined && ( + instance.$model.$config.strictProperties ?? instance.$model.$config.strict ?? false + )) { + throw new FosciaError( + `\`${instance.$model.$type}.${this.key}\` value was not retrieved from the data source and model uses strict properties.`, + ); + } + + const readHookEvent = { instance, def: this, value: current }; + runHooksSync(instance.$model, `property:read:${this.key}`, readHookEvent); + runHooksSync(instance.$model, 'property:read', readHookEvent); + + return current; + }, + set: (next: unknown) => { + const writeHookEvent = { instance, def: this, prev: instance.$values[this.key], next }; + + runHooksSync(instance.$model, `property:writing:${this.key}`, writeHookEvent); + runHooksSync(instance.$model, 'property:writing', writeHookEvent); + + if (this.readOnly && ( + instance.$model.$config.strictReadOnly ?? instance.$model.$config.strict ?? true + )) { + throw new FosciaError( + `\`${instance.$model.$type}.${this.key}\` cannot be set because it is read-only.`, + ); + } + + // eslint-disable-next-line no-param-reassign + instance.$values[this.key] = next; + + runHooksSync(instance.$model, `property:write:${this.key}`, writeHookEvent); + runHooksSync(instance.$model, 'property:write', writeHookEvent); + }, + }); + + if (this.default !== undefined) { + if (this.default && typeof this.default === 'object') { + logger.warn( + `Default \`${instance.$model.$type}.${this.key}\` object attribute's value must be defined using a factory function.`, + ); + } + + forceFill(instance, { [this.key]: value(this.default) }); + } + }, + ...pendingProp, +}, { + readOnly: (readOnly?: boolean) => ({ readOnly }), + alias: (alias: string) => ({ alias }), + sync: (sync: boolean | ModelPropSync) => ({ sync }), + default: (defaultValue: unknown | (() => unknown)) => ({ default: defaultValue }), + nullable: () => ({}), + ...modifiers, +}); diff --git a/packages/core/src/model/props/builders/parseValuePropConfig.ts b/packages/core/src/model/props/builders/parseValuePropConfig.ts new file mode 100644 index 00000000..4093daa3 --- /dev/null +++ b/packages/core/src/model/props/builders/parseValuePropConfig.ts @@ -0,0 +1,19 @@ +import isTransformer from '@foscia/core/transformers/isTransformer'; +import { ObjectTransformer } from '@foscia/core/transformers/types'; + +/** + * Parse a value property factory config. + * + * @param config + * @param otherConfig + * + * @internal + */ +export default ( + config?: ObjectTransformer | T | (() => T), + otherConfig?: C, +) => ( + isTransformer(config) + ? { transformer: config, ...otherConfig } + : { default: config, ...otherConfig } +); diff --git a/packages/core/src/model/props/builders/relation.ts b/packages/core/src/model/props/builders/relation.ts index 1ac7990e..eb8f2bfd 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -1,43 +1,38 @@ -import makePendingProp, { PROP_MODIFIERS } from '@foscia/core/model/props/builders/makePendingProp'; +import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; import { - PendingModelRelation, - PendingModelRelationConfig, + ModelPendingProp, + ModelRelationFactory, + ModelRelationFactoryConfig, } from '@foscia/core/model/props/builders/types'; -import { ModelInstance, ModelRelationType } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_RELATION } from '@foscia/core/symbols'; +import { ModelInstance, ModelRelation, ModelRelationType } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; +import { Awaitable } from '@foscia/shared'; /** - * Make a pending relation definition. + * Make a relation property definition factory. * * @param relationType - * @param config * * @internal */ export default ( relationType: ModelRelationType, - config?: PendingModelRelationConfig, -) => { - const resolveConfig = (configValue: PendingModelRelationConfig) => { - if (typeof configValue === 'string' || Array.isArray(configValue)) { - return { type: configValue }; +) => ( + config?: string | string[] | ModelRelationFactoryConfig | (() => Awaitable), + otherConfig?: ModelRelationFactoryConfig, +) => makeValuePropFactory({ + $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_RELATION, + $RELATION_TYPE: relationType, + ...(() => { + if (typeof config === 'string' || Array.isArray(config)) { + return { type: config, ...otherConfig }; } - if (typeof configValue === 'function') { - return { model: configValue }; - } - - return { ...configValue }; - }; - - const makePendingRelation = makePendingProp({ - ...PROP_MODIFIERS, - config: resolveConfig, - }); - - return makePendingRelation({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_RELATION, - $RELATION_TYPE: relationType, - ...resolveConfig(config ?? {}), - }) as unknown as PendingModelRelation; -}; + return typeof config === 'function' + ? { model: config, ...otherConfig } + : config; + })(), +} as ModelPendingProp, { + inverse: (inverse) => ({ inverse }), + path: (path) => ({ path }), +}) as ModelRelationFactory; diff --git a/packages/core/src/model/props/builders/types.ts b/packages/core/src/model/props/builders/types.ts index 6d5b91d5..158f5036 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -1,56 +1,259 @@ import { - Model, + ModelAssembled, + ModelAttribute, + ModelComposableFactory, + ModelId, ModelIdType, + ModelProp, ModelPropSync, - ModelRelationConfig, - PendingModelProp, - RawModelAttribute, - RawModelId, - RawModelRelation, + ModelRelation, + ModelRelationKey, } from '@foscia/core/model/types'; import { ObjectTransformer } from '@foscia/core/transformers/types'; -import { Awaitable, Constructor, Dictionary } from '@foscia/shared'; +import { Arrayable, Constructor, Dictionary, IfAny } from '@foscia/shared'; -export type PendingDefinitionModifiers = Dictionary<(...args: any[]) => PendingDefinition>; +/** + * Pending property. + * + * @internal + */ +export type ModelPendingProp

= + Omit & ThisType

; -export type PendingDefinition = PendingModelProp & PendingDefinitionModifiers; +/** + * Model chainable property factory. + * + * @internal + */ +export type ModelPropChainableFactory< + P extends ModelProp, + M extends Dictionary<(...args: any[]) => Partial

>, +> = + & { [K in keyof M]: (...args: Parameters) => ModelPropChainableFactory; } + & ModelComposableFactory

; -export type PendingModelId = { +/** + * Model ID factory. + * + * @interface + */ +export type ModelIdFactory = { + /** + * Define a transformer. + * + * @param transformer + */ transform: ( transformer: ObjectTransformer, - ) => PendingModelId; - default: (value: T | (() => T)) => PendingModelId; - readOnly: (readOnly?: NR) => PendingModelId; - nullable: unknown extends T ? never : (() => PendingModelId); -} & PendingModelProp>; + ) => ModelIdFactory; + /** + * Define default value. + * Object values should be provided with a factory function to avoid + * defining the same reference on multiple instance. + * + * @param value + */ + default: (value: T | (() => T)) => ModelIdFactory; + /** + * Mark read-only. + * + * @param readOnly + */ + readOnly: (readOnly?: NR) => ModelIdFactory; + /** + * Mark nullable. + */ + nullable: () => ModelIdFactory; +} & ModelComposableFactory>; -export type PendingModelAttribute = { +/** + * Model ID factory object config. + * + * @interface + * + * @internal + */ +export type ModelIdFactoryConfig = + Pick, 'transformer' | 'default' | 'readOnly'>; + +/** + * Model attribute factory. + * + * @interface + */ +export type ModelAttributeFactory = { + /** + * Define a transformer. + * + * @param transformer + */ transform: ( transformer: ObjectTransformer, - ) => PendingModelAttribute; - default: (value: T | (() => T)) => PendingModelAttribute; - readOnly: (readOnly?: NR) => PendingModelAttribute; - nullable: unknown extends T ? never : (() => PendingModelAttribute); - alias: (alias: string) => PendingModelAttribute; - sync: (alias: boolean | ModelPropSync) => PendingModelAttribute; -} & PendingModelProp>; - -export type PendingModelRelationInstance = - M extends Constructor[] ? I - : M extends Constructor ? I + ) => ModelAttributeFactory; + /** + * Define default value. + * Object values should be provided with a factory function to avoid + * defining the same reference on multiple instance. + * + * @param value + */ + default: (value: T | (() => T)) => ModelAttributeFactory; + /** + * Mark read-only. + * + * @param readOnly + */ + readOnly: (readOnly?: NR) => ModelAttributeFactory; + /** + * Mark nullable. + */ + nullable: () => ModelAttributeFactory; + /** + * Define the alias to use for data source interactions. + * + * @param alias + */ + alias: (alias: string) => ModelAttributeFactory; + /** + * Define when the property should be synced with data source. + * + * @param sync + */ + sync: (sync: boolean | ModelPropSync) => ModelAttributeFactory; +} & ModelComposableFactory>; + +/** + * Model attribute factory object config. + * + * @interface + * + * @internal + */ +export type ModelAttributeFactoryConfig = + Pick, 'transformer' | 'default' | 'readOnly' | 'alias' | 'sync'>; + +/** + * Infer related instance types from relationship models. + * + * @internal + */ +export type InferModelRelationFactoryInstance = + M extends Constructor[] ? I extends object ? I : never + : M extends Constructor ? I extends object ? I : never : never; -export type PendingModelRelationConfig = - | string - | string[] - | ModelRelationConfig - | (() => Awaitable); - -export type PendingModelRelation = { - config: (config: string | string[] | ModelRelationConfig) => PendingModelRelation; - default: (value: T | (() => T)) => PendingModelRelation; - readOnly: (readOnly?: NR) => PendingModelRelation; - nullable: unknown extends T ? never : (() => PendingModelRelation); - alias: (alias: string) => PendingModelRelation; - sync: (alias: boolean | ModelPropSync) => PendingModelRelation; -} & PendingModelProp>; +/** + * Infer a model's relation possible inverse key. + * + * @internal + */ +export type InferModelRelationInverseKey = + IfAny>; + +/** + * Model relationship factory. + * + * @interface + */ +export type ModelRelationFactory = { + /** + * Define default value. + * Object values should be provided with a factory function to avoid + * defining the same reference on multiple instance. + * + * @param value + */ + default: (value: T | (() => T)) => ModelRelationFactory; + /** + * Mark read-only. + * + * @param readOnly + */ + readOnly: (readOnly?: NR) => ModelRelationFactory; + /** + * Mark nullable. + */ + nullable: () => ModelRelationFactory; + /** + * Define the alias to use for data source interactions. + * + * @param alias + */ + alias: (alias: string) => ModelRelationFactory; + /** + * Define when the property should be synced with data source. + * + * @param sync + */ + sync: (sync: boolean | ModelPropSync) => ModelRelationFactory; + /** + * Define the inverse of the relation. + * + * @param inverse + */ + inverse: (inverse?: InferModelRelationInverseKey | boolean) => ModelRelationFactory; + /** + * Define the path to use when requesting relation's endpoint. + * + * @param path + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ + path: (path: string) => ModelRelationFactory; +} & ModelComposableFactory>; + +/** + * Model relation factory object options. + * + * @interface + * + * @internal + */ +export type ModelRelationFactoryConfig | null, R extends boolean> = + & { + /** + * The inverse relation key on related instances. + */ + inverse?: InferModelRelationInverseKey | boolean; + } + & Pick, 'type' | 'path' | 'default' | 'readOnly' | 'alias' | 'sync'>; + +/** + * Model assembled/memoized property factory. + * + * @interface + * + * @since 0.13.0 + * @experimental + */ +export type ModelAssembledFactory = { + /** + * Define the alias to use for data source interactions. + * + * @param alias + */ + alias: (alias: string) => ModelAssembledFactory; + /** + * Define when the property should be synced with data source. + * + * @param sync + */ + sync: (sync?: boolean | ModelPropSync) => ModelAssembledFactory; + /** + * Define if the computed value should be memoized. + * + * @param memo + */ + memo: (memo?: boolean) => ModelAssembledFactory; +} & ModelComposableFactory>; + +/** + * Model memoized factory object config. + * + * @interface + * + * @internal + */ +export type ModelAssembledFactoryConfig = + Pick, 'alias' | 'sync' | 'memo'>; diff --git a/packages/core/src/model/props/mappers/mapAttributes.ts b/packages/core/src/model/props/mappers/mapAttributes.ts index c5498353..15931ab4 100644 --- a/packages/core/src/model/props/mappers/mapAttributes.ts +++ b/packages/core/src/model/props/mappers/mapAttributes.ts @@ -1,12 +1,20 @@ import isAttributeDef from '@foscia/core/model/checks/isAttributeDef'; import mapProps from '@foscia/core/model/props/mappers/mapProps'; -import { ModelAttribute, ModelInstance, ModelKey } from '@foscia/core/model/types'; +import { Model, ModelAttribute } from '@foscia/core/model/types'; -export default ( - instance: I, - callback: (def: ModelAttribute>) => R, +/** + * Map all attributes of a model. + * + * @param model + * @param callback + * + * @category Utilities + */ +export default ( + model: M, + callback: (def: ModelAttribute) => R, ) => mapProps( - instance, + model, callback as any, - (def) => isAttributeDef(def), -) as R[]; + isAttributeDef, +); diff --git a/packages/core/src/model/props/mappers/mapIds.ts b/packages/core/src/model/props/mappers/mapIds.ts deleted file mode 100644 index d8b3b9c0..00000000 --- a/packages/core/src/model/props/mappers/mapIds.ts +++ /dev/null @@ -1,12 +0,0 @@ -import isIdDef from '@foscia/core/model/checks/isIdDef'; -import mapProps from '@foscia/core/model/props/mappers/mapProps'; -import { ModelId, ModelInstance, ModelKey } from '@foscia/core/model/types'; - -export default ( - instance: I, - callback: (def: ModelId>) => R, -) => mapProps( - instance, - callback as any, - (def) => isIdDef(def), -) as R[]; diff --git a/packages/core/src/model/props/mappers/mapProps.ts b/packages/core/src/model/props/mappers/mapProps.ts index 2d4b1cbd..e5411988 100644 --- a/packages/core/src/model/props/mappers/mapProps.ts +++ b/packages/core/src/model/props/mappers/mapProps.ts @@ -1,23 +1,19 @@ -import { - ModelAttribute, - ModelId, - ModelInstance, - ModelKey, - ModelRelation, -} from '@foscia/core/model/types'; +import { Model, ModelProp } from '@foscia/core/model/types'; +import { mapWithKeys } from '@foscia/shared'; -export default ( - instance: I, - callback: ( - def: ModelId> | ModelAttribute> | ModelRelation>, - ) => R, - predicate?: ( - def: ModelId> | ModelAttribute> | ModelRelation>, - ) => boolean, -) => Object.values(instance.$model.$schema).reduce((stack, def) => { - if (!predicate || predicate(def)) { - stack.push(callback(def)); - } - - return stack; -}, [] as R[]); +/** + * Map all properties of a model. + * + * @param model + * @param callback + * @param predicate + * + * @internal + */ +export default ( + model: M, + callback: (def: P) => R, + predicate?: (def: ModelProp) => def is P, +) => mapWithKeys(model.$schema, (def, key) => ( + !predicate || predicate(def) ? { [key]: callback(def as P) } : {} +)); diff --git a/packages/core/src/model/props/mappers/mapRelations.ts b/packages/core/src/model/props/mappers/mapRelations.ts index 33f84dac..d6f71a58 100644 --- a/packages/core/src/model/props/mappers/mapRelations.ts +++ b/packages/core/src/model/props/mappers/mapRelations.ts @@ -1,12 +1,20 @@ import isRelationDef from '@foscia/core/model/checks/isRelationDef'; import mapProps from '@foscia/core/model/props/mappers/mapProps'; -import { ModelInstance, ModelKey, ModelRelation } from '@foscia/core/model/types'; +import { Model, ModelRelation } from '@foscia/core/model/types'; -export default ( - instance: I, - callback: (def: ModelRelation>) => R, +/** + * Map all relations of a model. + * + * @param model + * @param callback + * + * @category Utilities + */ +export default ( + model: M, + callback: (def: ModelRelation) => R, ) => mapProps( - instance, + model, callback as any, - (def) => isRelationDef(def), -) as R[]; + isRelationDef, +); diff --git a/packages/core/src/model/props/shouldSync.ts b/packages/core/src/model/props/shouldSync.ts index 4acc7b90..ad289fde 100644 --- a/packages/core/src/model/props/shouldSync.ts +++ b/packages/core/src/model/props/shouldSync.ts @@ -1,6 +1,14 @@ -import { ModelPropSync, RawModelProp } from '@foscia/core/model/types'; +import { ModelProp, ModelPropSync } from '@foscia/core/model/types'; -export default (def: RawModelProp, actions: ModelPropSync[]) => ( +/** + * Check if a value property should be synced depending on the current action. + * + * @param def + * @param actions + * + * @internal + */ +export default (def: ModelProp, actions: ModelPropSync[]) => ( typeof def.sync === 'string' ? actions.indexOf(def.sync) !== -1 : (def.sync ?? true) diff --git a/packages/core/src/model/relations/loaded.ts b/packages/core/src/model/relations/loaded.ts index e0d45ac0..658b14a9 100644 --- a/packages/core/src/model/relations/loaded.ts +++ b/packages/core/src/model/relations/loaded.ts @@ -2,6 +2,25 @@ import isInstance from '@foscia/core/model/checks/isInstance'; import { ModelInstance, ModelRelationDotKey } from '@foscia/core/model/types'; import { ArrayableVariadic, isNone, wrapVariadic } from '@foscia/shared'; +/** + * Check if given relations are loaded on model. + * + * It will also check for sub relations through each related instances. + * + * @param instance + * @param relations + * + * @category Utilities + * + * @example + * ```typescript + * import { loaded } from '@foscia/core'; + * + * const isFullyLoaded = loaded(post, ['comments', 'comments.author']); + * if (!isFullyLoaded) { + * } + * ``` + */ const loaded = ( instance: I, ...relations: ArrayableVariadic> @@ -21,11 +40,7 @@ const loaded = ( return related.every((r) => loaded(r, subDotKey)); } - if (isInstance(related)) { - return loaded(related, subDotKey); - } - - return true; + return isInstance(related) ? loaded(related, subDotKey) : true; }); export default loaded; diff --git a/packages/core/src/model/relations/makeQueryModelLoader.ts b/packages/core/src/model/relations/makeQueryModelLoader.ts index 474c231b..0221c04a 100644 --- a/packages/core/src/model/relations/makeQueryModelLoader.ts +++ b/packages/core/src/model/relations/makeQueryModelLoader.ts @@ -31,27 +31,125 @@ import { IdentifiersMap, isNil, makeIdentifiersMap, + tap, wrap, } from '@foscia/shared'; -type ExtractedId = { id: ModelIdType; type?: string; }; - -type QueryModelLoaderOptions< +/** + * Configuration for the {@link makeQueryModelLoader | `makeQueryModelLoader`} factory. + * + * @internal + */ +export type QueryModelLoaderOptions< RawData, Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, > = { + /** + * Extract the related ID(s) from an instance's relation. + * You should use {@link makeQueryModelLoaderExtractor | `makeQueryModelLoaderExtractor`} + * to create an extractor function. Defaults to using the `$raw` record object. + * + * @param instance + * @param relation + * + * @example + * The default implementation extract related IDs from `$raw` record object. + * It supports record with IDs as relation values (`"comments": ["1", "2"]`) + * and even polymorphic relations values with objects containing both `type` and `ID` + * (`"comments": [{ "type": "comments", "id": "1" }, { "type": "comments", "id": "2" }]`). + * + * ```typescript + * import { makeQueryModelLoader, makeQueryModelLoaderExtractor } from '@foscia/core'; + * import { filterBy } from '@foscia/jsonapi'; + * + * export default makeQueryModelLoader(action, { + * extract: makeQueryModelLoaderExtractor( + * (instance, relation) => instance.$raw[normalizeKey(instance.$model, relation)], + * (value) => ( + * typeof value === 'object' ? { id: value.id, type: value.type } : { id: value } + * ), + * ), + * }); + * ``` + * + * @remarks + * Notice that Foscia will try to deserialize each relation were record is an object or array + * of objects value. This will mark the relation as loaded even if the related value + * attributes are not loaded. To avoid this, you can disable the relation's syncing on + * pull (e.g. hasOne().sync('push')), as it will disable relation deserialization. + * If you want a more global solution, you can update the `pullRelation` option on + * your deserializer configuration. + */ extract?: ( instance: I, relation: ModelRelationKey, - ) => Arrayable | null | undefined; + ) => Arrayable<{ id: ModelIdType; type?: string; }> | null | undefined; + /** + * Prepare the action using the given context. + * As an example, this can be used to filter the query on instances IDs. + * + * @param action + * @param context + * + * @example + * ```typescript + * import { makeQueryModelLoader } from '@foscia/core'; + * import { filterBy } from '@foscia/jsonapi'; + * + * export default makeQueryModelLoader(action, { + * prepare: (a, { ids }) => a.use(filterBy({ ids })), + * }); + * ``` + */ prepare?: ( - action: Action, + action: Action, context: { ids: ModelIdType[]; relations: string[] }, ) => Awaitable; - chunk?: (instances: ModelIdType[]) => ModelIdType[][]; + /** + * Chunk the IDs array into multiple arrays to make multiple queries + * instead of a unique one. + * As an example, this can be used to avoid hitting pagination limit of an API. + * + * @param ids + * + * @example + * ```typescript + * import { makeQueryModelLoader } from '@foscia/core'; + * + * const chunk = (items: T[], size: number) => { + * const chunks = [] as T[][]; + * for (let i = 0; i < array.length; i += size) { + * chunks.push(array.slice(i, i + chunkSize)); + * } + * + * return chunks; + * }; + * + * export default makeQueryModelLoader(action, { + * chunk: (ids) => chunk(ids, 20), + * }); + * ``` + */ + chunk?: (ids: ModelIdType[]) => ModelIdType[][]; + /** + * Determine if the given instance relation should be ignored from + * refresh. + * As an example, this can be used to load only missing relations. + * + * @param instance + * @param relation + * + * @example + * ```typescript + * import { makeQueryModelLoader, loaded } from '@foscia/core'; + * + * export default makeQueryModelLoader(action, { + * exclude: loaded, + * }); + * ``` + */ exclude?: (instance: I, relation: ModelRelationDotKey) => boolean; }; @@ -60,83 +158,82 @@ const extractIdsMap = < Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, I extends ModelInstance, >( - options: QueryModelLoaderOptions, + options: QueryModelLoaderOptions, instances: I[], relations: Map, string[]>, -) => { - const { exclude } = options; - const extract = options.extract ?? makeQueryModelLoaderExtractor( - (instance, relation) => instance.$raw[normalizeKey(instance.$model, relation)], - (value) => (typeof value === 'object' ? { id: value.id, type: value.type } : { id: value }), - ); +) => tap( + new Map, Arrayable<{ id: ModelIdType; type?: string; }> | null>>(), + (extractedIdsMap) => { + const { exclude } = options; + const extract = options.extract ?? makeQueryModelLoaderExtractor( + (instance, relation) => instance.$raw[normalizeKey(instance.$model, relation)], + (value) => (typeof value === 'object' ? { id: value.id, type: value.type } : { id: value }), + ); - const extractedIdsMap = new Map, Arrayable | null>>(); - instances.forEach((instance) => { - if (exclude && [...relations.entries()].every( - ([rootRelation, subRelations]) => shouldExcludeInstanceAndRelation( - instance, - rootRelation, - subRelations, - exclude, - ), - )) { - return; - } + instances.forEach((instance) => { + if (exclude && [...relations.entries()].every( + ([rootRelation, subRelations]) => shouldExcludeInstanceAndRelation( + instance, + rootRelation, + subRelations, + exclude, + ), + )) { + return; + } - [...relations.keys()].forEach((rootRelation) => { - const ids = extract(instance, rootRelation); - if (ids !== undefined) { - const extractedIdsForInstanceMap = extractedIdsMap.get(instance) ?? new Map(); + [...relations.keys()].forEach((rootRelation) => { + const ids = extract(instance, rootRelation); + if (ids !== undefined) { + const extractedIdsForInstanceMap = extractedIdsMap.get(instance) ?? new Map(); - extractedIdsForInstanceMap.set(rootRelation, ids); - extractedIdsMap.set(instance, extractedIdsForInstanceMap); - } + extractedIdsForInstanceMap.set(rootRelation, ids); + extractedIdsMap.set(instance, extractedIdsForInstanceMap); + } + }); }); - }); - - return extractedIdsMap; -}; + }, +); const fetchRelatedMap = async < RawData, Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, I extends ModelInstance, >( - action: ActionFactory<[], C, E>, - options: QueryModelLoaderOptions, + action: ActionFactory, + options: QueryModelLoaderOptions, relations: Map[]; nested: string[] }>, - ids: Map, Arrayable | null>>, + ids: Map, Arrayable<{ id: ModelIdType; type?: string; }> | null>>, ) => { const related = makeIdentifiersMap(); await Promise.all([...relations.entries()].map(async ([model, relationsData]) => { - const targetedIds = [...ids.values()].reduce((allIds, idsForInstance) => { - idsForInstance.forEach((extractedIds, relation) => { + const targetedIds = [...ids.values()].reduce((allIds, idsForInstance) => tap( + allIds, + () => idsForInstance.forEach((extractedIds, relation) => { if (relationsData.relations.indexOf(relation) !== -1) { allIds.push( ...wrap(extractedIds) .filter(({ type }) => isNil(type) || type === model.$type), ); } - }); - - return allIds; - }, [] as ExtractedId[]); + }), + ), [] as { id: ModelIdType; type?: string; }[]); if (targetedIds.length) { - const chunk = (options.chunk ?? ((i) => [i])) as (ids: ExtractedId[]) => ExtractedId[][]; + const chunk = ( + options.chunk ?? ((i) => [i]) + ) as (ids: { id: ModelIdType; type?: string; }[]) => { id: ModelIdType; type?: string; }[][]; await Promise.all(chunk(targetedIds).map(async (chunkIds) => { const chunkRelated = await action() .use(query(model), include(relationsData.nested as any)) .use(when(() => options.prepare, async (a, p) => { - await p(a as Action, { + await p(a as Action, { ids: chunkIds.map(({ id }) => id), relations: relationsData.nested, }); @@ -163,7 +260,7 @@ const fetchRelatedMap = async < }; const extractRelated = ( - ids: Arrayable | null, + ids: Arrayable<{ id: ModelIdType; type?: string; }> | null, related: IdentifiersMap, ) => { if (Array.isArray(ids)) { @@ -172,36 +269,49 @@ const extractRelated = ( .filter((value) => value !== undefined); } - if (!isNil(ids)) { - return related.find(ids.type ?? '', ids.id); - } - - return null; + return isNil(ids) ? null : related.find(ids.type ?? '', ids.id); }; const remapRelated = ( - ids: Map, Arrayable | null>>, + ids: Map, Arrayable<{ id: ModelIdType; type?: string; }> | null>>, related: IdentifiersMap, ) => ids.forEach((idsForInstance, instance) => { idsForInstance.forEach((extractIds, relation) => { const value = extractRelated(extractIds, related); - if (value === undefined) { - return; + if (value !== undefined) { + loadUsingValue(instance, relation, value as any); } - - loadUsingValue(instance, relation, value as any); }); }); +/** + * Create a relations loader using related model and IDs included in raw + * data source's records. + * + * @param action + * @param options + * + * @category Factories + * @since 0.8.2 + * + * @example + * ```typescript + * import { makeQueryModelLoader } from '@foscia/core'; + * import { param } from '@foscia/http'; + * + * export default makeQueryModelLoader(action, { + * prepare: (a, { ids }) => a.use(param({ ids })), + * }); + * ``` + */ export default < RawData, Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, >( - action: ActionFactory<[], C, E>, - options: QueryModelLoaderOptions = {}, + action: ActionFactory, + options: QueryModelLoaderOptions = {}, ) => async ( instances: Arrayable, ...relations: ArrayableVariadic> diff --git a/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts b/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts index 51ed161c..dc8b81e0 100644 --- a/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts +++ b/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts @@ -2,6 +2,15 @@ import logger from '@foscia/core/logger/logger'; import { ModelIdType, ModelInstance, ModelRelationKey } from '@foscia/core/model/types'; import { Arrayable } from '@foscia/shared'; +/** + * Create an extractor to retrieve related IDs and types. + * + * @param pullValue + * @param parseValue + * + * @category Factories + * @since 0.8.2 + */ export default ( pullValue: ( instance: I, @@ -25,9 +34,7 @@ export default ( return null; } - if (Array.isArray(related)) { - return related.map((value) => parseValue(value)); - } - - return parseValue(related); + return Array.isArray(related) + ? related.map((value) => parseValue(value)) + : parseValue(related); }; diff --git a/packages/core/src/model/relations/makeQueryRelationLoader.ts b/packages/core/src/model/relations/makeQueryRelationLoader.ts index 73b953a5..38d76d53 100644 --- a/packages/core/src/model/relations/makeQueryRelationLoader.ts +++ b/packages/core/src/model/relations/makeQueryRelationLoader.ts @@ -7,22 +7,78 @@ import loadUsingCallback from '@foscia/core/model/relations/utilities/loadUsingC import loadUsingValue from '@foscia/core/model/relations/utilities/loadUsingValue'; import shouldExcludeInstanceAndRelation from '@foscia/core/model/relations/utilities/shouldExcludeInstanceAndRelation'; -import { ModelInstance, ModelRelationDotKey, ModelRelationKey } from '@foscia/core/model/types'; +import { + ModelInstance, + ModelRelation, + ModelRelationDotKey, + ModelRelationKey, +} from '@foscia/core/model/types'; import { DeserializedData } from '@foscia/core/types'; import { Arrayable, ArrayableVariadic } from '@foscia/shared'; -type QueryRelationLoaderOptions = { +/** + * Configuration for the {@link makeQueryRelationLoader | `makeQueryRelationLoader`} factory. + * + * @internal + */ +export type QueryRelationLoaderOptions = { + /** + * Determine if the given instance relation should be ignored from + * refresh. + * As an example, this can be used to load only missing relations. + * + * @param instance + * @param relation + * + * @example + * ```typescript + * import { makeQueryRelationLoader, loaded } from '@foscia/core'; + * + * export default makeQueryRelationLoader(action, { + * exclude: loaded, + * }); + * ``` + */ exclude?: (instance: I, relation: ModelRelationDotKey) => boolean; + /** + * Disable the performance warning log when more than one action + * will run. + * + * @example + * ```typescript + * import { makeQueryRelationLoader, loaded } from '@foscia/core'; + * + * export default makeQueryRelationLoader(action, { + * disablePerformanceWarning: true, + * }); + * ``` + */ disablePerformanceWarning?: boolean; }; +/** + * Create a relations loader querying related model through the current + * instance relation. + * + * @param action + * @param options + * + * @category Factories + * + * @example + * ```typescript + * import { makeQueryRelationLoader } from '@foscia/core'; + * + * export default makeQueryRelationLoader(action); + * ``` + */ export default < RawData, Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, >( - action: ActionFactory<[], C, {}>, + action: ActionFactory, options: QueryRelationLoaderOptions = {}, ) => async ( instances: Arrayable, @@ -45,7 +101,7 @@ export default < return; } - const def = instance.$model.$schema[relation]; + const def = instance.$model.$schema[relation] as ModelRelation; const isPlural = isPluralRelationDef(def); const value = await action() diff --git a/packages/core/src/model/relations/makeRefreshIncludeLoader.ts b/packages/core/src/model/relations/makeRefreshIncludeLoader.ts index 889fd06b..c6f3cb72 100644 --- a/packages/core/src/model/relations/makeRefreshIncludeLoader.ts +++ b/packages/core/src/model/relations/makeRefreshIncludeLoader.ts @@ -22,18 +22,81 @@ import { import { DeserializedData } from '@foscia/core/types'; import { Arrayable, ArrayableVariadic, Awaitable, mapWithKeys, uniqueValues } from '@foscia/shared'; -type RefreshIncludeLoaderOptions< +/** + * Configuration for the {@link makeRefreshIncludeLoader | `makeRefreshIncludeLoader`} factory. + * + * @internal + */ +export type RefreshIncludeLoaderOptions< RawData, Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, > = { + /** + * Prepare the action using the given context. + * As an example, this can be used to filter the query on instances IDs. + * + * @param action + * @param context + * + * @example + * ```typescript + * import { makeRefreshIncludeLoader } from '@foscia/core'; + * import { filterBy } from '@foscia/jsonapi'; + * + * export default makeRefreshIncludeLoader(action, { + * prepare: (a, { instances }) => a.use(filterBy({ ids: instances.map((i) => i.id) })), + * }); + * ``` + */ prepare?: ( - action: Action, + action: Action, context: { instances: ModelInstance[]; relations: string[] }, ) => Awaitable; + /** + * Chunk the instances array into multiple arrays to make multiple queries + * instead of a unique one. + * As an example, this can be used to avoid hitting pagination limit of an API. + * + * @param instances + * + * @example + * ```typescript + * import { makeRefreshIncludeLoader } from '@foscia/core'; + * + * const chunk = (items: T[], size: number) => { + * const chunks = [] as T[][]; + * for (let i = 0; i < array.length; i += size) { + * chunks.push(array.slice(i, i + chunkSize)); + * } + * + * return chunks; + * }; + * + * export default makeRefreshIncludeLoader(action, { + * chunk: (instances) => chunk(instances, 20), + * }); + * ``` + */ chunk?: (instances: ModelInstance[]) => ModelInstance[][]; + /** + * Determine if the given instance relation should be ignored from + * refresh. + * As an example, this can be used to load only missing relations. + * + * @param instance + * @param relation + * + * @example + * ```typescript + * import { makeRefreshIncludeLoader, loaded } from '@foscia/core'; + * + * export default makeRefreshIncludeLoader(action, { + * exclude: loaded, + * }); + * ``` + */ exclude?: (instance: I, relation: ModelRelationDotKey) => boolean; }; @@ -42,11 +105,10 @@ const refreshLoad = async < Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, I extends ModelInstance, >( - action: ActionFactory<[], C, {}>, - options: RefreshIncludeLoaderOptions, + action: ActionFactory, + options: RefreshIncludeLoaderOptions, instances: I[], relations: ModelRelationDotKey[], ) => { @@ -55,7 +117,7 @@ const refreshLoad = async < .use(query(model as Model)) .use(include(relations as any)) .use(when(() => options.prepare, async (a, p) => { - await p(a as Action, { instances, relations }); + await p(a as Action, { instances, relations }); })) .run(all()); @@ -82,15 +144,33 @@ const refreshLoad = async < }); }; +/** + * Create a relations loader refreshing the instances while including + * missing relations. + * + * @param action + * @param options + * + * @category Factories + * + * @example + * ```typescript + * import { makeRefreshIncludeLoader } from '@foscia/core'; + * import { filterBy } from '@foscia/jsonapi'; + * + * export default makeRefreshIncludeLoader(action, { + * prepare: (a, { instances }) => a.use(filterBy({ ids: instances.map((i) => i.id) })), + * }); + * ``` + */ export default < RawData, Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, >( - action: ActionFactory<[], C, {}>, - options: RefreshIncludeLoaderOptions = {}, + action: ActionFactory, + options: RefreshIncludeLoaderOptions = {}, ) => async ( instances: Arrayable, ...relations: ArrayableVariadic> diff --git a/packages/core/src/model/relations/utilities/attachRelationInverse.ts b/packages/core/src/model/relations/utilities/attachRelationInverse.ts new file mode 100644 index 00000000..04e29c52 --- /dev/null +++ b/packages/core/src/model/relations/utilities/attachRelationInverse.ts @@ -0,0 +1,65 @@ +import logger from '@foscia/core/logger/logger'; +import isRelationDef from '@foscia/core/model/checks/isRelationDef'; +import isSingularRelationDef from '@foscia/core/model/checks/isSingularRelationDef'; +import forceFill from '@foscia/core/model/forceFill'; +import guessRelationInverses from '@foscia/core/model/relations/utilities/guessRelationInverses'; +import { ModelInstance, ModelRelation } from '@foscia/core/model/types'; +import { Arrayable, isNil, using, wrap } from '@foscia/shared'; + +/** + * Attach a relation inverse of related instances (if enabled). + * If inverse is automatic, it will guess, verify and save inverse before + * attaching parent. If automatic inverse do not pass verifications, it + * will be logged as a warning and will be disabled. + * + * @param parent + * @param def + * @param related + * + * @internal + */ +export default ( + parent: ModelInstance, + def: ModelRelation, + related: Arrayable | null, +) => { + const instances = wrap(related); + if (instances.length && !isNil(def.inverse) && def.inverse !== false) { + if (typeof def.inverse !== 'string') { + // eslint-disable-next-line no-param-reassign + def.inverse = using( + wrap((parent.$model.$config.guessRelationInverse ?? guessRelationInverses)(def)), + (inverseKeys) => inverseKeys.reduce((rel, key) => ( + rel ?? instances[0].$model.$schema[key]?.key as string | undefined + ), undefined as string | undefined), + ); + } + + const inverseKey = def.inverse as string | undefined; + if (instances.some((instance) => { + const inverse = ( + inverseKey ? instance.$model.$schema[inverseKey] : undefined + ); + if (inverse && isRelationDef(inverse)) { + if (isSingularRelationDef(inverse)) { + return false; + } + + logger.warn(`\`${inverseKey}\` inverse for \`${parent.$model.$type}.${def.key}\` must be singular. Inverse has been disabled.`); + + return true; + } + + logger.warn(`Could not found inverse for \`${parent.$model.$type}.${def.key}\`. Inverse has been disabled.`); + + return true; + })) { + // eslint-disable-next-line no-param-reassign + def.inverse = false; + + return; + } + + instances.forEach((instance) => forceFill(instance, { [inverseKey!]: parent })); + } +}; diff --git a/packages/core/src/model/relations/utilities/excludeInstancesAndRelations.ts b/packages/core/src/model/relations/utilities/excludeInstancesAndRelations.ts index 3a628884..2cba4894 100644 --- a/packages/core/src/model/relations/utilities/excludeInstancesAndRelations.ts +++ b/packages/core/src/model/relations/utilities/excludeInstancesAndRelations.ts @@ -1,5 +1,14 @@ import { ModelInstance, ModelRelationDotKey } from '@foscia/core/model/types'; +/** + * Exclude instances and relations depending on a predicate. + * + * @param instances + * @param relations + * @param exclude + * + * @internal + */ export default ( instances: I[], relations: ModelRelationDotKey[], diff --git a/packages/core/src/model/relations/utilities/groupRelationsByModels.ts b/packages/core/src/model/relations/utilities/groupRelationsByModels.ts index 6cfdca66..692951b0 100644 --- a/packages/core/src/model/relations/utilities/groupRelationsByModels.ts +++ b/packages/core/src/model/relations/utilities/groupRelationsByModels.ts @@ -1,7 +1,16 @@ import { consumeRegistry, guessContextModel } from '@foscia/core/actions'; import FosciaError from '@foscia/core/errors/fosciaError'; -import { Model, ModelRelationKey } from '@foscia/core/model/types'; +import { Model, ModelRelation, ModelRelationKey } from '@foscia/core/model/types'; +/** + * Group a map of relations by applicable models. + * + * @param model + * @param relations + * @param context + * + * @internal + */ export default async ( model: M, relations: Map, string[]>, @@ -9,7 +18,7 @@ export default async ( ) => { const modelsPerRelations = await Promise.all( [...relations.entries()].map(async ([relation, nested]) => { - const def = model.$schema[relation]; + const def = model.$schema[relation] as ModelRelation; const related = await guessContextModel({ model, relation: def, diff --git a/packages/core/src/model/relations/utilities/groupRelationsByRoots.ts b/packages/core/src/model/relations/utilities/groupRelationsByRoots.ts index 3d5f4337..a530d186 100644 --- a/packages/core/src/model/relations/utilities/groupRelationsByRoots.ts +++ b/packages/core/src/model/relations/utilities/groupRelationsByRoots.ts @@ -1,11 +1,16 @@ import { ModelInstance, ModelRelationDotKey, ModelRelationKey } from '@foscia/core/model/types'; -import { uniqueValues } from '@foscia/shared'; +import { tap, uniqueValues } from '@foscia/shared'; +/** + * Group relations by common roots. + * + * @param relations + * + * @internal + */ export default ( relations: ModelRelationDotKey[], -) => { - const groups = new Map, string[]>(); - +) => tap(new Map, string[]>(), (groups) => { relations.forEach((relation) => { const [rootRelation, ...subRelations] = relation.split('.'); @@ -14,6 +19,4 @@ export default ( ...subRelations, ])); }); - - return groups; -}; +}); diff --git a/packages/core/src/model/relations/utilities/guessRelationInverses.ts b/packages/core/src/model/relations/utilities/guessRelationInverses.ts new file mode 100644 index 00000000..5b8bdc31 --- /dev/null +++ b/packages/core/src/model/relations/utilities/guessRelationInverses.ts @@ -0,0 +1,13 @@ +import { ModelRelation } from '@foscia/core/model/types'; +import { camelCase, singularize } from '@foscia/shared'; + +/** + * Guess possible relation inverse keys. + * + * @param def + * + * @internal + */ +export default (def: ModelRelation) => [ + camelCase(singularize(def.parent.$type)), +].filter((k) => k !== undefined) as string[]; diff --git a/packages/core/src/model/relations/utilities/guessRelationType.ts b/packages/core/src/model/relations/utilities/guessRelationType.ts index d0788821..d8994ae2 100644 --- a/packages/core/src/model/relations/utilities/guessRelationType.ts +++ b/packages/core/src/model/relations/utilities/guessRelationType.ts @@ -2,6 +2,13 @@ import isPluralRelationDef from '@foscia/core/model/checks/isPluralRelationDef'; import { ModelRelation } from '@foscia/core/model/types'; import { pluralize } from '@foscia/shared'; +/** + * Guess a relation type using its name. + * + * @param def + * + * @internal + */ export default (def: ModelRelation) => ( isPluralRelationDef(def) ? def.key : pluralize(def.key) ); diff --git a/packages/core/src/model/relations/utilities/loadUsingCallback.ts b/packages/core/src/model/relations/utilities/loadUsingCallback.ts index c4cc5e9c..2476007d 100644 --- a/packages/core/src/model/relations/utilities/loadUsingCallback.ts +++ b/packages/core/src/model/relations/utilities/loadUsingCallback.ts @@ -1,6 +1,15 @@ import { ModelInstance, ModelRelationDotKey, ModelRelationKey } from '@foscia/core/model/types'; import { Arrayable, ArrayableVariadic, Awaitable, wrap, wrapVariadic } from '@foscia/shared'; +/** + * Load instances' relations if none are empty. + * + * @param instances + * @param relations + * @param loader + * + * @internal + */ export default async < I extends ModelInstance, K extends ModelRelationKey | ModelRelationDotKey, diff --git a/packages/core/src/model/relations/utilities/loadUsingValue.ts b/packages/core/src/model/relations/utilities/loadUsingValue.ts index 73211c7e..2d83252c 100644 --- a/packages/core/src/model/relations/utilities/loadUsingValue.ts +++ b/packages/core/src/model/relations/utilities/loadUsingValue.ts @@ -2,6 +2,15 @@ import forceFill from '@foscia/core/model/forceFill'; import markSynced from '@foscia/core/model/snapshots/markSynced'; import { ModelInstance, ModelRelationKey } from '@foscia/core/model/types'; +/** + * Fill and mark a relation's value as loaded on an instance. + * + * @param instance + * @param relation + * @param value + * + * @internal + */ export default ( instance: I, relation: ModelRelationKey, diff --git a/packages/core/src/model/relations/utilities/shouldExcludeInstanceAndRelation.ts b/packages/core/src/model/relations/utilities/shouldExcludeInstanceAndRelation.ts index 4aaf6cce..875ac6b7 100644 --- a/packages/core/src/model/relations/utilities/shouldExcludeInstanceAndRelation.ts +++ b/packages/core/src/model/relations/utilities/shouldExcludeInstanceAndRelation.ts @@ -1,5 +1,13 @@ import { ModelInstance, ModelRelationDotKey, ModelRelationKey } from '@foscia/core/model/types'; +/** + * Check if instance's relations should be excluded based on a predicate. + * + * @param instance + * @param relation + * @param nested + * @param exclude + */ export default ( instance: I, relation: ModelRelationKey, diff --git a/packages/core/src/model/revivers/makeModelsReducer.ts b/packages/core/src/model/revivers/makeModelsReducer.ts index 18ea67e2..6d02080d 100644 --- a/packages/core/src/model/revivers/makeModelsReducer.ts +++ b/packages/core/src/model/revivers/makeModelsReducer.ts @@ -1,34 +1,54 @@ import isInstance from '@foscia/core/model/checks/isInstance'; +import isSnapshot from '@foscia/core/model/checks/isSnapshot'; import { + ModelsReducerConfig, ReducedModel, ReducedModelCircularRef, ReducedModelInstance, ReducedModelInstanceCustomData, ReducedModelInstanceData, ReducedModelSnapshot, + ReducerParentsMap, + ReducerReferenceable, + ReviverDereferenceable, } from '@foscia/core/model/revivers/types'; -import { ModelClass, ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; -import { Dictionary, mapWithKeys, uniqueId, unsafeId } from '@foscia/shared'; +import { + Model, + ModelInstance, + ModelLimitedSnapshot, + ModelSnapshot, +} from '@foscia/core/model/types'; +import { Dictionary, mapWithKeys, uniqueId, unsafeId, using } from '@foscia/shared'; -export default () => { +/** + * Create a models reducer. + * + * @category Factories + * @since 0.8.6 + */ +export default (config: ModelsReducerConfig = {}) => { let reduceInstance: ( instance: ModelInstance, - parents: Map, + parents: ReducerParentsMap, ) => ReducedModelInstance | ReducedModelCircularRef; + let reduceSnapshot: ( + snapshot: ModelSnapshot | ModelLimitedSnapshot, + parents: ReducerParentsMap, + ) => ReducedModelSnapshot | ReducedModelCircularRef; const generateRef = (refs: string[]): string => uniqueId(unsafeId, refs); - const reduceModel = (model: ModelClass) => ({ - $FOSCIA_TYPE: 'model', - $type: model.$type, - } as ReducedModel); - const reduceCircularRef = (ref: string) => ({ $FOSCIA_TYPE: 'circular', $ref: ref, } as ReducedModelCircularRef); - const reduceValue = (value: unknown, parents: Map): unknown => { + const reduceModel = (model: Model) => ({ + $FOSCIA_TYPE: 'model', + $type: model.$type, + } as ReducedModel); + + const reduceValue = (value: unknown, parents: ReducerParentsMap): unknown => { if (Array.isArray(value)) { return value.map((item) => reduceValue(item, parents)); } @@ -37,60 +57,84 @@ export default () => { return reduceInstance(value, parents); } - return value; + if (isSnapshot(value)) { + return reduceSnapshot(value, parents); + } + + return config.reduce ? config.reduce(value) : value; }; - const reduceValues = (values: Dictionary, parents: Map) => mapWithKeys( + const reduceValues = (values: Dictionary, parents: ReducerParentsMap) => mapWithKeys( values, (value, key) => ({ [key]: reduceValue(value, parents) }), ); - const reduceSnapshot = (snapshot: ModelSnapshot, parents: Map) => ({ - $FOSCIA_TYPE: 'snapshot', - $model: reduceModel(snapshot.$model), - $exists: snapshot.$exists, - $raw: snapshot.$raw, - $loaded: snapshot.$loaded, - $values: reduceValues(snapshot.$values, parents), - } as ReducedModelSnapshot); - - const reduceInstanceData = (instance: ModelInstance, parents: Map) => ({ + const reduceInstanceData = (instance: ModelInstance, parents: ReducerParentsMap) => ({ $exists: instance.$exists, $raw: instance.$raw, $loaded: instance.$loaded, $values: reduceValues(instance.$values, parents), - $original: reduceSnapshot(instance.$original, parents), + $original: reduceSnapshot(instance.$original, parents) as ReducedModelSnapshot, }); - reduceInstance = (instance: ModelInstance, parents: Map) => { - let reference = parents.get(instance); - if (reference !== undefined) { - return reduceCircularRef(reference); - } + const makeReferenceableReducer = < + T extends ReducerReferenceable, + U extends ReviverDereferenceable, + >( + reducer: (value: T, ref: string, parents: ReducerParentsMap) => U, + ) => ( + value: T, + parents: ReducerParentsMap, + ) => using(parents.get(value), (prevRef) => ( + prevRef !== undefined + ? reduceCircularRef(prevRef) + : using( + generateRef([...parents.values()]), + (ref) => { + parents.set(value, ref); + return reducer(value, ref, parents); + }, + ) + )); - reference = generateRef([...parents.values()]); - parents.set(instance, reference); + reduceSnapshot = makeReferenceableReducer( + (snapshot: ModelSnapshot | ModelLimitedSnapshot, ref, parents): ReducedModelSnapshot => ({ + $FOSCIA_TYPE: 'snapshot', + $ref: ref, + $instance: reduceInstance(snapshot.$instance, parents), + $exists: snapshot.$exists, + $values: reduceValues(snapshot.$values, parents), + ...('$raw' in snapshot ? { + $original: snapshot.$original ? reduceSnapshot(snapshot.$original, parents) : null, + $raw: snapshot.$raw, + $loaded: snapshot.$loaded, + } : {}), + }), + ); - let data: ReducedModelInstanceData | undefined; - let custom: ReducedModelInstanceCustomData | undefined; - if ('$reduce' in instance && typeof instance.$reduce === 'function') { - custom = instance.$reduce({ - reduce: (i: ModelInstance) => reduceInstance(i, parents), - data: (i: ModelInstance) => ({ $data: reduceInstanceData(i, parents) }), - }) as ReducedModelInstanceCustomData; - data = custom.$data; - } else { - data = reduceInstanceData(instance, parents); - } + reduceInstance = makeReferenceableReducer( + (instance: ModelInstance, ref, parents): ReducedModelInstance => { + let data: ReducedModelInstanceData | undefined; + let custom: ReducedModelInstanceCustomData | undefined; + if ('$reduce' in instance && typeof instance.$reduce === 'function') { + custom = instance.$reduce({ + reduce: (i: ModelInstance) => reduceInstance(i, parents), + data: (i: ModelInstance) => ({ $data: reduceInstanceData(i, parents) }), + }) as ReducedModelInstanceCustomData; + data = custom.$data; + } else { + data = reduceInstanceData(instance, parents); + } - return { - $FOSCIA_TYPE: 'instance', - $model: reduceModel(instance.$model), - $ref: reference, - $data: data, - $custom: custom, - }; - }; + return { + $FOSCIA_TYPE: 'instance', + $ref: ref, + $model: reduceModel(instance.$model), + $data: data, + $custom: custom, + }; + }, + ); return { reduce: ( diff --git a/packages/core/src/model/revivers/makeModelsReviver.ts b/packages/core/src/model/revivers/makeModelsReviver.ts index 877fed3e..87f1e98c 100644 --- a/packages/core/src/model/revivers/makeModelsReviver.ts +++ b/packages/core/src/model/revivers/makeModelsReviver.ts @@ -1,19 +1,36 @@ import FosciaError from '@foscia/core/errors/fosciaError'; import { + ModelsReviverConfig, ReducedModel, ReducedModelCircularRef, ReducedModelInstance, ReducedModelInstanceData, ReducedModelSnapshot, + ReducerReferenceable, + ReviverDereferenceable, + ReviverParentsMap, } from '@foscia/core/model/revivers/types'; -import { Model, ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; -import { Dictionary, mapWithKeys } from '@foscia/shared'; - -export default (options: { models: Model[]; }) => { +import { ModelInstance, ModelLimitedSnapshot, ModelSnapshot } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_SNAPSHOT } from '@foscia/core/symbols'; +import { Dictionary, mapWithKeys, tap, using } from '@foscia/shared'; + +/** + * Create a models reviver. + * + * @param config + * + * @category Factories + * @since 0.8.6 + */ +export default (config: ModelsReviverConfig) => { let reviveInstance: ( instance: ReducedModelInstance | ReducedModelCircularRef, - parents: Map, + parents: ReviverParentsMap, ) => ModelInstance; + let reviveSnapshot: ( + snapshot: ReducedModelSnapshot | ReducedModelCircularRef, + parents: ReviverParentsMap, + ) => ModelSnapshot | ModelLimitedSnapshot; const isReducedType = ( type: T['$FOSCIA_TYPE'], @@ -23,99 +40,132 @@ export default (options: { models: Model[]; }) => { && '$FOSCIA_TYPE' in value && value.$FOSCIA_TYPE === type; - const modelsMap = new Map(options.models.map((model) => [model.$type, model])); - const reviveModel = (reducedModel: ReducedModel) => { - const model = modelsMap.get(reducedModel.$type); + const modelsMap = new Map(config.models.map((model) => [model.$type, model])); + const reviveModel = ( + reducedModel: ReducedModel, + ) => tap(modelsMap.get(reducedModel.$type), (model) => { if (!model) { throw new FosciaError(`Could not revive model with type \`${reducedModel.$type}\`.`); } + })!; - return model; - }; - - const reviveCircularRef = (ref: ReducedModelCircularRef, parents: Map) => { - const instance = parents.get(ref.$ref); + const reviveCircularRef = ( + ref: ReducedModelCircularRef, + parents: ReviverParentsMap, + ) => tap(parents.get(ref.$ref), (instance) => { if (!instance) { - throw new FosciaError('Could not revive not found instance, reduced data might be corrupted.'); + throw new FosciaError('Could not revive not found ref, reduced data might be corrupted.'); } + })!; - return instance; - }; - - const reviveValue = (value: unknown, parents: Map): unknown => { + const reviveValue = (value: unknown, parents: ReviverParentsMap): unknown => { if (Array.isArray(value)) { return value.map((item) => reviveValue(item, parents)); } - if ( - isReducedType('instance', value) - || isReducedType('circular', value) - ) { + if (isReducedType('circular', value)) { + return reviveCircularRef(value, parents); + } + + if (isReducedType('instance', value)) { return reviveInstance(value, parents); } - return value; + if (isReducedType('snapshot', value)) { + return reviveSnapshot(value, parents); + } + + return config.revive ? config.revive(value) : value; }; - const reviveValues = (values: Dictionary, parents: Map) => mapWithKeys( + const reviveValues = (values: Dictionary, parents: ReviverParentsMap) => mapWithKeys( values, (value, key) => ({ [key]: reviveValue(value, parents) }), ); - const reviveSnapshot = (snapshot: ReducedModelSnapshot, parents: Map) => ({ - $model: reviveModel(snapshot.$model), - $exists: snapshot.$exists, - $raw: snapshot.$raw, - $loaded: snapshot.$loaded, - $values: reviveValues(snapshot.$values, parents), - }) as ModelSnapshot; - const reviveInstanceData = ( instance: ModelInstance, data: ReducedModelInstanceData, - parents: Map, + parents: ReviverParentsMap, ) => { /* eslint-disable no-param-reassign */ instance.$exists = data.$exists; instance.$raw = data.$raw; instance.$loaded = data.$loaded; instance.$values = reviveValues(data.$values, parents); - instance.$original = reviveSnapshot(data.$original, parents); + instance.$original = reviveSnapshot(data.$original, parents) as ModelSnapshot; /* eslint-enable */ }; - reviveInstance = ( - reducedInstance: ReducedModelInstance | ReducedModelCircularRef, - parents: Map, - ) => { - if (reducedInstance.$FOSCIA_TYPE === 'circular') { - return reviveCircularRef(reducedInstance, parents); - } - - const RevivedModel = reviveModel(reducedInstance.$model); - const instance = new RevivedModel(); - parents.set(reducedInstance.$ref, instance); - - if (reducedInstance.$data) { - reviveInstanceData(instance, reducedInstance.$data, parents); - } - - if (reducedInstance.$custom) { - if (!('$revive' in instance) || typeof instance.$revive !== 'function') { - throw new FosciaError( - `Missing \`$revive\` method inside model with type \`${instance.$model.$type}\`.`, - ); + const makeReferenceableReviver = < + T extends ReviverDereferenceable, + U extends ReducerReferenceable, + >( + reviver: (value: T) => U, + hydrator: (value: T, revived: U, parents: ReviverParentsMap) => void, + ) => ( + value: T | ReducedModelCircularRef, + parents: ReviverParentsMap, + ) => ( + isReducedType('circular', value) + ? reviveCircularRef(value, parents) + : tap(reviver(value), (revived) => { + parents.set(value.$ref, revived); + hydrator(value, revived, parents); + }) + ) as U; + + reviveSnapshot = makeReferenceableReviver( + (value: ReducedModelSnapshot) => ({ + $FOSCIA_TYPE: SYMBOL_MODEL_SNAPSHOT, + $exists: value.$exists, + $instance: null as any, + $values: null as any, + ...('$raw' in value ? { + $original: null, + $raw: value.$raw, + $loaded: value.$loaded, + } : {}), + }), + (value: ReducedModelSnapshot, snapshot: ModelSnapshot | ModelLimitedSnapshot, parents) => { + /* eslint-disable no-param-reassign */ + // @ts-ignore + snapshot.$values = reviveValues(value.$values, parents); + // @ts-ignore + snapshot.$instance = reviveInstance(value.$instance, parents); + if ('$raw' in value && value.$original) { + // @ts-ignore + snapshot.$original = reviveSnapshot(value.$original, parents); } + /* eslint-enable */ + }, + ); - instance.$revive(reducedInstance.$custom, { - revive: ( - i: ReducedModelInstance | ReducedModelCircularRef, - ) => reviveInstance(i, parents), - }); - } + reviveInstance = makeReferenceableReviver( + (value: ReducedModelInstance) => using( + reviveModel(value.$model), + (RevivedModel) => new RevivedModel(), + ), + (value: ReducedModelInstance, instance: ModelInstance, parents) => { + if (value.$data) { + reviveInstanceData(instance, value.$data, parents); + } - return instance; - }; + if (value.$custom) { + if (!('$revive' in instance) || typeof instance.$revive !== 'function') { + throw new FosciaError( + `Missing \`$revive\` method inside model with type \`${instance.$model.$type}\`.`, + ); + } + + instance.$revive(value.$custom, { + revive: ( + i: ReducedModelInstance | ReducedModelCircularRef, + ) => reviveInstance(i, parents), + }); + } + }, + ); return { revive: ( diff --git a/packages/core/src/model/revivers/types.ts b/packages/core/src/model/revivers/types.ts index 9473efbd..e7f723d1 100644 --- a/packages/core/src/model/revivers/types.ts +++ b/packages/core/src/model/revivers/types.ts @@ -1,28 +1,96 @@ -import { ModelInstance } from '@foscia/core/model/types'; +import { + Model, + ModelInstance, + ModelLimitedSnapshot, + ModelSnapshot, +} from '@foscia/core/model/types'; import { Dictionary } from '@foscia/shared'; +/** + * Objects which have can be dereferenced to a + * {@link ReducerReferenceable | `ReducerReferenceable`}. + * + * @internal + */ +export type ReviverDereferenceable = { + $ref: string; +}; + +/** + * Objects which have can be referenced to a + * {@link ReviverDereferenceable | `ReviverDereferenceable`}. + * + * @internal + */ +export type ReducerReferenceable = + | ModelInstance + | ModelSnapshot + | ModelLimitedSnapshot; + +/** + * Map of parent objects which have been referenced. + * + * @internal + */ +export type ReducerParentsMap = Map; + +/** + * Map of parent objects which have been dereferenced. + * + * @internal + */ +export type ReviverParentsMap = Map; + /** * Reduced (serialized) model class. + * + * @internal */ export type ReducedModel = { $FOSCIA_TYPE: 'model'; $type: string; }; +/** + * Reduced (serialized) model limited snapshot. + * + * @internal + */ +export type ReducedModelLimitedSnapshot = + & { + $FOSCIA_TYPE: 'snapshot'; + $instance: ReducedModelInstance | ReducedModelCircularRef; + $exists: boolean; + $values: Dictionary; + } + & ReviverDereferenceable; + +/** + * Reduced (serialized) model limited snapshot. + * + * @internal + */ +export type ReducedModelFullSnapshot = + & { + $original: ReducedModelSnapshot | ReducedModelCircularRef | null; + $raw: any; + $loaded: Dictionary; + } + & ReducedModelLimitedSnapshot; + /** * Reduced (serialized) model snapshot. + * + * @internal */ -export type ReducedModelSnapshot = { - $FOSCIA_TYPE: 'snapshot'; - $model: ReducedModel; - $exists: boolean; - $raw: any; - $loaded: Dictionary; - $values: Dictionary; -}; +export type ReducedModelSnapshot = + | ReducedModelLimitedSnapshot + | ReducedModelFullSnapshot; /** * Reduced (serialized) model instance data. + * + * @internal */ export type ReducedModelInstanceData = { $exists: boolean; @@ -33,36 +101,55 @@ export type ReducedModelInstanceData = { }; /** - * Reduced (serialized) model instance custom data. + * Reduced (serialized) model instance. + * + * @internal */ -export type ReducedModelInstanceCustomData = { - $data?: ReducedModelInstanceData; -}; +export type ReducedModelInstance = + & { + $FOSCIA_TYPE: 'instance'; + $model: ReducedModel; + $data?: ReducedModelInstanceData; + $custom?: ReducedModelInstanceCustomData; + } + & ReviverDereferenceable; /** - * Reduced (serialized) model instance. + * Reduced (serialized) value reference. + * + * @internal */ -export type ReducedModelInstance = { - $FOSCIA_TYPE: 'instance'; - $model: ReducedModel; +export type ReducedModelCircularRef = { + $FOSCIA_TYPE: 'circular'; $ref: string; - $data?: ReducedModelInstanceData; - $custom?: ReducedModelInstanceCustomData; }; /** - * Reduced (serialized) model instance reference. + * Reduced (serialized) model instance custom data. */ -export type ReducedModelCircularRef = { - $FOSCIA_TYPE: 'circular'; - $ref: string; +export type ReducedModelInstanceCustomData = { + /** + * Contains the instance data reduced using `tools.data(instance)`. + * This provides Foscia data override. + */ + $data?: ReducedModelInstanceData; }; /** * Tools functions available when reducing an instance. */ export type ModelReduceTools = { + /** + * Reduce an instance into a reduced instance or instance reference. + * + * @param instance + */ reduce: (instance: ModelInstance) => ReducedModelInstance | ReducedModelCircularRef; + /** + * Reduce an instance default `$data`. + * + * @param instance + */ data: (instance: ModelInstance) => { $data: ReducedModelInstanceData; }; }; @@ -70,6 +157,11 @@ export type ModelReduceTools = { * Tools functions available when reviving an instance. */ export type ModelReviveTools = { + /** + * Revive an instance or an instance's reference. + * + * @param instance + */ revive: (instance: ReducedModelInstance | ReducedModelCircularRef) => ModelInstance; }; @@ -77,6 +169,36 @@ export type ModelReviveTools = { * Model which can reduce and revive using custom implementations. */ export type ModelCanReduceRevive = { + /** + * Reduce the instance data. + * + * @param tools + */ $reduce(tools: ModelReduceTools): T; + /** + * Revive the instance data. + * + * @param customData + * @param tools + */ $revive(customData: T, tools: ModelReviveTools): void; }; + +/** + * Config for the models reviver. + * + * @internal + */ +export type ModelsReviverConfig = { + models: Model[]; + revive?: (value: unknown) => unknown; +}; + +/** + * Config for the models reducer. + * + * @internal + */ +export type ModelsReducerConfig = { + reduce?: (value: unknown) => unknown; +}; diff --git a/packages/core/src/model/snapshots/changed.ts b/packages/core/src/model/snapshots/changed.ts index 85436ebf..3f17d4e1 100644 --- a/packages/core/src/model/snapshots/changed.ts +++ b/packages/core/src/model/snapshots/changed.ts @@ -1,12 +1,29 @@ -import compareSnapshots from '@foscia/core/model/snapshots/compareSnapshots'; +import isSameSnapshot from '@foscia/core/model/snapshots/isSameSnapshot'; import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; import { ModelInstance, ModelKey } from '@foscia/core/model/types'; import { ArrayableVariadic } from '@foscia/shared'; +/** + * Check if instance changed since last original snapshot capture. + * + * @param instance + * @param only + * + * @category Utilities + * + * @example + * ```typescript + * import { changed } from '@foscia/core'; + * + * const titleChanged = changed(post, ['title']); + * if (titleChanged) { + * } + * ``` + */ export default ( instance: I, ...only: ArrayableVariadic> -) => !compareSnapshots( +) => !isSameSnapshot( takeSnapshot(instance), instance.$original, ...only, diff --git a/packages/core/src/model/snapshots/cloneModelValue.ts b/packages/core/src/model/snapshots/cloneModelValue.ts deleted file mode 100644 index 033363ff..00000000 --- a/packages/core/src/model/snapshots/cloneModelValue.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ModelClass } from '@foscia/core/model/types'; - -export default (model: ModelClass, value: T) => ( - model.$config.cloneValue - ? model.$config.cloneValue(value) - : value -); diff --git a/packages/core/src/model/snapshots/compareModelValue.ts b/packages/core/src/model/snapshots/compareModelValue.ts deleted file mode 100644 index 056ab94d..00000000 --- a/packages/core/src/model/snapshots/compareModelValue.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ModelClass } from '@foscia/core/model/types'; - -export default ( - model: ModelClass, - nextValue: unknown, - prevValue: unknown, -) => ( - model.$config.compareValue - ? model.$config.compareValue(nextValue, prevValue) - : nextValue === prevValue -); diff --git a/packages/core/src/model/snapshots/compareSnapshots.ts b/packages/core/src/model/snapshots/compareSnapshots.ts deleted file mode 100644 index 54bac925..00000000 --- a/packages/core/src/model/snapshots/compareSnapshots.ts +++ /dev/null @@ -1,29 +0,0 @@ -import compareModelValue from '@foscia/core/model/snapshots/compareModelValue'; -import { ModelClass, ModelKey, ModelSnapshot } from '@foscia/core/model/types'; -import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; - -export default ( - nextSnapshot: ModelSnapshot, - prevSnapshot: ModelSnapshot, - ...only: ArrayableVariadic> -) => { - if (nextSnapshot.$model !== prevSnapshot.$model) { - return false; - } - - const keys = wrapVariadic(...only); - if (!keys.length && nextSnapshot.$exists !== prevSnapshot.$exists) { - return false; - } - - return ( - keys.length > 0 - || Object.keys(nextSnapshot.$values).length === Object.keys(prevSnapshot.$values).length - ) && (keys.length ? keys : Object.keys(nextSnapshot.$values) as ModelKey[]).every( - (key) => compareModelValue( - nextSnapshot.$model, - nextSnapshot.$values[key], - prevSnapshot.$values[key], - ), - ); -}; diff --git a/packages/core/src/model/snapshots/isSameSnapshot.ts b/packages/core/src/model/snapshots/isSameSnapshot.ts new file mode 100644 index 00000000..47b89290 --- /dev/null +++ b/packages/core/src/model/snapshots/isSameSnapshot.ts @@ -0,0 +1,46 @@ +import { Model, ModelKey, ModelLimitedSnapshot, ModelSnapshot } from '@foscia/core/model/types'; +import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; + +/** + * Check if two snapshots are similar (same model, same existence state + * and same values). + * + * @param nextSnapshot + * @param prevSnapshot + * @param only + * + * @category Utilities + * + * @example + * ```typescript + * import { isSameSnapshot } from '@foscia/core'; + * + * const titleChanged = isSameSnapshot(newSnapshot, oldSnapshot, ['title']); + * if (titleChanged) { + * } + * ``` + */ +export default ( + nextSnapshot: ModelSnapshot | ModelLimitedSnapshot, + prevSnapshot: ModelSnapshot | ModelLimitedSnapshot, + ...only: ArrayableVariadic> +) => { + if (nextSnapshot.$instance.$model !== prevSnapshot.$instance.$model) { + return false; + } + + const keys = wrapVariadic(...only); + if (!keys.length && nextSnapshot.$exists !== prevSnapshot.$exists) { + return false; + } + + return ( + keys.length > 0 + || Object.keys(nextSnapshot.$values).length === Object.keys(prevSnapshot.$values).length + ) && (keys.length ? keys : Object.keys(nextSnapshot.$values) as ModelKey[]).every( + (key) => nextSnapshot.$instance.$model.$config.compareSnapshotValues( + nextSnapshot.$values[key], + prevSnapshot.$values[key], + ), + ); +}; diff --git a/packages/core/src/model/snapshots/markSynced.ts b/packages/core/src/model/snapshots/markSynced.ts index 22264a19..4e6ca25f 100644 --- a/packages/core/src/model/snapshots/markSynced.ts +++ b/packages/core/src/model/snapshots/markSynced.ts @@ -1,25 +1,40 @@ /* eslint-disable no-param-reassign */ import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; import { ModelInstance, ModelKey } from '@foscia/core/model/types'; -import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; +import { ArrayableVariadic, tap, wrapVariadic } from '@foscia/shared'; +/** + * Take a snapshot and define it as the last original state of instance. + * + * @param instance + * @param only + * + * @category Utilities + * + * @example + * ```typescript + * import { markSynced } from '@foscia/core'; + * + * markSynced(post, ['title', 'description']); + * ``` + */ export default ( instance: I, ...only: ArrayableVariadic> -) => { +) => tap(instance, () => { const snapshot = takeSnapshot(instance); const keys = wrapVariadic(...only); if (keys.length) { keys.forEach((key) => { if (Object.prototype.hasOwnProperty.call(snapshot.$values, key)) { + // @ts-ignore instance.$original.$values[key] = snapshot.$values[key]; } else { + // @ts-ignore delete instance.$original.$values[key]; } }); } else { instance.$original = snapshot; } - - return instance; -}; +}); diff --git a/packages/core/src/model/snapshots/restore.ts b/packages/core/src/model/snapshots/restore.ts index c71e5919..8c7acd79 100644 --- a/packages/core/src/model/snapshots/restore.ts +++ b/packages/core/src/model/snapshots/restore.ts @@ -2,11 +2,22 @@ import restoreSnapshot from '@foscia/core/model/snapshots/restoreSnapshot'; import { ModelInstance, ModelKey } from '@foscia/core/model/types'; import { ArrayableVariadic } from '@foscia/shared'; +/** + * Restore the original snapshot of an instance. + * + * @param instance + * @param only + * + * @category Utilities + * + * @example + * ```typescript + * import { restore } from '@foscia/core'; + * + * restore(post, ['title']); + * ``` + */ export default ( instance: I, ...only: ArrayableVariadic> -) => { - restoreSnapshot(instance, instance.$original, ...only); - - return instance; -}; +) => restoreSnapshot(instance, instance.$original, ...only); diff --git a/packages/core/src/model/snapshots/restoreSnapshot.ts b/packages/core/src/model/snapshots/restoreSnapshot.ts index d4e45b8f..3e2ae2c3 100644 --- a/packages/core/src/model/snapshots/restoreSnapshot.ts +++ b/packages/core/src/model/snapshots/restoreSnapshot.ts @@ -1,24 +1,57 @@ /* eslint-disable no-param-reassign */ +import isPropDef from '@foscia/core/model/checks/isPropDef'; +import isRelationDef from '@foscia/core/model/checks/isRelationDef'; import forceFill from '@foscia/core/model/forceFill'; import mapProps from '@foscia/core/model/props/mappers/mapProps'; -import cloneModelValue from '@foscia/core/model/snapshots/cloneModelValue'; import markSynced from '@foscia/core/model/snapshots/markSynced'; import { - ModelAttribute, - ModelId, ModelInstance, ModelKey, - ModelRelation, + ModelLimitedSnapshot, + ModelProp, ModelSnapshot, ModelValues, } from '@foscia/core/model/types'; -import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; +import { Arrayable, ArrayableVariadic, isNil, tap, wrapVariadic } from '@foscia/shared'; +const restoreSnapshotRelation = ( + value: Arrayable, +) => ( + Array.isArray(value) + ? value.map((v) => v.$instance) + : value.$instance +); + +const restoreSnapshotValue = ( + snapshot: ModelSnapshot, + def: ModelProp, +) => snapshot.$instance.$model.$config.cloneSnapshotValue( + isRelationDef(def) && !isNil(snapshot.$values[def.key]) + ? restoreSnapshotRelation(snapshot.$values[def.key]) + : snapshot.$values[def.key], +); + +/** + * Restore a specific snapshot on instance. + * + * @param instance + * @param snapshot + * @param only + * + * @category Utilities + * + * @example + * ```typescript + * import { restoreSnapshot } from '@foscia/core'; + * + * restoreSnapshot(post, veryOldSnapshot, ['title']); + * ``` + */ export default ( instance: I, snapshot: ModelSnapshot, ...only: ArrayableVariadic> -) => { +) => tap(instance, () => { const keys = wrapVariadic(...only); if (!keys.length) { @@ -27,24 +60,20 @@ export default ( instance.$loaded = snapshot.$loaded; } - const restoreForDef = >( - def: ModelId | ModelAttribute | ModelRelation, - ) => { - if (keys.length && keys.indexOf(def.key) === -1) { + const restoreForDef = (def: ModelProp) => { + if (keys.length && keys.indexOf(def.key as ModelKey) === -1) { return; } if (Object.prototype.hasOwnProperty.call(snapshot.$values, def.key)) { forceFill(instance, { - [def.key]: cloneModelValue(instance.$model, snapshot.$values[def.key]), + [def.key]: restoreSnapshotValue(snapshot, def), } as Partial>); } else { delete instance.$values[def.key]; } }; - mapProps(instance, restoreForDef); + mapProps(instance.$model, restoreForDef, isPropDef as any); markSynced(instance, ...only); - - return instance; -}; +}); diff --git a/packages/core/src/model/snapshots/takeLimitedSnapshot.ts b/packages/core/src/model/snapshots/takeLimitedSnapshot.ts new file mode 100644 index 00000000..579ad272 --- /dev/null +++ b/packages/core/src/model/snapshots/takeLimitedSnapshot.ts @@ -0,0 +1,34 @@ +import captureSnapshotValues from '@foscia/core/model/snapshots/utilities/captureSnapshotValues'; +import { ModelInstance, ModelLimitedSnapshot } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_SNAPSHOT } from '@foscia/core/symbols'; + +/** + * Capture a limited snapshot of the instance. + * Snapshot will only contain ID and LID values, and won't capture + * raw record and loaded state. + * + * @param instance + * + * @category Utilities + * + * @example + * ```typescript + * import { takeLimitedSnapshot } from '@foscia/core'; + * + * const snapshot = takeLimitedSnapshot(post); + * ``` + */ +const takeLimitedSnapshot = ( + instance: I, +): ModelLimitedSnapshot => ({ + $FOSCIA_TYPE: SYMBOL_MODEL_SNAPSHOT, + $instance: instance, + $exists: instance.$exists, + $values: captureSnapshotValues( + instance, + takeLimitedSnapshot, + ['id', 'lid'], + ) as ModelLimitedSnapshot['$values'], +}); + +export default takeLimitedSnapshot; diff --git a/packages/core/src/model/snapshots/takeSnapshot.ts b/packages/core/src/model/snapshots/takeSnapshot.ts index 34aa02ba..84cb03e7 100644 --- a/packages/core/src/model/snapshots/takeSnapshot.ts +++ b/packages/core/src/model/snapshots/takeSnapshot.ts @@ -1,17 +1,46 @@ -import cloneModelValue from '@foscia/core/model/snapshots/cloneModelValue'; -import { ModelInstance, ModelSnapshot, ModelValues } from '@foscia/core/model/types'; -import { mapWithKeys } from '@foscia/shared'; +import takeLimitedSnapshot from '@foscia/core/model/snapshots/takeLimitedSnapshot'; +import captureSnapshotValues from '@foscia/core/model/snapshots/utilities/captureSnapshotValues'; +import { ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_SNAPSHOT } from '@foscia/core/symbols'; -export default ( +const takeFullSnapshot = ( instance: I, -): ModelSnapshot => ({ - $model: instance.$model, - $exists: instance.$exists, - $raw: instance.$raw, - $loaded: { ...instance.$loaded }, - $values: mapWithKeys(instance.$values, (value, key) => { - const clonedValue = cloneModelValue(instance.$model, value); + parents: ModelSnapshot[] = [], +) => { + const snapshot: ModelSnapshot = { + $FOSCIA_TYPE: SYMBOL_MODEL_SNAPSHOT, + $original: instance.$original ?? null, + $instance: instance, + $exists: instance.$exists, + $raw: instance.$raw, + $loaded: { ...instance.$loaded }, + $values: {}, + }; - return clonedValue !== undefined ? { [key]: clonedValue } : {}; - }) as Partial>, -}); + // @ts-ignore + snapshot.$values = captureSnapshotValues(instance, (related, parent) => ( + parents.find(({ $instance }) => $instance === related) ?? ( + (parent.$model.$config.limitedSnapshots ?? true) + ? takeLimitedSnapshot(related) + : takeFullSnapshot(related, [...parents, snapshot]) + ) + )) as ModelSnapshot['$values']; + + return snapshot; +}; + +/** + * Capture a snapshot of the instance. + * + * @param instance + * + * @category Utilities + * + * @example + * ```typescript + * import { takeSnapshot } from '@foscia/core'; + * + * const snapshot = takeSnapshot(post); + * ``` + */ +export default (instance: I) => takeFullSnapshot(instance); diff --git a/packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts b/packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts new file mode 100644 index 00000000..51a8fe76 --- /dev/null +++ b/packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts @@ -0,0 +1,50 @@ +import isRelationDef from '@foscia/core/model/checks/isRelationDef'; +import { ModelInstance, ModelLimitedSnapshot, ModelSnapshot } from '@foscia/core/model/types'; +import { Arrayable, isNil, mapWithKeys, using } from '@foscia/shared'; + +const captureSnapshotRelation = ( + instance: ModelInstance, + value: Arrayable, + takeSnapshot: ( + related: ModelInstance, + parent: ModelInstance, + ) => ModelSnapshot | ModelLimitedSnapshot, +) => ( + Array.isArray(value) + ? value.map((v) => takeSnapshot(v, instance)) + : takeSnapshot(value, instance) +); + +const captureSnapshotValue = ( + instance: ModelInstance, + key: string, + value: unknown, + takeSnapshot: ( + related: ModelInstance, + parent: ModelInstance, + ) => ModelSnapshot | ModelLimitedSnapshot, +) => using( + instance.$model.$config.cloneSnapshotValue(value), + (clone) => using(instance.$model.$schema[key], (def) => ( + isRelationDef(def) && !isNil(clone) + ? captureSnapshotRelation(instance, clone as Arrayable, takeSnapshot) + : clone + )), +); + +export default ( + instance: ModelInstance, + takeSnapshot: ( + related: ModelInstance, + parent: ModelInstance, + ) => ModelSnapshot | ModelLimitedSnapshot, + only?: string[], +) => mapWithKeys( + instance.$values, + (value, key) => ( + !only || only.indexOf(key) !== -1 ? using( + captureSnapshotValue(instance, key, value, takeSnapshot), + (snapshot) => (snapshot !== undefined ? { [key]: snapshot } : {}), + ) : {} + ), +); diff --git a/packages/core/src/model/types.ts b/packages/core/src/model/types.ts index 1b0d8321..2de92bd7 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -3,54 +3,194 @@ import { SYMBOL_MODEL_CLASS, SYMBOL_MODEL_COMPOSABLE, SYMBOL_MODEL_INSTANCE, - SYMBOL_MODEL_PROP_ATTRIBUTE, - SYMBOL_MODEL_PROP_ID, - SYMBOL_MODEL_PROP_PENDING, - SYMBOL_MODEL_PROP_RELATION, + SYMBOL_MODEL_PROP, + SYMBOL_MODEL_PROP_KIND_ATTRIBUTE, + SYMBOL_MODEL_PROP_KIND_ID, + SYMBOL_MODEL_PROP_KIND_RELATION, SYMBOL_MODEL_RELATION_HAS_MANY, SYMBOL_MODEL_RELATION_HAS_ONE, + SYMBOL_MODEL_SNAPSHOT, } from '@foscia/core/symbols'; import { ObjectTransformer } from '@foscia/core/transformers/types'; import { Awaitable, Constructor, DescriptorHolder, + DescriptorHolderOf, Dictionary, FosciaObject, + IfAny, OmitNever, Optional, Prev, + RestoreDescriptorHolder, Transformer, UnionToIntersection, } from '@foscia/shared'; /** * Configuration of a model class. + * + * @internal */ export type ModelConfig = { + /** + * Enable all strict policies on models. + * Defaults as `undefined`. + * + * @remarks + * Enabling or disabling a specific policy will supersede the strict setting. + */ strict?: boolean; + /** + * Enable strict properties. + * Defaults to `false`. + * + * @remarks + * When enabled, getting an instance's property value will throw an error + * if the value has not been set nor retrieved from data source. + */ strictProperties?: boolean; + /** + * Enable strict properties. + * Defaults to `false`. + * + * @remarks + * When enabled, writing a read-only instance's property will throw an error. + */ strictReadOnly?: boolean; + /** + * Guess a related type from a relation definition. + * Defaults is to use the relation name (and pluralize it if it is a "to one" + * relation). + */ + guessRelationType?: Transformer; + /** + * Guess a relation inverse. + * Defaults is to use the model type (and singularize it if it is a "to many" + * relation). + */ + guessRelationInverse?: Transformer; + /** + * Guess alias from a property's name. + * Defaults is to keep the property's name. + */ + guessAlias?: Transformer; + /** + * Compare two properties values when comparing snapshots. + * Defaults to {@link compareModelValues | `compareModelValues`}. + * + * @param nextValue + * @param prevValue + */ + compareSnapshotValues: (nextValue: unknown, prevValue: unknown) => boolean; + /** + * Clone a property value when creating a snapshot. + * Defaults to {@link cloneModelValue | `cloneModelValue`}. + * + * @param value + */ + cloneSnapshotValue: (value: T) => T; + /** + * Tells if snapshots of related instances should be using + * {@link ModelSnapshot | `ModelSnapshot`} or + * {@link ModelLimitedSnapshot | `ModelLimitedSnapshot`} + * to reduced memory footprint and improve performance. + * Defaults to `true` (uses {@link ModelLimitedSnapshot | `ModelLimitedSnapshot`}). + */ + limitedSnapshots?: boolean; + + // Specific HTTP config. + + /** + * Define the base URL to use. + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ + baseURL?: Optional; + /** + * Define the path to use. + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ path?: Optional; + /** + * Guess the path from the model type. + * Defaults to the model type. + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ guessPath?: Transformer; + /** + * Guess the ID path from the model ID. + * Defaults to converting the ID to string. + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ guessIdPath?: Transformer; + /** + * Guess the relation path from the relation definition. + * Defaults to the relation name. + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ guessRelationPath?: Transformer; - guessRelationType?: Transformer; - guessAlias?: Transformer; - compareValue?: (nextValue: unknown, prevValue: unknown) => boolean; - cloneValue?: (value: T) => T; - [K: string]: any; }; /** - * Model composable definition which can be included on any models. + * Model composable which adds features and typings to a model. + * + * @interface + */ +export interface ModelComposable { + /** + * The factory which produced the composable. + * + * @internal + */ + readonly factory: ModelComposableFactory; + /** + * The model the composable is bind to. + */ + readonly parent: Model; + /** + * The key the composable is bind to. + */ + readonly key: string; + /** + * Init the composable to a parent model's instance. + */ + readonly init?: (instance: ModelInstance) => void; + /** + * Stores the composable typing for type resolution. + * + * @internal + */ + readonly _type: {}; +} + +/** + * Model composable factory. + * + * @typeParam C Composable which the factory binds. + * + * @interface + * + * @internal */ -export type ModelComposable = +export type ModelComposableFactory = & { - $definition: D; + /** + * Create and bind the composable to a model. + */ + readonly bind: (ctx: { parent: Model, key: string }) => C; } - & FosciaObject - & Hookable; + & FosciaObject; /** * Model instance ID default typing. @@ -58,221 +198,213 @@ export type ModelComposable = export type ModelIdType = string | number; /** - * Sync precise configuration for a property (will only do defined action). + * Sync configuration for a property. `pull` means the property is only retrieved + * from the data source and never send to it. `push` is the opposite. + * + * @internal */ export type ModelPropSync = 'pull' | 'push'; /** - * Normalized part of a property's definition (ID, attribute or relation). - */ -export type ModelPropNormalized = { - key: K; -}; - -/** - * Pending definition for a model's property (ID, attribute or relation). - */ -export type PendingModelProp> = - { - definition: D; - } - & FosciaObject; - -/** - * Raw definition for a model's property (ID, attribute or relation). + * Model property. + * + * @internal */ -export type RawModelProp = { - /** - * Default value for the property. - */ - default?: T | (() => T) | undefined; +export interface ModelProp + extends ModelComposable, FosciaObject { /** * Alias of the property (might be used when (de)serializing). */ alias?: string | undefined; - /** - * Makes the property read-only. - */ - readOnly?: R; /** * Tells if the property should be synced with the data store. */ sync?: boolean | ModelPropSync; -}; + /** + * Tells if the property is read-only. + */ + readonly readOnly?: R; + + readonly _type: R extends false ? Record : Readonly>; +} /** - * Raw Definition for a model's ID. + * Model value property stored inside the instance internal values. + * + * @interface + * + * @internal */ -export type RawModelId = - { - transformer?: ObjectTransformer | undefined; +export type ModelValueProp = + & { + /** + * Default value for the property. + */ + default?: T | (() => T) | undefined; } - & RawModelProp - & FosciaObject; + & ModelProp; /** - * Definition for a model's ID. + * Model assembled/memoized property. + * + * @interface + * + * @since 0.13.0 + * @experimental */ -export type ModelId = - & ModelPropNormalized - & RawModelId; +export type ModelAssembled = + & { + /** + * Tells if the assembled property getter is memoized (enabled by default). + */ + memo?: boolean; + } + & ModelProp; /** - * Raw definition for a model's attribute. + * Model ID property. + * + * @interface */ -export type RawModelAttribute = +export type ModelId = { - transformer?: ObjectTransformer | undefined; + /** + * Type of property. + * + * @internal + */ + readonly $VALUE_PROP_TYPE: typeof SYMBOL_MODEL_PROP_KIND_ID; + transformer?: ObjectTransformer; } - & RawModelProp - & FosciaObject; + & ModelValueProp; /** - * Definition for a model's attribute. + * Model attribute property. + * + * @interface */ -export type ModelAttribute = - & ModelPropNormalized - & RawModelAttribute; +export type ModelAttribute = + { + /** + * Type of property. + * + * @internal + */ + readonly $VALUE_PROP_TYPE: typeof SYMBOL_MODEL_PROP_KIND_ATTRIBUTE; + transformer?: ObjectTransformer; + } + & ModelValueProp; /** * Available model relation types. + * + * @internal */ export type ModelRelationType = | typeof SYMBOL_MODEL_RELATION_HAS_ONE | typeof SYMBOL_MODEL_RELATION_HAS_MANY; /** - * Configuration of a model's relation. - */ -export type ModelRelationConfig = { - type?: string | string[]; - [K: string]: any; -}; - -/** - * Raw definition for a model's relation. + * Model relation property. + * + * @interface */ -export type RawModelRelation = +export type ModelRelation = { - $RELATION_TYPE: ModelRelationType; + /** + * Type of property. + * + * @internal + */ + readonly $VALUE_PROP_TYPE: typeof SYMBOL_MODEL_PROP_KIND_RELATION; + /** + * Type of relation. + * + * @internal + */ + readonly $RELATION_TYPE: ModelRelationType; + /** + * Resolve the related model(s). + */ model?: () => Awaitable; + /** + * The related type(s) to help Foscia resolving related models. + */ + type?: string | string[]; + /** + * The inverse relation key on related instances. + */ + inverse?: string | boolean; + + // Specific HTTP config. + + /** + * The path to use when requesting relation's endpoint. + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ + path?: string; } - & ModelRelationConfig - & RawModelProp - & FosciaObject; - -/** - * Definition for a model's attribute. - */ -export type ModelRelation = - & ModelPropNormalized - & RawModelRelation; - -/** - * Infer a model's property type from the property raw definition. - */ -export type ModelInferPropValue

= P extends RawModelProp ? T : never; - -/** - * A parsed model definition's prop with non attributes/relations properties' - * descriptors wrapped in holders. - */ -export type ModelParsedDefinitionProp = - V extends RawModelAttribute ? V & ModelPropNormalized - : V extends RawModelRelation ? V & ModelPropNormalized - : V extends RawModelId ? V & ModelPropNormalized - : V extends ModelComposable ? never - : V extends DescriptorHolder ? V - : DescriptorHolder; - -/** - * The parsed model definition with non composables/attributes/relations - * properties' descriptors wrapped in holders. - */ -export type ModelParsedDefinition = { - [K in keyof D]: D[K] extends ModelComposable - ? D[K] : D[K] extends PendingModelProp> - ? ModelParsedDefinitionProp : ModelParsedDefinitionProp; -}; + & ModelValueProp; /** - * The flatten and parsed model definition with composables properties - * flattened to the definition root. + * Model instance read property generic hook callback function. + * + * @internal */ -export type ModelFlattenDefinition = - & UnionToIntersection<{} | { - [K in keyof D]: D[K] extends ModelComposable - ? ModelFlattenDefinition : never; - }[keyof D]> - & OmitNever<{ [K in keyof D]: D[K] extends ModelComposable ? never : D[K] }>; +export type ModelInstancePropertyReadHookCallback = SyncHookCallback<{ + readonly instance: ModelInstance; + readonly def: ModelProp; + readonly value: unknown; +}>; /** - The flatten and parsed model definition from a composable object type. + * Model instance write property generic hook callback function. + * + * @internal */ -export type ModelComposableDefinition> = ModelFlattenDefinition<{ - composable: C; +export type ModelInstancePropertyWriteHookCallback = SyncHookCallback<{ + readonly instance: ModelInstance; + readonly def: ModelProp; + readonly prev: unknown; + readonly next: unknown; }>; /** - * Extract model's IDs, attributes and relations from the whole definition. + * Model hooks definition dedicated to a model. + * + * @internal */ -export type ModelSchema = { - [K in keyof D]: D[K] extends ModelAttribute - ? D[K] : D[K] extends ModelRelation - ? D[K] : D[K] extends ModelId - ? D[K] : never; +export type ModelHooksDefinitionForModel = { + boot: SyncHookCallback; }; /** - Extract model's relations from the whole definition. - */ -export type ModelSchemaRelations = { - [K in keyof D]: D[K] extends ModelRelation - ? D[K] : never; + * Model hooks definition dedicated to an instance. + * + * @internal + */ +export type ModelHooksDefinitionForInstance = { + init: SyncHookCallback; + retrieved: HookCallback; + creating: HookCallback; + created: HookCallback; + updating: HookCallback; + updated: HookCallback; + saving: HookCallback; + saved: HookCallback; + destroying: HookCallback; + destroyed: HookCallback; }; /** - * Model instance generic hook callback function. + * Model hooks definition dedicated to an instance property. * - * @deprecated Use `HookCallback` instead. + * @internal */ -export type ModelInstanceHookCallback = HookCallback; - -/** - * Model instance read property generic hook callback function. - */ -export type ModelInstancePropertyReadHookCallback = SyncHookCallback<{ - instance: ModelInstance; - def: ModelId | ModelAttribute | ModelRelation; - value: unknown; -}>; - -/** - * Model instance write property generic hook callback function. - */ -export type ModelInstancePropertyWriteHookCallback = SyncHookCallback<{ - instance: ModelInstance; - def: ModelId | ModelAttribute | ModelRelation; - prev: unknown; - next: unknown; -}>; - -/** - * Model's hooks definition. - */ -export type ModelHooksDefinition = +export type ModelHooksDefinitionForInstanceProperty = & { - boot: SyncHookCallback; - init: SyncHookCallback; - retrieved: HookCallback; - creating: HookCallback; - created: HookCallback; - updating: HookCallback; - updated: HookCallback; - saving: HookCallback; - saved: HookCallback; - destroying: HookCallback; - destroyed: HookCallback; 'property:reading': ModelInstancePropertyReadHookCallback; 'property:read': ModelInstancePropertyReadHookCallback; 'property:writing': ModelInstancePropertyWriteHookCallback; @@ -284,227 +416,447 @@ export type ModelHooksDefinition = & Record<`property:write:${string}`, ModelInstancePropertyWriteHookCallback>; /** - * Extendable model class holding the configuration and schema. + * Model hooks definition. + * + * @internal + */ +export type ModelHooksDefinition = + & ModelHooksDefinitionForModel + & ModelHooksDefinitionForInstance + & ModelHooksDefinitionForInstanceProperty; + +/** + * Model instance. + * + * @typeParam D Flatten definition of the model. + * + * @interface */ -export type ModelClass = +export type ModelInstance = { + /** + * Model this instance was created from. + */ + readonly $model: Model>; + /** + * Tells if instance exists in data source. + * This is `true` for retrieved or saved instance. + */ + $exists: boolean; + /** + * Internal values of the instance's properties. + */ + $values: Partial>; + /** + * Raw value of the data source record when available. + */ + $raw: any | null; + /** + * Latest sync snapshot. + */ + $original: ModelSnapshot; + /** + * Tells which relation is considered to be loaded. + * + * @internal + */ + $loaded: Dictionary; + /** + * Stores the flatten definition for type resolution. + * + * @internal + */ + readonly _definition: D; + /** + * Stores the schema for type resolution. + * + * @internal + */ + readonly _schema: ModelSchema; + } + & ModelProperties + & FosciaObject; + +/** + * Model class. + * + * @typeParam D Flatten definition of the model. + * @typeParam I Instance of the model. + * + * @interface + */ +export type Model = any> = + & { + /** + * Unique type of the model. + */ readonly $type: string; + /** + * Configuration of the model. + * Its properties can change during the model lifecycle. + */ readonly $config: ModelConfig; + /** + * Schema of the model. + * + * @internal + */ readonly $schema: ModelSchema; + /** + * Composables used by the model. + * + * @internal + */ readonly $composables: ModelComposable[]; + /** + * Tells if the model definition is parsed. + * + * @internal + */ + $parsed: boolean; + /** + * Tells if the model was already booted (constructed at least once). + * + * @internal + */ $booted: boolean; + /** + * Stores the flatten definition for type resolution. + * + * @internal + */ + readonly _definition: D; + /** + * Stores the schema for type resolution. + * + * @internal + */ + readonly _schema: ModelSchema; } - & FosciaObject - & Hookable; - -/** - * Model class of a dedicated instance. - * This type is used to keep instance generic typing across actions enhancements. - */ -export type Model = any> = - & ModelClass - & Constructor; - -/** - * Model class using a given composable. - */ -export type ModelUsing = - Model, ModelInstanceUsing>; + & Constructor + & Hookable + & FosciaObject; /** - * Model class which can be configured or extended. + * Model class which can be configured or extended to another model. + * + * @internal */ export type ExtendableModel = any> = & { - configure(config: ModelConfig, override?: boolean): ExtendableModel>; + /** + * Create a new model class with an overwritten configuration. + * + * @param config + * @param override + */ + configure( + config: Partial, + override?: boolean, + ): ExtendableModel>; + /** + * Create a new model class with an extended definition. + * + * @param rawDefinition + */ extend( + rawDefinition?: ND & ThisType>>, // eslint-disable-next-line max-len - rawDefinition?: ND & ThisType>>>, - // eslint-disable-next-line max-len - ): ExtendableModel>, ModelInstance>>>; + ): ExtendableModel, ModelInstance>>; } & Model; /** * Model class factory. + * + * @internal */ export type ModelFactory< D extends {} = {}, > = Hookable & (( - rawConfig: string | (ModelConfig & { type: string; }), - // eslint-disable-next-line max-len - rawDefinition?: ND & ThisType>>>, + rawConfig: string | (Partial & { type: string; }), + rawDefinition?: ND & ThisType>>, // eslint-disable-next-line max-len -) => ExtendableModel>, ModelInstance>>>); +) => ExtendableModel, ModelInstance>>); + +/** + * Model instance snapshot. + * + * @interface + */ +export type ModelSnapshot = { + readonly $instance: ModelInstance; + readonly $original: ModelSnapshot | null; + readonly $exists: boolean; + readonly $raw: any; + readonly $loaded: Dictionary; + readonly $values: Partial>>>; +} & FosciaObject; /** - * Model instance for a dedicated model class. - * This type is used to keep instance generic typing across actions enhancements. + * Model instance snapshot which only contains ID and LID + * and is used to track related records inside a snapshot. + * + * @interface */ -export type ModelClassInstance = { - readonly $model: ModelClass; -}; +export type ModelLimitedSnapshot = { + readonly $instance: ModelInstance; + readonly $exists: boolean; + readonly $values: Partial>]: K extends 'id' | 'lid' + ? ModelSnapshotValues>[K] + : never; + }>>>; +} & FosciaObject; + +/** + * Model class using a given composable or composable factory. + */ +export type ModelUsing = + Model, ModelInstanceUsing>; /** - * Model defaults IDs typing when not defined by definition. + * Model instance using a given composable or composable factory. */ -export type ModelIdsDefaults = - & (D extends { id: any } ? {} : { id: ModelIdType | null }) - & (D extends { lid: any } ? {} : { lid: ModelIdType | null }); +export type ModelInstanceUsing = + ModelInstance>; /** - * Model properties map (IDs/attributes/relations/custom props). + * Parsed model definition with non-composable properties stored into + * descriptor holders. + * + * @internal */ -export type ModelDefinitionProperties = ModelIdsDefaults & { - [K in keyof D]: D[K] extends ModelAttribute - ? T : D[K] extends ModelRelation - ? T : D[K] extends ModelId - ? T : D[K] extends DescriptorHolder - ? T : D[K]; +export type ModelParsedDefinition = { + [K in keyof D]: D[K] extends ModelComposableFactory | DescriptorHolder + ? D[K] : DescriptorHolderOf; }; /** - * Model properties key (only writable IDs/attributes/relations). + * Flatten non-properties composables to the definition's root. + * + * @internal */ -export type ModelDefinitionWritableKey = { - [K in keyof D]: D[K] extends DescriptorHolder - ? never - : D[K] extends RawModelProp ? never : K; -}[keyof D] | keyof ModelIdsDefaults; +export type ModelFlattenComposables = UnionToIntersection<{} | { + [K in keyof D]: D[K] extends ModelComposableFactory + ? C extends ModelProp ? never : ModelFlattenDefinition<(C & { key: K })['_type']> + : never; +}[keyof D]>; /** - * Model properties key (only readonly IDs/attributes/relations). + * Extract root properties composables or descriptors. + * + * @internal */ -export type ModelDefinitionReadOnlyKey = { - [K in keyof D]: D[K] extends DescriptorHolder - ? never - : D[K] extends RawModelProp ? K : never; -}[keyof D]; +export type ModelRootProperties = OmitNever<{ + [K in keyof D]: D[K] extends ModelComposableFactory + ? C extends ModelProp ? (D[K] & { key: K; }) : never : D[K]; +}>; /** - * Model descriptors key (only custom properties). + * Flatten definition typing with non-properties composable typings resolved to + * the definition root. + * + * @internal */ -export type ModelDefinitionDescriptorKey = { - [K in keyof D]: D[K] extends DescriptorHolder ? K : never; -}[keyof D]; +export type ModelFlattenDefinition = + & ModelFlattenComposables + & ModelRootProperties; /** - * Model properties map (only writable IDs/attributes/relations). + * {@link ModelParsedDefinition | `ModelParsedDefinition`} of a model + * {@link ModelFlattenDefinition | `ModelFlattenDefinition`}. + * + * @internal */ -export type ModelDefinitionWritableValues = - Pick, ModelDefinitionWritableKey>; +export type ModelParsedFlattenDefinition = + ModelParsedDefinition>; /** - * Model properties map (only readonly IDs/attributes/relations). + * Model real schema from definition. + * + * @internal */ -export type ModelDefinitionReadOnlyValues = - Readonly, ModelDefinitionReadOnlyKey>>; +export type ModelRealSchema = OmitNever<{ + [K in keyof D]: D[K] extends ModelComposableFactory + ? C extends ModelProp ? C : never : never; +}>; /** - * Model properties map (IDs/attributes/relations). + * Model default schema from definition. + * + * @internal */ -export type ModelDefinitionValues = - & ModelDefinitionWritableValues - & ModelDefinitionReadOnlyValues; +export type ModelDefaultSchema = + & (ModelRealSchema extends { id: any } ? {} : { id: ModelId; }) + & (ModelRealSchema extends { lid: any } ? {} : { lid: ModelId; }); /** - * Model descriptors map (only custom properties). + * Model schema from definition. + * + * @internal */ -export type ModelDefinitionDescriptors = - Pick, ModelDefinitionDescriptorKey>; +export type ModelSchema = + IfAny, ModelRealSchema & ModelDefaultSchema>; /** - * Model instance holding state and values. + * Model real properties from definition. + * + * @internal */ -export type ModelInstance = - { - readonly $model: ModelClass; - $exists: boolean; - $loaded: Dictionary; - $values: Partial>; - $raw: any; - $original: ModelSnapshot; - } - & ModelDefinitionValues - & ModelDefinitionDescriptors - & FosciaObject; +export type ModelDefinitionRealProperties = UnionToIntersection<{} | { + [K in keyof D]: D[K] extends ModelComposableFactory ? (C & { key: K })['_type'] + : RestoreDescriptorHolder; +}[keyof D]>; /** - * Model instance using a given composable. + * Model default properties from definition. + * + * @internal */ -export type ModelInstanceUsing = - ModelInstance>; +export type ModelDefinitionDefaultProperties = + & (ModelRealSchema extends { id: any } ? {} : { id: ModelIdType | null; }) + & (ModelRealSchema extends { lid: any } ? {} : { lid: ModelIdType | null; }); /** - * Model class or instance snapshot. + * Model properties from definition. + * + * @internal */ -export type ModelSnapshot = { - $model: ModelClass; - $exists: boolean; - $raw: any; - $loaded: Dictionary; - $values: Partial>; -}; +export type ModelDefinitionProperties = + & IfAny, ModelDefinitionRealProperties> + & IfAny>; + +/** + * Model real snapshot values from definition. + * + * @internal + */ +export type ModelRealSnapshotValues = OmitNever<{ + [K in keyof D]: D[K] extends ModelComposableFactory + ? C extends ModelRelation + ? T extends (infer I)[] + ? (ModelLimitedSnapshot | ModelSnapshot)[] + : (ModelLimitedSnapshot | ModelSnapshot) + : C extends ModelProp + ? T : never : never; +}>; + +/** + * Model default snapshot values from definition. + * + * @internal + */ +export type ModelDefaultSnapshotValues = + & (ModelRealSchema extends { id: any } ? {} : { id: ModelIdType | null; }) + & (ModelRealSchema extends { lid: any } ? {} : { lid: ModelIdType | null; }); /** - * Model class or instance. + * Model snapshot values from definition. + * + * @internal */ -export type ModelClassOrInstance = ModelClass | ModelClassInstance; +export type ModelSnapshotValues = + IfAny & ModelDefaultSnapshotValues>; /** - * Infer the definition from a model class or model instance. + * Infer the definition from a composable or a composable factory. + * + * @internal */ -export type ModelInferDefinition = M extends ModelClassOrInstance - ? D - : M extends {} - ? M - : never; +export type InferModelComposableDefinition = + C extends ModelComposableFactory ? T['_type'] + : C extends ModelComposable ? C['_type'] + : {}; + +/** + * Infer the definition from a model class, a model instance or a definition. + * + * @internal + */ +export type InferModelDefinition = + M extends FosciaObject & { + readonly _definition: infer D + } ? D extends {} ? D : never : M extends {} ? M : never; /** - * Infer the schema from a model class or model instance. + * Infer the schema from a model class, a model instance or a definition. + * + * @internal */ -export type ModelInferSchema = ModelSchema>; +export type InferModelSchema = + M extends FosciaObject & { + readonly _schema: infer D + } ? D extends {} ? D : never : M extends {} ? ModelSchema : never; /** - * Model class or instance values map (only IDs/attributes/relations). + * Infer a schema property from a model class, a model instance or a definition + * and ensure it extends the given `P` generic type. + * + * @internal */ -export type ModelValues = ModelDefinitionValues>; +export type InferModelSchemaProp = + K extends keyof InferModelSchema + ? InferModelSchema[K] extends P + ? InferModelSchema[K] + : never : never; /** - * Model class or instance values map (only writable IDs/attributes/relations). + * Model class or instance properties (IDs, attributes, relations and + * any other custom properties or methods). */ -export type ModelWritableValues = ModelDefinitionWritableValues>; +export type ModelProperties = ModelDefinitionProperties>; /** - * Model class or instance values map (only readonly IDs/attributes/relations). + * Model class or instance values (IDs, attributes and relations). + * + * @example + * const values: Partial> = { title: 'Hello' }; */ -export type ModelReadOnlyValues = ModelDefinitionReadOnlyValues>; +export type ModelValues = Pick, ModelKey>; /** - * Model class or instance IDs/attributes/relations key. + * Model class or instance values' possible keys + * (IDs, attributes and relations). + * + * @example + * const keys: ModelKey[] = ['title', 'body', 'publishedAt', 'comments']; */ export type ModelKey = & string - & keyof ModelInferSchema - & keyof ModelValues; + & keyof InferModelSchema + & keyof ModelProperties; + +/** + * Model class or instance values' possible keys that are not readonly + * (IDs, attributes and relations). + * + * @example + * const keys: ModelWritableKey[] = ['title', 'body', 'comments']; + */ +export type ModelWritableKey = ModelKey & { + [K in keyof InferModelSchema]: InferModelSchema[K] extends ModelProp + ? K + : never; +}[keyof InferModelSchema]; /** - * Model class or instance relations key (only direct relations). + * Model class or instance direct relations' possible keys. * * @example * const keys: ModelRelationKey[] = ['comments', 'tags']; */ -export type ModelRelationKey = - keyof ModelInferSchema extends infer K - ? K extends string & keyof ModelInferSchema & keyof ModelValues - ? ModelInferSchema[K] extends never - ? never - : ModelInferSchema[K] extends ModelRelation - ? K - : never : never : never; +export type ModelRelationKey = ModelKey & { + [K in keyof InferModelSchema]: InferModelSchema[K] extends ModelRelation + ? K + : InferModelSchema[K] extends ModelProp + ? IfAny + : never; +}[keyof InferModelSchema]; /** - * Model class or instance relations key (supports nested relation using dot separator). + * Model class or instance direct relations' possible keys, possibly chained + * using a dot (`.`) to their related models direct or deeper relations' keys. * * @example * const keys: ModelRelationDotKey[] = ['comments', 'comments.author', 'tags']; @@ -512,12 +864,12 @@ export type ModelRelationKey = export type ModelRelationDotKey = [Depth] extends [0] ? never - : keyof ModelInferSchema extends infer K - ? K extends string & keyof ModelInferSchema & keyof ModelValues - ? ModelInferSchema[K] extends never + : ModelKey extends infer K + ? K extends ModelKey + ? InferModelSchema[K] extends never ? never - : ModelInferSchema[K] extends ModelRelation - ? T extends any[] - ? K | `${K}.${ModelRelationDotKey}` - : K | `${K}.${ModelRelationDotKey}` - : never : never : never; + : InferModelSchema[K] extends ModelRelation + ? K | `${K}.${ModelRelationDotKey}` + : InferModelSchema[K] extends ModelProp + ? IfAny + : never : never : never; diff --git a/packages/core/src/model/utilities/cloneModelValue.ts b/packages/core/src/model/utilities/cloneModelValue.ts new file mode 100644 index 00000000..eed678fb --- /dev/null +++ b/packages/core/src/model/utilities/cloneModelValue.ts @@ -0,0 +1,11 @@ +/** + * Clone a model value. + * Will shallow copy arrays. + * + * @param value + * + * @since 0.13.0 + */ +export default (value: T) => ( + Array.isArray(value) ? [...value] : value +) as T; diff --git a/packages/core/src/model/utilities/compareModelValues.ts b/packages/core/src/model/utilities/compareModelValues.ts new file mode 100644 index 00000000..f35076ef --- /dev/null +++ b/packages/core/src/model/utilities/compareModelValues.ts @@ -0,0 +1,29 @@ +import isSnapshot from '@foscia/core/model/checks/isSnapshot'; +import isSame from '@foscia/core/model/isSame'; +import isSameSnapshot from '@foscia/core/model/snapshots/isSameSnapshot'; + +const compareValue = (next: unknown, prev: unknown) => ( + isSame(next, prev) + || next === prev + || (isSnapshot(next) && isSnapshot(prev) && isSameSnapshot(next, prev)) +); + +/** + * Compare two model values. + * Will check for {@link isSame | `isSame`} instances or strict equality, + * and inspect array deeply. + * + * @param next + * @param prev + * + * @since 0.13.0 + */ +export default (next: unknown, prev: unknown) => ( + Array.isArray(next) + ? ( + Array.isArray(prev) + && next.length === prev.length + && !next.some((v, i) => !compareValue(v, prev[i])) + ) + : compareValue(next, prev) +); diff --git a/packages/core/src/normalization/normalizeDotRelations.ts b/packages/core/src/normalization/normalizeDotRelations.ts index 901727f5..d98572e7 100644 --- a/packages/core/src/normalization/normalizeDotRelations.ts +++ b/packages/core/src/normalization/normalizeDotRelations.ts @@ -1,18 +1,27 @@ import guessContextModel from '@foscia/core/actions/context/guessers/guessContextModel'; import logger from '@foscia/core/logger/logger'; import isRelationDef from '@foscia/core/model/checks/isRelationDef'; -import { Model, ModelClass, ModelRelationDotKey, ModelRelationKey } from '@foscia/core/model/types'; +import { Model, ModelKey, ModelRelationDotKey } from '@foscia/core/model/types'; import normalizeKey from '@foscia/core/normalization/normalizeKey'; -import { RegistryI } from '@foscia/core/types'; +import { ModelsRegistry } from '@foscia/core/types'; import { isNone, Optional } from '@foscia/shared'; -const normalizeDotRelations = ( - model: Model, - relations: ModelRelationDotKey>[], - registry?: Optional, +/** + * Normalize a set of dot relation keys. + * + * @param model + * @param relations + * @param registry + * + * @internal + */ +const normalizeDotRelations = ( + model: M, + relations: ModelRelationDotKey[], + registry?: Optional, ): Promise => Promise.all(relations.map(async (dotKey) => { const [currentKey, ...subKeys] = dotKey.split('.'); - const def = model.$schema[currentKey as ModelRelationKey>]; + const def = model.$schema[currentKey]; if (!def || !isRelationDef(def)) { logger.warn( `Trying to normalize non-relation \`${model.$type}.${def?.key ?? currentKey}\`. Either this is not a relation or relation is not declared.`, @@ -21,7 +30,7 @@ const normalizeDotRelations = ( return dotKey; } - const normalizedKey = normalizeKey(model, def.key); + const normalizedKey = normalizeKey(model, def.key as ModelKey); const subDotKey = subKeys.join('.'); if (isNone(subDotKey)) { return normalizedKey; diff --git a/packages/core/src/normalization/normalizeKey.ts b/packages/core/src/normalization/normalizeKey.ts index 8730d24a..5eb65257 100644 --- a/packages/core/src/normalization/normalizeKey.ts +++ b/packages/core/src/normalization/normalizeKey.ts @@ -1,12 +1,17 @@ -import { ModelClass, ModelKey } from '@foscia/core/model/types'; +import { Model, ModelKey } from '@foscia/core/model/types'; +import { using } from '@foscia/shared'; -export default ( - model: ModelClass, - key: ModelKey>, -) => { - const def = model.$schema[key]; - - return def.alias ?? ( - model.$config.guessAlias ? model.$config.guessAlias(def.key) : def.key - ); -}; +/** + * Normalize a property key using its alias or a guessed alias if available. + * + * @param model + * @param key + * + * @internal + */ +export default ( + model: M, + key: ModelKey, +) => using(model.$schema[key], (def) => def.alias ?? ( + model.$config.guessAlias?.(def.key) ?? def.key +)); diff --git a/packages/core/src/registry/makeMapRegistry.ts b/packages/core/src/registry/makeMapRegistry.ts new file mode 100644 index 00000000..ebbd28a1 --- /dev/null +++ b/packages/core/src/registry/makeMapRegistry.ts @@ -0,0 +1,22 @@ +import { MapRegistry, MapRegistryConfig } from '@foscia/core/registry/types'; + +/** + * Make a registry which holds registered models in a map. + * + * @param config + * + * @category Factories + */ +export default (config: MapRegistryConfig) => { + const normalizeType = config.normalizeType ?? ((t) => t); + + const modelsMap = new Map((config.models ?? []).map((model) => [ + normalizeType(model.$type), model, + ])); + + return { + registry: { + modelFor: async (type: string) => modelsMap.get(normalizeType(type)) ?? null, + } as MapRegistry, + }; +}; diff --git a/packages/core/src/registry/makeMapRegistryWith.ts b/packages/core/src/registry/makeMapRegistryWith.ts deleted file mode 100644 index 023599e9..00000000 --- a/packages/core/src/registry/makeMapRegistryWith.ts +++ /dev/null @@ -1,83 +0,0 @@ -import isModel from '@foscia/core/model/checks/isModel'; -import { Model } from '@foscia/core/model/types'; -import { - MapRegistry, - MapRegistryConfig, - MapRegistryModelRegistration, - MapRegistryModelsRegistration, - ModelObjectResolver, -} from '@foscia/core/registry/types'; -import { wrap } from '@foscia/shared'; - -/** - * Make a {@link RegistryI} implementation allowing multiple resolve - * capabilities, such as sync/async models and with/without types. - * - * @param config - */ -export default (config: MapRegistryConfig): MapRegistry => { - const resolvers = [] as ModelObjectResolver[]; - const modelsMap = new Map(); - - const normalizeRawType = config.normalizeType ?? ((t) => t); - - const resolveModelWithType = async (type: string) => { - const typedResolver = resolvers.find((r) => r.type === type); - - return typedResolver ? typedResolver.resolve() : null; - }; - - const resolveModelsWithoutType = async () => Promise.all(resolvers.map((r) => r.resolve())); - - const markModelsResolved = async (models: Model[]) => models.forEach((model) => { - modelsMap.set(normalizeRawType(model.$type), model); - }); - - const registerModel = (model: MapRegistryModelRegistration) => { - if (isModel(model)) { - resolvers.push({ - type: normalizeRawType(model.$type), - resolve: () => model, - }); - } else if (typeof model === 'function') { - resolvers.push({ - resolve: model, - }); - } else { - resolvers.push({ - type: model.type ? normalizeRawType(model.type) : undefined, - resolve: model.resolve, - }); - } - }; - - const register = (models: MapRegistryModelsRegistration) => { - if (Array.isArray(models)) { - models.forEach( - (model) => registerModel(model), - ); - } else { - Object.entries(models).forEach( - ([type, model]) => registerModel({ type, resolve: model }), - ); - } - }; - - const modelFor = async (rawType: string) => { - const type = normalizeRawType(rawType); - - if (!modelsMap.has(type)) { - await markModelsResolved(wrap( - await resolveModelWithType(type) ?? await resolveModelsWithoutType(), - )); - } - - return modelsMap.get(type) ?? null; - }; - - return { - modelFor, - register, - registerModel, - }; -}; diff --git a/packages/core/src/registry/makeRegistry.ts b/packages/core/src/registry/makeRegistry.ts new file mode 100644 index 00000000..e946322c --- /dev/null +++ b/packages/core/src/registry/makeRegistry.ts @@ -0,0 +1,16 @@ +import { Model } from '@foscia/core/model/types'; +import makeMapRegistry from '@foscia/core/registry/makeMapRegistry'; +import { ModelsRegistry } from '@foscia/core/types'; +import { kebabCase } from '@foscia/shared'; + +/** + * Make a default {@link ModelsRegistry | `ModelsRegistry`} implementation. + * + * @param models + * + * @category Factories + */ +export default (models: Model[]): { registry: ModelsRegistry; } => makeMapRegistry({ + models, + normalizeType: kebabCase, +}); diff --git a/packages/core/src/registry/types.ts b/packages/core/src/registry/types.ts index 722e49a0..3338aa1d 100644 --- a/packages/core/src/registry/types.ts +++ b/packages/core/src/registry/types.ts @@ -1,28 +1,24 @@ import { Model } from '@foscia/core/model/types'; -import { RegistryI } from '@foscia/core/types'; -import { Awaitable, Dictionary, Optional, Transformer } from '@foscia/shared'; - -export type ModelFunctionResolver = () => Awaitable; - -export type ModelObjectResolver = { - type?: string; - resolve: ModelFunctionResolver; -}; - -export type MapRegistryModelRegistration = - | Model - | ModelFunctionResolver - | ModelObjectResolver; - -export type MapRegistryModelsRegistration = - | MapRegistryModelRegistration[] - | Dictionary; +import { ModelsRegistry } from '@foscia/core/types'; +import { Optional, Transformer } from '@foscia/shared'; +/** + * Config for registry map implementation. + * + * @interface + * + * @internal + */ export type MapRegistryConfig = { + models?: Model[]; normalizeType?: Optional>; }; -export interface MapRegistry extends RegistryI { - register: (models: MapRegistryModelsRegistration) => void; - registerModel: (model: MapRegistryModelRegistration) => void; -} +/** + * Registry implementation using mapped models by types. + * + * @interface + * + * @internal + */ +export type MapRegistry = ModelsRegistry; diff --git a/packages/core/src/symbols.ts b/packages/core/src/symbols.ts index 76ebb3f3..71c42f1c 100644 --- a/packages/core/src/symbols.ts +++ b/packages/core/src/symbols.ts @@ -1,9 +1,111 @@ -export const SYMBOL_MODEL_PROP_PENDING = Symbol('foscia:pending prop'); -export const SYMBOL_MODEL_PROP_ID: unique symbol = Symbol('foscia:id prop'); -export const SYMBOL_MODEL_PROP_ATTRIBUTE: unique symbol = Symbol('foscia:attr prop'); -export const SYMBOL_MODEL_PROP_RELATION: unique symbol = Symbol('foscia:rel prop'); -export const SYMBOL_MODEL_RELATION_HAS_ONE: unique symbol = Symbol('foscia:rel has one'); -export const SYMBOL_MODEL_RELATION_HAS_MANY: unique symbol = Symbol('foscia:rel has many'); +/** + * Unique symbol for a model property factory. + * + * @internal + */ +export const SYMBOL_MODEL_PROP_FACTORY: unique symbol = Symbol('foscia:prop factory'); + +/** + * Unique symbol for a model property transformer. + * + * @internal + */ +export const SYMBOL_MODEL_PROP_TRANSFORMER: unique symbol = Symbol('foscia:prop transformer'); + +/** + * Unique symbol for a model property. + * + * @internal + */ +export const SYMBOL_MODEL_PROP: unique symbol = Symbol('foscia:prop'); + +/** + * Unique symbol for a model ID property. + * + * @internal + */ +export const SYMBOL_MODEL_PROP_KIND_ID: unique symbol = Symbol('foscia:prop:id'); + +/** + * Unique symbol for a model attribute property factory. + * + * @internal + */ +export const SYMBOL_MODEL_PROP_KIND_ATTRIBUTE: unique symbol = Symbol('foscia:prop:attribute'); + +/** + * Unique symbol for a model relation property factory. + * + * @internal + */ +export const SYMBOL_MODEL_PROP_KIND_RELATION: unique symbol = Symbol('foscia:prop:relation'); + +/** + * Unique symbol for a model "has one" relation. + * + * @internal + */ +export const SYMBOL_MODEL_RELATION_HAS_ONE: unique symbol = Symbol('foscia:rel:has-one'); + +/** + * Unique symbol for a model "has one" relation. + * + * @internal + */ +export const SYMBOL_MODEL_RELATION_HAS_MANY: unique symbol = Symbol('foscia:rel:has-many'); + +/** + * Unique symbol for a model class. + * + * @internal + */ export const SYMBOL_MODEL_CLASS: unique symbol = Symbol('foscia:model'); + +/** + * Unique symbol for a model instance. + * + * @internal + */ export const SYMBOL_MODEL_INSTANCE: unique symbol = Symbol('foscia:instance'); + +/** + * Unique symbol for a model composable. + * + * @internal + */ export const SYMBOL_MODEL_COMPOSABLE: unique symbol = Symbol('foscia:composable'); + +/** + * Unique symbol for an instance snapshot. + * + * @internal + */ +export const SYMBOL_MODEL_SNAPSHOT: unique symbol = Symbol('foscia:snapshot'); + +/** + * Unique symbol for an action "when" context function. + * + * @internal + */ +export const SYMBOL_ACTION_CONTEXT_WHEN: unique symbol = Symbol('foscia:action:when'); + +/** + * Unique symbol for an action enhancer context function. + * + * @internal + */ +export const SYMBOL_ACTION_CONTEXT_ENHANCER: unique symbol = Symbol('foscia:action:enhancer'); + +/** + * Unique symbol for an action runner context function. + * + * @internal + */ +export const SYMBOL_ACTION_CONTEXT_RUNNER: unique symbol = Symbol('foscia:action:runner'); + +/** + * Unique symbol for an action context function factory. + * + * @internal + */ +export const SYMBOL_ACTION_CONTEXT_FUNCTION_FACTORY: unique symbol = Symbol('foscia:action:function-factory'); diff --git a/packages/core/src/transformers/isTransformer.ts b/packages/core/src/transformers/isTransformer.ts new file mode 100644 index 00000000..cd7648af --- /dev/null +++ b/packages/core/src/transformers/isTransformer.ts @@ -0,0 +1,14 @@ +import { SYMBOL_MODEL_PROP_TRANSFORMER } from '@foscia/core/symbols'; +import { ObjectTransformer } from '@foscia/core/transformers/types'; +import { isFosciaType } from '@foscia/shared'; + +/** + * Check if given value is a transformer object. + * + * @param value + * + * @internal + */ +export default ( + value: unknown, +): value is ObjectTransformer => isFosciaType(value, SYMBOL_MODEL_PROP_TRANSFORMER); diff --git a/packages/core/src/transformers/makeCustomTransformer.ts b/packages/core/src/transformers/makeCustomTransformer.ts new file mode 100644 index 00000000..26e7dce4 --- /dev/null +++ b/packages/core/src/transformers/makeCustomTransformer.ts @@ -0,0 +1,21 @@ +import { SYMBOL_MODEL_PROP_TRANSFORMER } from '@foscia/core/symbols'; +import { ObjectTransformer } from '@foscia/core/transformers/types'; +import { Awaitable } from '@foscia/shared'; + +/** + * Create a custom transformer without automatic support for `null` + * or `undefined` values. + * + * @param deserialize + * @param serialize + * + * @category Factories + */ +export default ( + deserialize: (value: DS) => Awaitable, + serialize: (value: T) => Awaitable, +) => ({ + $FOSCIA_TYPE: SYMBOL_MODEL_PROP_TRANSFORMER, + deserialize, + serialize, +} as ObjectTransformer); diff --git a/packages/core/src/transformers/makeDateTransformer.ts b/packages/core/src/transformers/makeDateTransformer.ts index 1b156e34..97f491cc 100644 --- a/packages/core/src/transformers/makeDateTransformer.ts +++ b/packages/core/src/transformers/makeDateTransformer.ts @@ -1,18 +1,25 @@ import logger from '@foscia/core/logger/logger'; import makeTransformer from '@foscia/core/transformers/makeTransformer'; +import { tap } from '@foscia/shared'; +/** + * Create a date transformer. + * + * @param name + * @param deserializeFn + * @param serializeFn + * + * @internal + */ export default ( name: string, deserializeFn: (value: unknown) => Date, serializeFn: (value: Date) => string, ) => () => makeTransformer( - (value: unknown) => { - const date = deserializeFn(value); + (value: unknown) => tap(deserializeFn(value), (date) => { if (Number.isNaN(date.getTime())) { logger.warn(`Transformer \`${name}\` transform resulted in NaN date value.`, [{ value }]); } - - return date; - }, - (value: Date) => serializeFn(value), + }), + serializeFn, ); diff --git a/packages/core/src/transformers/makeTransformer.ts b/packages/core/src/transformers/makeTransformer.ts index 029e33bb..2d968266 100644 --- a/packages/core/src/transformers/makeTransformer.ts +++ b/packages/core/src/transformers/makeTransformer.ts @@ -1,15 +1,24 @@ +import makeCustomTransformer from '@foscia/core/transformers/makeCustomTransformer'; import { ObjectTransformer } from '@foscia/core/transformers/types'; import { Awaitable, isNil, Optional } from '@foscia/shared'; +/** + * Create a transformer with automatic support for `null` + * or `undefined` values. + * + * @param deserialize + * @param serialize + * + * @category Factories + */ export default ( - deserializeFn: (value: DS) => Awaitable, - serializeFn?: (value: T) => Awaitable, -) => { - const deserialize = deserializeFn; - const serialize = serializeFn ?? deserializeFn; - - return { - deserialize: (value: Optional) => (isNil(value) ? null : deserialize(value)), - serialize: (value: T | null) => (isNil(value) ? null : serialize(value as any)), - } as ObjectTransformer, SR | null>; -}; + deserialize: (value: DS) => Awaitable, + serialize?: (value: T) => Awaitable, +) => makeCustomTransformer( + (value: Optional) => (isNil(value) ? null : deserialize(value)), + (value: T | null) => ( + isNil(value) + ? null + : (serialize ?? deserialize)(value as any) + ), +) as ObjectTransformer, SR | null>; diff --git a/packages/core/src/transformers/toArrayOf.ts b/packages/core/src/transformers/toArrayOf.ts index e468e4a8..1a4da56d 100644 --- a/packages/core/src/transformers/toArrayOf.ts +++ b/packages/core/src/transformers/toArrayOf.ts @@ -8,6 +8,13 @@ const makeValuesMapper = ( value.map((v) => transform(v)), ); +/** + * Create an array transformer. + * + * @param transformer + * + * @category Factories + */ export default (transformer: ObjectTransformer) => makeTransformer( makeValuesMapper(transformer.deserialize), makeValuesMapper(transformer.serialize), diff --git a/packages/core/src/transformers/toBoolean.ts b/packages/core/src/transformers/toBoolean.ts index 74ce8dcd..e73b317d 100644 --- a/packages/core/src/transformers/toBoolean.ts +++ b/packages/core/src/transformers/toBoolean.ts @@ -1,11 +1,14 @@ import makeTransformer from '@foscia/core/transformers/makeTransformer'; -type ToBooleanOptions = { - trueValues?: unknown[]; -}; - -const DEFAULT_TRUE_VALUES = [true, 1, '1', 'true', 'yes']; - -export default (options?: ToBooleanOptions) => makeTransformer( - (value: unknown) => (options?.trueValues ?? DEFAULT_TRUE_VALUES).indexOf(value) !== -1, +/** + * Create a boolean transformer. + * + * @param options + * + * @category Factories + */ +export default ( + options?: { trueValues?: unknown[]; }, +) => makeTransformer( + (value: unknown) => (options?.trueValues ?? [true, 1, '1', 'true', 'yes']).indexOf(value) !== -1, ); diff --git a/packages/core/src/transformers/toDate.ts b/packages/core/src/transformers/toDate.ts index 0ea3bee7..36e3d3a5 100644 --- a/packages/core/src/transformers/toDate.ts +++ b/packages/core/src/transformers/toDate.ts @@ -1,12 +1,16 @@ import makeDateTransformer from '@foscia/core/transformers/makeDateTransformer'; -import { removeTimezoneOffset } from '@foscia/shared'; +import { removeTimezoneOffset, using } from '@foscia/shared'; -export default makeDateTransformer( +/** + * Create a date transformer. + * + * @category Factories + */ +export default /* @__PURE__ */ makeDateTransformer( 'toDate', - (value: unknown) => { - const [y, m, d] = typeof value === 'string' ? value.split('-') : []; - - return new Date( + (value: unknown) => using( + typeof value === 'string' ? value.split('-') : [], + ([y, m, d]) => new Date( Number.parseInt(y, 10), Number.parseInt(m, 10) - 1, Number.parseInt(d, 10), @@ -14,8 +18,8 @@ export default makeDateTransformer( 0, 0, 0, - ); - }, + ), + ), // Removing timezone offset prevent date to shift on another day // when serializing to ISO UTC format. (value: Date) => removeTimezoneOffset(value).toISOString().split('T')[0], diff --git a/packages/core/src/transformers/toDateTime.ts b/packages/core/src/transformers/toDateTime.ts index cb8dd1df..76796962 100644 --- a/packages/core/src/transformers/toDateTime.ts +++ b/packages/core/src/transformers/toDateTime.ts @@ -1,6 +1,11 @@ import makeDateTransformer from '@foscia/core/transformers/makeDateTransformer'; -export default makeDateTransformer( +/** + * Create a date time transformer. + * + * @category Factories + */ +export default /* @__PURE__ */ makeDateTransformer( 'toDateTime', (value: unknown) => new Date(typeof value === 'string' ? Date.parse(value) : Number.NaN), (value: Date) => value.toISOString(), diff --git a/packages/core/src/transformers/toNumber.ts b/packages/core/src/transformers/toNumber.ts index 7224607c..be61ba64 100644 --- a/packages/core/src/transformers/toNumber.ts +++ b/packages/core/src/transformers/toNumber.ts @@ -1,11 +1,14 @@ import logger from '@foscia/core/logger/logger'; import makeTransformer from '@foscia/core/transformers/makeTransformer'; +import { tap } from '@foscia/shared'; -export default () => makeTransformer((value: unknown) => { - const number = Number(value); +/** + * Create a number transformer. + * + * @category Factories + */ +export default () => makeTransformer((value: unknown) => tap(Number(value), (number) => { if (Number.isNaN(number)) { logger.warn('Transformer `toNumber` transform resulted in NaN value.', [{ value }]); } - - return number; -}); +})); diff --git a/packages/core/src/transformers/toString.ts b/packages/core/src/transformers/toString.ts index e67d7d73..37940910 100644 --- a/packages/core/src/transformers/toString.ts +++ b/packages/core/src/transformers/toString.ts @@ -1,3 +1,8 @@ import makeTransformer from '@foscia/core/transformers/makeTransformer'; +/** + * Create a string transformer. + * + * @category Factories + */ export default () => makeTransformer((value: unknown) => String(value)); diff --git a/packages/core/src/transformers/types.ts b/packages/core/src/transformers/types.ts index 70098cbe..30a57638 100644 --- a/packages/core/src/transformers/types.ts +++ b/packages/core/src/transformers/types.ts @@ -1,16 +1,14 @@ -import { Awaitable, Optional, Transformer } from '@foscia/shared'; +import { SYMBOL_MODEL_PROP_TRANSFORMER } from '@foscia/core/symbols'; +import { Awaitable, FosciaObject, Transformer } from '@foscia/shared'; -export type ObjectTransformer = { - deserialize: Transformer>; - serialize: Transformer>; -}; - -export type ObjectTransformerFactoryOptions = { - nullable?: N; -}; - -export type ObjectTransformerFactoryResult = ( - options?: ObjectTransformerFactoryOptions, -) => N extends true - ? ObjectTransformer, SR | null> - : ObjectTransformer; +/** + * Bi-directional object transformer. + * + * @internal + */ +export type ObjectTransformer = + & { + deserialize: Transformer>; + serialize: Transformer>; + } + & FosciaObject; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 0852071c..6e03081a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,23 +1,42 @@ -import type { Model, ModelIdType, ModelInstance, ModelRelation } from '@foscia/core/model/types'; +import type { + Model, + ModelIdType, + ModelInstance, + ModelRelation, + ModelSnapshot, +} from '@foscia/core/model/types'; import type { Arrayable, Awaitable } from '@foscia/shared'; /** - * Registry containing available models for actions. + * Registry containing available models. + * + * It will be used by other dependencies, like {@link Deserializer | `Deserializer`}, + * to deserialize raw data source records in the correct models. + * + * @interface */ -export interface RegistryI { +export type ModelsRegistry = { /** * Resolve a registered model by its type. - * Type may be normalized for easier resolve. + * Type may be normalized during resolution to support multiple casing/patterns. * * @param rawType */ modelFor(rawType: string): Promise; -} +}; /** * Cache containing already synced models instances. + * + * It is used by some runners, like {@link cachedOr | `cachedOr`}, to extract + * a cached instance instead of fetching it. + * It will also be used by other dependencies, like {@link Deserializer | `Deserializer`}, + * to prevent multiple instance of the same record coexisting or to store an instance + * as retrieved. + * + * @interface */ -export interface CacheI { +export type InstancesCache = { /** * Retrieve a model instance from cache. * @@ -25,7 +44,6 @@ export interface CacheI { * @param id */ find(type: string, id: ModelIdType): Promise; - /** * Put a model instance inside cache. * @@ -34,7 +52,6 @@ export interface CacheI { * @param instance */ put(type: string, id: ModelIdType, instance: ModelInstance): Promise; - /** * Forget a model's instance. * @@ -42,123 +59,193 @@ export interface CacheI { * @param id */ forget(type: string, id: ModelIdType): Promise; - /** * Forget all model's instances. * * @param type */ forgetAll(type: string): Promise; - /** * Forget all models' instances. */ clear(): Promise; -} +}; /** - * Adapter response data wrapper object. + * Adapter raw response wrapper. * - * @typeParam RawData Adapter's original response its implementation - * (e.g. a Response object for HTTP adapter using fetch). - * @typeParam Data Content of the adapter's original response, containing - * records or relations data. + * @typeParam RawData Adapter's original response (e.g. a + * {@link !Response | `Response`} object for HTTP adapter). + * @typeParam Data Adapter's original response data, containing records or relations data. */ -export interface AdapterResponseI { +export type AdapterResponse = { /** - * The raw original data (e.g. a Response object for HttpAdapter). + * The original response (e.g. a {@link !Response | `Response`} object for HTTP adapter). */ readonly raw: RawData; - /** * Read the original response data. - * This will be used to deserialize instances from data. + * This will be used to deserialize instances from data + * by {@link Deserializer | `Deserializer`}. * This method may not support to be called multiple times, * prefer calling it only once and reusing returned value. */ read(): Promise; -} +}; /** * Adapter interacting with the data source. * - * @typeParam RawData Adapter's original response its implementation - * (e.g. a Response object for HTTP adapter using fetch). - * @typeParam Data Content of the adapter's original response, containing - * records or relations data. + * @typeParam RawData Adapter's original response (e.g. a + * {@link !Response | `Response`} object for HTTP adapter). + * @typeParam Data Adapter's original response data, containing records or relations data. + * + * @interface */ -export interface AdapterI { +export type Adapter = { /** * Execute a given context to retrieve a raw data response. * Context data will already be serialized using serializer if available. * * @param context */ - execute(context: {}): Awaitable>; -} + execute(context: {}): Awaitable>; +}; + +/** + * Base deserialized data which must contain at least an instances array. + */ +export type DeserializedData = { + /** + * Deserialized instances. + */ + instances: I[]; +}; + +/** + * Deserializer converting adapter response read data to a deserialized array of instances. + * + * @typeParam Data Adapter's original response data, containing records or relations data. + * @typeParam Deserialized Object containing deserialized instances and other + * relevant deserialized data (e.g. the document for a JSON:API response). + * + * @interface + */ +export type Deserializer = { + /** + * Deserialize adapter data to a deserialized array of instances. + * + * @param data + * @param context + */ + deserialize(data: Data, context: {}): Awaitable; +}; /** * Serializer converting model instances to adapter data source format. * * @typeParam Record Serialized value for an instance. - * @typeParam Related Serialized value for a related instance. + * @typeParam RelatedRecord Serialized value for a related instance. * @typeParam Data Serialized value for one/many/none instances. + * Usually, it is a wrapper type for `Record` or `RelatedRecord` records. + * + * @interface */ -export interface SerializerI { +export type Serializer = { /** - * Serialize a given instance value. + * Serialize snapshots to records. * - * @param value + * @param snapshot * @param context */ - serializeInstance(value: ModelInstance, context: {}): Awaitable; - + serializeToRecords(snapshot: ModelSnapshot, context: {}): Awaitable; /** - * Serialize a given instance's relation value. + * Serialize snapshots to records. * - * @param instance + * @param snapshots + * @param context + */ + serializeToRecords(snapshots: ModelSnapshot[], context: {}): Awaitable; + /** + * Serialize snapshots to records. + * + * @param snapshot + * @param context + */ + serializeToRecords(snapshot: null, context: {}): Awaitable; + /** + * Serialize snapshots to records. + * + * @param snapshot + * @param context + */ + serializeToRecords( + snapshot: ModelSnapshot[] | ModelSnapshot | null, + context: {}, + ): Awaitable; + /** + * Serialize related snapshots to related records. + * + * @param parent * @param def - * @param value + * @param snapshot * @param context */ - serializeRelation( - instance: ModelInstance, + serializeToRelatedRecords( + parent: ModelSnapshot, def: ModelRelation, - value: Arrayable | null, + snapshot: ModelSnapshot, context: {}, - ): Awaitable | null>; - + ): Awaitable; /** - * Serialize a set of already serialized records. - * This can be used to "wrap" records. + * Serialize related snapshots to related records. * - * @param records + * @param parent + * @param def + * @param snapshots * @param context */ - serialize(records: Arrayable | null, context: {}): Awaitable; -} - -/** - * Base deserialized data which must contain at least an instances set. - */ -export interface DeserializedData { - instances: I[]; -} - -/** - * Deserializer converting adapter data to a deserialized set of instances. - * - * @typeParam Data Content of the adapter's original response, containing - * records or relations data. - * @typeParam Deserialized Object containing deserialized instances and other - * relevant deserialized data (e.g. the document for a JSON:API response). - */ -export interface DeserializerI { + serializeToRelatedRecords( + parent: ModelSnapshot, + def: ModelRelation, + snapshots: ModelSnapshot[], + context: {}, + ): Awaitable; /** - * Deserialize adapter data to a deserialized set of instances. + * Serialize related snapshots to related records. * - * @param data + * @param parent + * @param def + * @param snapshot * @param context */ - deserialize(data: Data, context: {}): Awaitable; -} + serializeToRelatedRecords( + parent: ModelSnapshot, + def: ModelRelation, + snapshot: null, + context: {}, + ): Awaitable; + /** + * Serialize related snapshots to related records. + * + * @param parent + * @param def + * @param snapshot + * @param context + */ + serializeToRelatedRecords( + parent: ModelSnapshot, + def: ModelRelation, + snapshot: ModelSnapshot[] | ModelSnapshot | null, + context: {}, + ): Awaitable; + /** + * Serialize already serialized records to data. + * It will usually only wrap records if necessary (e.g. to a `data` key + * in a JSON:API context). + * + * @param records + * @param context + */ + serializeToData(records: Arrayable | null, context: {}): Awaitable; +}; diff --git a/packages/core/tests/mocks/composables/commentable.mock.ts b/packages/core/tests/mocks/composables/commentable.mock.ts index 9f7d6f14..b0d077f5 100644 --- a/packages/core/tests/mocks/composables/commentable.mock.ts +++ b/packages/core/tests/mocks/composables/commentable.mock.ts @@ -2,6 +2,9 @@ import { attr, hasMany, makeComposable } from '@foscia/core'; import CommentMock from '../models/comment.mock'; export default makeComposable({ - commentsCount: attr(0).sync('pull'), + commentsCount: attr(0, { + sync: 'pull', + readOnly: true, + }), comments: hasMany(() => CommentMock), }); diff --git a/packages/core/tests/mocks/composables/imageable.mock.ts b/packages/core/tests/mocks/composables/imageable.mock.ts new file mode 100644 index 00000000..3d304f97 --- /dev/null +++ b/packages/core/tests/mocks/composables/imageable.mock.ts @@ -0,0 +1,6 @@ +import { hasMany, makeComposable } from '@foscia/core'; +import type FileMock from '../models/file.mock'; + +export default makeComposable({ + images: hasMany(), +}); diff --git a/packages/core/tests/mocks/makeFakeAction.mock.ts b/packages/core/tests/mocks/makeFakeAction.mock.ts deleted file mode 100644 index 4da9438e..00000000 --- a/packages/core/tests/mocks/makeFakeAction.mock.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { makeActionClass } from '@foscia/core'; - -export default function makeFakeAction() { - const ActionClass = makeActionClass(); - - return new ActionClass(); -} diff --git a/packages/core/tests/mocks/models/comment.mock.ts b/packages/core/tests/mocks/models/comment.mock.ts index 68a63f21..21f13e61 100644 --- a/packages/core/tests/mocks/models/comment.mock.ts +++ b/packages/core/tests/mocks/models/comment.mock.ts @@ -1,7 +1,9 @@ import { attr, hasOne, id, makeModel, toDateTime, toNumber, toString } from '@foscia/core'; +import imageable from '../composables/imageable.mock'; import UserMock from './user.mock'; export default class CommentMock extends makeModel('comments', { + imageable, id: id(toNumber()).nullable(), lid: id(toString()), body: attr(toString()), diff --git a/packages/core/tests/mocks/models/post.mock.ts b/packages/core/tests/mocks/models/post.mock.ts index b11c0994..48f675c7 100644 --- a/packages/core/tests/mocks/models/post.mock.ts +++ b/packages/core/tests/mocks/models/post.mock.ts @@ -1,13 +1,15 @@ import { attr, makeModel, toDateTime } from '@foscia/core'; import commentable from '../composables/commentable.mock'; +import imageable from '../composables/imageable.mock'; export default class PostMock extends makeModel('posts', { + imageable, commentable, title: attr(), body: attr(), publishedAt: attr(toDateTime()).nullable().readOnly(), -}) { get published() { return !!this.publishedAt; - } + }, +}) { } diff --git a/packages/core/tests/typecheck/action-factories.test-d.ts b/packages/core/tests/typecheck/action-factories.test-d.ts index 620ee864..d1f29a17 100644 --- a/packages/core/tests/typecheck/action-factories.test-d.ts +++ b/packages/core/tests/typecheck/action-factories.test-d.ts @@ -1,18 +1,15 @@ -import { all, CacheI, makeActionFactory, makeCache, makeRegistry, RegistryI } from '@foscia/core'; +import { InstancesCache, makeActionFactory, makeCache, makeRegistry, ModelsRegistry } from '@foscia/core'; import { expectTypeOf, test } from 'vitest'; test('Action factories are type safe', async () => { const actionFactory = makeActionFactory({ ...makeRegistry([]), ...makeCache(), - }, { - ...all.extension(), }); const action = actionFactory(); const context = await action.useContext(); - expectTypeOf(action.all).toMatchTypeOf(); - expectTypeOf(context.registry).toMatchTypeOf(); - expectTypeOf(context.cache).toMatchTypeOf(); + expectTypeOf(context.registry).toMatchTypeOf(); + expectTypeOf(context.cache).toMatchTypeOf(); }); diff --git a/packages/core/tests/typecheck/action-generics.test-d.ts b/packages/core/tests/typecheck/action-generics.test-d.ts index e4a780e8..39b1f97f 100644 --- a/packages/core/tests/typecheck/action-generics.test-d.ts +++ b/packages/core/tests/typecheck/action-generics.test-d.ts @@ -1,15 +1,12 @@ import { Action, - AdapterI, - all, - cachedOr, - CacheI, + Adapter, + InstancesCache, ConsumeInstance, ConsumeModel, - context, - DeserializerI, + Deserializer, include, - makeActionClass, + makeActionFactory, Model, ModelIdType, ModelInstance, @@ -17,29 +14,18 @@ import { oneOrFail, query, save, - SerializerI, - when, + Serializer, } from '@foscia/core'; import { expectTypeOf, test } from 'vitest'; import PostMock from '../mocks/models/post.mock'; test('Actions generics are type safe', async () => { - const action = () => { - const ActionClass = makeActionClass().extend({ - ...query.extension, - ...include.extension, - ...all.extension, - ...cachedOr.extension, - ...when.extension, - }); - - return new ActionClass().use(context({ - adapter: null as unknown as AdapterI, - cache: null as unknown as CacheI, - deserializer: null as unknown as DeserializerI, - serializer: null as unknown as SerializerI, - })); - }; + const action = makeActionFactory({ + adapter: null as unknown as Adapter, + cache: null as unknown as InstancesCache, + deserializer: null as unknown as Deserializer, + serializer: null as unknown as Serializer, + }); const normalFindModel = ( model: Model, @@ -57,13 +43,13 @@ test('Actions generics are type safe', async () => { tap: (action: Action>) => void, ) => action().use(query(model, id)).use(tap).run(oneOrFail()); - expectTypeOf(await normalFindModel(PostMock, '1', ['comments'])).toMatchTypeOf(); - expectTypeOf(await normalFindModel(PostMock, '1', ['postedBy'])).toMatchTypeOf(); - expectTypeOf(await normalFindModel(PostMock, '1', ['foo'])).toMatchTypeOf(); - expectTypeOf(await genericFindModel(PostMock, '1', ['comments'])).toMatchTypeOf(); + expectTypeOf(await normalFindModel(PostMock, '1', ['comments'])).toEqualTypeOf(); + expectTypeOf(await normalFindModel(PostMock, '1', ['postedBy'])).toEqualTypeOf(); + expectTypeOf(await normalFindModel(PostMock, '1', ['foo'])).toEqualTypeOf(); + expectTypeOf(await genericFindModel(PostMock, '1', ['comments'])).toEqualTypeOf(); expectTypeOf( await genericCallbackFindModel(PostMock, '1', (a) => a.use(include('comments'))), - ).toMatchTypeOf(); + ).toEqualTypeOf(); // @ts-expect-error foo is not a relation of PostMock await genericFindModel(PostMock, '1', ['foo']); @@ -78,18 +64,18 @@ test('Actions generics are type safe', async () => { instance: I, relations: ModelRelationDotKey[], ) => action().use(save(instance), include(relations)).run(oneOrFail()); - const genericCallbackSaveInstance = >( + const genericCallbackSaveInstance = ( instance: I, tap: (action: Action>) => void, ) => action().use(save(instance)).use(tap).run(oneOrFail()); - expectTypeOf(await normalSaveInstance(new PostMock(), ['comments'])).toMatchTypeOf(); - expectTypeOf(await normalSaveInstance(new PostMock(), ['postedBy'])).toMatchTypeOf(); - expectTypeOf(await normalSaveInstance(new PostMock(), ['foo'])).toMatchTypeOf(); - expectTypeOf(await genericSaveInstance(new PostMock(), ['comments'])).toMatchTypeOf(); + expectTypeOf(await normalSaveInstance(new PostMock(), ['comments'])).toEqualTypeOf(); + expectTypeOf(await normalSaveInstance(new PostMock(), ['postedBy'])).toEqualTypeOf(); + expectTypeOf(await normalSaveInstance(new PostMock(), ['foo'])).toEqualTypeOf(); + expectTypeOf(await genericSaveInstance(new PostMock(), ['comments'])).toEqualTypeOf(); expectTypeOf( await genericCallbackSaveInstance(new PostMock(), (a) => a.use(include('comments'))), - ).toMatchTypeOf(); + ).toEqualTypeOf(); // @ts-expect-error foo is not a relation of PostMock await genericSaveInstance(new PostMock(), ['foo']); diff --git a/packages/core/tests/typecheck/actions.test-d.ts b/packages/core/tests/typecheck/actions.test-d.ts index 3c2b2cd7..59c32f99 100644 --- a/packages/core/tests/typecheck/actions.test-d.ts +++ b/packages/core/tests/typecheck/actions.test-d.ts @@ -1,22 +1,27 @@ import { - AdapterI, + Adapter, all, + appendActionMiddlewares, associate, + attach, + cached, cachedOr, - CacheI, context, create, - DeserializerI, + Deserializer, destroy, dissociate, include, - makeActionClass, + InstancesCache, + makeActionFactory, one, oneOrCurrent, oneOrFail, + onRunning, query, + queryAs, raw, - SerializerI, + Serializer, update, when, } from '@foscia/core'; @@ -26,37 +31,17 @@ import PostMock from '../mocks/models/post.mock'; import UserMock from '../mocks/models/user.mock'; test('Actions are type safe', async () => { - const action = () => { - const ActionClass = makeActionClass().extend({ - ...query.extension(), - ...include.extension(), - ...all.extension(), - ...oneOrFail.extension(), - ...cachedOr.extension(), - ...when.extension(), - ...create.extension(), - ...update.extension(), - ...destroy.extension(), - ...associate.extension(), - ...dissociate.extension(), - }); - - return new ActionClass().use(context({ - adapter: null as unknown as AdapterI, - cache: null as unknown as CacheI, - serializer: null as unknown as SerializerI, - deserializer: null as unknown as DeserializerI, - })); - }; + const action = makeActionFactory({ + adapter: null as unknown as Adapter, + cache: null as unknown as InstancesCache, + serializer: null as unknown as Serializer, + deserializer: null as unknown as Deserializer, + }); const postsUsingFunc = await action() .use(query(PostMock)) .use(include('comments.postedBy')) .run(all()); - const postsUsingBuild = await action() - .query(PostMock) - .include('comments.postedBy') - .all(); const postsUsingVariadic = await action() .use(query(PostMock), include('comments.postedBy')) .run(all()); @@ -65,37 +50,62 @@ test('Actions are type safe', async () => { include('comments.postedBy'), all(), ); + const postsUsingFactoryVariadic = await action( + query(PostMock), + include('comments.postedBy'), + all(), + ); + const manualPostsUsingRunVariadic = await action().run( + all(), + ) as PostMock[]; - expectTypeOf(postsUsingFunc).toMatchTypeOf(); - expectTypeOf(postsUsingBuild).toMatchTypeOf(); - expectTypeOf(postsUsingVariadic).toMatchTypeOf(); - expectTypeOf(postsUsingRunVariadic).toMatchTypeOf(); + expectTypeOf(postsUsingFunc).toEqualTypeOf(); + expectTypeOf(postsUsingVariadic).toEqualTypeOf(); + expectTypeOf(postsUsingRunVariadic).toEqualTypeOf(); + expectTypeOf(postsUsingFactoryVariadic).toEqualTypeOf(); + expectTypeOf(manualPostsUsingRunVariadic).toEqualTypeOf(); - const postUsingFunc = await action() + const postNullUsingFunc = await action() .use(query(new PostMock())) - .run(cachedOr(oneOrCurrent())); - const postUsingBuild = await action() - .query(new PostMock()) - .cachedOr(oneOrCurrent()); - const postUsingVariadic = await action() + .run(cached()); + const postUsingFunc = await action() .use(query(new PostMock())) .run(cachedOr(oneOrCurrent())); const postUsingRunVariadic = await action() .run(query(new PostMock()), cachedOr(oneOrCurrent())); - expectTypeOf(postUsingFunc).toMatchTypeOf(); - expectTypeOf(postUsingBuild).toMatchTypeOf(); - expectTypeOf(postUsingVariadic).toMatchTypeOf(); - expectTypeOf(postUsingRunVariadic).toMatchTypeOf(); + expectTypeOf(postNullUsingFunc).toEqualTypeOf(); + expectTypeOf(postUsingFunc).toEqualTypeOf(); + expectTypeOf(postUsingRunVariadic).toEqualTypeOf(); + + const asPostsUsingFunc = await action() + .use(queryAs(PostMock)) + .use(include('comments.postedBy')) + .run(all()); + const asPostsOrCommentsUsingFunc = await action() + .use(queryAs(PostMock, CommentMock)) + .use(include('images')) + .run(all()); + const asPostsOrCommentsArrayUsingFunc = await action() + .use(queryAs([PostMock, CommentMock])) + .use(include('images')) + .run(all()); + + expectTypeOf(asPostsUsingFunc).toEqualTypeOf(); + expectTypeOf(asPostsOrCommentsUsingFunc).toEqualTypeOf<(PostMock | CommentMock)[]>(); + expectTypeOf(asPostsOrCommentsArrayUsingFunc).toEqualTypeOf<(PostMock | CommentMock)[]>(); + + // @ts-expect-error title is not a post relation + await action().use(queryAs(PostMock), include('title')); + // @ts-expect-error title is not a post and comment relation + await action().use(queryAs(PostMock, CommentMock), include('title')); + // @ts-expect-error comments is not a post and comment relation + await action().use(queryAs(PostMock, CommentMock), include('comments')); const createdPostUsingFunc = await action() .use(query(new PostMock())) .use(include('comments.postedBy')) .run(when(new PostMock().$exists, one())); - const createdPostUsingBuild = await action() - .query(new PostMock()) - .include('comments.postedBy') - .when(new PostMock().$exists, one()); const createdPostUsingVariadic = await action().run( query(new PostMock()), include('comments.postedBy'), @@ -104,74 +114,80 @@ test('Actions are type safe', async () => { const createdPostUsingFuncOrCurrent = await action() .use(query(new PostMock())) .run(oneOrFail()); - const createdPostUsingBuildOrCurrent = await action() - .query(new PostMock()) - .oneOrFail(); - expectTypeOf(createdPostUsingFunc).toMatchTypeOf(); - expectTypeOf(createdPostUsingBuild).toMatchTypeOf(); - expectTypeOf(createdPostUsingVariadic).toMatchTypeOf(); - expectTypeOf(createdPostUsingFuncOrCurrent).toMatchTypeOf(); - expectTypeOf(createdPostUsingBuildOrCurrent).toMatchTypeOf(); + expectTypeOf(createdPostUsingFunc).toEqualTypeOf(); + expectTypeOf(createdPostUsingVariadic).toEqualTypeOf(); + expectTypeOf(createdPostUsingFuncOrCurrent).toEqualTypeOf(); expectTypeOf( await action() .use(create(new PostMock())) .run(one()), - ).toMatchTypeOf(); + ).toEqualTypeOf(); expectTypeOf( await action() .use(create(new CommentMock(), new PostMock(), 'comments')) .run(one()), - ).toMatchTypeOf(); + ).toEqualTypeOf(); expectTypeOf( await action() .use(update(new PostMock())) .run(one()), - ).toMatchTypeOf(); + ).toEqualTypeOf(); expectTypeOf( await action() .use(destroy(new PostMock())) .run(one()), - ).toMatchTypeOf(); + ).toEqualTypeOf(); + expectTypeOf( + await action() + .use(destroy(PostMock, '123')) + .run(one()), + ).toEqualTypeOf(); expectTypeOf( await action().run( associate(new CommentMock(), 'postedBy', new UserMock()), one(), ), - ).toMatchTypeOf(); + ).toEqualTypeOf(); expectTypeOf( await action().run( dissociate(new CommentMock(), 'postedBy'), one(), ), - ).toMatchTypeOf(); + ).toEqualTypeOf(); expectTypeOf( - await action().create(new PostMock()).oneOrFail(), - ).toMatchTypeOf(); - expectTypeOf( - await action().create(new CommentMock(), new PostMock(), 'comments').oneOrFail(), - ).toMatchTypeOf(); - expectTypeOf( - await action().update(new PostMock()).oneOrFail(), - ).toMatchTypeOf(); + await action().run( + associate(new CommentMock(), 'postedBy', new UserMock()), + oneOrFail(), + ), + ).toEqualTypeOf(); expectTypeOf( - await action().destroy(new PostMock()).oneOrFail(), - ).toMatchTypeOf(); + await action().run( + dissociate(new CommentMock(), 'postedBy'), + oneOrFail(), + ), + ).toEqualTypeOf(); expectTypeOf( - await action() - .associate(new CommentMock(), 'postedBy', new UserMock()) - .oneOrFail(), - ).toMatchTypeOf(); + await action().run( + attach(new PostMock(), 'comments', new CommentMock()), + oneOrFail(), + ), + ).toEqualTypeOf(); expectTypeOf( - await action() - .dissociate(new CommentMock(), 'postedBy') - .oneOrFail(), - ).toMatchTypeOf(); - - // @ts-expect-error title is not a post relation - await action().use(query(PostMock), include('title')); + await action().run( + attach(new PostMock(), 'comments', [new CommentMock(), new CommentMock()]), + oneOrFail(), + ), + ).toEqualTypeOf(); + + await action().use(query(PostMock), include( + // Placing the expectation here ensure the error comes + // from the parameter and not from the function typing. + // @ts-expect-error title is not a post relation + 'title', + )); // @ts-expect-error unknown is not a post relation await action().use(query(PostMock), include('unknown')); // @ts-expect-error postedBy is not a post relation @@ -194,45 +210,40 @@ test('Actions are type safe', async () => { await action().use(query(new PostMock(), 'comments'), include('comments')); // @ts-expect-error postedBy.unknown is not a comment relation await action().use(query(new PostMock(), 'comments'), include('postedBy.unknown')); - - // @ts-expect-error title is not a post relation - await action().query(PostMock).include('title'); - // @ts-expect-error unknown is not a post relation - await action().query(PostMock).include('unknown'); - // @ts-expect-error postedBy is not a post relation - await action().query(PostMock).include('postedBy'); - // @ts-expect-error unknown is not a comment relation - await action().query(PostMock).include('comments.unknown'); - // @ts-expect-error title is not a post relation - await action().query(new PostMock()).include('title'); - // @ts-expect-error unknown is not a post relation - await action().query(new PostMock()).include('unknown'); - // @ts-expect-error postedBy is not a post relation - await action().query(new PostMock()).include('postedBy'); - // @ts-expect-error unknown is not a comment relation - await action().query(new PostMock()).include('comments.unknown'); - // @ts-expect-error body is not a comment relation - await action().query(new PostMock(), 'comments').include('body'); - // @ts-expect-error unknown is not a comment relation - await action().query(new PostMock(), 'comments').include('unknown'); - // @ts-expect-error comments is not a comment relation - await action().query(new PostMock(), 'comments').include('comments'); - // @ts-expect-error postedBy.unknown is not a comment relation - await action().query(new PostMock(), 'comments').include('postedBy.unknown'); + // @ts-expect-error PostMock not assignable to UserMock + await action().use(associate(new CommentMock(), 'postedBy', new PostMock())); + // @ts-expect-error [PostMock] not assignable to UserMock + await action().use(associate(new CommentMock(), 'postedBy', [new PostMock()])); + // @ts-expect-error [UserMock] not assignable to UserMock + await action().use(associate(new CommentMock(), 'postedBy', [new UserMock()])); + // @ts-expect-error PostMock not assignable to CommentMock + await action().use(attach(new PostMock(), 'comments', new PostMock())); + // @ts-expect-error PostMock not assignable to CommentMock + await action().use(attach(new PostMock(), 'comments', [new PostMock()])); const commentsUsingFunc = await action() .use(query(new PostMock(), 'comments')) .run(all()); - const commentsUsingBuild = await action() - .query(new PostMock(), 'comments') - .all(); - expectTypeOf(commentsUsingFunc).toMatchTypeOf(); - expectTypeOf(commentsUsingBuild).toMatchTypeOf(); + expectTypeOf(commentsUsingFunc).toEqualTypeOf(); const response = await action().run(raw()); - expectTypeOf(response).toMatchTypeOf(); + expectTypeOf(response).toEqualTypeOf(); // This will ensure `response` is not `any`. - expectTypeOf(response).not.toMatchTypeOf(); + expectTypeOf(response).not.toEqualTypeOf(); + + action().use(context({ foo: 'bar' }), onRunning(async (event) => { + expectTypeOf((await event.action.useContext()).foo).toEqualTypeOf(); + expectTypeOf((await event.action.useContext()).foo).not.toEqualTypeOf(); + })); + + action().use(context({ foo: 'bar' }), appendActionMiddlewares([async (a, next) => { + const ctx = await a.useContext(); + + expectTypeOf(ctx.foo).toEqualTypeOf(); + expectTypeOf(ctx.foo).not.toEqualTypeOf(); + + return next(a); + }])); }); diff --git a/packages/core/tests/typecheck/models-composition.test-d.ts b/packages/core/tests/typecheck/models-composition.test-d.ts index 20edd6a2..fa5256a0 100644 --- a/packages/core/tests/typecheck/models-composition.test-d.ts +++ b/packages/core/tests/typecheck/models-composition.test-d.ts @@ -1,8 +1,22 @@ +/* eslint-disable max-classes-per-file */ + import { + applyDefinition, attr, + hasOne, + id, makeComposable, + makeComposableFactory, + makeDefinition, makeModel, + ModelAttribute, + ModelAttributeFactory, + ModelComposable, + ModelIdFactory, + ModelIdType, + ModelInstance, ModelInstanceUsing, + ModelRelationFactory, ModelUsing, onBoot, onInit, @@ -27,14 +41,15 @@ test('Models compositions are type safe', () => { onBoot(foo, () => undefined); onInit(foo, (instance) => { - expectTypeOf(instance.foo).toMatchTypeOf(); + expectTypeOf(instance.foo).toEqualTypeOf(); // @ts-expect-error property does not exist - expectTypeOf(instance.bar).toMatchTypeOf(); + expectTypeOf(instance.bar).toEqualTypeOf(); }); - onPropertyWrite(foo, 'foo', ({ instance }) => { - expectTypeOf(instance.foo).toMatchTypeOf(); + onPropertyWrite(foo, 'foo', ({ instance, def }) => { + expectTypeOf(def).toMatchTypeOf>(); + expectTypeOf(instance.foo).toEqualTypeOf(); // @ts-expect-error property does not exist - expectTypeOf(instance.bar).toMatchTypeOf(); + expectTypeOf(instance.bar).toEqualTypeOf(); }); // @ts-expect-error property does not exist onPropertyWrite(foo, 'bar', () => undefined); @@ -43,16 +58,17 @@ test('Models compositions are type safe', () => { onBoot(Model, () => undefined); onInit(Model, (instance) => { - expectTypeOf(instance.foo).toMatchTypeOf(); - expectTypeOf(instance.baz).toMatchTypeOf(); + expectTypeOf(instance.foo).toEqualTypeOf(); + expectTypeOf(instance.baz).toEqualTypeOf(); // @ts-expect-error property does not exist - expectTypeOf(instance.unknown).toMatchTypeOf(); + expectTypeOf(instance.unknown).toEqualTypeOf(); }); - onPropertyWrite(Model, 'foo', ({ instance }) => { - expectTypeOf(instance.foo).toMatchTypeOf(); - expectTypeOf(instance.baz).toMatchTypeOf(); + onPropertyWrite(Model, 'foo', ({ instance, def }) => { + expectTypeOf(def).toMatchTypeOf>(); + expectTypeOf(instance.foo).toEqualTypeOf(); + expectTypeOf(instance.baz).toEqualTypeOf(); // @ts-expect-error property does not exist - expectTypeOf(instance.unknown).toMatchTypeOf(); + expectTypeOf(instance.unknown).toEqualTypeOf(); }); onPropertyWrite(Model, 'baz', () => undefined); // @ts-expect-error property does not exist @@ -67,38 +83,38 @@ test('Models compositions are type safe', () => { expectTypeOf(model).toMatchTypeOf>(); expectTypeOf(model).toMatchTypeOf>(); expectTypeOf(model).toMatchTypeOf>(); - expectTypeOf(model.foo).toMatchTypeOf(); - expectTypeOf(model.bar).toMatchTypeOf(); - expectTypeOf(model.baz).toMatchTypeOf(); + expectTypeOf(model.foo).toEqualTypeOf(); + expectTypeOf(model.bar).toEqualTypeOf(); + expectTypeOf(model.baz).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(model.foo).toMatchTypeOf(); + expectTypeOf(model.foo).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(model.bar).toMatchTypeOf(); + expectTypeOf(model.bar).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(model.baz).toMatchTypeOf(); + expectTypeOf(model.baz).toEqualTypeOf(); const ModelUsingType = null as unknown as ModelUsing; const modelUsingType = new ModelUsingType(); - expectTypeOf(modelUsingType.foo).toMatchTypeOf(); - expectTypeOf(modelUsingType.bar).toMatchTypeOf(); - expectTypeOf(modelUsingType.baz).toMatchTypeOf(); + expectTypeOf(modelUsingType.foo).toEqualTypeOf(); + expectTypeOf(modelUsingType.bar).toEqualTypeOf(); + expectTypeOf(modelUsingType.baz).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(modelUsingType.foo).toMatchTypeOf(); + expectTypeOf(modelUsingType.foo).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(modelUsingType.bar).toMatchTypeOf(); + expectTypeOf(modelUsingType.bar).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(modelUsingType.baz).toMatchTypeOf(); + expectTypeOf(modelUsingType.baz).toEqualTypeOf(); const instanceUsingType = null as unknown as ModelInstanceUsing; - expectTypeOf(instanceUsingType.foo).toMatchTypeOf(); - expectTypeOf(instanceUsingType.bar).toMatchTypeOf(); - expectTypeOf(instanceUsingType.baz).toMatchTypeOf(); + expectTypeOf(instanceUsingType.foo).toEqualTypeOf(); + expectTypeOf(instanceUsingType.bar).toEqualTypeOf(); + expectTypeOf(instanceUsingType.baz).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(instanceUsingType.foo).toMatchTypeOf(); + expectTypeOf(instanceUsingType.foo).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(instanceUsingType.bar).toMatchTypeOf(); + expectTypeOf(instanceUsingType.bar).toEqualTypeOf(); // @ts-expect-error property cannot be null - expectTypeOf(instanceUsingType.baz).toMatchTypeOf(); + expectTypeOf(instanceUsingType.baz).toEqualTypeOf(); const timestamps = makeComposable({ timestamps: true, @@ -116,4 +132,65 @@ test('Models compositions are type safe', () => { } } }); + + class User extends makeModel('users', { + id: id(), + }) { + } + + class Image extends makeModel('images', {}) { + } + + type ImageableDefinition = + & Record> + & Record<`${K}URL`, ModelAttributeFactory>; + + interface Imageable extends ModelComposable { + readonly _type: ImageableDefinition; + } + + const imageable = makeComposable(({ key }) => ({ + [key]: hasOne(() => Image), + [`${key}URL`]: attr(() => '', { readOnly: true }), + })); + + type IdOf = 'id' extends keyof T + ? T['id'] extends ModelIdType | null ? T['id'] + : D : D; + + type BelongsTo = + & Record> + & Record<`${K}Id`, ModelIdFactory, false>>; + + interface BelongsToComposable + extends ModelComposable { + readonly _type: BelongsTo; + } + + const belongsTo = < + T extends ModelInstance | null, + >() => makeComposableFactory>({ + bind: (composable) => { + applyDefinition(composable.parent, makeDefinition({ + [composable.key]: hasOne(), + [`${composable.key}Id`]: attr(), + })); + }, + }); + + class Post extends makeModel('posts', { + mainImage: belongsTo(), + user: belongsTo(), + userImage: imageable, + }) { + } + + const post = new Post(); + + expectTypeOf(post.user).toEqualTypeOf(); + expectTypeOf(post.userId).toEqualTypeOf(); + expectTypeOf(post.mainImage).toEqualTypeOf(); + expectTypeOf(post.mainImageId).toEqualTypeOf(); + expectTypeOf(post.userImage).toEqualTypeOf(); + expectTypeOf(post.userImageURL).toEqualTypeOf(); }); diff --git a/packages/core/tests/typecheck/models.test-d.ts b/packages/core/tests/typecheck/models.test-d.ts index ae36414b..57effbf3 100644 --- a/packages/core/tests/typecheck/models.test-d.ts +++ b/packages/core/tests/typecheck/models.test-d.ts @@ -1,4 +1,22 @@ -import { attr, fill, makeModel, normalizeDotRelations } from '@foscia/core'; +/* eslint-disable max-classes-per-file */ +/* eslint-disable @typescript-eslint/no-unused-expressions */ + +import { + attr, + fill, + hasMany, + hasOne, + makeComposable, + makeModel, + Model, + ModelIdType, + ModelInstance, + ModelLimitedSnapshot, + ModelSnapshot, + normalizeDotRelations, + takeSnapshot, + toString, +} from '@foscia/core'; import { expectTypeOf, test } from 'vitest'; import CommentMock from '../mocks/models/comment.mock'; import FileMock from '../mocks/models/file.mock'; @@ -7,35 +25,109 @@ import TagMock from '../mocks/models/tag.mock'; import UserMock from '../mocks/models/user.mock'; test('Models are type safe', () => { + const anyInstance = {} as unknown as ModelInstance; + + expectTypeOf(anyInstance.id).toEqualTypeOf(); + expectTypeOf(anyInstance.anything).toEqualTypeOf(); + expectTypeOf(anyInstance.anything.toUpperCase()).toEqualTypeOf(); + // @ts-expect-error id is any + expectTypeOf(anyInstance.id).toEqualTypeOf(); + // @ts-expect-error anything is any + expectTypeOf(anyInstance.anything).toEqualTypeOf(); + // @ts-expect-error id is any + expectTypeOf(anyInstance.id).toEqualTypeOf(); + // @ts-expect-error anything is any + expectTypeOf(anyInstance.anything).toEqualTypeOf(); + + fill(anyInstance, { anything: 'hello world' }); + + const anySnapshot1 = {} as unknown as ModelSnapshot; + const anySnapshot2 = takeSnapshot(anyInstance); + + expectTypeOf(anySnapshot1.$values.id).toEqualTypeOf(); + expectTypeOf(anySnapshot1.$values.anything).toEqualTypeOf(); + expectTypeOf(anySnapshot1.$values.anything.toUpperCase()).toEqualTypeOf(); + // @ts-expect-error id is any + expectTypeOf(anySnapshot1.$values.id).toEqualTypeOf(); + // @ts-expect-error anything is any + expectTypeOf(anySnapshot1.$values.anything).toEqualTypeOf(); + // @ts-expect-error id is any + expectTypeOf(anySnapshot1.$values.id).toEqualTypeOf(); + // @ts-expect-error anything is any + expectTypeOf(anySnapshot1.$values.anything).toEqualTypeOf(); + + expectTypeOf(anySnapshot2.$values.id).toEqualTypeOf(); + expectTypeOf(anySnapshot2.$values.anything).toEqualTypeOf(); + expectTypeOf(anySnapshot2.$values.anything.toUpperCase()).toEqualTypeOf(); + // @ts-expect-error id is any + expectTypeOf(anySnapshot2.$values.id).toEqualTypeOf(); + // @ts-expect-error anything is any + expectTypeOf(anySnapshot2.$values.anything).toEqualTypeOf(); + // @ts-expect-error id is any + expectTypeOf(anySnapshot2.$values.id).toEqualTypeOf(); + // @ts-expect-error anything is any + expectTypeOf(anySnapshot2.$values.anything).toEqualTypeOf(); + + const anyInstanceCast: ModelInstance = new PostMock(); + expectTypeOf(anyInstanceCast.anything).toEqualTypeOf(); + + normalizeDotRelations(anyInstanceCast.$model, ['anything']); + const post = new PostMock(); - expectTypeOf(post.id).toMatchTypeOf(); - expectTypeOf(post.lid).toMatchTypeOf(); - expectTypeOf(post.title).toMatchTypeOf(); - expectTypeOf(post.body).toMatchTypeOf(); - expectTypeOf(post.publishedAt).toMatchTypeOf(); - expectTypeOf(post.comments).toMatchTypeOf(); - expectTypeOf(post.published).toMatchTypeOf(); + expectTypeOf(post.id).toEqualTypeOf(); + expectTypeOf(post.lid).toEqualTypeOf(); + expectTypeOf(post.title).toEqualTypeOf(); + expectTypeOf(post.body).toEqualTypeOf(); + expectTypeOf(post.publishedAt).toEqualTypeOf(); + expectTypeOf(post.comments).toEqualTypeOf(); + expectTypeOf(post.commentsCount).toEqualTypeOf(); + expectTypeOf(post.published).toEqualTypeOf(); post.title = 'Hello World'; // @ts-expect-error publishedAt is readonly post.publishedAt = new Date(); + // @ts-expect-error commentsCount is readonly + post.commentsCount = 1; // @ts-expect-error published is readonly post.published = false; fill(post, { title: 'Hello World' }); // @ts-expect-error publishedAt is readonly fill(post, { publishedAt: new Date() }); + // @ts-expect-error publishedAt is readonly + fill(post, { commentsCount: 1 }); // @ts-expect-error published is not a model's value fill(post, { published: false }); + const postSnapshot = takeSnapshot(post); + + expectTypeOf(postSnapshot.$values.id).toEqualTypeOf(); + expectTypeOf(postSnapshot.$values.lid).toEqualTypeOf(); + expectTypeOf(postSnapshot.$values.title).toEqualTypeOf(); + expectTypeOf(postSnapshot.$values.body).toEqualTypeOf(); + expectTypeOf(postSnapshot.$values.publishedAt).toEqualTypeOf(); + expectTypeOf(postSnapshot.$values.commentsCount).toEqualTypeOf(); + // eslint-disable-next-line max-len + expectTypeOf(postSnapshot.$values.comments).toEqualTypeOf<(ModelSnapshot | ModelLimitedSnapshot)[] | undefined>(); + // @ts-expect-error notFound property does not exists + postSnapshot.$values.notFound; + // @ts-expect-error published property does not exists + postSnapshot.$values.published; + // @ts-expect-error title is readonly + postSnapshot.$values.title = 'Hello World'; + + expectTypeOf(postSnapshot.$values.comments![0].$values.id) + .toEqualTypeOf(); + expectTypeOf(postSnapshot.$values.comments![0].$values.lid).toEqualTypeOf(); + const comment = new CommentMock(); - expectTypeOf(comment.id).toMatchTypeOf(); - expectTypeOf(comment.lid).toMatchTypeOf(); - expectTypeOf(comment.body).toMatchTypeOf(); - expectTypeOf(comment.postedAt).toMatchTypeOf(); - expectTypeOf(comment.postedBy).toMatchTypeOf(); + expectTypeOf(comment.id).toEqualTypeOf(); + expectTypeOf(comment.lid).toEqualTypeOf(); + expectTypeOf(comment.body).toEqualTypeOf(); + expectTypeOf(comment.postedAt).toEqualTypeOf(); + expectTypeOf(comment.postedBy).toEqualTypeOf(); fill(comment, { body: 'Hello World', postedAt: new Date() }); // @ts-expect-error id is a number @@ -47,6 +139,15 @@ test('Models are type safe', () => { // @ts-expect-error postedAt is a date fill(comment, { postedAt: 'Hello World' }); + const commentSnapshot = takeSnapshot(comment); + + expectTypeOf(commentSnapshot.$values.id).toEqualTypeOf(); + expectTypeOf(commentSnapshot.$values.lid).toEqualTypeOf(); + expectTypeOf(commentSnapshot.$values.body).toEqualTypeOf(); + expectTypeOf(commentSnapshot.$values.postedAt).toEqualTypeOf(); + expectTypeOf(commentSnapshot.$values.postedBy) + .toEqualTypeOf<(ModelSnapshot | ModelLimitedSnapshot) | undefined>(); + normalizeDotRelations(PostMock, ['comments']); normalizeDotRelations(PostMock, ['comments.postedBy']); // @ts-expect-error unknown is not a Post relation @@ -55,11 +156,11 @@ test('Models are type safe', () => { normalizeDotRelations(PostMock, ['comments.unknown']); const tag = new TagMock(); - expectTypeOf(tag.taggables).toMatchTypeOf<(PostMock | UserMock)[]>(); + expectTypeOf(tag.taggables).toEqualTypeOf<(PostMock | UserMock)[]>(); const file = new FileMock(); - expectTypeOf(file.parent).toMatchTypeOf(); - expectTypeOf(file.children).toMatchTypeOf(); + expectTypeOf(file.parent).toEqualTypeOf(); + expectTypeOf(file.children).toEqualTypeOf(); class ChainedModel extends makeModel('chained', { name: attr() }) .configure({ strict: true }) @@ -69,7 +170,75 @@ test('Models are type safe', () => { } const chained = new ChainedModel(); - expectTypeOf(chained.name).toMatchTypeOf(); - expectTypeOf(chained.email).toMatchTypeOf(); - expectTypeOf(chained.age).toMatchTypeOf(); + expectTypeOf(chained.name).toEqualTypeOf(); + expectTypeOf(chained.email).toEqualTypeOf(); + expectTypeOf(chained.age).toEqualTypeOf(); + + class ModelProps extends makeModel('model-props', { + attr1: attr('', { readOnly: true }), + attr2: attr(toString(), { default: null }), + attr3: attr(toString(), { readOnly: true }), + rel1: hasOne('posts'), + rel2: hasOne({ readOnly: true }), + rel3: hasOne(() => PostMock, { readOnly: true }), + }) { + } + + const modelProps = new ModelProps(); + + expectTypeOf(modelProps.attr1).toEqualTypeOf(); + // @ts-expect-error attr3 is readonly + modelProps.attr1 = 'hello'; + expectTypeOf(modelProps.attr2).toEqualTypeOf(); + modelProps.attr2 = 'hello'; + expectTypeOf(modelProps.attr3).toEqualTypeOf(); + // @ts-expect-error attr3 is readonly + modelProps.attr3 = 'hello'; + + expectTypeOf(modelProps.rel1).toEqualTypeOf(); + modelProps.rel1 = new PostMock(); + expectTypeOf(modelProps.rel2).toEqualTypeOf(); + // @ts-expect-error rel2 is readonly + modelProps.rel2 = new CommentMock(); + expectTypeOf(modelProps.rel3).toEqualTypeOf(); + // @ts-expect-error rel3 is readonly + modelProps.rel3 = new CommentMock(); + + class ModelComposite extends makeModel('model-composite', { + user: makeComposable({ + user: hasOne(), + userId: attr(), + }), + }) { + } + + const modelComposite = new ModelComposite(); + + expectTypeOf(modelComposite.user).toEqualTypeOf(); + expectTypeOf(modelComposite.userId).toEqualTypeOf(); + + class ModelInverse extends makeModel('model-inverse', { + comments1: hasMany(() => CommentMock, { inverse: 'postedBy' }), + comments2: hasMany(() => CommentMock).inverse('postedBy'), + comments3: hasMany().inverse('postedBy'), + + any1: hasMany(() => CommentMock as Model, { inverse: 'postedBy' }), + any2: hasMany(() => CommentMock as Model).inverse('postedBy'), + any3: hasMany().inverse('postedBy'), + any4: hasMany().inverse('postedBy'), + + // @ts-expect-error postedAt is not a relation + attrInvalid1: hasMany(() => CommentMock, { inverse: 'postedAt' }), + // @ts-expect-error postedAt is not a relation + attrInvalid2: hasMany(() => CommentMock).inverse('postedAt'), + // @ts-expect-error postedAt is not a relation + attrInvalid3: hasMany().inverse('postedAt'), + + relShouldFail1: hasOne(() => UserMock, { inverse: 'comments' }), + relShouldFail2: hasOne(() => UserMock).inverse('comments'), + relShouldFail3: hasOne().inverse('comments'), + }) { + } + + expectTypeOf(new ModelInverse()).toEqualTypeOf(); }); diff --git a/packages/core/tests/unit/actions/context/enhancers/context.test.ts b/packages/core/tests/unit/actions/context/enhancers/context.test.ts index 093412ee..6c8c9e66 100644 --- a/packages/core/tests/unit/actions/context/enhancers/context.test.ts +++ b/packages/core/tests/unit/actions/context/enhancers/context.test.ts @@ -1,10 +1,9 @@ -import { context } from '@foscia/core'; +import { context, makeActionFactory } from '@foscia/core'; import { describe, expect, it } from 'vitest'; -import makeFakeAction from '../../../../mocks/makeFakeAction.mock'; describe.concurrent('unit: context', () => { it('should use merged contexts', async () => { - const action = makeFakeAction(); + const action = makeActionFactory()(); action.updateContext({ foo: 'foo', bar: 'bar' }); action.use(context({ bar: 'foo', baz: 'baz' })); diff --git a/packages/core/tests/unit/actions/makeActionClass.test.ts b/packages/core/tests/unit/actions/makeActionClass.test.ts deleted file mode 100644 index 468fcb1a..00000000 --- a/packages/core/tests/unit/actions/makeActionClass.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { - Action, - context, - logger, - makeActionClass, - onError, - onFinally, - onRunning, - onSuccess, -} from '@foscia/core'; -import { describe, expect, it, vi } from 'vitest'; - -describe('unit: makeActionClass', () => { - it.concurrent('should make an extended action', async () => { - const dummyEnhancer = (dummy: string) => (action: Action) => action.updateContext({ dummy }); - const dummyRunner = () => async (action: Action<{ dummy: string }>) => ( - (await action.useContext()).dummy - ); - - const ActionClass = makeActionClass({ - dummyEnhance(dummy: string) { - return this.use(dummyEnhancer(dummy)); - }, - dummyRun() { - return (this as unknown as Action<{ dummy: string }>).run(dummyRunner()); - }, - }); - - const funcAction = new ActionClass(); - expect(await funcAction.useContext()).toStrictEqual({}); - expect(await funcAction.use(dummyEnhancer('foo')).run(dummyRunner())).toStrictEqual('foo'); - - const builderAction = new ActionClass(); - expect(await builderAction.useContext()).toStrictEqual({}); - expect(await builderAction.dummyEnhance('foo').dummyRun()).toStrictEqual('foo'); - }); - - it('should trigger hooks', async () => { - const ActionClass = makeActionClass(); - - const loggerDebugMock = vi.spyOn(logger, 'debug').mockImplementation(() => undefined); - const runningMock = vi.fn(); - const successMock = vi.fn(); - const errorMock = vi.fn(); - const finallyMock = vi.fn(); - - const runner = () => 'dummy'; - - await expect( - new ActionClass() - .use(onRunning(runningMock)) - .use(onSuccess(successMock)) - .use(onError(errorMock)) - .use(onFinally(finallyMock)) - .run(runner), - ).resolves.toStrictEqual('dummy'); - - expect(loggerDebugMock.mock.calls).toStrictEqual([ - ['Action running.', [{ context: {}, runner }]], - ['Action success.', [{ context: {}, result: 'dummy' }]], - ]); - expect(runningMock.mock.calls).toStrictEqual([[{ context: {}, runner }]]); - expect(successMock.mock.calls).toStrictEqual([[{ context: {}, result: 'dummy' }]]); - expect(errorMock.mock.calls).toStrictEqual([]); - expect(finallyMock.mock.calls).toStrictEqual([[{ context: {} }]]); - - loggerDebugMock.mockReset(); - runningMock.mockReset(); - successMock.mockReset(); - errorMock.mockReset(); - finallyMock.mockReset(); - - const error = new Error('Dummy error'); - const failingRunner = () => { - throw error; - }; - - await expect( - new ActionClass() - .use(onRunning(runningMock)) - .use(onSuccess(successMock)) - .use(onError(errorMock)) - .use(onFinally(finallyMock)) - .run(failingRunner), - ).rejects.toThrowError(error); - - expect(loggerDebugMock.mock.calls).toStrictEqual([ - ['Action running.', [{ context: {}, runner: failingRunner }]], - ['Action error.', [{ context: {}, error }]], - ]); - expect(runningMock.mock.calls).toStrictEqual([[{ context: {}, runner: failingRunner }]]); - expect(successMock.mock.calls).toStrictEqual([]); - expect(errorMock.mock.calls).toStrictEqual([[{ context: {}, error }]]); - expect(finallyMock.mock.calls).toStrictEqual([[{ context: {} }]]); - - loggerDebugMock.mockRestore(); - }); - - it.concurrent('should dequeue enhancers sequentially', async () => { - const defineValue = (value: number) => async (action: Action) => { - action.use(context({ value })); - }; - - const ActionClass = makeActionClass(); - const action = new ActionClass(); - - expect(await action.useContext()).toStrictEqual({}); - - action.use(defineValue(1)); - - expect(await action.useContext()).toStrictEqual({ value: 1 }); - - action.use(defineValue(2)); - action.use(defineValue(3)); - - expect(await action.useContext()).toStrictEqual({ value: 3 }); - }); -}); diff --git a/packages/core/tests/unit/actions/makeActionFactory.test.ts b/packages/core/tests/unit/actions/makeActionFactory.test.ts new file mode 100644 index 00000000..05e0afbf --- /dev/null +++ b/packages/core/tests/unit/actions/makeActionFactory.test.ts @@ -0,0 +1,253 @@ +import { + Action, + ActionCall, + appendActionMiddlewares, + cachedOr, + context, + ContextFunctionType, + isContextFunction, + logger, + makeActionFactory, + makeCache, + makeModel, + onError, + onFinally, + onRunning, + onSuccess, + prependActionMiddlewares, + query, + SYMBOL_ACTION_CONTEXT_ENHANCER, + SYMBOL_ACTION_CONTEXT_RUNNER, + SYMBOL_ACTION_CONTEXT_WHEN, + when, +} from '@foscia/core'; +import { describe, expect, it, vi } from 'vitest'; + +describe('unit: makeActionFactory', () => { + it('should trigger hooks', async () => { + const loggerDebugMock = vi.spyOn(logger, 'debug').mockImplementation(() => undefined); + const runningMock = vi.fn(); + const successMock = vi.fn(); + const errorMock = vi.fn(); + const finallyMock = vi.fn(); + + const runner = () => 'dummy'; + + let action = makeActionFactory()(); + + await expect( + action + .use(onRunning(runningMock)) + .use(onSuccess(successMock)) + .use(onError(errorMock)) + .use(onFinally(finallyMock)) + .run(runner), + ).resolves.toStrictEqual('dummy'); + + expect(loggerDebugMock.mock.calls).toStrictEqual([ + ['Action running.', [{ action, runner }]], + ['Action success.', [{ action, result: 'dummy' }]], + ]); + expect(runningMock.mock.calls).toStrictEqual([[{ action, runner }]]); + expect(successMock.mock.calls).toStrictEqual([[{ action, result: 'dummy' }]]); + expect(errorMock.mock.calls).toStrictEqual([]); + expect(finallyMock.mock.calls).toStrictEqual([[{ action }]]); + + loggerDebugMock.mockReset(); + runningMock.mockReset(); + successMock.mockReset(); + errorMock.mockReset(); + finallyMock.mockReset(); + + const error = new Error('Dummy error'); + const failingRunner = () => { + throw error; + }; + + action = makeActionFactory()(); + + await expect( + action + .use(onRunning(runningMock)) + .use(onSuccess(successMock)) + .use(onError(errorMock)) + .use(onFinally(finallyMock)) + .run(failingRunner), + ).rejects.toThrowError(error); + + expect(loggerDebugMock.mock.calls).toStrictEqual([ + ['Action running.', [{ action, runner: failingRunner }]], + ['Action error.', [{ action, error }]], + ]); + expect(runningMock.mock.calls).toStrictEqual([[{ action, runner: failingRunner }]]); + expect(successMock.mock.calls).toStrictEqual([]); + expect(errorMock.mock.calls).toStrictEqual([[{ action, error }]]); + expect(finallyMock.mock.calls).toStrictEqual([[{ action }]]); + + loggerDebugMock.mockRestore(); + }); + + it('should trigger middlewares', async () => { + const action = makeActionFactory()(); + const result = await action + .use(context({ value: 'foo' })) + .use(appendActionMiddlewares([ + async (a, next) => { + a.use(context({ value: `${(await a.useContext()).value}1` })); + + const r = await next(a); + + return `${r}2`; + }, + async (a, next) => { + a.use(context({ value: `${(await a.useContext()).value}2` })); + + const r = await next(a); + + return `${r}1`; + }, + ])) + .use(prependActionMiddlewares(async (a, next) => `${await next(a)}3`)) + .run(() => 'bar'); + + expect(result).toEqual('bar123'); + expect(await action.useContext()).toEqual({ value: 'foo12' }); + }); + + it.concurrent('should dequeue enhancers sequentially', async () => { + const concatFoo = (value: string) => async ( + action: Action<{ foo: string; }>, + ) => action.use(context({ + foo: `${(await action.useContext()).foo}${value}`, + })); + + const action = makeActionFactory({ foo: 'foo' })(); + + expect(await action.useContext()).toStrictEqual({ foo: 'foo' }); + + action.use(concatFoo('1')); + + expect(await action.useContext()).toStrictEqual({ foo: 'foo1' }); + + action.use(concatFoo('2')); + action.use((a) => { + a.use(concatFoo('3')); + }); + action.use(concatFoo('4')); + + expect(await action.useContext()).toStrictEqual({ foo: 'foo1234' }); + }); + + it.concurrent('should store calls correctly', async () => { + const Post = makeModel('posts'); + + const firstWhenPredicate = async () => true; + const firstWhenEnhancer = context({ baz: 'baz' }); + const secondWhenPredicate = () => true; + const secondWhenEnhancer = (a: any) => a.use(context({ boo: 'boo' })); + const cachedOrRunner = () => null; + + const { cache } = makeCache(); + const action = makeActionFactory({ cache })(); + const result = await action + .use( + query(Post, 1), + context({ foo: 'foo' }), + ) + .use(when(firstWhenPredicate, firstWhenEnhancer)) + .use(context({ bar: 'bar' })) + .run( + when(secondWhenPredicate, secondWhenEnhancer), + cachedOr(cachedOrRunner), + ); + + expect(result).toBeNull(); + expect(await action.useContext()).toStrictEqual({ + cache, + model: Post, + id: 1, + foo: 'foo', + baz: 'baz', + bar: 'bar', + boo: 'boo', + }); + + const calls = action.calls(); + + type FormattedCall = { + type: ContextFunctionType; + name: string; + args: unknown[]; + calls: FormattedCall[]; + } | { + calls: FormattedCall[]; + }; + const formatCall = (call: ActionCall): FormattedCall => (isContextFunction(call.call) ? { + type: call.call.$FOSCIA_TYPE, + name: call.call.meta.factory.meta.name, + args: call.call.meta.args, + calls: call.calls.map(formatCall), + } : { calls: call.calls.map(formatCall) }); + + expect(calls.map(formatCall)).toStrictEqual([ + { + type: SYMBOL_ACTION_CONTEXT_ENHANCER, + name: 'query', + args: [Post, 1], + calls: [], + }, + { + type: SYMBOL_ACTION_CONTEXT_ENHANCER, + name: 'context', + args: [{ foo: 'foo' }], + calls: [], + }, + { + type: SYMBOL_ACTION_CONTEXT_WHEN, + name: 'when', + args: [firstWhenPredicate, firstWhenEnhancer], + calls: [ + { + type: SYMBOL_ACTION_CONTEXT_ENHANCER, + name: 'context', + args: [{ baz: 'baz' }], + calls: [], + }, + ], + }, + { + type: SYMBOL_ACTION_CONTEXT_ENHANCER, + name: 'context', + args: [{ bar: 'bar' }], + calls: [], + }, + { + type: SYMBOL_ACTION_CONTEXT_WHEN, + name: 'when', + args: [secondWhenPredicate, secondWhenEnhancer], + calls: [ + { + calls: [ + { + type: SYMBOL_ACTION_CONTEXT_ENHANCER, + name: 'context', + args: [{ boo: 'boo' }], + calls: [], + }, + ], + }, + ], + }, + { + type: SYMBOL_ACTION_CONTEXT_RUNNER, + name: 'cachedOr', + args: [cachedOrRunner], + calls: [ + { + calls: [], + }, + ], + }, + ]); + }); +}); diff --git a/packages/core/tests/unit/cache/makeRefsCacheWith.test.ts b/packages/core/tests/unit/cache/makeRefsCache.test.ts similarity index 76% rename from packages/core/tests/unit/cache/makeRefsCacheWith.test.ts rename to packages/core/tests/unit/cache/makeRefsCache.test.ts index a8c20d39..bd7f4ee7 100644 --- a/packages/core/tests/unit/cache/makeRefsCacheWith.test.ts +++ b/packages/core/tests/unit/cache/makeRefsCache.test.ts @@ -1,15 +1,15 @@ -import { makeRefsCacheWith, weakRefManager } from '@foscia/core'; -import { describe, expect, it, vi } from 'vitest'; +import { makeRefsCache, makeWeakRefFactory } from '@foscia/core'; +import { describe, expect, it } from 'vitest'; import CommentMock from '../../mocks/models/comment.mock'; import PostMock from '../../mocks/models/post.mock'; -describe.concurrent('unit: makeRefsCacheWith', () => { +describe.concurrent('unit: makeRefsCache', () => { it('should put, find and remove from cache', async () => { const firstPost = new PostMock(); const secondPost = new PostMock(); const comment = new CommentMock(); - const cache = makeRefsCacheWith({ manager: weakRefManager }); + const { cache } = makeRefsCache({ makeRef: makeWeakRefFactory() }); expect(await cache.find('posts', '1')).toBeNull(); expect(await cache.find('posts', '2')).toBeNull(); @@ -58,26 +58,15 @@ describe.concurrent('unit: makeRefsCacheWith', () => { }); it('should forget if ref has expired', async () => { - const post = new PostMock(); - const refManager = { - ref: vi.fn().mockImplementation(() => post), - value: vi.fn(), - }; + let post: PostMock | null = new PostMock(); + const fakeRef = () => () => post as any; - const cache = makeRefsCacheWith({ manager: refManager }); + const { cache } = makeRefsCache({ makeRef: fakeRef }); expect(await cache.find('posts', '1')).toBeNull(); - expect(refManager.value).not.toHaveBeenCalled(); - - refManager.value.mockImplementation(() => post); await cache.put('posts', '1', post); - expect(await cache.find('posts', '1')).toBe(post); - expect(refManager.value).toHaveBeenCalledOnce(); - - refManager.value.mockImplementation(() => undefined); - + post = null; expect(await cache.find('posts', '1')).toBeNull(); - expect(refManager.value).toHaveBeenCalledTimes(2); }); }); diff --git a/packages/core/tests/unit/cache/makeTimedRefFactory.test.ts b/packages/core/tests/unit/cache/makeTimedRefFactory.test.ts new file mode 100644 index 00000000..b6031667 --- /dev/null +++ b/packages/core/tests/unit/cache/makeTimedRefFactory.test.ts @@ -0,0 +1,41 @@ +import { makeTimedRefFactory } from '@foscia/core'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +describe('unit: makeTimedRefFactory', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should make a timed ref with default configuration', async () => { + const factory = makeTimedRefFactory(); + const ref = factory(42); + + expect(ref()).toBe(42); + + vi.advanceTimersByTime(4 * 60 * 1000); + expect(ref()).toBe(42); + + vi.advanceTimersByTime(4 * 60 * 1000); + expect(ref()).toBe(42); + + vi.advanceTimersByTime(6 * 60 * 1000); + expect(ref()).toBe(null); + }); + + it('should make a timed ref with custom configuration', async () => { + const factory = makeTimedRefFactory({ lifetime: 10 * 60, postpone: false }); + const ref = factory(42); + + expect(ref()).toBe(42); + + vi.advanceTimersByTime(9 * 60 * 1000); + expect(ref()).toBe(42); + + vi.advanceTimersByTime(2 * 60 * 1000); + expect(ref()).toBe(null); + }); +}); diff --git a/packages/core/tests/unit/cache/makeWeakRefFactory.test.ts b/packages/core/tests/unit/cache/makeWeakRefFactory.test.ts new file mode 100644 index 00000000..de5aa4ff --- /dev/null +++ b/packages/core/tests/unit/cache/makeWeakRefFactory.test.ts @@ -0,0 +1,13 @@ +import { makeWeakRefFactory } from '@foscia/core'; +import { describe, expect, it } from 'vitest'; + +describe('unit: makeWeakRefFactory', () => { + it('should make a weak ref', async () => { + const value = {}; + + const factory = makeWeakRefFactory(); + const ref = factory(value); + + expect(ref()).toBe(value); + }); +}); diff --git a/packages/core/tests/unit/cache/weakRefManager.test.ts b/packages/core/tests/unit/cache/weakRefManager.test.ts deleted file mode 100644 index 1e39677a..00000000 --- a/packages/core/tests/unit/cache/weakRefManager.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { weakRefManager } from '@foscia/core'; -import { describe, expect, it } from 'vitest'; -import PostMock from '../../mocks/models/post.mock'; - -describe.concurrent('unit: weakRefCacheMode', () => { - it('should use a weak ref instance', async () => { - const post = new PostMock(); - - const ref = await weakRefManager.ref(post); - - expect(ref).toBeInstanceOf(WeakRef); - expect(weakRefManager.value(ref)).toBe(post); - }); -}); diff --git a/packages/core/tests/unit/model/assembled.test.ts b/packages/core/tests/unit/model/assembled.test.ts new file mode 100644 index 00000000..625e412a --- /dev/null +++ b/packages/core/tests/unit/model/assembled.test.ts @@ -0,0 +1,100 @@ +import { assembled, attr, fill, makeModel } from '@foscia/core'; +import { describe, expect, it, vi } from 'vitest'; + +describe.concurrent('unit: assembled', () => { + it('should memoized sync value', () => { + const getterSpy = vi.fn((user: any) => ( + user.onlyUsername + ? user.username + : `${user.username} (${user.fullName})` + )); + + const User = makeModel('users', { + username: attr(() => ''), + fullName: attr(() => ''), + onlyUsername: attr(() => true), + printableUsername: assembled(getterSpy), + }); + + const user = fill(new User(), { + username: 'john', + fullName: 'John Doe', + onlyUsername: true, + }); + + expect(getterSpy.mock.calls.length).toBe(0); + + expect(user.printableUsername).toEqual('john'); + expect(getterSpy.mock.calls.length).toBe(1); + expect(user.printableUsername).toEqual('john'); + expect(getterSpy.mock.calls.length).toBe(1); + + user.fullName = 'Jane Doe'; + + expect(user.printableUsername).toEqual('john'); + expect(getterSpy.mock.calls.length).toBe(1); + expect(user.printableUsername).toEqual('john'); + expect(getterSpy.mock.calls.length).toBe(1); + + user.username = 'jane'; + + expect(user.printableUsername).toEqual('jane'); + expect(getterSpy.mock.calls.length).toBe(2); + expect(user.printableUsername).toEqual('jane'); + expect(getterSpy.mock.calls.length).toBe(2); + + user.onlyUsername = false; + + expect(user.printableUsername).toEqual('jane (Jane Doe)'); + expect(getterSpy.mock.calls.length).toBe(3); + expect(user.printableUsername).toEqual('jane (Jane Doe)'); + expect(getterSpy.mock.calls.length).toBe(3); + }); + + it('should memoized async value', async () => { + const getterSpy = vi.fn(async (user: any) => user.username); + + const User = makeModel('users', { + username: attr(() => ''), + asyncUsername: assembled(getterSpy), + }); + + const user = fill(new User(), { + username: 'john', + }); + + expect(getterSpy.mock.calls.length).toBe(0); + + expect(await user.asyncUsername).toEqual('john'); + expect(getterSpy.mock.calls.length).toBe(1); + expect(await user.asyncUsername).toEqual('john'); + expect(getterSpy.mock.calls.length).toBe(1); + }); + + it('should get/set assembled value', () => { + const User = makeModel('users', { + firstName: attr(() => ''), + lastName: attr(() => ''), + fullName: assembled({ + get: (user) => `${user.firstName} ${user.lastName}`, + set: (user, fullName) => { + // eslint-disable-next-line no-param-reassign + [user.firstName, user.lastName] = fullName.split(' '); + }, + }), + }); + + const user = fill(new User(), { + firstName: 'John', + lastName: 'Doe', + }); + + expect(user.fullName).toEqual('John Doe'); + + user.fullName = 'Foo Bar'; + + expect(user.firstName).toEqual('Foo'); + expect(user.lastName).toEqual('Bar'); + expect(user.fullName).toEqual('Foo Bar'); + }); +}); diff --git a/packages/core/tests/unit/model/composition.test.ts b/packages/core/tests/unit/model/composition.test.ts index 18d3d399..2448f8ad 100644 --- a/packages/core/tests/unit/model/composition.test.ts +++ b/packages/core/tests/unit/model/composition.test.ts @@ -40,6 +40,8 @@ describe.concurrent('unit: composition', () => { } const model = new Model(); + expect(model).toBeInstanceOf(Model); + expect(model.$model).toStrictEqual(Model); expect(model.foob).toStrictEqual(false); expect(model.foo).toStrictEqual('foo'); expect(model.bar).toStrictEqual('bar'); diff --git a/packages/core/tests/unit/model/hooks.test.ts b/packages/core/tests/unit/model/hooks.test.ts index 1cd10502..0ca75c95 100644 --- a/packages/core/tests/unit/model/hooks.test.ts +++ b/packages/core/tests/unit/model/hooks.test.ts @@ -94,8 +94,8 @@ describe.concurrent('unit: hooks', () => { // eslint-disable-next-line no-new new Post(); - expect(bootCalls).toStrictEqual(['composable1', 'composable2', 'factory', 'post']); - expect(initCalls).toStrictEqual(['composable1', 'composable2', 'factory', 'post']); + expect(bootCalls).toStrictEqual(['factory', 'composable1', 'composable2', 'post']); + expect(initCalls).toStrictEqual(['factory', 'composable1', 'composable2', 'post']); expect(bootFn).toHaveBeenCalledTimes(4); expect(bootFn.mock.calls.every(([arg]) => arg === Post)).toBeTruthy(); expect(initFn).toHaveBeenCalledTimes(4); @@ -113,8 +113,8 @@ describe.concurrent('unit: hooks', () => { initFn(instance); }); - expect(bootCalls).toStrictEqual(['composable1', 'composable2', 'factory', 'post']); - expect(initCalls).toStrictEqual(['composable1', 'composable2', 'factory', 'post']); + expect(bootCalls).toStrictEqual(['factory', 'composable1', 'composable2', 'post']); + expect(initCalls).toStrictEqual(['factory', 'composable1', 'composable2', 'post']); expect(bootFn).toHaveBeenCalledTimes(4); expect(initFn).toHaveBeenCalledTimes(4); @@ -122,12 +122,12 @@ describe.concurrent('unit: hooks', () => { new Comment(); expect(bootCalls).toStrictEqual([ - 'composable1', 'composable2', 'factory', 'post', - 'composable1', 'composable2', 'factory', 'comment', + 'factory', 'composable1', 'composable2', 'post', + 'factory', 'composable1', 'composable2', 'comment', ]); expect(initCalls).toStrictEqual([ - 'composable1', 'composable2', 'factory', 'post', - 'composable1', 'composable2', 'factory', 'comment', + 'factory', 'composable1', 'composable2', 'post', + 'factory', 'composable1', 'composable2', 'comment', ]); expect(bootFn).toHaveBeenCalledTimes(8); expect(bootFn.mock.calls.slice(0, 4).every(([arg]) => arg === Post)).toBeTruthy(); diff --git a/packages/core/tests/unit/model/snapshots.test.ts b/packages/core/tests/unit/model/snapshots.test.ts index fc323ee8..762cd73a 100644 --- a/packages/core/tests/unit/model/snapshots.test.ts +++ b/packages/core/tests/unit/model/snapshots.test.ts @@ -1,6 +1,11 @@ import { + attr, changed, - compareSnapshots, + fill, + hasOne, + isSameSnapshot, + isSnapshot, + makeModel, markSynced, restore, restoreSnapshot, @@ -15,9 +20,9 @@ describe.concurrent('unit: snapshots', () => { const post = new PostMock(); const comment = new CommentMock(); - expect(compareSnapshots(takeSnapshot(post), takeSnapshot(comment))).toStrictEqual(false); - expect(compareSnapshots(takeSnapshot(post), takeSnapshot(post))).toStrictEqual(true); - expect(compareSnapshots(takeSnapshot(post), post.$original)).toStrictEqual(false); + expect(isSameSnapshot(takeSnapshot(post), takeSnapshot(comment))).toStrictEqual(false); + expect(isSameSnapshot(takeSnapshot(post), takeSnapshot(post))).toStrictEqual(true); + expect(isSameSnapshot(takeSnapshot(post), post.$original)).toStrictEqual(false); expect(changed(post)).toStrictEqual(true); expect(changed(post, 'commentsCount')).toStrictEqual(true); expect(changed(post, 'title')).toStrictEqual(false); @@ -100,37 +105,82 @@ describe.concurrent('unit: snapshots', () => { }); it('should use clone and compare model options', () => { - const cloneValue = vi.fn((v) => (Array.isArray(v) ? [...v] : v)); - const compareValue = vi.fn((n, p) => n !== p); + const cloneSnapshotValue = vi.fn((v) => (Array.isArray(v) ? [...v] : v)); + const compareSnapshotValues = vi.fn((n, p) => n !== p); const ExtendedPostMock = PostMock.configure({ - cloneValue, - compareValue, + cloneSnapshotValue, + compareSnapshotValues, }); - expect(cloneValue).not.toHaveBeenCalled(); - expect(compareValue).not.toHaveBeenCalled(); + expect(cloneSnapshotValue).not.toHaveBeenCalled(); + expect(compareSnapshotValues).not.toHaveBeenCalled(); const post = new ExtendedPostMock(); - expect(cloneValue).not.toHaveBeenCalled(); - expect(compareValue).not.toHaveBeenCalled(); + expect(cloneSnapshotValue).not.toHaveBeenCalled(); + expect(compareSnapshotValues).not.toHaveBeenCalled(); post.title = 'foo'; post.comments = []; const snapshot = takeSnapshot(post); - expect(cloneValue).toHaveBeenCalledTimes(3); - expect(compareValue).not.toHaveBeenCalled(); + expect(cloneSnapshotValue).toHaveBeenCalledTimes(3); + expect(compareSnapshotValues).not.toHaveBeenCalled(); - expect(compareSnapshots(snapshot, post.$original, 'title')).toStrictEqual(true); - expect(compareValue).toHaveBeenCalledTimes(1); - expect(compareSnapshots(snapshot, post.$original, 'comments')).toStrictEqual(true); - expect(compareValue).toHaveBeenCalledTimes(2); - expect(compareSnapshots(snapshot, post.$original, 'commentsCount')).toStrictEqual(true); - expect(compareValue).toHaveBeenCalledTimes(3); + expect(isSameSnapshot(snapshot, post.$original, 'title')).toStrictEqual(true); + expect(compareSnapshotValues).toHaveBeenCalledTimes(1); + expect(isSameSnapshot(snapshot, post.$original, 'comments')).toStrictEqual(true); + expect(compareSnapshotValues).toHaveBeenCalledTimes(2); + expect(isSameSnapshot(snapshot, post.$original, 'commentsCount')).toStrictEqual(true); + expect(compareSnapshotValues).toHaveBeenCalledTimes(3); - expect(cloneValue).toHaveBeenCalledTimes(3); + expect(cloneSnapshotValue).toHaveBeenCalledTimes(3); + }); + + it('should take deep and limited snapshots', () => { + const FooModel = makeModel({ type: 'foo', limitedSnapshots: false }, { + foo: attr(), + bar: hasOne(), + }); + + const BarModel = makeModel({ type: 'bar', limitedSnapshots: true }, { + bar: attr(), + baz: hasOne(), + }); + + const BazModel = makeModel({ type: 'baz' }, { + baz: attr(), + }); + + const fooSnapshot = takeSnapshot(fill(new FooModel(), { + id: 'foo', + foo: 'foo', + bar: fill(new BarModel(), { + id: 'bar', + bar: 'bar', + baz: fill(new BazModel(), { + id: 'baz', + baz: 'baz', + }), + }), + })); + + expect(isSnapshot(fooSnapshot)).toStrictEqual(true); + expect(Object.keys(fooSnapshot.$values)).toStrictEqual(['id', 'foo', 'bar']); + expect(fooSnapshot.$values.id).toStrictEqual('foo'); + expect(fooSnapshot.$values.foo).toStrictEqual('foo'); + + expect(isSnapshot(fooSnapshot.$values.bar)).toStrictEqual(true); + expect(Object.keys((fooSnapshot.$values.bar as any).$values)) + .toStrictEqual(['id', 'bar', 'baz']); + expect((fooSnapshot.$values.bar as any).$values.id).toStrictEqual('bar'); + expect((fooSnapshot.$values.bar as any).$values.bar).toStrictEqual('bar'); + + expect(isSnapshot((fooSnapshot.$values.bar as any).$values.baz)).toStrictEqual(true); + expect(Object.keys(((fooSnapshot.$values.bar as any).$values.baz as any).$values)) + .toStrictEqual(['id']); + expect(((fooSnapshot.$values.bar as any).$values.baz as any).$values.id).toStrictEqual('baz'); }); }); diff --git a/packages/core/tests/unit/normalization/normalizeDotRelations.test.ts b/packages/core/tests/unit/normalization/normalizeDotRelations.test.ts index e6272c43..88d5df94 100644 --- a/packages/core/tests/unit/normalization/normalizeDotRelations.test.ts +++ b/packages/core/tests/unit/normalization/normalizeDotRelations.test.ts @@ -1,5 +1,5 @@ /* eslint-disable max-classes-per-file */ -import { hasOne, logger, makeMapRegistryWith, makeModel, normalizeDotRelations } from '@foscia/core'; +import { hasOne, logger, makeMapRegistry, makeModel, normalizeDotRelations } from '@foscia/core'; import { describe, expect, it, vi } from 'vitest'; describe('unit: normalizeDotRelations', () => { @@ -32,8 +32,7 @@ describe('unit: normalizeDotRelations', () => { foo: hasOne('sub-model').alias('bar'), }); - const registry = makeMapRegistryWith({}); - registry.register([model, SubModel]); + const { registry } = makeMapRegistry({ models: [model, SubModel] }); expect(await normalizeDotRelations(model, ['foo', 'foo.baz'], registry)) .toStrictEqual(['bar', 'bar.foobar']); diff --git a/packages/core/tests/unit/registry/makeMapRegistry.test.ts b/packages/core/tests/unit/registry/makeMapRegistry.test.ts new file mode 100644 index 00000000..73b718c4 --- /dev/null +++ b/packages/core/tests/unit/registry/makeMapRegistry.test.ts @@ -0,0 +1,28 @@ +import { makeMapRegistry, makeModel } from '@foscia/core'; +import { describe, expect, it } from 'vitest'; + +describe.concurrent('unit: makeMapRegistry', () => { + it('should register and resolve models', async () => { + const modelFoo = makeModel('foo'); + const modelBar = makeModel('bar'); + + const { registry } = makeMapRegistry({ + models: [modelFoo, modelBar], + }); + + expect(await registry.modelFor('foo')).toBe(modelFoo); + expect(await registry.modelFor('bar')).toBe(modelBar); + }); + + it('should normalize types', async () => { + const modelFooBar = makeModel('foo-bar'); + + const { registry } = makeMapRegistry({ + models: [modelFooBar], + normalizeType: (t) => t.toUpperCase(), + }); + + expect(await registry.modelFor('foo-bar')).toBe(modelFooBar); + expect(await registry.modelFor('FOO-BAR')).toBe(modelFooBar); + }); +}); diff --git a/packages/core/tests/unit/registry/makeMapRegistryWith.test.ts b/packages/core/tests/unit/registry/makeMapRegistryWith.test.ts deleted file mode 100644 index 9504a6a3..00000000 --- a/packages/core/tests/unit/registry/makeMapRegistryWith.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { makeMapRegistryWith, makeModel } from '@foscia/core'; -import { describe, expect, it } from 'vitest'; - -describe.concurrent('unit: makeMapRegistryWith', () => { - it('should register and resolve models', async () => { - const modelFoo = makeModel('foo'); - const modelBar = makeModel('bar'); - const modelBaz = makeModel('baz'); - const modelFooBar = makeModel('foo-bar'); - - const registry = makeMapRegistryWith({}); - - expect(await registry.modelFor('foo')).toBeNull(); - expect(await registry.modelFor('bar')).toBeNull(); - expect(await registry.modelFor('baz')).toBeNull(); - expect(await registry.modelFor('foo-bar')).toBeNull(); - - registry.register([modelFoo, modelBar]); - - expect(await registry.modelFor('foo')).toBe(modelFoo); - expect(await registry.modelFor('bar')).toBe(modelBar); - expect(await registry.modelFor('baz')).toBeNull(); - expect(await registry.modelFor('foo-bar')).toBeNull(); - - registry.register([{ resolve: () => modelBaz }]); - - expect(await registry.modelFor('foo')).toBe(modelFoo); - expect(await registry.modelFor('bar')).toBe(modelBar); - expect(await registry.modelFor('baz')).toBe(modelBaz); - expect(await registry.modelFor('foo-bar')).toBeNull(); - - registry.register({ 'foo-bar': async () => modelFooBar }); - - expect(await registry.modelFor('foo')).toBe(modelFoo); - expect(await registry.modelFor('bar')).toBe(modelBar); - expect(await registry.modelFor('baz')).toBe(modelBaz); - expect(await registry.modelFor('foo-bar')).toBe(modelFooBar); - }); - - it('should normalize types', async () => { - const modelFooBar = makeModel('foo-bar'); - - const registry = makeMapRegistryWith({ normalizeType: (t) => t.toUpperCase() }); - registry.register({ 'foo-bar': async () => modelFooBar }); - - const resolvedModel = await registry.modelFor('foo-bar'); - expect(resolvedModel).toBe(modelFooBar); - expect(await registry.modelFor('FOO-BAR')).toBe(modelFooBar); - }); -}); diff --git a/packages/core/tests/utils/evaluateContext.ts b/packages/core/tests/utils/evaluateContext.ts index 755af425..4b9d1c28 100644 --- a/packages/core/tests/utils/evaluateContext.ts +++ b/packages/core/tests/utils/evaluateContext.ts @@ -1,11 +1,10 @@ -import { context, ContextEnhancer } from '@foscia/core'; -import makeFakeAction from '../mocks/makeFakeAction.mock'; +import { context, ContextEnhancer, makeActionFactory } from '@foscia/core'; export default function evaluateContext( - callback: ContextEnhancer, + callback: ContextEnhancer, initial?: any, ): Promise { - return makeFakeAction() + return makeActionFactory()() .use(context(initial ?? {})) .use(callback as any) .useContext(); diff --git a/packages/http/src/actions/context/consumers/consumeRequestConfig.ts b/packages/http/src/actions/context/consumers/consumeRequestConfig.ts index 74e72ca5..fbc3e0ab 100644 --- a/packages/http/src/actions/context/consumers/consumeRequestConfig.ts +++ b/packages/http/src/actions/context/consumers/consumeRequestConfig.ts @@ -1,6 +1,12 @@ import { consumeContext } from '@foscia/core'; import { ConsumeHttpRequestConfig } from '@foscia/http/types'; +/** + * Consume the configured request. + * + * @param context + * @param defaultValue + */ export default ( context: C & Partial, defaultValue?: D, diff --git a/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts b/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts index 948d24d6..71c00dc7 100644 --- a/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts +++ b/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts @@ -1,11 +1,14 @@ import { FosciaError } from '@foscia/core'; import consumeRequestConfig from '@foscia/http/actions/context/consumers/consumeRequestConfig'; +import { Dictionary, tap } from '@foscia/shared'; -export default (context: {}) => { - const config = consumeRequestConfig(context, null); - if (typeof config?.params === 'string') { +/** + * Consume object params. Will throw an exception if string params are configured. + * + * @param context + */ +export default (context: {}) => tap(consumeRequestConfig(context, null)?.params, (params) => { + if (typeof params === 'string') { throw new FosciaError('Object and string URL params cannot be merged in action context.'); } - - return config?.params; -}; +}) as Dictionary | undefined; diff --git a/packages/http/src/actions/context/enhancers/abortSignal.ts b/packages/http/src/actions/context/enhancers/abortSignal.ts index 170c7403..ffc5a91a 100644 --- a/packages/http/src/actions/context/enhancers/abortSignal.ts +++ b/packages/http/src/actions/context/enhancers/abortSignal.ts @@ -1,30 +1,31 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import configureRequest from '@foscia/http/actions/context/enhancers/configureRequest'; import { Optional } from '@foscia/shared'; /** - * Configure an abort signal on the request to - * [make it abortable](https://developer.chrome.com/blog/abortable-fetch/). + * Configure an abort controller or signal on the request to support calling + * {@link !AbortController#abort | `AbortController.abort()`}. * * @param controllerOrSignal * * @category Enhancers + * + * @example + * ```typescript + * import { raw } from '@foscia/core'; + * import { abortSignal, makeGet } from '@foscia/http'; + * + * const abortController = new AbortController(); + * + * const promise = action().run(makeGet('posts'), abortSignal(abortController), raw()); + * + * abortController.abort(); + * ``` */ -const abortSignal = ( +export default /* @__PURE__ */ makeEnhancer('abortSignal', ( controllerOrSignal?: Optional, ) => configureRequest({ signal: controllerOrSignal instanceof AbortController ? controllerOrSignal.signal : controllerOrSignal, -}); - -export default /* @__PURE__ */ appendExtension( - 'abortSignal', - abortSignal, - 'use', -) as WithParsedExtension( - this: Action, - controllerOrSignal?: Optional, - ): Action; -}>; +})); diff --git a/packages/http/src/actions/context/enhancers/configureRequest.ts b/packages/http/src/actions/context/enhancers/configureRequest.ts index f8471c57..a8551e02 100644 --- a/packages/http/src/actions/context/enhancers/configureRequest.ts +++ b/packages/http/src/actions/context/enhancers/configureRequest.ts @@ -1,60 +1,79 @@ -import { Action, appendExtension, context, WithParsedExtension } from '@foscia/core'; +import { Action, context, makeEnhancer } from '@foscia/core'; import consumeRequestConfig from '@foscia/http/actions/context/consumers/consumeRequestConfig'; import { HttpRequestConfig } from '@foscia/http/types'; +import { using } from '@foscia/shared'; /** - * Configure an HTTP request used by the HttpAdapter (see {@link makeHttpAdapterWith}). + * Configure an HTTP request used by the HTTP adapter. + * * Some configuration options will be merged when possible (object query params, - * headers, transformers, etc.). + * headers, middlewares, etc.). * This enhancer can be used to configure a full request object or preconfigure - * some common options (e.g. headers and transformers). - * Passing a [Fetch request object](https://developer.mozilla.org/docs/Web/API/Request) + * some common options (e.g. headers and middlewares). + * Passing a {@link !Request | fetch `Request` object} * as `request` option will ignore any other configuration and request - * object will be directly passed to the adapter. Transformers will still be + * object will be directly passed to the adapter. Middlewares will still be * applied, but other automatic transformation or data passing (params, body, etc.) * won't be applied. * * @param nextConfig * * @category Enhancers + * + * @example + * `configureRequest` can be used as any other request enhancers (such as + * {@link makeGet | `makeGet`}). + * + * ```typescript + * import { raw } from '@foscia/core'; + * import { configureRequest } from '@foscia/http'; + * + * const response = await action().run(configureRequest({ + * method: 'GET', + * baseURL: 'https://example.com/api, + * path: 'posts', + * params: { search: 'foo' }, + * }), raw()); + * ``` + * + * `configureRequest` can also be used to preconfigure every future requests, + * directly in your action factory. + * + * ```typescript + * import { makeActionFactory } from '@foscia/core'; + * import { configureRequest } from '@foscia/http'; + * + * export default makeActionFactory({ + * // ... + * configureRequest({ headers: { Authorization: 'Bearer super-secret' } }), + * }); + * ``` + * + * Finally, to match complex use case, `configureRequest` can also receive a + * {@link !Request | `Request`} object. + * + * ```typescript + * import { makeActionFactory } from '@foscia/core'; + * import { configureRequest } from '@foscia/http'; + * + * const response = await action().run(configureRequest({ + * request: new Request('https://example.com/special/request', {}), + * }), raw()); + * ``` */ -const configureRequest = ( +export default /* @__PURE__ */ makeEnhancer('configureRequest', ( nextConfig: HttpRequestConfig, -) => async (action: Action) => { - const prevRequestConfig = consumeRequestConfig(await action.useContext(), null); - - return action.use(context({ +) => async (action: Action) => using( + consumeRequestConfig(await action.useContext(), {} as HttpRequestConfig), + (prevRequestConfig) => action.use(context({ httpRequestConfig: { ...prevRequestConfig, ...nextConfig, - headers: { ...prevRequestConfig?.headers, ...nextConfig?.headers }, + headers: { ...prevRequestConfig.headers, ...nextConfig?.headers }, params: typeof nextConfig.params === 'string' ? nextConfig.params : { - ...(typeof prevRequestConfig?.params === 'string' ? {} : prevRequestConfig?.params), + ...(typeof prevRequestConfig.params === 'string' ? {} : prevRequestConfig.params), ...nextConfig.params, }, - requestTransformers: [ - ...(prevRequestConfig?.requestTransformers ?? []), - ...(nextConfig?.requestTransformers ?? []), - ], - responseTransformers: [ - ...(prevRequestConfig?.responseTransformers ?? []), - ...(nextConfig?.responseTransformers ?? []), - ], - errorTransformers: [ - ...(prevRequestConfig?.errorTransformers ?? []), - ...(nextConfig?.errorTransformers ?? []), - ], } as HttpRequestConfig, - })); -}; - -export default /* @__PURE__ */ appendExtension( - 'configureRequest', - configureRequest, - 'use', -) as WithParsedExtension( - this: Action, - nextConfig: HttpRequestConfig, - ): Action; -}>; + })), +)); diff --git a/packages/http/src/actions/context/enhancers/makeDelete.ts b/packages/http/src/actions/context/enhancers/makeDelete.ts index c65b1357..edfab8f2 100644 --- a/packages/http/src/actions/context/enhancers/makeDelete.ts +++ b/packages/http/src/actions/context/enhancers/makeDelete.ts @@ -1,17 +1,25 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import makeRequest from '@foscia/http/actions/context/enhancers/makeRequest'; import { HttpRequestConfig } from '@foscia/http/types'; /** - * HTTP DELETE method shortcut for the {@link makeRequest} function. + * HTTP DELETE method shortcut for the {@link makeRequest | `makeRequest`} function. * * @param pathOrBaseURL * @param body * @param config * * @category Enhancers + * + * @example + * ```typescript + * import { none } from '@foscia/core'; + * import { makeDelete } from '@foscia/http'; + * + * const response = await action().run(makeDelete('posts/1'), none()); + * ``` */ -const makeDelete = ( +export default /* @__PURE__ */ makeEnhancer('makeDelete', ( pathOrBaseURL: string, body?: HttpRequestConfig['body'], config?: Omit, @@ -19,17 +27,4 @@ const makeDelete = ( method: 'DELETE', body, ...config, -}); - -export default /* @__PURE__ */ appendExtension( - 'makeDelete', - makeDelete, - 'use', -) as WithParsedExtension( - this: Action, - pathOrBaseURL: string, - body?: HttpRequestConfig['body'], - config?: Omit, - ): Action; -}>; +})); diff --git a/packages/http/src/actions/context/enhancers/makeGet.ts b/packages/http/src/actions/context/enhancers/makeGet.ts index 5a45d435..c156ac5c 100644 --- a/packages/http/src/actions/context/enhancers/makeGet.ts +++ b/packages/http/src/actions/context/enhancers/makeGet.ts @@ -1,31 +1,30 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import makeRequest from '@foscia/http/actions/context/enhancers/makeRequest'; import { HttpRequestConfig } from '@foscia/http/types'; /** - * HTTP GET method shortcut for the {@link makeRequest} function. + * HTTP GET method shortcut for the {@link makeRequest | `makeRequest`} function. * * @param pathOrBaseURL * @param config * * @category Enhancers + * + * @example + * ```typescript + * import { raw } from '@foscia/core'; + * import { makeGet } from '@foscia/http'; + * + * const response = await action().run( + * makeGet('posts', { params: { search: 'foo' } }), + * raw(), + * ); + * ``` */ -const makeGet = ( +export default /* @__PURE__ */ makeEnhancer('makeGet', ( pathOrBaseURL: string, config?: Omit, ) => makeRequest(pathOrBaseURL, { method: 'GET', ...config, -}); - -export default /* @__PURE__ */ appendExtension( - 'makeGet', - makeGet, - 'use', -) as WithParsedExtension( - this: Action, - pathOrBaseURL: string, - config?: Omit, - ): Action; -}>; +})); diff --git a/packages/http/src/actions/context/enhancers/makePatch.ts b/packages/http/src/actions/context/enhancers/makePatch.ts index e0193814..ec920ad3 100644 --- a/packages/http/src/actions/context/enhancers/makePatch.ts +++ b/packages/http/src/actions/context/enhancers/makePatch.ts @@ -1,17 +1,28 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import makeRequest from '@foscia/http/actions/context/enhancers/makeRequest'; import { HttpRequestConfig } from '@foscia/http/types'; /** - * HTTP PATCH method shortcut for the {@link makeRequest} function. + * HTTP PATCH method shortcut for the {@link makeRequest | `makeRequest`} function. * * @param pathOrBaseURL * @param body * @param config * * @category Enhancers + * + * @example + * ```typescript + * import { raw } from '@foscia/core'; + * import { makePatch } from '@foscia/http'; + * + * const response = await action().run( + * makePatch('posts/1', { title: 'Hello World V2' }), + * raw(), + * ); + * ``` */ -const makePatch = ( +export default /* @__PURE__ */ makeEnhancer('makePatch', ( pathOrBaseURL: string, body?: HttpRequestConfig['body'], config?: Omit, @@ -19,17 +30,4 @@ const makePatch = ( method: 'PATCH', body, ...config, -}); - -export default /* @__PURE__ */ appendExtension( - 'makePatch', - makePatch, - 'use', -) as WithParsedExtension( - this: Action, - pathOrBaseURL: string, - body?: HttpRequestConfig['body'], - config?: Omit, - ): Action; -}>; +})); diff --git a/packages/http/src/actions/context/enhancers/makePost.ts b/packages/http/src/actions/context/enhancers/makePost.ts index 12e91e3b..3a2c758b 100644 --- a/packages/http/src/actions/context/enhancers/makePost.ts +++ b/packages/http/src/actions/context/enhancers/makePost.ts @@ -1,17 +1,28 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import makeRequest from '@foscia/http/actions/context/enhancers/makeRequest'; import { HttpRequestConfig } from '@foscia/http/types'; /** - * HTTP POST method shortcut for the {@link makeRequest} function. + * HTTP POST method shortcut for the {@link makeRequest | `makeRequest`} function. * * @param pathOrBaseURL * @param body * @param config * * @category Enhancers + * + * @example + * ```typescript + * import { raw } from '@foscia/core'; + * import { makePost } from '@foscia/http'; + * + * const response = await action().run( + * makePost('posts', { title: 'Hello World' }), + * raw(), + * ); + * ``` */ -const makePost = ( +export default /* @__PURE__ */ makeEnhancer('makePost', ( pathOrBaseURL: string, body?: HttpRequestConfig['body'], config?: Omit, @@ -19,17 +30,4 @@ const makePost = ( method: 'POST', body, ...config, -}); - -export default /* @__PURE__ */ appendExtension( - 'makePost', - makePost, - 'use', -) as WithParsedExtension( - this: Action, - pathOrBaseURL: string, - body?: HttpRequestConfig['body'], - config?: Omit, - ): Action; -}>; +})); diff --git a/packages/http/src/actions/context/enhancers/makePut.ts b/packages/http/src/actions/context/enhancers/makePut.ts index f4e7fa42..159f0792 100644 --- a/packages/http/src/actions/context/enhancers/makePut.ts +++ b/packages/http/src/actions/context/enhancers/makePut.ts @@ -1,17 +1,28 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import makeRequest from '@foscia/http/actions/context/enhancers/makeRequest'; import { HttpRequestConfig } from '@foscia/http/types'; /** - * HTTP PUT method shortcut for the {@link makeRequest} function. + * HTTP PUT method shortcut for the {@link makeRequest | `makeRequest`} function. * * @param pathOrBaseURL * @param body * @param config * * @category Enhancers + * + * @example + * ```typescript + * import { raw } from '@foscia/core'; + * import { makePut } from '@foscia/http'; + * + * const response = await action().run( + * makePut('posts/1', { title: 'Hello World V2' }), + * raw(), + * ); + * ``` */ -const makePut = ( +export default /* @__PURE__ */ makeEnhancer('makePut', ( pathOrBaseURL: string, body?: HttpRequestConfig['body'], config?: Omit, @@ -19,17 +30,4 @@ const makePut = ( method: 'PUT', body, ...config, -}); - -export default /* @__PURE__ */ appendExtension( - 'makePut', - makePut, - 'use', -) as WithParsedExtension( - this: Action, - pathOrBaseURL: string, - body?: HttpRequestConfig['body'], - config?: Omit, - ): Action; -}>; +})); diff --git a/packages/http/src/actions/context/enhancers/makeRequest.ts b/packages/http/src/actions/context/enhancers/makeRequest.ts index 29f9fa5d..50b2403a 100644 --- a/packages/http/src/actions/context/enhancers/makeRequest.ts +++ b/packages/http/src/actions/context/enhancers/makeRequest.ts @@ -1,4 +1,4 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import configureRequest from '@foscia/http/actions/context/enhancers/configureRequest'; import { HttpRequestConfig } from '@foscia/http/types'; @@ -10,27 +10,28 @@ const decomposeURL = (pathOrBaseURL: string) => ( /** * Prepare a generic HTTP request. - * If given path starts with scheme (HTTPS, etc.), it will be used as the base - * URL of action, otherwise it will only be used as path. + * + * If given path starts with scheme (`https:`, etc.) or a slash `/`, + * it will be used as the base URL of action, otherwise it will only be used + * as path after the configured base URL. * * @param pathOrBaseURL * @param config * * @category Enhancers + * + * @example + * ```typescript + * import { raw } from '@foscia/core'; + * import { makeRequest } from '@foscia/http'; + * + * const response = await action().run( + * makeRequest('posts', { params: { search: 'foo' } }), + * raw(), + * ); + * ``` */ -const makeRequest = ( +export default /* @__PURE__ */ makeEnhancer('makeRequest', ( pathOrBaseURL: string, config?: HttpRequestConfig, -) => configureRequest({ ...decomposeURL(pathOrBaseURL), ...config }); - -export default /* @__PURE__ */ appendExtension( - 'makeRequest', - makeRequest, - 'use', -) as WithParsedExtension( - this: Action, - pathOrBaseURL: string, - config?: HttpRequestConfig, - ): Action; -}>; +) => configureRequest({ ...decomposeURL(pathOrBaseURL), ...config })); diff --git a/packages/http/src/actions/context/enhancers/param.ts b/packages/http/src/actions/context/enhancers/param.ts index 292ce11b..a7b4efdf 100644 --- a/packages/http/src/actions/context/enhancers/param.ts +++ b/packages/http/src/actions/context/enhancers/param.ts @@ -1,4 +1,4 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { Action, makeEnhancer } from '@foscia/core'; import consumeRequestObjectParams from '@foscia/http/actions/context/consumers/consumeRequestObjectParams'; import configureRequest from '@foscia/http/actions/context/enhancers/configureRequest'; @@ -12,8 +12,25 @@ import { Dictionary } from '@foscia/shared'; * @param value * * @category Enhancers + * + * @example + * ```typescript + * import { raw } from '@foscia/core'; + * import { makeGet, params } from '@foscia/http'; + * + * const response = await action().run( + * makeGet('posts'), + * params({ search: 'foo' }), + * params('sort', 'title'), + * raw(), + * ); + * ``` + * + * @remarks + * `params` provides an object params configuration and cannot be used in + * combination with a query string (such as `search=foo&sort=title`). */ -const param = ( +export default /* @__PURE__ */ makeEnhancer('param', ( key: string | Dictionary, value?: unknown, ) => async (action: Action) => action.use( @@ -23,16 +40,4 @@ const param = ( ...(typeof key === 'string' ? { [key]: value } : key), }, }), -); - -export default /* @__PURE__ */ appendExtension( - 'param', - param, - 'use', -) as WithParsedExtension( - this: Action, - key: string | Dictionary, - value?: unknown, - ): Action; -}>; +)); diff --git a/packages/http/src/blueprints/makeHttpAdapter.ts b/packages/http/src/blueprints/makeHttpAdapter.ts deleted file mode 100644 index bda839df..00000000 --- a/packages/http/src/blueprints/makeHttpAdapter.ts +++ /dev/null @@ -1,11 +0,0 @@ -import makeHttpAdapterWith from '@foscia/http/makeHttpAdapterWith'; -import { HttpAdapterConfig } from '@foscia/http/types'; -import paramsSerializer from '@foscia/http/utilities/paramsSerializer'; - -export default (config: Partial> = {}) => ({ - adapter: makeHttpAdapterWith({ - baseURL: '/', - serializeParams: paramsSerializer, - ...config, - }), -}); diff --git a/packages/http/src/errors/httpAbortedError.ts b/packages/http/src/errors/httpAbortedError.ts index 68e1d386..3c55f5c8 100644 --- a/packages/http/src/errors/httpAbortedError.ts +++ b/packages/http/src/errors/httpAbortedError.ts @@ -1,4 +1,10 @@ import HttpInterruptedError from '@foscia/http/errors/httpInterruptedError'; -export default class HttpAbortedError extends HttpInterruptedError { +/** + * Error thrown when HTTP adapter catch a {@link !fetch | `fetch`} + * {@link !DOMException | `DOMException`} with name `AbortError`. + * + * @group Errors + */ +export default class HttpAbortedError extends HttpInterruptedError { } diff --git a/packages/http/src/errors/httpAdapterError.ts b/packages/http/src/errors/httpAdapterError.ts index 985cbd5d..ede2f6f0 100644 --- a/packages/http/src/errors/httpAdapterError.ts +++ b/packages/http/src/errors/httpAdapterError.ts @@ -1,5 +1,10 @@ import { FosciaError } from '@foscia/core'; +/** + * Base error class thrown by the adapter. + * + * @group Errors + */ export default class HttpAdapterError extends FosciaError { public request: Request; diff --git a/packages/http/src/errors/httpConflictError.ts b/packages/http/src/errors/httpConflictError.ts index 087e8d36..2551417d 100644 --- a/packages/http/src/errors/httpConflictError.ts +++ b/packages/http/src/errors/httpConflictError.ts @@ -1,4 +1,9 @@ import HttpInvalidRequestError from '@foscia/http/errors/httpInvalidRequestError'; +/** + * Error thrown on HTTP response status `409 Conflict`. + * + * @group Errors + */ export default class HttpConflictError extends HttpInvalidRequestError { } diff --git a/packages/http/src/errors/httpForbiddenError.ts b/packages/http/src/errors/httpForbiddenError.ts index 4e6a6c1d..db7ea91b 100644 --- a/packages/http/src/errors/httpForbiddenError.ts +++ b/packages/http/src/errors/httpForbiddenError.ts @@ -1,4 +1,9 @@ import HttpInvalidRequestError from '@foscia/http/errors/httpInvalidRequestError'; +/** + * Error thrown on HTTP response status `403 Forbidden`. + * + * @group Errors + */ export default class HttpForbiddenError extends HttpInvalidRequestError { } diff --git a/packages/http/src/errors/httpInterruptedError.ts b/packages/http/src/errors/httpInterruptedError.ts index 48eb79dd..b51ed30d 100644 --- a/packages/http/src/errors/httpInterruptedError.ts +++ b/packages/http/src/errors/httpInterruptedError.ts @@ -1,9 +1,14 @@ import HttpAdapterError from '@foscia/http/errors/httpAdapterError'; -export default class HttpInterruptedError extends HttpAdapterError { - public source: unknown; +/** + * Error thrown when HTTP adapter catch a {@link !fetch | `fetch`} error. + * + * @group Errors + */ +export default class HttpInterruptedError extends HttpAdapterError { + public readonly source: Source; - public constructor(message: string, request: Request, source: unknown) { + public constructor(message: string, request: Request, source: Source) { super(message, request); this.source = source; diff --git a/packages/http/src/errors/httpInvalidRequestError.ts b/packages/http/src/errors/httpInvalidRequestError.ts index 0fde9783..e1779947 100644 --- a/packages/http/src/errors/httpInvalidRequestError.ts +++ b/packages/http/src/errors/httpInvalidRequestError.ts @@ -1,4 +1,9 @@ import HttpResponseError from '@foscia/http/errors/httpResponseError'; +/** + * Error thrown on any HTTP response status `4xx`. + * + * @group Errors + */ export default class HttpInvalidRequestError extends HttpResponseError { } diff --git a/packages/http/src/errors/httpNotFoundError.ts b/packages/http/src/errors/httpNotFoundError.ts index fc913073..0540c69b 100644 --- a/packages/http/src/errors/httpNotFoundError.ts +++ b/packages/http/src/errors/httpNotFoundError.ts @@ -1,6 +1,14 @@ -import { NotFoundErrorI } from '@foscia/core'; +import { FLAG_ERROR_NOT_FOUND } from '@foscia/core'; import HttpInvalidRequestError from '@foscia/http/errors/httpInvalidRequestError'; +import { FosciaFlaggedObject } from '@foscia/shared'; -export default class HttpNotFoundError extends HttpInvalidRequestError implements NotFoundErrorI { - public readonly $FOSCIA_ERROR_NOT_FOUND = true; +/** + * Error thrown on HTTP response status `404 Not Found`. + * + * @group Errors + */ +export default class HttpNotFoundError + extends HttpInvalidRequestError + implements FosciaFlaggedObject { + public readonly $FOSCIA_FLAGS = FLAG_ERROR_NOT_FOUND; } diff --git a/packages/http/src/errors/httpResponseError.ts b/packages/http/src/errors/httpResponseError.ts index 19221a37..b8fe756f 100644 --- a/packages/http/src/errors/httpResponseError.ts +++ b/packages/http/src/errors/httpResponseError.ts @@ -1,5 +1,10 @@ import HttpAdapterError from '@foscia/http/errors/httpAdapterError'; +/** + * Error thrown on any HTTP response status `4xx` or `5xx`. + * + * @group Errors + */ export default abstract class HttpResponseError extends HttpAdapterError { public response: Response; diff --git a/packages/http/src/errors/httpServerError.ts b/packages/http/src/errors/httpServerError.ts index ef4286d4..0325015d 100644 --- a/packages/http/src/errors/httpServerError.ts +++ b/packages/http/src/errors/httpServerError.ts @@ -1,4 +1,9 @@ import HttpResponseError from '@foscia/http/errors/httpResponseError'; +/** + * Error thrown on any HTTP response status `5xx`. + * + * @group Errors + */ export default class HttpServerError extends HttpResponseError { } diff --git a/packages/http/src/errors/httpTooManyRequestsError.ts b/packages/http/src/errors/httpTooManyRequestsError.ts index b3172217..9a660a47 100644 --- a/packages/http/src/errors/httpTooManyRequestsError.ts +++ b/packages/http/src/errors/httpTooManyRequestsError.ts @@ -1,4 +1,9 @@ import HttpInvalidRequestError from '@foscia/http/errors/httpInvalidRequestError'; +/** + * Error thrown on HTTP response status `429 Too Many Requests`. + * + * @group Errors + */ export default class HttpTooManyRequestsError extends HttpInvalidRequestError { } diff --git a/packages/http/src/errors/httpUnauthorizedError.ts b/packages/http/src/errors/httpUnauthorizedError.ts index dfd63442..859899c0 100644 --- a/packages/http/src/errors/httpUnauthorizedError.ts +++ b/packages/http/src/errors/httpUnauthorizedError.ts @@ -1,4 +1,9 @@ import HttpInvalidRequestError from '@foscia/http/errors/httpInvalidRequestError'; +/** + * Error thrown on HTTP response status `401 Unauthorized`. + * + * @group Errors + */ export default class HttpUnauthorizedError extends HttpInvalidRequestError { } diff --git a/packages/http/src/httpExtensions.ts b/packages/http/src/httpExtensions.ts deleted file mode 100644 index 8980acf9..00000000 --- a/packages/http/src/httpExtensions.ts +++ /dev/null @@ -1,19 +0,0 @@ -import abortSignal from '@foscia/http/actions/context/enhancers/abortSignal'; -import makeDelete from '@foscia/http/actions/context/enhancers/makeDelete'; -import makeGet from '@foscia/http/actions/context/enhancers/makeGet'; -import makePatch from '@foscia/http/actions/context/enhancers/makePatch'; -import makePost from '@foscia/http/actions/context/enhancers/makePost'; -import makePut from '@foscia/http/actions/context/enhancers/makePut'; -import makeRequest from '@foscia/http/actions/context/enhancers/makeRequest'; -import param from '@foscia/http/actions/context/enhancers/param'; - -export default () => ({ - ...makeGet.extension(), - ...makePost.extension(), - ...makePut.extension(), - ...makePatch.extension(), - ...makeDelete.extension(), - ...makeRequest.extension(), - ...param.extension(), - ...abortSignal.extension(), -}); diff --git a/packages/http/src/index.ts b/packages/http/src/index.ts index 86fdc4a7..1b75da5e 100644 --- a/packages/http/src/index.ts +++ b/packages/http/src/index.ts @@ -10,7 +10,6 @@ import makePost from '@foscia/http/actions/context/enhancers/makePost'; import makePut from '@foscia/http/actions/context/enhancers/makePut'; import makeRequest from '@foscia/http/actions/context/enhancers/makeRequest'; import param from '@foscia/http/actions/context/enhancers/param'; -import makeHttpAdapter from '@foscia/http/blueprints/makeHttpAdapter'; import HttpAbortedError from '@foscia/http/errors/httpAbortedError'; import HttpAdapterError from '@foscia/http/errors/httpAdapterError'; import HttpConflictError from '@foscia/http/errors/httpConflictError'; @@ -22,20 +21,16 @@ import HttpResponseError from '@foscia/http/errors/httpResponseError'; import HttpServerError from '@foscia/http/errors/httpServerError'; import HttpTooManyRequestsError from '@foscia/http/errors/httpTooManyRequestsError'; import HttpUnauthorizedError from '@foscia/http/errors/httpUnauthorizedError'; -import httpExtensions from '@foscia/http/httpExtensions'; +import makeHttpAdapter from '@foscia/http/makeHttpAdapter'; import makeHttpAdapterResponse from '@foscia/http/makeHttpAdapterResponse'; -import makeHttpAdapterWith from '@foscia/http/makeHttpAdapterWith'; import clearEndpoint from '@foscia/http/utilities/clearEndpoint'; import deepParamsSerializer from '@foscia/http/utilities/deepParamsSerializer'; -import paramsSerializer from '@foscia/http/utilities/paramsSerializer'; export * from '@foscia/http/types'; export { makeHttpAdapter, - makeHttpAdapterWith, makeHttpAdapterResponse, - paramsSerializer, deepParamsSerializer, clearEndpoint, HttpAbortedError, @@ -60,5 +55,4 @@ export { configureRequest, consumeRequestConfig, consumeRequestObjectParams, - httpExtensions, }; diff --git a/packages/http/src/makeHttpAdapterWith.ts b/packages/http/src/makeHttpAdapter.ts similarity index 69% rename from packages/http/src/makeHttpAdapterWith.ts rename to packages/http/src/makeHttpAdapter.ts index 84552e89..d7103d1f 100644 --- a/packages/http/src/makeHttpAdapterWith.ts +++ b/packages/http/src/makeHttpAdapter.ts @@ -25,33 +25,16 @@ import { HttpRequestInitPickKey, } from '@foscia/http/types'; import clearEndpoint from '@foscia/http/utilities/clearEndpoint'; -import { Dictionary, isNil, optionalJoin, sequentialTransform } from '@foscia/shared'; - +import { Dictionary, isNil, optionalJoin, throughMiddlewares } from '@foscia/shared'; + +/** + * Make a {@link HttpAdapter | `HttpAdapter`}. + * + * @param config + * + * @category Factories + */ export default (config: HttpAdapterConfig) => { - const transformRequest = ( - contextConfig: HttpRequestConfig, - request: Request, - ) => sequentialTransform([ - ...(config.requestTransformers ?? []), - ...(contextConfig.requestTransformers ?? []), - ], request); - - const transformResponse = ( - contextConfig: HttpRequestConfig, - response: Response, - ) => sequentialTransform([ - ...(config.responseTransformers ?? []), - ...(contextConfig.responseTransformers ?? []), - ], response); - - const transformError = ( - contextConfig: HttpRequestConfig, - error: unknown, - ) => sequentialTransform([ - ...(config.errorTransformers ?? []), - ...(contextConfig.errorTransformers ?? []), - ], error); - const makeRequestError = (request: Request, error: unknown) => ( error instanceof DOMException && error.name === 'AbortError' ? new HttpAbortedError(error.message, request, error) @@ -63,12 +46,14 @@ export default (config: HttpAdapterConfig) => { ); const makeResponseError = (request: Request, response: Response) => { - if (response.status >= 500) return new HttpServerError(request, response); - if (response.status === 401) return new HttpUnauthorizedError(request, response); - if (response.status === 403) return new HttpForbiddenError(request, response); - if (response.status === 404) return new HttpNotFoundError(request, response); - if (response.status === 409) return new HttpConflictError(request, response); - if (response.status === 429) return new HttpTooManyRequestsError(request, response); + const { status } = response; + + if (status >= 500) return new HttpServerError(request, response); + if (status === 401) return new HttpUnauthorizedError(request, response); + if (status === 403) return new HttpForbiddenError(request, response); + if (status === 404) return new HttpNotFoundError(request, response); + if (status === 409) return new HttpConflictError(request, response); + if (status === 429) return new HttpTooManyRequestsError(request, response); return new HttpInvalidRequestError(request, response); }; @@ -94,15 +79,15 @@ export default (config: HttpAdapterConfig) => { baseURL: contextConfig.baseURL ?? model?.$config.baseURL ?? config.baseURL ?? '/', additionalPath: contextConfig.path, ...(contextConfig.modelPaths !== false ? { - modelPath: isNil(model) ? undefined : modelPathTransformer( + modelPath: model ? modelPathTransformer( model.$config.path ?? (model.$config.guessPath ?? ((t) => t))(model.$type), - ), + ) : undefined, idPath: isNil(id) ? undefined : idPathTransformer( String((model?.$config.guessIdPath ?? ((t) => t))(id)), ), - relationPath: isNil(relation) ? undefined : relationPathTransformer( + relationPath: relation ? relationPathTransformer( relation.path ?? (model?.$config.guessRelationPath ?? ((r) => r.key))(relation), - ), + ) : undefined, } : {}), }, context); }; @@ -119,11 +104,10 @@ export default (config: HttpAdapterConfig) => { [ActionName.UPDATE_RELATION]: 'PATCH', [ActionName.DETACH_RELATION]: 'DELETE', }; - if (action && actionsMethodsMap[action]) { - return actionsMethodsMap[action] as HttpMethod; - } - return 'GET'; + return action && actionsMethodsMap[action] + ? actionsMethodsMap[action] as HttpMethod + : 'GET'; })() ).toUpperCase(); @@ -148,7 +132,7 @@ export default (config: HttpAdapterConfig) => { const appendHeaders = config.appendHeaders ?? (() => ({})); - const init = { + const init: Pick = { cache: contextConfig.cache, credentials: contextConfig.credentials, integrity: contextConfig.integrity, @@ -159,7 +143,7 @@ export default (config: HttpAdapterConfig) => { referrerPolicy: contextConfig.referrerPolicy, signal: contextConfig.signal, window: contextConfig.window, - } as { [K in HttpRequestInitPickKey]: RequestInit[K]; }; + }; return { body, @@ -169,11 +153,17 @@ export default (config: HttpAdapterConfig) => { } as RequestInit; }; + const makeRequestQueryFromObject = (params: Dictionary) => ( + Object.keys(params).length > 0 ? ( + config.serializeParams ?? ((p: Dictionary) => new URLSearchParams(p).toString()) + )(params) : undefined + ); + const makeRequestQuery = async (context: {}, contextConfig: HttpRequestConfig) => optionalJoin([ typeof contextConfig.params === 'object' - ? config.serializeParams(contextConfig.params) + ? makeRequestQueryFromObject(contextConfig.params) : contextConfig.params, - config.serializeParams(await (config.appendParams ?? (() => ({})))(context)), + makeRequestQueryFromObject(await (config.appendParams ?? (() => ({})))(context)), ], '&'); const makeRequest = async (context: {}, contextConfig: HttpRequestConfig) => ( @@ -187,6 +177,8 @@ export default (config: HttpAdapterConfig) => { ); const runRequest = (request: Request) => { + // We must extract fetch from config like this before using to avoid errors. + // Do not modify this function body. const { fetch } = config; return (fetch ?? globalThis.fetch)(request); @@ -194,26 +186,30 @@ export default (config: HttpAdapterConfig) => { const execute = async (context: {}) => { const contextConfig = consumeRequestConfig(context, null) ?? {}; - const request = await transformRequest( - contextConfig, - await makeRequest(context, contextConfig), - ); - - let response: Response; - try { - response = await runRequest(request); - } catch (error) { - throw await transformError(contextConfig, makeRequestError(request, error)); - } - - if (response.status >= 200 && response.status < 300) { - return makeHttpAdapterResponse(await transformResponse(context, response), { - reader: contextConfig.responseReader ?? config.defaultResponseReader ?? ((r) => r.json()), - }); - } - - throw await transformError(contextConfig, makeResponseError(request, response)); + const middlewares = [...config.middlewares ?? []]; + + return makeHttpAdapterResponse(await throughMiddlewares( + typeof contextConfig.middlewares === 'function' + ? await contextConfig.middlewares(middlewares) + : [...middlewares, ...(contextConfig.middlewares ?? [])], + async (request) => { + let response: Response; + try { + response = await runRequest(request); + } catch (error) { + throw makeRequestError(request, error); + } + + if (response.status >= 200 && response.status < 300) { + return response; + } + + throw makeResponseError(request, response); + }, + )(await makeRequest(context, contextConfig)), { + reader: contextConfig.responseReader ?? config.defaultResponseReader ?? ((r) => r.json()), + }); }; - return { execute } as HttpAdapter; + return { adapter: { execute } as HttpAdapter }; }; diff --git a/packages/http/src/makeHttpAdapterResponse.ts b/packages/http/src/makeHttpAdapterResponse.ts index a065201f..26e7d64d 100644 --- a/packages/http/src/makeHttpAdapterResponse.ts +++ b/packages/http/src/makeHttpAdapterResponse.ts @@ -1,12 +1,18 @@ -import { AdapterResponseI } from '@foscia/core'; +import { AdapterResponse } from '@foscia/core'; import { HttpResponseReader } from '@foscia/http/types'; +/** + * Make an HTTP adapter response from the given response and reader. + * + * @param response + * @param config + * + * @internal + */ export default ( response: Response, config: { reader: HttpResponseReader }, -): AdapterResponseI => ({ +): AdapterResponse => ({ read: async () => (response.status === 204 ? undefined : config.reader(response)), - get raw() { - return response; - }, + raw: response, }); diff --git a/packages/http/src/types.ts b/packages/http/src/types.ts index eea4fd19..c45d4493 100644 --- a/packages/http/src/types.ts +++ b/packages/http/src/types.ts @@ -1,8 +1,10 @@ -import { AdapterI } from '@foscia/core'; -import { Awaitable, Dictionary, Transformer } from '@foscia/shared'; +import { Adapter } from '@foscia/core'; +import { Awaitable, Dictionary, Middleware, Transformer } from '@foscia/shared'; /** * The HTTP method to use in request. + * + * @internal */ export type HttpMethod = | 'get' | 'GET' @@ -18,6 +20,8 @@ export type HttpMethod = /** * Keys which are simply inherited from request init. + * + * @internal */ export type HttpRequestInitPickKey = | 'cache' @@ -33,27 +37,69 @@ export type HttpRequestInitPickKey = /** * Context value representing a HTTP request config. + * + * @interface + * + * @internal */ export type HttpRequestConfig = & { + /** + * Standard {@link !Request | `Request`} object. + * If defined, it will ignore any other request config options. + */ request?: Request; + /** + * HTTP method. + */ method?: HttpMethod; + /** + * Base URL for request. + */ baseURL?: string; + /** + * Endpoint path to append after base URL and model paths. + */ path?: string; + /** + * Request query params. + */ params?: Dictionary | string; + /** + * Request headers (will override default adapter headers). + */ headers?: Dictionary; + /** + * Request body. + */ body?: unknown; + /** + * Override default body transformer. + */ bodyAs?: BodyAsTransformer; + /** + * Disable computing endpoint using model context values (model, ID, etc.). + */ modelPaths?: boolean; + /** + * Override default adapter response reader. + */ responseReader?: HttpResponseReader; - requestTransformers?: RequestTransformer[]; - responseTransformers?: ResponseTransformer[]; - errorTransformers?: ErrorTransformer[]; + /** + * Middlewares to affect requests, responses, and errors. + * If a callback is given, it makes possible to replace globally configured + * middlewares. + */ + middlewares?: HttpAdapterMiddleware[] | (( + prev: HttpAdapterMiddleware[], + ) => Awaitable); } & Pick; /** * Context containing a HTTP request config. + * + * @internal */ export type ConsumeHttpRequestConfig = { httpRequestConfig: HttpRequestConfig; @@ -61,47 +107,143 @@ export type ConsumeHttpRequestConfig = { /** * Prepared context for URL building. + * + * @internal */ export type HttpURLContext = { + /** + * Request base URL. + */ baseURL: string; + /** + * Transformed model path. + */ modelPath?: string; + /** + * Transformed ID path. + */ idPath?: string; + /** + * Transformed relation path. + */ relationPath?: string; + /** + * Request path. + */ additionalPath?: string; }; /** * Read the response's content to a deserializable data. + * + * @internal */ export type HttpResponseReader = (response: Response) => Promise; +/** + * Dedicated middleware for the HTTP adapter implementation. + * + * @internal + */ +export type HttpAdapterMiddleware = Middleware>; + /** * The configuration for the HTTP adapter implementation. + * + * @interface + * + * @internal */ export type HttpAdapterConfig = { + /** + * {@link !fetch | `fetch`} API implementation to use. + * Defaults to `globalThis.fetch`. + */ fetch?: typeof fetch; + /** + * Base URL. Defaults to `/`. + */ baseURL?: string | null; + /** + * Build the URL using the given contexts. + * Defaults to `` joined with `/`. + * + * @param urlContext + * @param context + */ buildURL?: (urlContext: HttpURLContext, context: {}) => string; - serializeParams: HttpParamsSerializer; + /** + * Serialize a query params object to a string. + */ + serializeParams?: HttpParamsSerializer; + /** + * Initial headers for every request. + */ defaultHeaders?: Dictionary; + /** + * Body transformer. + * Defaults to {@link !JSON.stringify | `JSON.stringify()`} if body is not + * a {@link !FormData | `FormData`}, {@link !URLSearchParams | `URLSearchParams`} or `undefined`. + * When null, body won't be transformed. + */ defaultBodyAs?: BodyAsTransformer | null; + /** + * Response reader. + * Defaults to {@link !Response#json | `response.json()`}. + */ defaultResponseReader?: HttpResponseReader; + /** + * Append query params to request based on context. + * Returned params are **not** merged with other query + * params, because request params might be a query string. + * + * @param context + */ appendParams?: (context: {}) => Awaitable>; + /** + * Append headers to request based on context. + * Returned headers are merged with other headers. + * + * @param context + */ appendHeaders?: (context: {}) => Awaitable>; + /** + * Transforms the model path. + */ modelPathTransformer?: Transformer; + /** + * Transforms the ID path. + */ idPathTransformer?: Transformer; + /** + * Transforms the relation path. + */ relationPathTransformer?: Transformer; - requestTransformers?: RequestTransformer[]; - responseTransformers?: ResponseTransformer[]; - errorTransformers?: ErrorTransformer[]; + /** + * Middlewares to affect requests, responses, and errors. + */ + middlewares?: HttpAdapterMiddleware[]; }; -export interface HttpAdapter extends AdapterI { -} +/** + * HTTP adapter. + * + * @interface + * + * @internal + */ +export type HttpAdapter = Adapter; +/** + * HTTP query params serializer. + * + * @internal + */ export type HttpParamsSerializer = (params: Dictionary) => string | undefined; -export type RequestTransformer = (request: Request) => Awaitable; -export type ResponseTransformer = (response: Response) => Awaitable; -export type ErrorTransformer = (error: unknown) => Awaitable; +/** + * Converts given body as a valid {@link !Request | `Request`} body. + * + * @internal + */ export type BodyAsTransformer = (body: unknown, headers: Dictionary) => Awaitable; diff --git a/packages/http/src/utilities/clearEndpoint.ts b/packages/http/src/utilities/clearEndpoint.ts index 2aaaafaf..c32c79ef 100644 --- a/packages/http/src/utilities/clearEndpoint.ts +++ b/packages/http/src/utilities/clearEndpoint.ts @@ -1 +1,12 @@ -export default (endpoint: string) => endpoint.replace(/([^:]\/)\/+/g, '$1'); +/** + * Clear given endpoint (remove double slashes, etc.). + * + * @param endpoint + * + * @category Utilities + */ +export default (endpoint: string) => endpoint + // Remove multiple slashes at start. + .replace(/^(\/)\/+/g, '$1') + // Remove multiple slashes inside. + .replace(/([^:]\/)\/+/g, '$1'); diff --git a/packages/http/src/utilities/deepParamsSerializer.ts b/packages/http/src/utilities/deepParamsSerializer.ts index c28e198e..42c1d209 100644 --- a/packages/http/src/utilities/deepParamsSerializer.ts +++ b/packages/http/src/utilities/deepParamsSerializer.ts @@ -1,8 +1,24 @@ -import { Dictionary } from '@foscia/shared'; - -export default (params: Dictionary) => { - const urlSearchParams = new URLSearchParams(); +import { Dictionary, tap } from '@foscia/shared'; +/** + * Deeply serialize given query params (including objects) + * using {@link URLSearchParams | `URLSearchParams`}. + * + * @param params + * + * @category Utilities + * + * @example + * ```typescript + * import { deepParamsSerializer } from '@foscia/http'; + * + * console.log(deepParamsSerializer({ + * search: 'foo', sort: 'title', filter: { category: 'news' }, + * }); + * // search=foo&sort=title&filter[category]=news + * ``` + */ +export default (params: Dictionary) => tap(new URLSearchParams(), (urlParams) => { const appendParam = (key: string, value: unknown) => { if (Array.isArray(value)) { value.forEach((subValue) => appendParam(`${key}[]`, subValue)); @@ -13,7 +29,7 @@ export default (params: Dictionary) => { } else { const finalValue = value; if (finalValue !== undefined) { - urlSearchParams.append(key, String(finalValue)); + urlParams.append(key, String(finalValue)); } } }; @@ -21,6 +37,4 @@ export default (params: Dictionary) => { Object.entries(params ?? {}).forEach(([key, value]) => { appendParam(key, value); }); - - return urlSearchParams.toString() || undefined; -}; +}).toString() || undefined; diff --git a/packages/http/src/utilities/paramsSerializer.ts b/packages/http/src/utilities/paramsSerializer.ts deleted file mode 100644 index de8289b3..00000000 --- a/packages/http/src/utilities/paramsSerializer.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Dictionary } from '@foscia/shared'; - -export default (params: Dictionary) => new URLSearchParams(params).toString() || undefined; diff --git a/packages/http/tests/unit/utilities/clearEndpoint.test.ts b/packages/http/tests/unit/utilities/clearEndpoint.test.ts new file mode 100644 index 00000000..bb9fb5bb --- /dev/null +++ b/packages/http/tests/unit/utilities/clearEndpoint.test.ts @@ -0,0 +1,23 @@ +import { clearEndpoint } from '@foscia/http'; +import { describe, expect, it } from 'vitest'; + +describe.concurrent('unit: clearEndpoint', () => { + it.each([ + ['', ''], + ['/api/posts', '/api/posts'], + ['/api/posts/1', '/api/posts/1'], + // Leaves ending slashes. + ['/api/posts/1/', '/api/posts/1/'], + // Leaves multiple ending slashes. + ['/api/posts/1//', '/api/posts/1/'], + // Removes multiple starting slashes. + ['///api/posts/1', '/api/posts/1'], + // Leaves multiple scheme slashes. + ['https://example.com/api/posts/1', 'https://example.com/api/posts/1'], + // Removes multiple inner slashes. + ['/api//posts///1', '/api/posts/1'], + ['/api//posts///1/', '/api/posts/1/'], + ])('should clear endpoint', async (endpoint, expected) => { + expect(clearEndpoint(endpoint)).toStrictEqual(expected); + }); +}); diff --git a/packages/http/tests/unit/utilities/deepParamsSerializer.test.ts b/packages/http/tests/unit/utilities/deepParamsSerializer.test.ts new file mode 100644 index 00000000..f61bd550 --- /dev/null +++ b/packages/http/tests/unit/utilities/deepParamsSerializer.test.ts @@ -0,0 +1,22 @@ +import { deepParamsSerializer } from '@foscia/http'; +import { describe, expect, it } from 'vitest'; + +describe.concurrent('unit: deepParamsSerializer', () => { + it.each([ + [{}, undefined], + [ + { search: 'foo' }, + 'search=foo', + ], + [ + { search: 'foo', sort: 'bar' }, + 'search=foo&sort=bar', + ], + [ + { search: 'foo', sort: 'bar', filter: { foo: 'foo', bar: 'bar' } }, + 'search=foo&sort=bar&filter%5Bfoo%5D=foo&filter%5Bbar%5D=bar', + ], + ])('should deeply serialize query params', async (params, expected) => { + expect(deepParamsSerializer(params)).toStrictEqual(expected); + }); +}); diff --git a/packages/jsonapi/src/actions/context/enhancers/fields.ts b/packages/jsonapi/src/actions/context/enhancers/fields.ts index 257b735c..d958598f 100644 --- a/packages/jsonapi/src/actions/context/enhancers/fields.ts +++ b/packages/jsonapi/src/actions/context/enhancers/fields.ts @@ -1,46 +1,45 @@ import { Action, - appendExtension, FosciaError, guessContextModel, - InferConsumedModelOrInstance, + InferQueryModelOrInstance, + makeEnhancer, ModelKey, - WithParsedExtension, } from '@foscia/core'; import fieldsFor from '@foscia/jsonapi/actions/context/enhancers/fieldsFor'; -import { ArrayableVariadic, isNil } from '@foscia/shared'; +import { ArrayableVariadic } from '@foscia/shared'; /** * [Select the given JSON:API fieldsets](https://jsonapi.org/format/#fetching-sparse-fieldsets) * for the current context's model. * The new fieldsets will be merged with the previous ones. + * This is a shortcut of {@link fieldsFor | `fieldsFor`} using the context model. * * @param fieldset * * @category Enhancers + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { fields } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * fields(['title', 'body']), + * all(), + * ); + * ``` */ -const fields = ( - ...fieldset: ArrayableVariadic>> +export default /* @__PURE__ */ makeEnhancer('fields', ( + ...fieldset: ArrayableVariadic>> ) => async (action: Action) => { - const context = await action.useContext(); - const model = await guessContextModel(context); - - if (isNil(model)) { - throw new FosciaError( - 'Could not detect context\'s model when applying fieldsets.', - ); + const model = await guessContextModel(await action.useContext()); + if (model) { + return action.use(fieldsFor(model as any, ...fieldset)); } - return action.use(fieldsFor(model as any, ...fieldset)); -}; - -export default /* @__PURE__ */ appendExtension( - 'fields', - fields, - 'use', -) as WithParsedExtension( - this: Action, - ...fieldset: ArrayableVariadic>> - ): Action; -}>; + throw new FosciaError( + 'Could not detect context\'s model when applying fieldsets.', + ); +}); diff --git a/packages/jsonapi/src/actions/context/enhancers/fieldsFor.ts b/packages/jsonapi/src/actions/context/enhancers/fieldsFor.ts index 546c7dc4..2d12ea2c 100644 --- a/packages/jsonapi/src/actions/context/enhancers/fieldsFor.ts +++ b/packages/jsonapi/src/actions/context/enhancers/fieldsFor.ts @@ -1,11 +1,4 @@ -import { - Action, - appendExtension, - Model, - ModelKey, - normalizeKey, - WithParsedExtension, -} from '@foscia/core'; +import { Action, makeEnhancer, Model, ModelKey, normalizeKey } from '@foscia/core'; import { consumeRequestObjectParams, param } from '@foscia/http'; import { ArrayableVariadic, optionalJoin, uniqueValues, wrapVariadic } from '@foscia/shared'; @@ -17,8 +10,21 @@ import { ArrayableVariadic, optionalJoin, uniqueValues, wrapVariadic } from '@fo * @param fieldset * * @category Enhancers + * + * @example + * ```typescript + * import { query, include, all } from '@foscia/core'; + * import { fieldsFor } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * include('comments'), + * fields(Comment, ['body', 'author']), + * all(), + * ); + * ``` */ -const fieldsFor = ( +export default /* @__PURE__ */ makeEnhancer('fieldsFor', ( model: M, ...fieldset: ArrayableVariadic> ) => async (action: Action) => { @@ -33,16 +39,4 @@ const fieldsFor = ( ...nextFields, ]), ','), })); -}; - -export default /* @__PURE__ */ appendExtension( - 'fieldsFor', - fieldsFor, - 'use', -) as WithParsedExtension( - this: Action, - model: M, - ...fieldset: ArrayableVariadic> - ): Action; -}>; +}); diff --git a/packages/jsonapi/src/actions/context/enhancers/filterBy.ts b/packages/jsonapi/src/actions/context/enhancers/filterBy.ts index 6aa39eb1..17546bab 100644 --- a/packages/jsonapi/src/actions/context/enhancers/filterBy.ts +++ b/packages/jsonapi/src/actions/context/enhancers/filterBy.ts @@ -1,4 +1,4 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { Action, makeEnhancer } from '@foscia/core'; import { consumeRequestObjectParams, param } from '@foscia/http'; import { Dictionary } from '@foscia/shared'; @@ -12,23 +12,24 @@ import { Dictionary } from '@foscia/shared'; * @param value * * @category Enhancers + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { filterBy } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * filterBy('tag', 'news'), + * filterBy({ published: 1 }), + * all(), + * ); + * ``` */ -const filterBy = ( +export default /* @__PURE__ */ makeEnhancer('filterBy', ( key: string | Dictionary, value?: unknown, -) => async (action: Action) => action.use(param('filter', { +) => async (action: Action) => action.use(param('filter', { ...consumeRequestObjectParams(await action.useContext())?.filter, ...(typeof key === 'string' ? { [key]: value } : key), -})); - -export default /* @__PURE__ */ appendExtension( - 'filterBy', - filterBy, - 'use', -) as WithParsedExtension( - this: Action, - key: string | Dictionary, - value?: unknown, - ): Action; -}>; +}))); diff --git a/packages/jsonapi/src/actions/context/enhancers/paginate.ts b/packages/jsonapi/src/actions/context/enhancers/paginate.ts index 41b56059..46d75d46 100644 --- a/packages/jsonapi/src/actions/context/enhancers/paginate.ts +++ b/packages/jsonapi/src/actions/context/enhancers/paginate.ts @@ -1,4 +1,4 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import { param } from '@foscia/http'; /** @@ -10,16 +10,17 @@ import { param } from '@foscia/http'; * @param page * * @category Enhancers + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { paginate } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * paginate({ number: 1, size: 15 }), + * all(), + * ); + * ``` */ -const paginate = (page: unknown) => param('page', page); - -export default /* @__PURE__ */ appendExtension( - 'paginate', - paginate, - 'use', -) as WithParsedExtension( - this: Action, - page: unknown, - ): Action; -}>; +export default /* @__PURE__ */ makeEnhancer('paginate', (page: unknown) => param('page', page)); diff --git a/packages/jsonapi/src/actions/context/enhancers/sortBy.ts b/packages/jsonapi/src/actions/context/enhancers/sortBy.ts index 167fd86f..29476c77 100644 --- a/packages/jsonapi/src/actions/context/enhancers/sortBy.ts +++ b/packages/jsonapi/src/actions/context/enhancers/sortBy.ts @@ -1,11 +1,13 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { Action, makeEnhancer } from '@foscia/core'; import { consumeRequestObjectParams, param } from '@foscia/http'; import { Arrayable, Dictionary, optionalJoin, uniqueValues, wrap } from '@foscia/shared'; /** * Sort direction to apply. + * + * @internal */ -type SortDirection = 'asc' | 'desc'; +export type SortDirection = 'asc' | 'desc'; /** * Resolve the keys and directions arrays from given sort parameters. @@ -33,29 +35,10 @@ const serializeSort = ( direction: SortDirection, ) => `${direction === 'desc' ? '-' : ''}${key}`; -/** - * [Sort the JSON:API resource](https://jsonapi.org/format/#fetching-sorting) - * by the given keys and directions. - * The new sort will be merged with the previous ones. - * Sorts priority are kept. - * - * @param keys - * @param directions - * - * @category Enhancers - */ -const sortBy: { - ( - keys: Dictionary, - ): (action: Action) => Promise; - ( - keys: Arrayable, - directions?: Arrayable, - ): (action: Action) => Promise; -} = ( +export default /* @__PURE__ */ makeEnhancer('sortBy', (( keys: Arrayable | Dictionary, directions: Arrayable = 'asc', -) => async (action: Action) => { +) => async (action: Action) => { const [newKeys, newDirections] = resolveKeysDirections(keys, directions); action.use(param( @@ -65,16 +48,58 @@ const sortBy: { ...newKeys.map((k, i) => serializeSort(k, newDirections[i] ?? newDirections[0])), ]), ','), )); -}; - -export default /* @__PURE__ */ appendExtension( - 'sortBy', - sortBy, - 'use', -) as WithParsedExtension( - this: Action, +}) as { + /** + * [Sort the JSON:API resource](https://jsonapi.org/format/#fetching-sorting) + * by the given keys and directions. + * The new sort will be merged with the previous ones. + * Sorts priority are kept. + * + * @param keys + * + * @category Enhancers + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { sortBy } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * sortBy({ publishedAt: 'desc', title: 'asc' }), + * all(), + * ); + * ``` + */ + ( + keys: Dictionary, + ): (action: Action) => Promise; + /** + * [Sort the JSON:API resource](https://jsonapi.org/format/#fetching-sorting) + * by the given keys and directions. + * The new sort will be merged with the previous ones. + * Sorts priority are kept. + * + * @param keys + * @param directions + * + * @category Enhancers + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { sortBy } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * sortBy('title'), + * sortBy(['publishedAt', 'title'], ['desc', 'asc']), + * all(), + * ); + * ``` + */ + ( keys: Arrayable, - direction?: 'asc' | 'desc', - ): Action; -}>; + directions?: Arrayable, + ): (action: Action) => Promise; +}); diff --git a/packages/jsonapi/src/actions/context/enhancers/sortByAsc.ts b/packages/jsonapi/src/actions/context/enhancers/sortByAsc.ts index 469c1127..849c4de9 100644 --- a/packages/jsonapi/src/actions/context/enhancers/sortByAsc.ts +++ b/packages/jsonapi/src/actions/context/enhancers/sortByAsc.ts @@ -1,23 +1,27 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import sortBy from '@foscia/jsonapi/actions/context/enhancers/sortBy'; import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; /** - * Shortcut for the {@link sortBy} function with an asc direction. + * Shortcut for the {@link sortBy | `sortBy`} function with an ascending direction. * * @param keys * * @category Enhancers + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { sortByAsc } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * sortByAsc('title'), + * sortByAsc(['publishedAt', 'title']), + * all(), + * ); + * ``` */ -const sortByAsc = (...keys: ArrayableVariadic) => sortBy(wrapVariadic(...keys), 'asc'); - -export default /* @__PURE__ */ appendExtension( - 'sortByAsc', - sortByAsc, - 'use', -) as WithParsedExtension( - this: Action, - ...keys: ArrayableVariadic - ): Action; -}>; +export default /* @__PURE__ */ makeEnhancer('sortByAsc', ( + ...keys: ArrayableVariadic +) => sortBy(wrapVariadic(...keys), 'asc')); diff --git a/packages/jsonapi/src/actions/context/enhancers/sortByDesc.ts b/packages/jsonapi/src/actions/context/enhancers/sortByDesc.ts index 270bd1ba..da7742b5 100644 --- a/packages/jsonapi/src/actions/context/enhancers/sortByDesc.ts +++ b/packages/jsonapi/src/actions/context/enhancers/sortByDesc.ts @@ -1,23 +1,27 @@ -import { Action, appendExtension, WithParsedExtension } from '@foscia/core'; +import { makeEnhancer } from '@foscia/core'; import sortBy from '@foscia/jsonapi/actions/context/enhancers/sortBy'; import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; /** - * Shortcut for the {@link sortBy} function with a desc direction. + * Shortcut for the {@link sortBy | `sortBy`} function with a descending direction. * * @param keys * * @category Enhancers + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { sortByDesc } from '@foscia/jsonapi'; + * + * const posts = await action().run( + * query(Post), + * sortByDesc('title'), + * sortByDesc(['publishedAt', 'title']), + * all(), + * ); + * ``` */ -const sortByDesc = (...keys: ArrayableVariadic) => sortBy(wrapVariadic(...keys), 'desc'); - -export default /* @__PURE__ */ appendExtension( - 'sortByDesc', - sortByDesc, - 'use', -) as WithParsedExtension( - this: Action, - ...keys: ArrayableVariadic - ): Action; -}>; +export default /* @__PURE__ */ makeEnhancer('sortByDesc', ( + ...keys: ArrayableVariadic +) => sortBy(wrapVariadic(...keys), 'desc')); diff --git a/packages/jsonapi/src/actions/context/runners/usingDocument.ts b/packages/jsonapi/src/actions/context/runners/usingDocument.ts index b26fb41c..2035a8f2 100644 --- a/packages/jsonapi/src/actions/context/runners/usingDocument.ts +++ b/packages/jsonapi/src/actions/context/runners/usingDocument.ts @@ -2,10 +2,26 @@ import { AllData, ModelInstance, OneData } from '@foscia/core'; import { JsonApiDeserializedData } from '@foscia/jsonapi/types'; /** - * Append the JSON:API document object to data object. - * Use it as the parameter of `allUsing` and `oneUsing` runners. + * Append the {@link JsonApiDocument | JSON:API document object} to data object. + * Use it as the parameter of `all` or `one` runners. * * @param data + * + * @category Runners + * + * @example + * ```typescript + * import { query, all } from '@foscia/core'; + * import { usingDocument } from '@foscia/jsonapi'; + * + * const data = await action().run( + * query(Post), + * all(usingDocument), + * ); + * + * console.log(data.instances); // Post array. + * console.log(data.document); // JSON:API document, with meta, etc. + * ``` */ export default < I extends ModelInstance, diff --git a/packages/jsonapi/src/blueprints/makeJsonApiAdapter.ts b/packages/jsonapi/src/blueprints/makeJsonApiAdapter.ts deleted file mode 100644 index 8646aabf..00000000 --- a/packages/jsonapi/src/blueprints/makeJsonApiAdapter.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ActionName, consumeAction } from '@foscia/core'; -import { clearEndpoint, deepParamsSerializer } from '@foscia/http'; -import { makeRestAdapterWith, RestAdapterConfig } from '@foscia/rest'; -import { optionalJoin, toKebabCase } from '@foscia/shared'; - -export default ( - config: Partial> = {}, -) => ({ - adapter: makeRestAdapterWith({ - baseURL: '/api/v1', - buildURL: (endpoint, context) => clearEndpoint(optionalJoin([ - endpoint.baseURL, - endpoint.modelPath, - endpoint.idPath, - (([ - ActionName.ATTACH_RELATION, - ActionName.UPDATE_RELATION, - ActionName.DETACH_RELATION, - ] as any[]).indexOf(consumeAction(context, null)!) !== -1 ? 'relationships' : null), - endpoint.relationPath, - endpoint.additionalPath, - ], '/')), - modelPathTransformer: toKebabCase, - relationPathTransformer: toKebabCase, - serializeParams: deepParamsSerializer, - defaultHeaders: { - Accept: 'application/vnd.api+json', - 'Content-Type': 'application/vnd.api+json', - ...config.defaultHeaders, - }, - ...config, - }), -}); diff --git a/packages/jsonapi/src/blueprints/makeJsonApiDeserializer.ts b/packages/jsonapi/src/blueprints/makeJsonApiDeserializer.ts deleted file mode 100644 index fac52716..00000000 --- a/packages/jsonapi/src/blueprints/makeJsonApiDeserializer.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ModelIdType } from '@foscia/core'; -import { - JsonApiDeserializedData, - JsonApiDeserializerConfig, - JsonApiDocument, - JsonApiExtractedData, - JsonApiNewResource, -} from '@foscia/jsonapi/types'; -import { makeDeserializerRecordFactory, makeDeserializerWith } from '@foscia/serialization'; -import { isNil, makeIdentifiersMap, mapArrayable, wrap } from '@foscia/shared'; - -export default < - Record extends JsonApiNewResource = JsonApiNewResource, - Data extends JsonApiDocument = JsonApiDocument, - Deserialized extends JsonApiDeserializedData = JsonApiDeserializedData, - Extract extends JsonApiExtractedData = JsonApiExtractedData, ->(config: Partial> = {}) => ({ - deserializer: makeDeserializerWith({ - extractData: (data: Data) => { - const included = makeIdentifiersMap(); - - [...wrap(data.data), ...wrap(data.included)].forEach( - (record) => !isNil(record.id) && included.put(record.type, record.id, record as Record), - ); - - return { - records: data.data, - document: data as JsonApiDocument, - included, - } as Extract; - }, - createData: (instances, extract) => ({ - instances, document: extract.document, - } as Deserialized), - createRecord: makeDeserializerRecordFactory( - config.pullIdentifier ?? ((record) => record), - config.pullAttribute ?? ((record, { key }) => record.attributes?.[key]), - config.pullRelation ?? ((record, { key }, extract) => mapArrayable( - record.relationships?.[key]?.data, - (value) => extract.included.find(value.type, value.id) as Record, - )), - ), - ...config, - }), -}); diff --git a/packages/jsonapi/src/blueprints/makeJsonApiSerializer.ts b/packages/jsonapi/src/blueprints/makeJsonApiSerializer.ts deleted file mode 100644 index 8a365aed..00000000 --- a/packages/jsonapi/src/blueprints/makeJsonApiSerializer.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { isAttributeDef, ModelIdType } from '@foscia/core'; -import { - JsonApiNewResource, - JsonApiResourceIdentifier, - JsonApiSerializerConfig, -} from '@foscia/jsonapi/types'; -import { makeSerializerRecordFactory, makeSerializerWith } from '@foscia/serialization'; -import { Arrayable, isNil, Optional } from '@foscia/shared'; - -export default < - Record extends JsonApiNewResource = JsonApiNewResource, - Related extends JsonApiResourceIdentifier = JsonApiResourceIdentifier, - Data = { data: Arrayable | null }, ->(config?: Partial>) => { - const serializeId = (id: Optional) => (isNil(id) ? undefined : String(id)); - - return { - serializer: makeSerializerWith({ - serializeRelation: (_, related) => ({ - type: related.$model.$type, - id: serializeId(related.id), - lid: serializeId(related.lid), - }), - serializeRelated: (_, related) => ({ - type: related.$model.$type, - id: serializeId(related.id), - } as Related), - createData: (records) => ({ data: records } as Data), - createRecord: makeSerializerRecordFactory( - (instance) => ({ - type: instance.$model.$type, - id: serializeId(instance.id), - lid: serializeId(instance.lid), - attributes: {}, - relationships: {}, - } as Record), - (record, { def, key, value }) => { - if (isAttributeDef(def)) { - // eslint-disable-next-line no-param-reassign - record.attributes![key] = value; - } else { - // eslint-disable-next-line no-param-reassign - record.relationships![key] = { data: value as any }; - } - }, - ), - ...config, - }), - }; -}; diff --git a/packages/jsonapi/src/index.ts b/packages/jsonapi/src/index.ts index e1388e86..03e6c7a9 100644 --- a/packages/jsonapi/src/index.ts +++ b/packages/jsonapi/src/index.ts @@ -6,11 +6,9 @@ import sortBy from '@foscia/jsonapi/actions/context/enhancers/sortBy'; import sortByAsc from '@foscia/jsonapi/actions/context/enhancers/sortByAsc'; import sortByDesc from '@foscia/jsonapi/actions/context/enhancers/sortByDesc'; import usingDocument from '@foscia/jsonapi/actions/context/runners/usingDocument'; -import makeJsonApiAdapter from '@foscia/jsonapi/blueprints/makeJsonApiAdapter'; -import makeJsonApiDeserializer from '@foscia/jsonapi/blueprints/makeJsonApiDeserializer'; -import makeJsonApiSerializer from '@foscia/jsonapi/blueprints/makeJsonApiSerializer'; -import jsonApiExtensions from '@foscia/jsonapi/jsonApiExtensions'; -import jsonApiStarterExtensions from '@foscia/jsonapi/jsonApiStarterExtensions'; +import makeJsonApiAdapter from '@foscia/jsonapi/makeJsonApiAdapter'; +import makeJsonApiDeserializer from '@foscia/jsonapi/makeJsonApiDeserializer'; +import makeJsonApiSerializer from '@foscia/jsonapi/makeJsonApiSerializer'; export * from '@foscia/jsonapi/types'; @@ -26,6 +24,4 @@ export { makeJsonApiAdapter, makeJsonApiDeserializer, makeJsonApiSerializer, - jsonApiExtensions, - jsonApiStarterExtensions, }; diff --git a/packages/jsonapi/src/jsonApiExtensions.ts b/packages/jsonapi/src/jsonApiExtensions.ts deleted file mode 100644 index 7112d4d1..00000000 --- a/packages/jsonapi/src/jsonApiExtensions.ts +++ /dev/null @@ -1,17 +0,0 @@ -import fields from '@foscia/jsonapi/actions/context/enhancers/fields'; -import fieldsFor from '@foscia/jsonapi/actions/context/enhancers/fieldsFor'; -import filterBy from '@foscia/jsonapi/actions/context/enhancers/filterBy'; -import paginate from '@foscia/jsonapi/actions/context/enhancers/paginate'; -import sortBy from '@foscia/jsonapi/actions/context/enhancers/sortBy'; -import sortByAsc from '@foscia/jsonapi/actions/context/enhancers/sortByAsc'; -import sortByDesc from '@foscia/jsonapi/actions/context/enhancers/sortByDesc'; - -export default () => ({ - ...filterBy.extension(), - ...fields.extension(), - ...fieldsFor.extension(), - ...sortBy.extension(), - ...sortByAsc.extension(), - ...sortByDesc.extension(), - ...paginate.extension(), -}); diff --git a/packages/jsonapi/src/jsonApiStarterExtensions.ts b/packages/jsonapi/src/jsonApiStarterExtensions.ts deleted file mode 100644 index 32d349cb..00000000 --- a/packages/jsonapi/src/jsonApiStarterExtensions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { coreExtensions, crudExtensions, hooksExtensions } from '@foscia/core'; -import { httpExtensions } from '@foscia/http'; -import jsonApiExtensions from '@foscia/jsonapi/jsonApiExtensions'; - -export default () => ({ - ...coreExtensions(), - ...crudExtensions(), - ...hooksExtensions(), - ...httpExtensions(), - ...jsonApiExtensions(), -}); diff --git a/packages/jsonapi/src/makeJsonApiAdapter.ts b/packages/jsonapi/src/makeJsonApiAdapter.ts new file mode 100644 index 00000000..a098af14 --- /dev/null +++ b/packages/jsonapi/src/makeJsonApiAdapter.ts @@ -0,0 +1,37 @@ +import { ActionName, consumeAction } from '@foscia/core'; +import { clearEndpoint, deepParamsSerializer } from '@foscia/http'; +import { JsonApiAdapterConfig } from '@foscia/jsonapi/types'; +import { makeRestAdapter } from '@foscia/rest'; +import { optionalJoin } from '@foscia/shared'; + +/** + * Make a JSON:API adapter object. + * + * @param config + * + * @category Factories + */ +export default ( + config: Partial> = {}, +) => makeRestAdapter({ + baseURL: '/api/v1', + buildURL: (endpoint, context) => clearEndpoint(optionalJoin([ + endpoint.baseURL, + endpoint.modelPath, + endpoint.idPath, + (([ + ActionName.ATTACH_RELATION, + ActionName.UPDATE_RELATION, + ActionName.DETACH_RELATION, + ] as any[]).indexOf(consumeAction(context, null)!) !== -1 ? 'relationships' : null), + endpoint.relationPath, + endpoint.additionalPath, + ], '/')), + serializeParams: deepParamsSerializer, + defaultHeaders: { + Accept: 'application/vnd.api+json', + 'Content-Type': 'application/vnd.api+json', + ...config.defaultHeaders, + }, + ...config, +}); diff --git a/packages/jsonapi/src/makeJsonApiDeserializer.ts b/packages/jsonapi/src/makeJsonApiDeserializer.ts new file mode 100644 index 00000000..df75ec70 --- /dev/null +++ b/packages/jsonapi/src/makeJsonApiDeserializer.ts @@ -0,0 +1,52 @@ +import { ModelIdType } from '@foscia/core'; +import { + JsonApiDeserializedData, + JsonApiDeserializerConfig, + JsonApiDocument, + JsonApiExtractedData, + JsonApiNewResource, +} from '@foscia/jsonapi/types'; +import { makeDeserializer, makeDeserializerRecordFactory } from '@foscia/serialization'; +import { isNil, makeIdentifiersMap, mapArrayable, wrap } from '@foscia/shared'; + +/** + * Make a JSON:API deserializer object. + * + * @param config + * + * @category Factories + */ +export default < + Record extends JsonApiNewResource = JsonApiNewResource, + Data extends JsonApiDocument = JsonApiDocument, + Deserialized extends JsonApiDeserializedData = JsonApiDeserializedData, + Extract extends JsonApiExtractedData = JsonApiExtractedData, +>( + config: Partial> = {}, +) => makeDeserializer({ + extractData: (data: Data) => { + const included = makeIdentifiersMap(); + + [...wrap(data.data), ...wrap(data.included)].forEach( + (record) => !isNil(record.id) && included.put(record.type, record.id, record as Record), + ); + + return { + records: data.data, + document: data as JsonApiDocument, + included, + } as Extract; + }, + createData: (instances, extract) => ({ + instances, document: extract.document, + } as Deserialized), + createRecord: makeDeserializerRecordFactory( + config.pullIdentifier ?? ((record) => record), + config.pullAttribute ?? ((record, { key }) => record.attributes?.[key]), + config.pullRelation ?? ((record, { key }, extract) => mapArrayable( + record.relationships?.[key]?.data, + (value) => extract.included.find(value.type, value.id) as Record, + )), + ), + ...config, +}); diff --git a/packages/jsonapi/src/makeJsonApiSerializer.ts b/packages/jsonapi/src/makeJsonApiSerializer.ts new file mode 100644 index 00000000..1b71810f --- /dev/null +++ b/packages/jsonapi/src/makeJsonApiSerializer.ts @@ -0,0 +1,55 @@ +import { isAttributeDef, ModelIdType } from '@foscia/core'; +import { + JsonApiNewResource, + JsonApiResourceIdentifier, + JsonApiSerializerConfig, +} from '@foscia/jsonapi/types'; +import { makeSerializer, makeSerializerRecordFactory } from '@foscia/serialization'; +import { Arrayable, isNil, Optional } from '@foscia/shared'; + +const serializeId = (id: Optional) => (isNil(id) ? undefined : String(id)); + +/** + * Make a JSON:API serializer object. + * + * @param config + * + * @category Factories + */ +export default < + Record extends JsonApiNewResource = JsonApiNewResource, + Related extends JsonApiResourceIdentifier = JsonApiResourceIdentifier, + Data = { data: Arrayable | null }, +>( + config?: Partial>, +) => makeSerializer({ + createData: (records) => ({ data: records } as Data), + createRecord: makeSerializerRecordFactory( + (snapshot) => ({ + type: snapshot.$instance.$model.$type, + id: serializeId(snapshot.$values.id), + lid: serializeId(snapshot.$values.lid), + attributes: {}, + relationships: {}, + } as Record), + (record, { def, key, value }) => { + if (isAttributeDef(def)) { + // eslint-disable-next-line no-param-reassign + record.attributes![key] = value; + } else { + // eslint-disable-next-line no-param-reassign + record.relationships![key] = { data: value as any }; + } + }, + ), + serializeRelation: (_, related) => ({ + type: related.$instance.$model.$type, + id: serializeId(related.$values.id), + lid: serializeId(related.$values.lid), + }), + serializeRelated: (_, related) => ({ + type: related.$instance.$model.$type, + id: serializeId(related.$values.id), + } as Related), + ...config, +}); diff --git a/packages/jsonapi/src/types.ts b/packages/jsonapi/src/types.ts index 18b38cde..690f495f 100644 --- a/packages/jsonapi/src/types.ts +++ b/packages/jsonapi/src/types.ts @@ -5,17 +5,25 @@ import { ModelInstance, ModelRelation, } from '@foscia/core'; +import type { SortDirection } from '@foscia/jsonapi/actions/context/enhancers/sortBy'; +import { RestAdapterConfig } from '@foscia/rest'; import { - DeserializerConfig, + RecordDeserializerConfig, DeserializerContext, DeserializerExtract, DeserializerRecordIdentifier, - SerializerConfig, + RecordSerializerConfig, } from '@foscia/serialization'; import { Arrayable, Awaitable, Dictionary, IdentifiersMap } from '@foscia/shared'; +export type { + SortDirection, +}; + /** * @see [JSON:API specification](https://jsonapi.org/format/#document-links) + * + * @internal */ export type JsonApiLink = { href: string; @@ -24,16 +32,22 @@ export type JsonApiLink = { /** * @see [JSON:API specification](https://jsonapi.org/format/#document-links) + * + * @internal */ export type JsonApiLinks = Dictionary; /** * @see [JSON:API specification](https://jsonapi.org/format/#document-meta) + * + * @internal */ export type JsonApiMeta = Dictionary; /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-identifier-objects) + * + * @internal */ export type JsonApiResourceIdentifier = { type: string; @@ -43,11 +57,15 @@ export type JsonApiResourceIdentifier = { /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-object-attributes) + * + * @internal */ export type JsonApiAttributes = Dictionary; /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-object-relationships) + * + * @internal */ export type JsonApiRelationship = { data?: JsonApiResourceIdentifier[] | JsonApiResourceIdentifier | null; @@ -57,11 +75,15 @@ export type JsonApiRelationship = { /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-object-relationships) + * + * @internal */ export type JsonApiRelationships = Dictionary; /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-objects) + * + * @internal */ export type JsonApiAbstractResource = { type: string; @@ -74,6 +96,8 @@ export type JsonApiAbstractResource = { /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-objects) + * + * @internal */ export type JsonApiResource = JsonApiAbstractResource & { id: string; @@ -81,6 +105,8 @@ export type JsonApiResource = JsonApiAbstractResource & { /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-objects) + * + * @internal */ export type JsonApiNewResource = JsonApiAbstractResource & { id?: string; @@ -88,6 +114,8 @@ export type JsonApiNewResource = JsonApiAbstractResource & { /** * @see [JSON:API specification](https://jsonapi.org/format/#error-objects) + * + * @internal */ export type JsonApiError = { status?: string; @@ -104,6 +132,8 @@ export type JsonApiError = { /** * @see [JSON:API specification](https://jsonapi.org/format/#document-top-level) + * + * @internal */ export type JsonApiDocument = { data?: JsonApiResource[] | JsonApiResource | JsonApiNewResource | null; @@ -119,6 +149,8 @@ export type JsonApiDocument = { /** * Extracted data from a JSON:API backend Response object. + * + * @internal */ export type JsonApiExtractedData = & { @@ -134,6 +166,22 @@ export type JsonApiDeserializedData = & DeserializedData & { document: JsonApiDocument; }; +/** + * Configuration for JSON:API adapter. + * + * @interface + * + * @internal + */ +export type JsonApiAdapterConfig = RestAdapterConfig; + +/** + * Configuration for JSON:API deserializer. + * + * @interface + * + * @internal + */ export type JsonApiDeserializerConfig< Record extends JsonApiNewResource, Data extends JsonApiDocument | undefined, @@ -141,22 +189,53 @@ export type JsonApiDeserializerConfig< Extract extends JsonApiExtractedData, > = & { + /** + * Extract identifier (type, ID and LID) from a JSON:API record. + * Defaults to the record `type`, `id` and `lid` root fields. + * + * @param record + * @param context + */ pullIdentifier: (record: Record, context: {}) => Awaitable; + /** + * Extract an attribute's value from a JSON:API record. + * Defaults to the record attribute's value from `attributes` fields. + * + * @param record + * @param deserializerContext + * @param extract + */ pullAttribute: ( record: Record, deserializerContext: DeserializerContext, extract: Extract, ) => Awaitable; + /** + * Extract a relation's value from a JSON:API record. + * Defaults to the record relation's value(s) from `relationships` fields + * mapped with their record found in `included` document key. + * + * @param record + * @param deserializerContext + * @param extract + */ pullRelation: ( record: Record, deserializerContext: DeserializerContext, extract: Extract, ) => Awaitable | null | undefined>; } - & DeserializerConfig; + & RecordDeserializerConfig; +/** + * Configuration for JSON:API serializer. + * + * @interface + * + * @internal + */ export type JsonApiSerializerConfig< Record extends JsonApiNewResource, Related extends JsonApiResourceIdentifier, Data, -> = SerializerConfig; +> = RecordSerializerConfig; diff --git a/packages/jsonapi/tests/integration/crud.test.ts b/packages/jsonapi/tests/integration/crud.test.ts index 631ad585..c5404043 100644 --- a/packages/jsonapi/tests/integration/crud.test.ts +++ b/packages/jsonapi/tests/integration/crud.test.ts @@ -17,6 +17,7 @@ import { one, oneOrFail, query, + queryAs, save, updateRelation, when, @@ -616,7 +617,28 @@ describe('integration: JSON:API', () => { expect(post.comments).toBeUndefined(); }); - it('should run action: custom', async () => { + it('should run action: destroy record', async () => { + const fetchMock = createFetchMock(); + fetchMock.mockImplementationOnce(createFetchResponse().noContent()); + + const action = makeJsonApiActionMock(); + + const data = await action() + .use(destroy(PostMock, '1')) + .run(one()); + + expect(fetchMock).toHaveBeenCalledOnce(); + const request = fetchMock.mock.calls[0][0] as Request; + expect(request.url).toStrictEqual('https://example.com/api/v1/posts/1'); + expect(request.method).toStrictEqual('DELETE'); + expect(request.headers.get('Accept')).toStrictEqual('application/vnd.api+json'); + expect(request.headers.get('Content-Type')).toStrictEqual('application/vnd.api+json'); + expect(request.body).toBeNull(); + + expect(data).toBeNull(); + }); + + it('should run action: contextualized custom', async () => { const fetchMock = createFetchMock(); fetchMock.mockImplementationOnce(createFetchResponse().json({ data: [ @@ -651,6 +673,75 @@ describe('integration: JSON:API', () => { expect(post.title).toStrictEqual('Foo'); }); + it('should run action: contextualized custom no paths', async () => { + const fetchMock = createFetchMock(); + fetchMock.mockImplementationOnce(createFetchResponse().json({ + data: [ + { + type: 'posts', + id: '1', + attributes: { title: 'Foo' }, + }, + ], + })); + + const action = makeJsonApiActionMock(); + + const [post] = await action() + .use( + query(PostMock), + makeGet('most-viewed-post', { + modelPaths: false, + }), + ) + .run(all()); + + expect(fetchMock).toHaveBeenCalledOnce(); + const request = fetchMock.mock.calls[0][0] as Request; + expect(request.url).toStrictEqual('https://example.com/api/v1/most-viewed-post'); + expect(request.method).toStrictEqual('GET'); + expect(request.headers.get('Accept')).toStrictEqual('application/vnd.api+json'); + expect(request.headers.get('Content-Type')).toStrictEqual('application/vnd.api+json'); + expect(request.body).toBeNull(); + + expect(post).toBeInstanceOf(PostMock); + expect(post.$exists).toStrictEqual(true); + expect(post.id).toStrictEqual('1'); + expect(post.title).toStrictEqual('Foo'); + }); + + it('should run action: custom query as models', async () => { + const fetchMock = createFetchMock(); + fetchMock.mockImplementationOnce(createFetchResponse().json({ + data: [ + { type: 'posts', id: '1', attributes: { title: 'Foo' } }, + { type: 'comments', id: '1', attributes: { body: 'Foo bar' } }, + ], + })); + + const action = makeJsonApiActionMock(); + + const [post, comment] = await action() + .use( + queryAs(PostMock, CommentMock), + makeGet('global-search', { params: { search: 'foo' } }), + ) + .run(all()); + + expect(fetchMock).toHaveBeenCalledOnce(); + const request = fetchMock.mock.calls[0][0] as Request; + expect(request.url).toStrictEqual('https://example.com/api/v1/global-search?search=foo'); + expect(request.method).toStrictEqual('GET'); + expect(request.headers.get('Accept')).toStrictEqual('application/vnd.api+json'); + expect(request.headers.get('Content-Type')).toStrictEqual('application/vnd.api+json'); + expect(request.body).toBeNull(); + + expect(post).toBeInstanceOf(PostMock); + expect((post as PostMock).title).toStrictEqual('Foo'); + expect(comment).toBeInstanceOf(CommentMock); + expect((comment as CommentMock).body).toStrictEqual('Foo bar'); + }); + it('should load relations with refresh', async () => { const fetchMock = createFetchMock(); fetchMock.mockImplementationOnce(createFetchResponse().json({ diff --git a/packages/jsonapi/tests/mocks/makeJsonApiAction.mock.ts b/packages/jsonapi/tests/mocks/makeJsonApiAction.mock.ts index c516b0a0..b8f7ea12 100644 --- a/packages/jsonapi/tests/mocks/makeJsonApiAction.mock.ts +++ b/packages/jsonapi/tests/mocks/makeJsonApiAction.mock.ts @@ -15,10 +15,10 @@ export default function makeJsonApiActionMock() { ...makeJsonApiSerializer(), ...makeJsonApiAdapter({ baseURL: 'https://example.com/api/v1', - requestTransformers: [(request) => { + middlewares: [(request, next) => { request.headers.set('X-Foo-Header', 'bar'); - return request; + return next(request); }], }), }); diff --git a/packages/rest/src/blueprints/makeJsonRestAdapter.ts b/packages/rest/src/blueprints/makeJsonRestAdapter.ts deleted file mode 100644 index 15e80e95..00000000 --- a/packages/rest/src/blueprints/makeJsonRestAdapter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { paramsSerializer } from '@foscia/http'; -import makeRestAdapterWith from '@foscia/rest/makeRestAdapterWith'; -import { RestAdapterConfig } from '@foscia/rest/types'; -import { toKebabCase } from '@foscia/shared'; - -export default ( - config: Partial> = {}, -) => ({ - adapter: makeRestAdapterWith({ - baseURL: '/api', - serializeParams: paramsSerializer, - modelPathTransformer: toKebabCase, - relationPathTransformer: toKebabCase, - ...config, - }), -}); diff --git a/packages/rest/src/blueprints/makeJsonRestDeserializer.ts b/packages/rest/src/blueprints/makeJsonRestDeserializer.ts deleted file mode 100644 index 8fca0f22..00000000 --- a/packages/rest/src/blueprints/makeJsonRestDeserializer.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DeserializedData } from '@foscia/core'; -import { RestDeserializerConfig, RestNewResource } from '@foscia/rest/types'; -import { - DeserializerExtract, - makeDeserializerRecordFactory, - makeDeserializerWith, -} from '@foscia/serialization'; -import { Arrayable, mapArrayable } from '@foscia/shared'; - -export default < - Record extends RestNewResource = RestNewResource, - Data = Arrayable | null, - Deserialized extends DeserializedData = DeserializedData, - Extract extends DeserializerExtract = DeserializerExtract, ->(config: Partial> = {}) => ({ - deserializer: makeDeserializerWith({ - extractData: (data) => ({ - records: data as Arrayable | null, - } as Extract), - createRecord: makeDeserializerRecordFactory( - config.pullIdentifier ?? ((record) => record), - config.pullAttribute ?? ((record, { key }) => record[key]), - config.pullRelation ?? ((record, { key }) => mapArrayable(record[key], (value) => ( - (typeof value === 'object' ? value : { id: value }) as Record - ))), - ), - ...config, - }), -}); diff --git a/packages/rest/src/blueprints/makeJsonRestSerializer.ts b/packages/rest/src/blueprints/makeJsonRestSerializer.ts deleted file mode 100644 index 6aa76538..00000000 --- a/packages/rest/src/blueprints/makeJsonRestSerializer.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { RestNewResource, RestSerializerConfig } from '@foscia/rest/types'; -import { makeSerializerRecordFactory, makeSerializerWith } from '@foscia/serialization'; -import { Arrayable } from '@foscia/shared'; - -export default < - Record extends RestNewResource = RestNewResource, - Related = string, - Data = Arrayable | null, ->(config?: Partial>) => ({ - serializer: makeSerializerWith({ - createRecord: makeSerializerRecordFactory( - (instance) => { - const record = { id: instance.id } as Record; - - if (config?.serializeType) { - record.type = instance.$model.$type; - } - - return record; - }, - (record, { key, value }) => { - // eslint-disable-next-line no-param-reassign - record[key as keyof Record] = value as Record[keyof Record]; - }, - ), - ...config, - }), -}); diff --git a/packages/rest/src/index.ts b/packages/rest/src/index.ts index 7c20ebc4..97c9f056 100644 --- a/packages/rest/src/index.ts +++ b/packages/rest/src/index.ts @@ -1,19 +1,13 @@ -import makeJsonRestAdapter from '@foscia/rest/blueprints/makeJsonRestAdapter'; -import makeJsonRestDeserializer from '@foscia/rest/blueprints/makeJsonRestDeserializer'; -import makeJsonRestSerializer from '@foscia/rest/blueprints/makeJsonRestSerializer'; -import jsonRestExtensions from '@foscia/rest/jsonRestExtensions'; -import jsonRestStarterExtensions from '@foscia/rest/jsonRestStarterExtensions'; +import makeRestAdapter from '@foscia/rest/makeRestAdapter'; +import makeRestDeserializer from '@foscia/rest/makeRestDeserializer'; +import makeRestSerializer from '@foscia/rest/makeRestSerializer'; import makeIncludeParam from '@foscia/rest/makeIncludeParam'; -import makeRestAdapterWith from '@foscia/rest/makeRestAdapterWith'; export * from '@foscia/rest/types'; export { - makeRestAdapterWith, - makeJsonRestAdapter, - makeJsonRestDeserializer, - makeJsonRestSerializer, + makeRestAdapter, + makeRestSerializer, + makeRestDeserializer, makeIncludeParam, - jsonRestExtensions, - jsonRestStarterExtensions, }; diff --git a/packages/rest/src/jsonRestExtensions.ts b/packages/rest/src/jsonRestExtensions.ts deleted file mode 100644 index 56bf55ff..00000000 --- a/packages/rest/src/jsonRestExtensions.ts +++ /dev/null @@ -1 +0,0 @@ -export default () => ({}); diff --git a/packages/rest/src/jsonRestStarterExtensions.ts b/packages/rest/src/jsonRestStarterExtensions.ts deleted file mode 100644 index da99373d..00000000 --- a/packages/rest/src/jsonRestStarterExtensions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { coreExtensions, crudExtensions, hooksExtensions } from '@foscia/core'; -import { httpExtensions } from '@foscia/http'; -import jsonRestExtensions from '@foscia/rest/jsonRestExtensions'; - -export default () => ({ - ...coreExtensions(), - ...crudExtensions(), - ...hooksExtensions(), - ...httpExtensions(), - ...jsonRestExtensions(), -}); diff --git a/packages/rest/src/makeIncludeParam.ts b/packages/rest/src/makeIncludeParam.ts index 477459f7..14e28aa0 100644 --- a/packages/rest/src/makeIncludeParam.ts +++ b/packages/rest/src/makeIncludeParam.ts @@ -1,12 +1,20 @@ import { consumeInclude, normalizeInclude } from '@foscia/core'; import { isNil, optionalJoin } from '@foscia/shared'; +/** + * Make a query parameters object containing requested included relations. + * + * @param context + * @param includeParamKey + * + * @internal + */ export default async ( context: {}, includeParamKey: string | null = 'include', ) => { if (!isNil(includeParamKey)) { - const include = consumeInclude(context, null) ?? []; + const include = consumeInclude(context, []); if (include.length) { return { [includeParamKey]: optionalJoin(await normalizeInclude(context, include), ','), diff --git a/packages/rest/src/makeRestAdapter.ts b/packages/rest/src/makeRestAdapter.ts new file mode 100644 index 00000000..2c23a14e --- /dev/null +++ b/packages/rest/src/makeRestAdapter.ts @@ -0,0 +1,25 @@ +import { makeHttpAdapter } from '@foscia/http'; +import makeIncludeParam from '@foscia/rest/makeIncludeParam'; +import { RestAdapterConfig } from '@foscia/rest/types'; +import { kebabCase } from '@foscia/shared'; + +/** + * Make a REST adapter object. + * + * @param config + * + * @category Factories + * @since 0.13.0 + */ +export default ( + config: Partial> = {}, +) => makeHttpAdapter({ + baseURL: '/api', + modelPathTransformer: kebabCase, + relationPathTransformer: kebabCase, + appendParams: async (context) => ({ + ...await makeIncludeParam(context, config.includeParamKey), + ...(await config.appendParams?.(context)), + }), + ...config, +}); diff --git a/packages/rest/src/makeRestAdapterWith.ts b/packages/rest/src/makeRestAdapterWith.ts deleted file mode 100644 index 55f65576..00000000 --- a/packages/rest/src/makeRestAdapterWith.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { makeHttpAdapterWith } from '@foscia/http'; -import makeIncludeParam from '@foscia/rest/makeIncludeParam'; -import { RestAdapterConfig } from '@foscia/rest/types'; - -export default (config: RestAdapterConfig) => makeHttpAdapterWith({ - ...config, - appendParams: async (context) => ({ - ...await makeIncludeParam(context, config.includeParamKey), - ...(await config.appendParams?.(context)), - }), -}); diff --git a/packages/rest/src/makeRestDeserializer.ts b/packages/rest/src/makeRestDeserializer.ts new file mode 100644 index 00000000..cc69fd18 --- /dev/null +++ b/packages/rest/src/makeRestDeserializer.ts @@ -0,0 +1,37 @@ +import { DeserializedData } from '@foscia/core'; +import { RestDeserializerConfig, RestNewResource } from '@foscia/rest/types'; +import { + DeserializerExtract, + makeDeserializer, + makeDeserializerRecordFactory, +} from '@foscia/serialization'; +import { Arrayable, mapArrayable } from '@foscia/shared'; + +/** + * Make a REST deserializer object. + * + * @param config + * + * @category Factories + * @since 0.13.0 + */ +export default < + Record extends RestNewResource = RestNewResource, + Data = Arrayable | null, + Deserialized extends DeserializedData = DeserializedData, + Extract extends DeserializerExtract = DeserializerExtract, +>( + config: Partial> = {}, +) => makeDeserializer({ + extractData: (data) => ({ + records: data as Arrayable | null, + } as Extract), + createRecord: makeDeserializerRecordFactory( + config.pullIdentifier ?? ((record) => record), + config.pullAttribute ?? ((record, { key }) => record[key]), + config.pullRelation ?? ((record, { key }) => mapArrayable(record[key], (value) => ( + (typeof value === 'object' ? value : { id: value }) as Record + ))), + ), + ...config, +}); diff --git a/packages/rest/src/makeRestSerializer.ts b/packages/rest/src/makeRestSerializer.ts new file mode 100644 index 00000000..0a5e50a4 --- /dev/null +++ b/packages/rest/src/makeRestSerializer.ts @@ -0,0 +1,33 @@ +import { RestNewResource, RestSerializerConfig } from '@foscia/rest/types'; +import { makeSerializer, makeSerializerRecordFactory } from '@foscia/serialization'; +import { Arrayable, tap } from '@foscia/shared'; + +/** + * Make a REST serializer object. + * + * @param config + * + * @category Factories + * @since 0.13.0 + */ +export default < + Record extends RestNewResource = RestNewResource, + Related = string, + Data = Arrayable | null, +>( + config?: Partial>, +) => makeSerializer({ + createRecord: makeSerializerRecordFactory( + (snapshot) => tap({ id: snapshot.$values.id } as Record, (record) => { + if (config?.serializeType) { + // eslint-disable-next-line no-param-reassign + record.type = snapshot.$instance.$model.$type; + } + }), + (record, { key, value }) => { + // eslint-disable-next-line no-param-reassign + record[key as keyof Record] = value as Record[keyof Record]; + }, + ), + ...config, +}); diff --git a/packages/rest/src/types.ts b/packages/rest/src/types.ts index 373c04c4..fbb97774 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -1,30 +1,55 @@ import { DeserializedData, ModelAttribute, ModelIdType, ModelRelation } from '@foscia/core'; import { HttpAdapterConfig } from '@foscia/http'; import { - DeserializerConfig, + RecordDeserializerConfig, DeserializerContext, DeserializerExtract, DeserializerRecordIdentifier, - SerializerConfig, + RecordSerializerConfig, } from '@foscia/serialization'; import { Arrayable, Awaitable, Dictionary } from '@foscia/shared'; +/** + * Abstract definition of a REST record. + * + * @internal + */ export type RestAbstractResource = Dictionary & { type?: string; }; +/** + * Abstract definition of a new REST record. + * + * @internal + */ export type RestNewResource = RestAbstractResource & { id?: ModelIdType; }; +/** + * Configuration for REST adapter. + * + * @interface + * + * @internal + */ export type RestAdapterConfig = HttpAdapterConfig & { - includeParamKey?: string | null; /** - * @deprecated Use `includeParamKey` option instead. + * Change the `include` query parameter key to append on the request. + * Defaults to `include`. If `null`, it will not append included relations + * on the request. */ - includeQueryParameter?: string | null; + includeParamKey?: string | null; }; +/** + * Configuration for REST deserializer. + * + * @interface + * + * @internal + */ export type RestDeserializerConfig< Record, Data, @@ -32,26 +57,61 @@ export type RestDeserializerConfig< Extract extends DeserializerExtract, > = & { + /** + * Extract identifier (type, ID and LID) from a REST record. + * Defaults to the record `type`, `id` and `lid` root fields. + * + * @param record + * @param context + */ pullIdentifier: (record: Record, context: {}) => Awaitable; + /** + * Extract an attribute's value from a REST record. + * Defaults to the record attribute's value from root fields. + * + * @param record + * @param deserializerContext + * @param extract + */ pullAttribute: ( record: Record, deserializerContext: DeserializerContext, extract: Extract, ) => Awaitable; + /** + * Extract a relation's value from a REST record. + * Defaults to the record relation's value(s) from root fields. + * When value is an object, it will be deserialized as a normal record. + * + * @param record + * @param deserializerContext + * @param extract + */ pullRelation: ( record: Record, deserializerContext: DeserializerContext, extract: Extract, ) => Awaitable | null | undefined>; } - & DeserializerConfig; + & RecordDeserializerConfig; +/** + * Configuration for REST serializer. + * + * @interface + * + * @internal + */ export type RestSerializerConfig< Record extends RestNewResource, Related, Data, > = & { + /** + * Append a `type` field on the serialized record containing the + * model type. Defaults to `false`. + */ serializeType?: boolean; } - & SerializerConfig; + & RecordSerializerConfig; diff --git a/packages/rest/tests/integration/crud.test.ts b/packages/rest/tests/integration/crud.test.ts index fe43f128..d4b30e52 100644 --- a/packages/rest/tests/integration/crud.test.ts +++ b/packages/rest/tests/integration/crud.test.ts @@ -11,14 +11,15 @@ import { none, one, query, + queryAs, save, when, } from '@foscia/core'; -import { param } from '@foscia/http'; +import { makeGet, param } from '@foscia/http'; import { describe, expect, it, vi } from 'vitest'; import createFetchMock from '../../../../tests/mocks/createFetchMock.mock'; import createFetchResponse from '../../../../tests/mocks/createFetchResponse.mock'; -import makeJsonRestActionMock from '../mocks/makeJsonRestAction.mock'; +import makeRestActionMock from '../mocks/makeRestAction.mock'; import CommentMock from '../mocks/models/comment.mock'; import GalleryMock from '../mocks/models/gallery.mock'; import PostMock from '../mocks/models/post.mock'; @@ -52,7 +53,7 @@ describe('integration: JSON REST', () => { }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const posts = await action() .use(query(PostMock)) @@ -80,10 +81,12 @@ describe('integration: JSON REST', () => { expect(posts[0].comments[0]).toBeInstanceOf(CommentMock); expect(posts[0].comments[0].id).toStrictEqual('1'); expect(posts[0].comments[0].body).toStrictEqual('Foo Comment'); + expect(posts[0].comments[0].commentable).toStrictEqual(posts[0]); expect(posts[0].comments[1].$exists).toStrictEqual(true); expect(posts[0].comments[1]).toBeInstanceOf(CommentMock); expect(posts[0].comments[1].id).toStrictEqual('2'); expect(posts[0].comments[1].body).toStrictEqual('Bar Comment'); + expect(posts[0].comments[0].commentable).toStrictEqual(posts[0]); expect(posts[1]).toBeInstanceOf(PostMock); expect(posts[1].$exists).toStrictEqual(true); @@ -107,7 +110,7 @@ describe('integration: JSON REST', () => { }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const post = fill(new PostMock(), { id: '1' }); post.$exists = true; @@ -144,7 +147,7 @@ describe('integration: JSON REST', () => { body: 'Foo Body', })); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const comment = fill(new CommentMock(), { id: '1' }); const post = fill(new PostMock(), { title: 'Foo', body: 'Foo Body', comments: [comment] }); @@ -181,7 +184,7 @@ describe('integration: JSON REST', () => { body: 'Bar', })); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const comment = fill(new CommentMock(), { body: 'Bar' }); const post = fill(new PostMock(), { id: '1', title: 'Foo' }); @@ -218,7 +221,7 @@ describe('integration: JSON REST', () => { const notChangedMock = vi.fn(() => null); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const post = fill(new PostMock(), { title: 'Foo', body: 'Foo Body' }); post.id = '1'; @@ -261,7 +264,7 @@ describe('integration: JSON REST', () => { const fetchMock = createFetchMock(); fetchMock.mockImplementationOnce(createFetchResponse().noContent()); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const post = fill(new PostMock(), { title: 'Foo', body: 'Foo Body' }); post.id = '1'; @@ -289,6 +292,63 @@ describe('integration: JSON REST', () => { expect(post.comments).toBeUndefined(); }); + it('should run action: custom query as models', async () => { + const fetchMock = createFetchMock(); + fetchMock.mockImplementationOnce(createFetchResponse().json([ + { id: '1', title: 'Foo' }, + ])); + + const action = makeRestActionMock(); + + const [post] = await action() + .use( + queryAs(PostMock), + makeGet('global-search', { params: { search: 'foo' } }), + ) + .run(all()); + + expect(fetchMock).toHaveBeenCalledOnce(); + const request = fetchMock.mock.calls[0][0] as Request; + expect(request.url).toStrictEqual('https://example.com/api/global-search?search=foo'); + expect(request.method).toStrictEqual('GET'); + expect(request.headers.get('Accept')).toStrictEqual('application/json'); + expect(request.headers.get('Content-Type')).toStrictEqual('application/json'); + expect(request.body).toBeNull(); + + expect(post).toBeInstanceOf(PostMock); + expect((post as PostMock).title).toStrictEqual('Foo'); + }); + + it('should run action: custom query as models (polymorphic)', async () => { + const fetchMock = createFetchMock(); + fetchMock.mockImplementationOnce(createFetchResponse().json([ + { type: 'posts', id: '1', title: 'Foo' }, + { type: 'comments', id: '1', body: 'Foo bar' }, + ])); + + const action = makeRestActionMock(); + + const [post, comment] = await action() + .use( + queryAs(PostMock, CommentMock), + makeGet('global-search', { params: { search: 'foo' } }), + ) + .run(all()); + + expect(fetchMock).toHaveBeenCalledOnce(); + const request = fetchMock.mock.calls[0][0] as Request; + expect(request.url).toStrictEqual('https://example.com/api/global-search?search=foo'); + expect(request.method).toStrictEqual('GET'); + expect(request.headers.get('Accept')).toStrictEqual('application/json'); + expect(request.headers.get('Content-Type')).toStrictEqual('application/json'); + expect(request.body).toBeNull(); + + expect(post).toBeInstanceOf(PostMock); + expect((post as PostMock).title).toStrictEqual('Foo'); + expect(comment).toBeInstanceOf(CommentMock); + expect((comment as CommentMock).body).toStrictEqual('Foo bar'); + }); + it('should load relations with model query', async () => { const fetchMock = createFetchMock(); fetchMock.mockImplementationOnce(createFetchResponse().json([ @@ -297,7 +357,7 @@ describe('integration: JSON REST', () => { { id: '3' }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const loadWithQuery = makeQueryModelLoader(action, { prepare: (a, { ids }) => a.use(param('ids', ids)), @@ -363,7 +423,7 @@ describe('integration: JSON REST', () => { { id: '1' }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const loadWithQuery = makeQueryModelLoader(action, { prepare: (a, { ids }) => a.use(param('ids', ids)), diff --git a/packages/rest/tests/integration/endpoint-ids.test.ts b/packages/rest/tests/integration/endpoint-ids.test.ts index 23c2d05c..2d454dd9 100644 --- a/packages/rest/tests/integration/endpoint-ids.test.ts +++ b/packages/rest/tests/integration/endpoint-ids.test.ts @@ -8,11 +8,7 @@ import { oneOrFail, query, } from '@foscia/core'; -import { - makeJsonRestAdapter, - makeJsonRestDeserializer, - makeJsonRestSerializer, -} from '@foscia/rest'; +import { makeRestAdapter, makeRestDeserializer, makeRestSerializer } from '@foscia/rest'; import { describe, expect, it } from 'vitest'; import createFetchMock from '../../../../tests/mocks/createFetchMock.mock'; import createFetchResponse from '../../../../tests/mocks/createFetchResponse.mock'; @@ -33,15 +29,15 @@ describe('integration: endpoint IDs', () => { const action = makeActionFactory({ ...makeRegistry([PostMock, CommentMock]), - ...makeJsonRestDeserializer({ + ...makeRestDeserializer({ pullIdentifier: (record) => { const [id, type] = String(record.id).split('/').reverse(); return { id, type }; }, }), - ...makeJsonRestSerializer(), - ...makeJsonRestAdapter({ + ...makeRestSerializer(), + ...makeRestAdapter({ baseURL: 'https://example.com/api', }), }); diff --git a/packages/rest/tests/mocks/composables/commentable.mock.ts b/packages/rest/tests/mocks/composables/commentable.mock.ts index c2c75b17..f04539ea 100644 --- a/packages/rest/tests/mocks/composables/commentable.mock.ts +++ b/packages/rest/tests/mocks/composables/commentable.mock.ts @@ -2,5 +2,5 @@ import { hasMany, makeComposable } from '@foscia/core'; import CommentMock from '../models/comment.mock'; export default makeComposable({ - comments: hasMany(() => CommentMock), + comments: hasMany(() => CommentMock, { inverse: 'commentable' }), }); diff --git a/packages/rest/tests/mocks/makeJsonRestAction.mock.ts b/packages/rest/tests/mocks/makeRestAction.mock.ts similarity index 59% rename from packages/rest/tests/mocks/makeJsonRestAction.mock.ts rename to packages/rest/tests/mocks/makeRestAction.mock.ts index c7ba1741..4baf425b 100644 --- a/packages/rest/tests/mocks/makeJsonRestAction.mock.ts +++ b/packages/rest/tests/mocks/makeRestAction.mock.ts @@ -1,20 +1,16 @@ import { makeActionFactory, makeCache, makeRegistry } from '@foscia/core'; -import { - makeJsonRestAdapter, - makeJsonRestDeserializer, - makeJsonRestSerializer, -} from '@foscia/rest'; +import { makeRestAdapter, makeRestDeserializer, makeRestSerializer } from '@foscia/rest'; import CommentMock from './models/comment.mock'; import GalleryMock from './models/gallery.mock'; import PostMock from './models/post.mock'; -export default function makeJsonRestActionMock() { +export default function makeRestActionMock() { return makeActionFactory({ ...makeRegistry([PostMock, CommentMock, GalleryMock]), ...makeCache(), - ...makeJsonRestDeserializer(), - ...makeJsonRestSerializer(), - ...makeJsonRestAdapter({ + ...makeRestDeserializer(), + ...makeRestSerializer(), + ...makeRestAdapter({ baseURL: 'https://example.com/api', }), }); diff --git a/packages/rest/tests/mocks/models/comment.mock.ts b/packages/rest/tests/mocks/models/comment.mock.ts index 826d4344..ca7f1ba8 100644 --- a/packages/rest/tests/mocks/models/comment.mock.ts +++ b/packages/rest/tests/mocks/models/comment.mock.ts @@ -1,6 +1,9 @@ -import { attr, makeModel, toString } from '@foscia/core'; +import { attr, hasOne, makeModel, toString } from '@foscia/core'; +import type GalleryMock from './gallery.mock'; +import type Post from './post.mock'; export default class CommentMock extends makeModel('comments', { body: attr(toString()), + commentable: hasOne(['posts', 'galleries']), }) { } diff --git a/packages/rest/tests/unit/makeJsonRestDeserializer.test.ts b/packages/rest/tests/unit/makeRestDeserializer.test.ts similarity index 93% rename from packages/rest/tests/unit/makeJsonRestDeserializer.test.ts rename to packages/rest/tests/unit/makeRestDeserializer.test.ts index 48779154..dbf61966 100644 --- a/packages/rest/tests/unit/makeJsonRestDeserializer.test.ts +++ b/packages/rest/tests/unit/makeRestDeserializer.test.ts @@ -10,10 +10,10 @@ import { makeTransformer, Model, } from '@foscia/core'; -import { makeJsonRestDeserializer } from '@foscia/rest'; +import { makeRestDeserializer } from '@foscia/rest'; import { describe, expect, it } from 'vitest'; -describe.concurrent('unit: makeJsonRestSerializer', () => { +describe.concurrent('unit: makeRestDeserializer', () => { (() => { const input = [{ id: '1', @@ -126,7 +126,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { [{ model: User, instance: new User() }], [{ model: Post, instance: new Post(), relation: Post.$schema.author }], ])('should deserialize using various models context and no registry', async (context) => { - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); const { instances } = await deserializer.deserialize(input, context); @@ -168,7 +168,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { [{ registry, model: User, instance: new User() }], [{ registry, model: Post, instance: new Post(), relation: Post.$schema.author }], ])('should deserialize using various models context and registry', async (context) => { - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); const { instances } = await deserializer.deserialize(input, context); @@ -178,7 +178,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { })(); it('should fail when no model found using context only', () => { - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); expect(deserializer.deserialize({ id: '1' }, {})).rejects.toThrow( /No alternative found to resolve model of resource with ID `1`\./, @@ -186,7 +186,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { }); it('should fail when no model found using context and type', () => { - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); expect(deserializer.deserialize({ id: '1', type: 'categories' }, {})).rejects.toThrow( /No alternative found to resolve model of resource with ID `1` and type `categories`\./, @@ -196,7 +196,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { it('should fail when model found but not matching', () => { const model = makeModel('comments'); - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); expect(deserializer.deserialize({ id: '1', type: 'categories' }, { model })).rejects.toThrow( /No alternative found to resolve model of resource with ID `1` and type `categories`\./, @@ -211,7 +211,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { author: hasOne(() => User), }); - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); const { instances } = await deserializer.deserialize([ { id: '1', @@ -248,7 +248,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { author: hasOne(() => User), }); - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); const { instances } = await deserializer.deserialize([ { id: '1', @@ -279,7 +279,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { const Post = makeModel('posts'); const post = new Post(); - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); const { instances } = await deserializer.deserialize({ id: '1', type: 'posts', @@ -294,7 +294,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { const Post = makeModel('posts'); const post = fill(new Post(), { id: '1' }); - const { deserializer } = makeJsonRestDeserializer(); + const { deserializer } = makeRestDeserializer(); const { instances } = await deserializer.deserialize({ id: '1', type: 'posts', diff --git a/packages/rest/tests/unit/makeJsonRestSerializer.test.ts b/packages/rest/tests/unit/makeRestSerializer.test.ts similarity index 87% rename from packages/rest/tests/unit/makeJsonRestSerializer.test.ts rename to packages/rest/tests/unit/makeRestSerializer.test.ts index aec7a874..12a84fa8 100644 --- a/packages/rest/tests/unit/makeJsonRestSerializer.test.ts +++ b/packages/rest/tests/unit/makeRestSerializer.test.ts @@ -4,15 +4,19 @@ import { hasMany, hasOne, makeComposable, - makeModel, + makeModelFactory, makeTransformer, - ModelInstance, + takeSnapshot, } from '@foscia/core'; -import { makeJsonRestSerializer, RestSerializerConfig } from '@foscia/rest'; +import { makeRestSerializer, RestSerializerConfig } from '@foscia/rest'; import { Awaitable } from '@foscia/shared'; import { Assertion, describe, expect, it } from 'vitest'; -describe.concurrent('unit: makeJsonRestSerializer', () => { +describe.concurrent('unit: makeRestSerializer', () => { + const makeModel = makeModelFactory({ + limitedSnapshots: false, + }); + const authored = makeComposable({ author: hasOne(), }); @@ -79,7 +83,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { forceFill(user2, { posts: [post2] }); type TestDataProvider = [ - ModelInstance, + typeof post1, Partial>, {}, (assertion: Assertion) => Awaitable, @@ -133,7 +137,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { post1, { serializeRelation: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), }, {}, (assertion: Assertion) => assertion.resolves.toStrictEqual({ @@ -170,7 +174,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { { serializeType: true, serializeRelation: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), circularRelationBehavior: () => 'keep', }, {}, @@ -271,7 +275,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { { circularRelationBehavior: () => 'throw', serializeRelation: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), }, {}, (assertion: Assertion) => assertion.rejects.toThrowError( @@ -281,9 +285,9 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { ] as TestDataProvider[])( 'should serialize instance with configuration', async (instance, config, context, expectation) => { - const { serializer } = makeJsonRestSerializer(config); - const serialize = async () => serializer.serialize( - await serializer.serializeInstance(instance, context), + const { serializer } = makeRestSerializer(config); + const serialize = async () => serializer.serializeToData( + await serializer.serializeToRecords(takeSnapshot(instance), context), context, ); @@ -302,8 +306,8 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { post1, { serializeRelated: (_, related) => ({ - type: related.$model.$type, - id: related.id, + type: related.$instance.$model.$type, + id: related.$values.id, }), }, {}, @@ -316,7 +320,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { post1, { serializeRelated: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), }, {}, (assertion: Assertion) => assertion.resolves.toStrictEqual({ @@ -328,12 +332,12 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { ] as TestDataProvider[])( 'should serialize relation with configuration', async (instance, config, context, expectation) => { - const { serializer } = makeJsonRestSerializer(config); - const serialize = async () => serializer.serialize( - await serializer.serializeRelation( - instance, + const { serializer } = makeRestSerializer(config); + const serialize = async () => serializer.serializeToData( + await serializer.serializeToRelatedRecords( + takeSnapshot(instance), instance.$model.$schema.author, - instance.author, + takeSnapshot(instance.author), context, ), context, @@ -351,13 +355,13 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { const instance = new Model(); - const { serializer } = makeJsonRestSerializer({ + const { serializer } = makeRestSerializer({ shouldSerialize: ({ key }) => key === 'bar', serializeKey: ({ key }) => key.toUpperCase(), serializeAttribute: ({ value }) => String(value).toUpperCase(), }); - await expect(serializer.serializeInstance(instance, {})).resolves.toStrictEqual({ + await expect(serializer.serializeToRecords(takeSnapshot(instance), {})).resolves.toStrictEqual({ id: undefined, BAR: 'BAR', }); diff --git a/packages/serialization/src/errors/serializerCircularRelationError.ts b/packages/serialization/src/errors/serializerCircularRelationError.ts index 5fcd3d8e..9c720876 100644 --- a/packages/serialization/src/errors/serializerCircularRelationError.ts +++ b/packages/serialization/src/errors/serializerCircularRelationError.ts @@ -1,19 +1,21 @@ -import { ModelInstance, ModelRelation, SerializerError } from '@foscia/core'; +import { ModelRelation, ModelSnapshot, SerializerError } from '@foscia/core'; import { SerializerCircularRelationBehavior } from '@foscia/serialization/types'; /** * Error which occurs on circular relation detection during serialization. + * + * @group Errors */ export default class SerializerCircularRelationError extends SerializerError { public readonly behavior: SerializerCircularRelationBehavior; public constructor( - instance: ModelInstance, + snapshot: ModelSnapshot, relation: ModelRelation, behavior: SerializerCircularRelationBehavior, ) { super( - `Circular relation detected on \`${instance.$model.$type}.${relation.key}\` during serialization. Handling it with behavior \`${behavior}\`.`, + `Circular relation detected on \`${snapshot.$instance.$model.$type}.${relation.key}\` during serialization. Handling it with behavior \`${behavior}\`.`, ); this.behavior = behavior; diff --git a/packages/serialization/src/index.ts b/packages/serialization/src/index.ts index ecfda1a7..60795c53 100644 --- a/packages/serialization/src/index.ts +++ b/packages/serialization/src/index.ts @@ -1,13 +1,13 @@ import makeDeserializerRecordFactory from '@foscia/serialization/makeDeserializerRecordFactory'; -import makeDeserializerWith from '@foscia/serialization/makeDeserializerWith'; +import makeDeserializer from '@foscia/serialization/makeDeserializer'; import makeSerializerRecordFactory from '@foscia/serialization/makeSerializerRecordFactory'; -import makeSerializerWith from '@foscia/serialization/makeSerializerWith'; +import makeSerializer from '@foscia/serialization/makeSerializer'; export * from '@foscia/serialization/types'; export { makeDeserializerRecordFactory, - makeDeserializerWith, + makeDeserializer, makeSerializerRecordFactory, - makeSerializerWith, + makeSerializer, }; diff --git a/packages/serialization/src/makeDeserializerWith.ts b/packages/serialization/src/makeDeserializer.ts similarity index 81% rename from packages/serialization/src/makeDeserializerWith.ts rename to packages/serialization/src/makeDeserializer.ts index 70530191..008a7e76 100644 --- a/packages/serialization/src/makeDeserializerWith.ts +++ b/packages/serialization/src/makeDeserializer.ts @@ -1,14 +1,18 @@ import { + attachRelationInverse, consumeAction, consumeCache, + consumeId, consumeInstance, consumeModel, + consumeQueryAs, consumeRegistry, consumeRelation, DeserializedData, DeserializerError, forceFill, guessContextModel, + isSame, mapAttributes, mapRelations, markSynced, @@ -21,13 +25,13 @@ import { shouldSync, } from '@foscia/core'; import { - Deserializer, - DeserializerConfig, + RecordDeserializerConfig, DeserializerContext, DeserializerExtract, DeserializerInstancesMap, DeserializerModelIdentifier, DeserializerRecord, + RecordDeserializer, } from '@foscia/serialization/types'; import { type Arrayable, @@ -36,18 +40,26 @@ import { isNone, makeIdentifiersMap, mapArrayable, + using, wrap, } from '@foscia/shared'; // eslint-disable-next-line max-len /* eslint no-param-reassign: ["error", { "props": true, "ignorePropertyModificationsForRegex": ["^instance"] } ] */ +/** + * Make a {@link RecordDeserializer | `RecordDeserializer`} using the given config. + * + * @param config + * + * @category Factories + */ export default < Record, Data, Deserialized extends DeserializedData, Extract extends DeserializerExtract, ->(config: DeserializerConfig) => { +>(config: RecordDeserializerConfig) => { const NON_IDENTIFIED_LOCAL_ID = '__foscia_non_identified_local_id__'; const makeModelIdentifier = async ( @@ -71,7 +83,8 @@ export default < // we'll try to resolve the model from the context. // This will also ensure guessed model type matches deserializing record. const guessedModel = await guessContextModel({ - model: (record.parent?.instance.$model ?? consumeModel(context, null)) as Model, + queryAs: consumeQueryAs(context, null), + model: (record.parent?.instance.$model ?? consumeModel(context, null)) as Model | null, relation: record.parent?.def ?? consumeRelation(context, null), registry, ensureType: identifier.type, @@ -104,23 +117,17 @@ You should either: const findInstance = async ( identifier: DeserializerModelIdentifier, context: {}, - ) => { - const cache = await consumeCache(context, null); - if (cache && !isNil(identifier.id)) { - return cache.find(identifier.model.$type, identifier.id); - } - - const instance = consumeInstance(context, null); - if ( - instance - && identifier.id === instance.id - && identifier.model.$type === instance.$model.$type - ) { - return instance; - } - - return null; - }; + ) => using(await consumeCache(context, null), (cache) => ( + cache && !isNil(identifier.id) + ? cache.find(identifier.model.$type, identifier.id) + : using(consumeInstance(context, null), (instance) => ( + instance + && identifier.id === instance.id + && identifier.model.$type === instance.$model.$type + ? instance + : null + )) + )); const findOrMakeInstance = async ( identifier: DeserializerModelIdentifier, @@ -156,7 +163,7 @@ You should either: instancesMap, )); - let deserializer: Deserializer; + let deserializer: RecordDeserializer; const makeInstancesMapIdentifier = (identifier: DeserializerModelIdentifier) => ( identifier.id ?? identifier.lid ?? NON_IDENTIFIED_LOCAL_ID @@ -205,8 +212,8 @@ You should either: setInstanceId('id'); setInstanceId('lid'); - await Promise.all([ - ...mapAttributes(instance, async (def) => { + await Promise.all(Object.values({ + ...mapAttributes(instance.$model, async (def) => { const deserializerContext = await makeDeserializerContext( instance, def, @@ -214,12 +221,10 @@ You should either: context, ); if (await shouldDeserialize(deserializerContext)) { - forceFill(instance, { - [def.key]: await deserializeAttributeValue(deserializerContext), - }); + forceFill(instance, { [def.key]: await deserializeAttributeValue(deserializerContext) }); } }), - ...mapRelations(instance, async (def) => { + ...mapRelations(instance.$model, async (def) => { const deserializerContext = await makeDeserializerContext( instance, def, @@ -227,26 +232,25 @@ You should either: context, ); if (await shouldDeserialize(deserializerContext)) { - forceFill(instance, { - [def.key]: await deserializeRelationValue(deserializerContext, instancesMap), - }); + const related = await deserializeRelationValue(deserializerContext, instancesMap); + + forceFill(instance, { [def.key]: related }); instance.$loaded[def.key] = true; + + attachRelationInverse(instance, def, wrap(related)); } }), - ]); + })); const action = consumeAction(context, null); - instance.$exists = action !== 'destroy'; - instance.$raw = record.raw; - markSynced(instance); - await runHooks(instance.$model, 'retrieved', instance); - - const cache = await consumeCache(context, null); - if (cache && !isNil(instance.id)) { - await cache.put(instance.$model.$type, instance.id, instance); - } + instance.$exists = !(action === 'destroy' && ( + isSame(instance, consumeInstance(context, null)) || ( + instance.$model === consumeModel(context, null) && instance.id === consumeId(context, null) + ) + )); + instance.$raw = record.raw; return instance; }; @@ -298,6 +302,21 @@ You should either: } }; + const releaseInstancesMap = ( + context: {}, + instancesMap: DeserializerInstancesMap, + ) => Promise.all(instancesMap.all().map(async (instancePromise) => { + const instance = await instancePromise; + + markSynced(instance); + await runHooks(instance.$model, 'retrieved', instance); + + const cache = await consumeCache(context, null); + if (cache && !isNil(instance.id)) { + await cache.put(instance.$model.$type, instance.id, instance); + } + })); + const deserialize = async (data: Data, context: {}) => { const extract = await config.extractData(data, context); const records = await mapArrayable( @@ -315,6 +334,14 @@ You should either: instancesMap, ))); + const parent = consumeInstance(context, null); + const relation = consumeRelation(context, null); + if (parent && relation) { + attachRelationInverse(parent, relation, instances); + } + + await releaseInstancesMap(context, instancesMap); + return ( config.createData ? config.createData(instances, extract, context) : { instances } ) as Deserialized; @@ -325,5 +352,5 @@ You should either: deserializeRecord, }; - return deserializer; + return { deserializer }; }; diff --git a/packages/serialization/src/makeDeserializerRecordFactory.ts b/packages/serialization/src/makeDeserializerRecordFactory.ts index 1356cf87..768632e7 100644 --- a/packages/serialization/src/makeDeserializerRecordFactory.ts +++ b/packages/serialization/src/makeDeserializerRecordFactory.ts @@ -8,6 +8,15 @@ import { } from '@foscia/serialization/types'; import { Arrayable, Awaitable, mapArrayable } from '@foscia/shared'; +/** + * Make a {@link DeserializerRecordFactory | `DeserializerRecordFactory`} implementation. + * + * @param pullIdentifier + * @param pullAttribute + * @param pullRelation + * + * @category Factories + */ export default < Record, Data = unknown, diff --git a/packages/serialization/src/makeSerializerWith.ts b/packages/serialization/src/makeSerializer.ts similarity index 57% rename from packages/serialization/src/makeSerializerWith.ts rename to packages/serialization/src/makeSerializer.ts index 04b58db0..790cf05a 100644 --- a/packages/serialization/src/makeSerializerWith.ts +++ b/packages/serialization/src/makeSerializer.ts @@ -1,85 +1,95 @@ import { - changed, + isSameSnapshot, mapAttributes, mapRelations, ModelAttribute, - ModelInstance, ModelRelation, + ModelSnapshot, normalizeKey, shouldSync, } from '@foscia/core'; import SerializerCircularRelationError from '@foscia/serialization/errors/serializerCircularRelationError'; import { - Serializer, - SerializerConfig, + RecordSerializer, + RecordSerializerConfig, SerializerContext, SerializerParents, } from '@foscia/serialization/types'; -import { Arrayable, Awaitable, mapArrayable } from '@foscia/shared'; - +import { Arrayable, Awaitable, mapArrayable, using } from '@foscia/shared'; + +/** + * Make a {@link RecordSerializer | `RecordSerializer`} using the given config. + * + * @param config + * + * @category Factories + */ export default ( - config: SerializerConfig, + config: RecordSerializerConfig, ) => { const shouldSerialize = config.shouldSerialize ?? ((context) => ( shouldSync(context.def, ['push']) && context.value !== undefined - && changed(context.instance, context.def.key) + && ( + !context.snapshot.$original + || !isSameSnapshot(context.snapshot, context.snapshot.$original, context.def.key) + ) )); const serializeKey = config.serializeKey - ?? ((context) => normalizeKey(context.instance.$model, context.def.key)); + ?? ((context) => normalizeKey(context.snapshot.$instance.$model, context.def.key)); const serializeAttributeValue = config.serializeAttribute ?? ((context) => (context.def.transformer?.serialize ?? ((v) => v))(context.value)); const serializeRelation = config.serializeRelation - ?? ((_, related) => related.id); + ?? ((_, related) => related.$values.id); const serializeRelated = config.serializeRelated - ?? ((_, related) => related.id); + ?? ((_, related) => related.$values.id); const serializeRelationWith = async ( context: SerializerContext, serialize: ( context: SerializerContext, - related: ModelInstance, + snapshot: ModelSnapshot, parents: SerializerParents, ) => Awaitable, parents: SerializerParents, ) => mapArrayable(context.value, (related) => serialize( context, - related as ModelInstance, + related as ModelSnapshot, parents, )); const isCircularRelation = config.isCircularRelation ?? ((context, parents) => parents.some((parent) => ( - parent.instance.$model === context.instance.$model && parent.def === context.def + parent.model === context.snapshot.$instance.$model && parent.def === context.def ))); const circularRelationBehavior = config.circularRelationBehavior ?? (() => 'skip'); - let serializer: Serializer; + let serializer: RecordSerializer; const makeSerializerContext = ( - instance: ModelInstance, + snapshot: ModelSnapshot, def: Def, context: {}, - ) => ({ instance, def, key: def.key, value: instance[def.key], context, serializer }); + ) => ({ snapshot, def, key: def.key, value: snapshot.$values[def.key], context, serializer }); - const serializeInstance = async ( - instance: ModelInstance, + const serializeToRecords = async ( + snapshots: ModelSnapshot[] | ModelSnapshot | null, context: {}, parents: SerializerParents = [], - ) => { - const record = await config.createRecord(instance, context); + ) => mapArrayable(snapshots, async (snapshot) => { + const record = await config.createRecord(snapshot, context); - await Promise.all([ - ...mapAttributes(instance, async (def) => { - const serializerContext = makeSerializerContext(instance, def, context); + await Promise.all(Object.values({ + ...mapAttributes(snapshot.$instance.$model, async (def) => { + const serializerContext = makeSerializerContext(snapshot, def, context); if (await shouldSerialize(serializerContext)) { const key = await serializeKey(serializerContext); const value = await serializeAttributeValue(serializerContext); @@ -87,8 +97,8 @@ export default ( await record.put({ ...serializerContext, key, value }); } }), - ...mapRelations(instance, async (def) => { - const serializerContext = makeSerializerContext(instance, def, context); + ...mapRelations(snapshot.$instance.$model, async (def) => { + const serializerContext = makeSerializerContext(snapshot, def, context); const isCircular = await isCircularRelation(serializerContext, parents); if (isCircular) { const circularBehavior = await circularRelationBehavior(serializerContext, parents); @@ -96,7 +106,7 @@ export default ( return; } - throw new SerializerCircularRelationError(instance, def, circularBehavior); + throw new SerializerCircularRelationError(snapshot, def, circularBehavior); } if (await shouldSerialize(serializerContext)) { @@ -104,7 +114,7 @@ export default ( try { const value = await serializeRelationWith(serializerContext, serializeRelation, [ ...parents, - { instance, def }, + { model: snapshot.$instance.$model, def }, ]); await record.put({ ...serializerContext, key, value }); @@ -119,34 +129,31 @@ export default ( } } }), - ]); + })); return record.retrieve(); - }; - - const serialize = async ( - records: Arrayable | null, - context: {}, - ) => (config.createData ? config.createData(records, context) : records) as Data; + }); serializer = { - serializeRelation: async ( - instance: ModelInstance, + serializeToRelatedRecords: async ( + parent: ModelSnapshot, def: ModelRelation, - value: Arrayable | null, + value: ModelSnapshot[] | ModelSnapshot | null, context: {}, - ) => { - const serializerContext = makeSerializerContext(instance, def, context); - - return serializeRelationWith( + ) => using( + makeSerializerContext(parent, def, context), + (serializerContext) => serializeRelationWith( { ...serializerContext, value }, serializeRelated, - [{ instance, def }], - ); - }, - serializeInstance, - serialize, - }; - - return serializer; + [{ model: parent.$instance.$model, def }], + ), + ), + serializeToRecords, + serializeToData: async ( + records: Arrayable | null, + context: {}, + ) => (config.createData ? config.createData(records, context) : records) as Data, + } as RecordSerializer; + + return { serializer }; }; diff --git a/packages/serialization/src/makeSerializerRecordFactory.ts b/packages/serialization/src/makeSerializerRecordFactory.ts index acfc0989..66586565 100644 --- a/packages/serialization/src/makeSerializerRecordFactory.ts +++ b/packages/serialization/src/makeSerializerRecordFactory.ts @@ -1,27 +1,31 @@ -import { ModelInstance } from '@foscia/core'; +import { ModelSnapshot } from '@foscia/core'; import { SerializerContext, SerializerRecordFactory } from '@foscia/serialization/types'; -import { Awaitable } from '@foscia/shared'; +import { Awaitable, using } from '@foscia/shared'; +/** + * Make a {@link SerializerRecordFactory | `SerializerRecordFactory`} implementation. + * + * @param initialize + * @param put + * + * @category Factories + */ export default < Record, Related, Data, >( - initialize: (instance: ModelInstance, context: {}) => Awaitable, + initialize: (snapshot: ModelSnapshot, context: {}) => Awaitable, put: ( record: Record, serializerContext: SerializerContext, ) => Awaitable, ): SerializerRecordFactory => async ( - instance: ModelInstance, + snapshot: ModelSnapshot, context: {}, -) => { - const record = await initialize(instance, context); - - return { - put: ( - serializerContext: SerializerContext, - ) => put(record, serializerContext), - retrieve: () => record, - }; -}; +) => using(await initialize(snapshot, context), (record) => ({ + put: ( + serializerContext: SerializerContext, + ) => put(record, serializerContext), + retrieve: () => record, +})); diff --git a/packages/serialization/src/types.ts b/packages/serialization/src/types.ts index 14861ac8..d8243fa3 100644 --- a/packages/serialization/src/types.ts +++ b/packages/serialization/src/types.ts @@ -1,15 +1,19 @@ import { DeserializedData, - DeserializerI, + Deserializer, Model, ModelAttribute, ModelIdType, ModelInstance, ModelRelation, - SerializerI, + ModelSnapshot, + Serializer, } from '@foscia/core'; import { type Arrayable, Awaitable, IdentifiersMap } from '@foscia/shared'; +/** + * Context given for an instance property deserialization. + */ export type DeserializerContext< Record, Data = unknown, @@ -21,15 +25,21 @@ export type DeserializerContext< key: string; value: unknown; context: {}; - deserializer: Deserializer; + deserializer: RecordDeserializer; }; +/** + * Deserializer record identifiers object. + */ export type DeserializerRecordIdentifier = { type?: string; id?: ModelIdType; lid?: ModelIdType; }; +/** + * Deserializer record identifiers object with resolved model. + */ export type DeserializerModelIdentifier = { model: Model; type: string; @@ -37,15 +47,29 @@ export type DeserializerModelIdentifier = { lid?: ModelIdType; }; +/** + * Data extracted from adapter data. + */ export type DeserializerExtract = { records: Arrayable | null; }; +/** + * Parent context of a deserializer record. + * + * @internal + */ export type DeserializerRecordParent = { instance: ModelInstance; def: ModelRelation; }; +/** + * Wrapper for a deserializer record with identification properties + * and raw attributes/relations data extraction. + * + * @internal + */ export type DeserializerRecord = { readonly raw: Record; readonly identifier: DeserializerRecordIdentifier; @@ -58,6 +82,11 @@ export type DeserializerRecord> | null | undefined>; }; +/** + * Factory to create a deserializer record from a data extraction and a context. + * + * @internal + */ export type DeserializerRecordFactory< Record, Data = unknown, @@ -70,45 +99,117 @@ export type DeserializerRecordFactory< parent?: DeserializerRecordParent, ) => Promise>; +/** + * Deserializer map of instance promises by type and ID. + * + * @internal + */ export type DeserializerInstancesMap = IdentifiersMap>; -export type DeserializerConfig< +/** + * Configuration for generic record deserializer. + * + * @interface + * + * @internal + */ +export type RecordDeserializerConfig< Record, Data, Deserialized extends DeserializedData, Extract extends DeserializerExtract, > = { + /** + * Extract a records set from adapter's response data. + * + * @param data + * @param context + */ extractData: (data: Data, context: {}) => Awaitable; + /** + * Create a deserializer record from. + */ createRecord: DeserializerRecordFactory; + /** + * Create deserialized instances wrapper object which might contain other data. + * Defaults to no additional data provided. + * + * @param instances + * @param extract + * @param context + */ createData?: ( instances: ModelInstance[], extract: Extract, context: {}, ) => Awaitable; + /** + * Check if an instance attribute or relation should be deserialized or not. + * Defaults to checking if value is not `undefined`. + * + * @param deserializerContext + */ shouldDeserialize?: ( deserializerContext: DeserializerContext, ) => Awaitable; + /** + * Deserialize an instance attribute or relation key. + * Defaults to key aliasing and normalization. + * + * @param deserializerContext + */ deserializeKey?: ( deserializerContext: DeserializerContext, ) => Awaitable; + /** + * Deserialize an instance attribute value. + * Defaults to the use of attribute transformer if set. + * + * @param deserializerContext + */ deserializeAttribute?: ( deserializerContext: DeserializerContext, ) => Awaitable; + /** + * Deserialize an instance relation's related instance(s). + * Defaults to instance deserialization through deserializer. + * + * @param deserializerContext + * @param related + * @param instancesMap + */ deserializeRelated?: ( deserializerContext: DeserializerContext, related: DeserializerRecord, instancesMap: DeserializerInstancesMap, - ) => Awaitable; + ) => Awaitable; }; -export interface Deserializer - extends DeserializerI { - deserializeRecord( - record: DeserializerRecord, - context: {}, - instancesMap?: DeserializerInstancesMap, - ): Awaitable; -} +/** + * Generic record deserializer. + * + * @interface + * + * @internal + */ +export type RecordDeserializer = + & { + /** + * Deserialize one record. + * + * @param record + * @param context + * @param instancesMap + * + * @internal + */ + deserializeRecord( + record: DeserializerRecord, + context: {}, + instancesMap?: DeserializerInstancesMap, + ): Awaitable; + } + & Deserializer; /** * Serializer context passed to config callbacks. @@ -119,16 +220,18 @@ export type SerializerContext< Data = unknown, Def = ModelAttribute | ModelRelation, > = { - instance: ModelInstance; + snapshot: ModelSnapshot; def: Def; key: string; value: unknown; context: {}; - serializer: Serializer; + serializer: RecordSerializer; }; /** - * Pending serializer record. + * Pending serializer record which holds properties building. + * + * @internal */ export type SerializerRecord = { /** @@ -145,58 +248,157 @@ export type SerializerRecord = { retrieve(): Awaitable; }; +/** + * Factory to create a pending serializer record. + * + * @internal + */ export type SerializerRecordFactory = ( - instance: ModelInstance, + snapshot: ModelSnapshot, context: {}, ) => Awaitable>; /** * Array of previously serialized relationships to avoid circular relations * serialization. + * + * @internal */ -export type SerializerParents = { instance: ModelInstance; def: ModelRelation }[]; +export type SerializerParents = { model: Model; def: ModelRelation }[]; /** - * Behavior when encountering a circular relation. + * Available behaviors to apply when encountering a circular relation: + * + * - `throw` will throw an exception on circular relation encounter. + * - `keep` will keep the circular relation serialized value. + * - `skip` will not serialize the circular relation. + * + * @internal */ export type SerializerCircularRelationBehavior = 'throw' | 'skip' | 'keep'; -export type SerializerConfig = { +/** + * Configuration for generic record serializer. + * + * @interface + * + * @internal + */ +export type RecordSerializerConfig = { + /** + * Create a serializer record object which can be hydrated and retrieved. + */ createRecord: SerializerRecordFactory; + /** + * Create adapter data value from a set of serialized record. + * Defaults to records without any wrapper object or anything. + * + * @param records + * @param context + */ createData?: (records: Arrayable | null, context: {}) => Awaitable; + /** + * Check if an instance attribute or relation should be serialized or not. + * Defaults to checking if value did not change since last sync and is not `undefined`. + * + * @param serializerContext + */ shouldSerialize?: ( serializerContext: SerializerContext, ) => Awaitable; + /** + * Serialize an instance attribute or relation key. + * Defaults to key aliasing and normalization. + * + * @param serializerContext + */ serializeKey?: ( serializerContext: SerializerContext, ) => Awaitable; + /** + * Serialize an instance attribute value. + * Defaults to the use of attribute transformer if set. + * + * @param serializerContext + */ serializeAttribute?: ( serializerContext: SerializerContext, ) => Awaitable; + /** + * Serialize an instance relation's related instance(s). + * Defaults to the instance ID. + * + * @param serializerContext + * @param related + * @param parents + */ serializeRelation?: ( serializerContext: SerializerContext, - related: ModelInstance, + related: ModelSnapshot, parents: SerializerParents, ) => Awaitable; + /** + * Serialize an instance relation's related instance(s) when outside a parent + * serialization context, such as in attach/detach queries. + * Defaults to the instance ID. + * + * @param serializerContext + * @param related + * @param parents + */ serializeRelated?: ( serializerContext: SerializerContext, - related: ModelInstance, + related: ModelSnapshot, parents: SerializerParents, ) => Awaitable | null>; + /** + * Detect if the given context is a circular relation. + * Defaults to true if model's relation is in the parents chain. + * + * @param serializerContext + * @param parents + */ isCircularRelation?: ( serializerContext: SerializerContext, parents: SerializerParents, ) => Awaitable; + /** + * Tell how circular relation should be handled by returning the + * {@link SerializerCircularRelationBehavior | behavior} to apply. + * Default to `skip`. + * + * @param serializerContext + * @param parents + */ circularRelationBehavior?: ( serializerContext: SerializerContext, parents: SerializerParents, ) => Awaitable; }; -export interface Serializer extends SerializerI { - serializeInstance( - instance: ModelInstance, - context: {}, - parents?: SerializerParents, - ): Awaitable; -} +/** + * Generic record serializer. + * + * @interface + * + * @internal + */ +export type RecordSerializer = + & { + /** + * Serialize snapshots to records. + * This overload handles circular relations using the parents of the instance. + * + * @param value + * @param context + * @param parents + * + * @internal + */ + serializeToRecords( + value: ModelSnapshot, + context: {}, + parents: SerializerParents, + ): Awaitable; + } + & Serializer; diff --git a/packages/serialization/tests/unit/makeSerializerWith.test.ts b/packages/serialization/tests/unit/makeSerializer.test.ts similarity index 61% rename from packages/serialization/tests/unit/makeSerializerWith.test.ts rename to packages/serialization/tests/unit/makeSerializer.test.ts index 32573738..f529c718 100644 --- a/packages/serialization/tests/unit/makeSerializerWith.test.ts +++ b/packages/serialization/tests/unit/makeSerializer.test.ts @@ -1,11 +1,18 @@ -import { fill, hasMany, hasOne, makeModel } from '@foscia/core'; -import { makeSerializerRecordFactory, makeSerializerWith } from '@foscia/serialization'; +import { fill, hasMany, hasOne, makeModelFactory, takeSnapshot } from '@foscia/core'; +import { makeSerializer, makeSerializerRecordFactory } from '@foscia/serialization'; import { Dictionary } from '@foscia/shared'; import { describe, expect, it } from 'vitest'; -describe('unit: makeSerializerWith', () => { +describe('unit: makeSerializer', () => { it.concurrent('should support circular relations', async () => { - const PostMock = makeModel('posts', { + const makeModel = makeModelFactory({ + limitedSnapshots: false, + }); + + const PostMock = makeModel({ + type: 'posts', + limitedSnapshots: false, + }, { comments: hasMany(), }); const CommentMock = makeModel('comments', { @@ -23,20 +30,20 @@ describe('unit: makeSerializerWith', () => { fill(comment, { id: 2, author: user }); fill(user, { id: 3, posts: [post] }); - const deepSerializer = makeSerializerWith({ + const { serializer: deepSerializer } = makeSerializer({ createRecord: makeSerializerRecordFactory( - (instance) => ({ id: instance.id } as Dictionary), + (snapshot) => ({ id: snapshot.$values.id } as Dictionary), (record, { key, value }) => { // eslint-disable-next-line no-param-reassign record[key] = value; }, ), serializeRelation: ({ serializer, context }, related, parents) => serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), }); await expect( - deepSerializer.serializeInstance(post, {}), + deepSerializer.serializeToRecords(takeSnapshot(post), {}), ).resolves.toStrictEqual({ id: 1, comments: [{ id: 2, author: { id: 3 } }], diff --git a/packages/shared/README.md b/packages/shared/README.md index 4c3959c8..a3470b6c 100644 --- a/packages/shared/README.md +++ b/packages/shared/README.md @@ -18,7 +18,8 @@ Shared utilities types and functions for [Foscia](https://foscia.dev). -> :warning: You should not use this package as its API may not be stable. +> :warning: This package is internal to Foscia and should not be used by +> external code. It may contain breaking change on each new version. ## License diff --git a/packages/shared/src/arrays/mapArrayable.ts b/packages/shared/src/arrays/mapArrayable.ts index 405e44dd..cda085d8 100644 --- a/packages/shared/src/arrays/mapArrayable.ts +++ b/packages/shared/src/arrays/mapArrayable.ts @@ -1,17 +1,21 @@ import isNil from '@foscia/shared/checks/isNil'; -import { Arrayable, Awaitable, Optional } from '@foscia/shared/types'; +import { Awaitable } from '@foscia/shared/types'; +/** + * Map an optional arrayable value. + * + * @param value + * @param callback + * + * @internal + */ export default async ( - value: Optional>, - callback: (value: T) => Awaitable, + value: T[] | T, + callback: (value: NonNullable) => Awaitable, ) => { if (Array.isArray(value)) { - return Promise.all(value.map((v) => callback(v))); + return Promise.all(value.map((v) => callback(v as NonNullable))); } - if (!isNil(value)) { - return callback(value); - } - - return value; + return isNil(value) ? value : callback(value as NonNullable); }; diff --git a/packages/shared/src/arrays/mapWithKeys.ts b/packages/shared/src/arrays/mapWithKeys.ts index 7042cc0c..5839a4d6 100644 --- a/packages/shared/src/arrays/mapWithKeys.ts +++ b/packages/shared/src/arrays/mapWithKeys.ts @@ -1,11 +1,25 @@ import { Dictionary } from '@foscia/shared/types'; const mapWithKeys: { - ( + /** + * Map array to an object. + * + * @param values + * @param callback + * + * @internal + */( values: T[], callback: (value: T, key: number) => U, ): U; - ( + /** + * Map object to another object. + * + * @param values + * @param callback + * + * @internal + */( values: T, callback: (value: T[K], key: K) => U, ): U; diff --git a/packages/shared/src/arrays/uniqueValues.ts b/packages/shared/src/arrays/uniqueValues.ts index 69311166..93a45b29 100644 --- a/packages/shared/src/arrays/uniqueValues.ts +++ b/packages/shared/src/arrays/uniqueValues.ts @@ -1 +1,8 @@ +/** + * Remove duplicates from array. + * + * @param values + * + * @internal + */ export default (values: T[]) => [...new Set(values)]; diff --git a/packages/shared/src/arrays/wrap.ts b/packages/shared/src/arrays/wrap.ts index baef1af1..6b756515 100644 --- a/packages/shared/src/arrays/wrap.ts +++ b/packages/shared/src/arrays/wrap.ts @@ -1,6 +1,13 @@ import isNil from '@foscia/shared/checks/isNil'; import { Arrayable, Optional } from '@foscia/shared/types'; +/** + * Wrap value to array. + * + * @param value + * + * @internal + */ export default (value?: Optional>): T[] => { if (isNil(value)) { return []; diff --git a/packages/shared/src/arrays/wrapVariadic.ts b/packages/shared/src/arrays/wrapVariadic.ts index fb796abf..5aed818e 100644 --- a/packages/shared/src/arrays/wrapVariadic.ts +++ b/packages/shared/src/arrays/wrapVariadic.ts @@ -1,5 +1,12 @@ import { ArrayableVariadic } from '@foscia/shared/types'; +/** + * Wrap value (from variadic parameter) to array. + * + * @param values + * + * @internal + */ export default (...values: ArrayableVariadic): T[] => ( values.length === 1 && Array.isArray(values[0]) ? values[0] : values as T[] ); diff --git a/packages/shared/src/checks/isFosciaFlag.ts b/packages/shared/src/checks/isFosciaFlag.ts new file mode 100644 index 00000000..dbefa517 --- /dev/null +++ b/packages/shared/src/checks/isFosciaFlag.ts @@ -0,0 +1,19 @@ +import { FosciaFlaggedObject } from '@foscia/shared/types'; + +/** + * Check if value is a Foscia flagged object with given bit flag. + * + * @param value + * @param flag + * + * @internal + */ +export default ( + value: unknown, + flag: number, +): value is T => !!value + && (typeof value === 'object' || typeof value === 'function') + && '$FOSCIA_FLAGS' in value + && typeof value.$FOSCIA_FLAGS === 'number' + // eslint-disable-next-line no-bitwise + && !!(flag & value.$FOSCIA_FLAGS); diff --git a/packages/shared/src/checks/isFosciaType.ts b/packages/shared/src/checks/isFosciaType.ts index 3a4c92a6..5a68f083 100644 --- a/packages/shared/src/checks/isFosciaType.ts +++ b/packages/shared/src/checks/isFosciaType.ts @@ -1,4 +1,17 @@ -export default (value: unknown, symbol: Symbol) => !!value +import { FosciaObject } from '@foscia/shared/types'; + +/** + * Check if value is a Foscia object of given type. + * + * @param value + * @param symbol + * + * @internal + */ +export default ( + value: unknown, + symbol: S, +): value is FosciaObject => !!value && (typeof value === 'object' || typeof value === 'function') && '$FOSCIA_TYPE' in value && value.$FOSCIA_TYPE === symbol; diff --git a/packages/shared/src/checks/isNil.ts b/packages/shared/src/checks/isNil.ts index c19857ba..44938806 100644 --- a/packages/shared/src/checks/isNil.ts +++ b/packages/shared/src/checks/isNil.ts @@ -1,2 +1,9 @@ +/** + * Check if value is `undefined` or `null`. + * + * @param value + * + * @internal + */ export default (value: unknown): value is undefined | null => value === undefined || value === null; diff --git a/packages/shared/src/checks/isNone.ts b/packages/shared/src/checks/isNone.ts index f74bb7c3..3db8f394 100644 --- a/packages/shared/src/checks/isNone.ts +++ b/packages/shared/src/checks/isNone.ts @@ -1,4 +1,11 @@ import isNil from '@foscia/shared/checks/isNil'; import { Optional } from '@foscia/shared/types'; +/** + * Check if value is `undefined`, `null` or an empty string. + * + * @param value + * + * @internal + */ export default (value: unknown): value is Optional<''> => isNil(value) || value === ''; diff --git a/packages/shared/src/configs/mergeConfig.ts b/packages/shared/src/configs/mergeConfig.ts index 7b420143..6cea9c5b 100644 --- a/packages/shared/src/configs/mergeConfig.ts +++ b/packages/shared/src/configs/mergeConfig.ts @@ -1,9 +1,13 @@ +import tap from '@foscia/shared/functions/tap'; + /** * Merge two config objects. * * @param config * @param newConfig * @param override + * + * @internal */ export default ( config: C, @@ -11,12 +15,13 @@ export default ( override = true, ): C => ({ ...config, - ...(Object.entries(newConfig) as [keyof C, C[keyof C]][]).reduce((keptConfig, [key, value]) => { - if (value !== undefined && (override || config[key] === undefined)) { - // eslint-disable-next-line no-param-reassign - keptConfig[key] = value; - } - - return keptConfig; - }, {} as Partial), + ...(Object.entries(newConfig) as [keyof C, C[keyof C]][]).reduce( + (keptConfig, [key, value]) => tap(keptConfig, () => { + if (value !== undefined && (override || config[key] === undefined)) { + // eslint-disable-next-line no-param-reassign + keptConfig[key] = value; + } + }), + {} as Partial, + ), }); diff --git a/packages/shared/src/dates/removeTimezoneOffset.ts b/packages/shared/src/dates/removeTimezoneOffset.ts index 7d6eae4b..346e353a 100644 --- a/packages/shared/src/dates/removeTimezoneOffset.ts +++ b/packages/shared/src/dates/removeTimezoneOffset.ts @@ -2,5 +2,7 @@ * Remove current timezone offset of a localized date. * * @param date + * + * @internal */ export default (date: Date) => new Date(date.getTime() - (date.getTimezoneOffset() * 60 * 1000)); diff --git a/packages/shared/src/descriptors/eachDescriptors.ts b/packages/shared/src/descriptors/eachDescriptors.ts index 45dd075c..46adf6dc 100644 --- a/packages/shared/src/descriptors/eachDescriptors.ts +++ b/packages/shared/src/descriptors/eachDescriptors.ts @@ -1,6 +1,14 @@ import isNil from '@foscia/shared/checks/isNil'; import isDescriptorHolder from '@foscia/shared/descriptors/isDescriptorHolder'; +/** + * Map every descriptor of object (while unwrapping description holder). + * + * @param obj + * @param callback + * + * @internal + */ export default ( obj: object, callback: (key: string, descriptor: PropertyDescriptor) => void, diff --git a/packages/shared/src/descriptors/isDescriptorHolder.ts b/packages/shared/src/descriptors/isDescriptorHolder.ts index 23b705b2..4f624b13 100644 --- a/packages/shared/src/descriptors/isDescriptorHolder.ts +++ b/packages/shared/src/descriptors/isDescriptorHolder.ts @@ -2,6 +2,13 @@ import isFosciaType from '@foscia/shared/checks/isFosciaType'; import { SYMBOL_DESCRIPTOR_HOLDER } from '@foscia/shared/descriptors/makeDescriptorHolder'; import { DescriptorHolder } from '@foscia/shared/descriptors/types'; +/** + * Check if value is a descriptor holder. + * + * @param value + * + * @internal + */ export default ( value: unknown, ): value is DescriptorHolder => isFosciaType(value, SYMBOL_DESCRIPTOR_HOLDER); diff --git a/packages/shared/src/descriptors/makeDescriptorHolder.ts b/packages/shared/src/descriptors/makeDescriptorHolder.ts index 706cbb85..231eaab1 100644 --- a/packages/shared/src/descriptors/makeDescriptorHolder.ts +++ b/packages/shared/src/descriptors/makeDescriptorHolder.ts @@ -1,8 +1,20 @@ import type { DescriptorHolder } from '@foscia/shared/descriptors/types'; +/** + * Symbol for the descriptor holder. + * + * @internal + */ export const SYMBOL_DESCRIPTOR_HOLDER = Symbol('foscia: descriptor holder'); -export default (descriptor: PropertyDescriptor) => ({ +/** + * Create a descriptor holder. + * + * @param descriptor + * + * @internal + */ +export default (descriptor: PropertyDescriptor) => ({ $FOSCIA_TYPE: SYMBOL_DESCRIPTOR_HOLDER, descriptor, -}) as DescriptorHolder; +}) as DescriptorHolder; diff --git a/packages/shared/src/descriptors/types.ts b/packages/shared/src/descriptors/types.ts index e42eee13..8466e6b4 100644 --- a/packages/shared/src/descriptors/types.ts +++ b/packages/shared/src/descriptors/types.ts @@ -1,10 +1,39 @@ import type { SYMBOL_DESCRIPTOR_HOLDER } from '@foscia/shared/descriptors/makeDescriptorHolder'; -import { FosciaObject } from '@foscia/shared/types'; +import type { FosciaObject, WritableKeys } from '@foscia/shared/types'; /** - * Type and descriptor holder for custom properties. + * Type to hold a property descriptor specificities. + * + * @internal */ -export type DescriptorHolder = { +export type DescriptorHolder = { descriptor: PropertyDescriptor; - __type__: T; + /** + * Stores the value typing for type resolution. + * + * @internal + */ + _type: T; + /** + * Stores the readonly state for type resolution. + */ + _readOnly: R; } & FosciaObject; + +/** + * Type a descriptor holder an object property. + * + * @internal + */ +export type DescriptorHolderOf = + DescriptorHolder ? false : true>; + +/** + * Restore the property typing for a descriptor holder. + * + * @internal + */ +export type RestoreDescriptorHolder = + D extends DescriptorHolder + ? R extends false ? Record : Readonly> + : U; diff --git a/packages/shared/src/env.ts b/packages/shared/src/env.ts index 9bd9747d..c1885a42 100644 --- a/packages/shared/src/env.ts +++ b/packages/shared/src/env.ts @@ -14,11 +14,22 @@ const detectEnv = () => { return 'production'; } catch { - return false; + return 'production'; } }; const detectedEnv = detectEnv(); +/** + * `true` if node detected env is `development`. + * + * @internal + */ export const IS_DEV = detectedEnv === 'development'; + +/** + * `true` if node detected env is `test`. + * + * @internal + */ export const IS_TEST = detectedEnv === 'test'; diff --git a/packages/shared/src/functions/tap.ts b/packages/shared/src/functions/tap.ts new file mode 100644 index 00000000..d696960b --- /dev/null +++ b/packages/shared/src/functions/tap.ts @@ -0,0 +1,17 @@ +/** + * Call given callback and return value. + * Since callback is not awaited, it does not support async callbacks. + * + * @param value + * @param callback + * + * @internal + */ +export default any>( + value: T, + callback: ReturnType extends Promise ? never : C, +) => { + callback(value); + + return value; +}; diff --git a/packages/shared/src/functions/using.ts b/packages/shared/src/functions/using.ts new file mode 100644 index 00000000..359498d0 --- /dev/null +++ b/packages/shared/src/functions/using.ts @@ -0,0 +1,9 @@ +/** + * Call given callback with given value. + * + * @param value + * @param callback + * + * @internal + */ +export default (value: T, callback: (value: T) => U) => callback(value); diff --git a/packages/shared/src/functions/value.ts b/packages/shared/src/functions/value.ts index 4cb3c3f3..7cf57687 100644 --- a/packages/shared/src/functions/value.ts +++ b/packages/shared/src/functions/value.ts @@ -1,5 +1,12 @@ import { Value } from '@foscia/shared/types'; +/** + * Run callback if it is a function, otherwise return value. + * + * @param valueOrCallback + * + * @internal + */ export default (valueOrCallback: T): Value => ( typeof valueOrCallback === 'function' ? valueOrCallback() diff --git a/packages/shared/src/identifiers/uniqueId.ts b/packages/shared/src/identifiers/uniqueId.ts index 35bb588d..8ae2caae 100644 --- a/packages/shared/src/identifiers/uniqueId.ts +++ b/packages/shared/src/identifiers/uniqueId.ts @@ -1,7 +1,18 @@ -const uniqueId = (generator: () => string, notIds: string[]): string => { - const id = generator(); +import using from '@foscia/shared/functions/using'; - return notIds.indexOf(id) !== -1 ? uniqueId(generator, notIds) : id; -}; +/** + * Generate a unique ID using generator. + * + * @param generator + * @param notIds + * + * @internal + */ +const uniqueId = ( + generator: () => string, + notIds: string[], +): string => using(generator(), (id) => ( + notIds.indexOf(id) !== -1 ? uniqueId(generator, notIds) : id +)); export default uniqueId; diff --git a/packages/shared/src/identifiers/unsafeId.ts b/packages/shared/src/identifiers/unsafeId.ts index 1d167434..df681b61 100644 --- a/packages/shared/src/identifiers/unsafeId.ts +++ b/packages/shared/src/identifiers/unsafeId.ts @@ -1 +1,6 @@ +/** + * Generate an unsafe ID. + * + * @internal + */ export default () => Math.random().toString(26).slice(2); diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index aeb23268..ef4a49c9 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,9 +1,9 @@ import mapArrayable from '@foscia/shared/arrays/mapArrayable'; import mapWithKeys from '@foscia/shared/arrays/mapWithKeys'; -import sequentialTransform from '@foscia/shared/arrays/sequentialTransform'; import uniqueValues from '@foscia/shared/arrays/uniqueValues'; import wrap from '@foscia/shared/arrays/wrap'; import wrapVariadic from '@foscia/shared/arrays/wrapVariadic'; +import isFosciaFlag from '@foscia/shared/checks/isFosciaFlag'; import isFosciaType from '@foscia/shared/checks/isFosciaType'; import isNil from '@foscia/shared/checks/isNil'; import isNone from '@foscia/shared/checks/isNone'; @@ -15,13 +15,19 @@ import makeDescriptorHolder, { SYMBOL_DESCRIPTOR_HOLDER, } from '@foscia/shared/descriptors/makeDescriptorHolder'; import { IS_DEV, IS_TEST } from '@foscia/shared/env'; +import tap from '@foscia/shared/functions/tap'; +import using from '@foscia/shared/functions/using'; import value from '@foscia/shared/functions/value'; import uniqueId from '@foscia/shared/identifiers/uniqueId'; import unsafeId from '@foscia/shared/identifiers/unsafeId'; import makeIdentifiersMap from '@foscia/shared/maps/makeIdentifiersMap'; +import sequentialTransform from '@foscia/shared/miscellaneous/sequentialTransform'; +import throughMiddlewares from '@foscia/shared/miscellaneous/throughMiddlewares'; +import camelCase from '@foscia/shared/strings/camelCase'; +import kebabCase from '@foscia/shared/strings/kebabCase'; import optionalJoin from '@foscia/shared/strings/optionalJoin'; import pluralize from '@foscia/shared/strings/pluralize'; -import toKebabCase from '@foscia/shared/strings/toKebabCase'; +import singularize from '@foscia/shared/strings/singularize'; export * from '@foscia/shared/descriptors/types'; export * from '@foscia/shared/types'; @@ -29,6 +35,7 @@ export * from '@foscia/shared/types'; export { IS_DEV, IS_TEST, + SYMBOL_DESCRIPTOR_HOLDER, mergeConfig, eachDescriptors, makeDescriptorHolder, @@ -37,18 +44,23 @@ export { mapWithKeys, isDescriptorHolder, isFosciaType, + isFosciaFlag, isNil, isNone, optionalJoin, pluralize, + singularize, sequentialTransform, + throughMiddlewares, removeTimezoneOffset, - toKebabCase, + kebabCase, + camelCase, unsafeId, uniqueId, uniqueValues, + tap, + using, value, wrap, wrapVariadic, - SYMBOL_DESCRIPTOR_HOLDER, }; diff --git a/packages/shared/src/maps/makeIdentifiersMap.ts b/packages/shared/src/maps/makeIdentifiersMap.ts index 7cf310bb..43c09185 100644 --- a/packages/shared/src/maps/makeIdentifiersMap.ts +++ b/packages/shared/src/maps/makeIdentifiersMap.ts @@ -1,33 +1,29 @@ +/** + * Create an identifiers map object. + * + * @internal + */ export default () => { const values: Map> = new Map(); - const find = (type: Type, id: Id) => values.get(type)?.get(id) ?? null; - - const put = (type: Type, id: Id, value: T) => { - if (!values.get(type)) { - values.set(type, new Map()); - } - - values.get(type)!.set(id, value); - }; - - const forget = (type: Type, id: Id) => { - values.get(type)?.delete(id); - }; - - const forgetAll = (type: Type) => { - values.delete(type); - }; - - const clear = () => { - values.clear(); - }; - return { - find, - put, - forget, - forgetAll, - clear, + all: () => [...values.values()].map((v) => [...v.values()]).flat(), + find: (type: Type, id: Id) => values.get(type)?.get(id) ?? null, + put: (type: Type, id: Id, value: T) => { + if (!values.get(type)) { + values.set(type, new Map()); + } + + values.get(type)!.set(id, value); + }, + forget: (type: Type, id: Id) => { + values.get(type)?.delete(id); + }, + forgetAll: (type: Type) => { + values.delete(type); + }, + clear: () => { + values.clear(); + }, }; }; diff --git a/packages/shared/src/arrays/sequentialTransform.ts b/packages/shared/src/miscellaneous/sequentialTransform.ts similarity index 56% rename from packages/shared/src/arrays/sequentialTransform.ts rename to packages/shared/src/miscellaneous/sequentialTransform.ts index 50a7c436..a5666c37 100644 --- a/packages/shared/src/arrays/sequentialTransform.ts +++ b/packages/shared/src/miscellaneous/sequentialTransform.ts @@ -1,8 +1,22 @@ import { Awaitable, Transformer } from '@foscia/shared/types'; const sequentialTransform: { + /** + * Call given async functions sequentially. + * + * @param transformers + * + * @internal + */ (transformers: Transformer>[]): Promise; - (transformers: Transformer>[], value: T): Promise; + /** + * Transform value with async functions sequentially. + * + * @param transformers + * @param value + * + * @internal + */(transformers: Transformer>[], value: T): Promise; } = ( transformers: Transformer>[], value?: T, diff --git a/packages/shared/src/miscellaneous/throughMiddlewares.ts b/packages/shared/src/miscellaneous/throughMiddlewares.ts new file mode 100644 index 00000000..a9184c94 --- /dev/null +++ b/packages/shared/src/miscellaneous/throughMiddlewares.ts @@ -0,0 +1,17 @@ +import { Middleware, MiddlewareNext } from '@foscia/shared/types'; + +/** + * Execute a callback through a set of middlewares. + * + * @param middlewares + * @param callback + * + * @internal + */ +export default ( + middlewares: Middleware[], + callback: MiddlewareNext, +) => middlewares.reduceRight>( + (next, middleware) => (value) => middleware(value, next), + callback, +); diff --git a/packages/shared/src/strings/camelCase.ts b/packages/shared/src/strings/camelCase.ts new file mode 100644 index 00000000..9b164a42 --- /dev/null +++ b/packages/shared/src/strings/camelCase.ts @@ -0,0 +1,13 @@ +import capitalize from '@foscia/shared/strings/capitalize'; +import toCase from '@foscia/shared/strings/toCase'; + +/** + * Convert a string to camel case. + * + * @param value + * + * @internal + */ +export default /* @__PURE__ */ toCase((w, i) => ( + i ? capitalize(w.toLowerCase()) : w.toLowerCase() +), ''); diff --git a/packages/shared/src/strings/capitalize.ts b/packages/shared/src/strings/capitalize.ts new file mode 100644 index 00000000..ca93227c --- /dev/null +++ b/packages/shared/src/strings/capitalize.ts @@ -0,0 +1,8 @@ +/** + * Convert the first char of a string to upper case and remaining to lower case. + * + * @param value + * + * @internal + */ +export default (value: string) => `${value.charAt(0).toUpperCase()}${value.slice(1).toLowerCase()}`; diff --git a/packages/shared/src/strings/kebabCase.ts b/packages/shared/src/strings/kebabCase.ts new file mode 100644 index 00000000..d7ece24c --- /dev/null +++ b/packages/shared/src/strings/kebabCase.ts @@ -0,0 +1,10 @@ +import toCase from '@foscia/shared/strings/toCase'; + +/** + * Convert a string to kebab case. + * + * @param value + * + * @internal + */ +export default /* @__PURE__ */ toCase((w) => w.toLowerCase(), '-'); diff --git a/packages/shared/src/strings/makePluralizer.ts b/packages/shared/src/strings/makePluralizer.ts new file mode 100644 index 00000000..fe3074f5 --- /dev/null +++ b/packages/shared/src/strings/makePluralizer.ts @@ -0,0 +1,17 @@ +/** + * Create a function to pluralize or singularize words based on given rules. + * + * @param rules + * + * @internal + */ +export default (rules: [RegExp, string][]) => (word: string) => { + let nextWord: string | undefined; + rules.some(([regexp, replacement]) => { + nextWord = word.replace(regexp, replacement); + + return word !== nextWord; + }); + + return nextWord ?? word; +}; diff --git a/packages/shared/src/strings/optionalJoin.ts b/packages/shared/src/strings/optionalJoin.ts index 3d9adbc9..acb7b1ef 100644 --- a/packages/shared/src/strings/optionalJoin.ts +++ b/packages/shared/src/strings/optionalJoin.ts @@ -1,6 +1,14 @@ import isNone from '@foscia/shared/checks/isNone'; import { Optional } from '@foscia/shared/types'; +/** + * Join strings which are not "none" with separator. + * + * @param strings + * @param separator + * + * @internal + */ export default ( strings: Optional[], separator: string, diff --git a/packages/shared/src/strings/pluralize.ts b/packages/shared/src/strings/pluralize.ts index dd712b54..4666a7ca 100644 --- a/packages/shared/src/strings/pluralize.ts +++ b/packages/shared/src/strings/pluralize.ts @@ -1,18 +1,17 @@ -const rules: [RegExp, string][] = [ +import makePluralizer from '@foscia/shared/strings/makePluralizer'; + +/** + * Pluralize a word. + * + * @param word + * + * @internal + */ +export default /* @__PURE__ */ makePluralizer([ + [/(child)$/i, '$1ren'], [/(f|fe)$/i, 'ves'], [/([^aeiouy])y$/i, '$1ies'], [/([^aeiouy]o)$/i, '$1es'], [/(s|ch|sh|x|z)$/i, '$1es'], [/(.)$/i, '$1s'], -]; - -export default (word: string) => { - let pluralizedWord: string | undefined; - rules.some(([regexp, replacement]) => { - pluralizedWord = word.replace(regexp, replacement); - - return word !== pluralizedWord; - }); - - return pluralizedWord ?? word; -}; +]); diff --git a/packages/shared/src/strings/singularize.ts b/packages/shared/src/strings/singularize.ts new file mode 100644 index 00000000..92bd6017 --- /dev/null +++ b/packages/shared/src/strings/singularize.ts @@ -0,0 +1,21 @@ +import makePluralizer from '@foscia/shared/strings/makePluralizer'; + +/** + * Singularize a word. + * + * @param word + * + * @internal + */ +export default /* @__PURE__ */ makePluralizer([ + [/(child)ren$/i, '$1'], + [/(kni|wi|li)(ves)$/i, '$1fe'], + [/(ves)$/i, 'f'], + [/(ies)$/i, 'y'], + [/(i)$/i, 'us'], + [/(zes)$/i, 'ze'], + [/(shes)$/i, 'sh'], + [/(ses)$/i, 's'], + [/(oes)$/i, 'o'], + [/(s)$/i, ''], +]); diff --git a/packages/shared/src/strings/toCase.ts b/packages/shared/src/strings/toCase.ts new file mode 100644 index 00000000..4041c93e --- /dev/null +++ b/packages/shared/src/strings/toCase.ts @@ -0,0 +1,19 @@ +import using from '@foscia/shared/functions/using'; + +/** + * Create a string case converter using a word transformer and separator. + * + * @param transformer + * @param separator + * + * @internal + */ +export default ( + transformer: (word: string, index: number) => string, + separator: string, +) => (value: string) => using( + value.match( + /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g, + ), + (matches) => (matches === null ? value : matches.map(transformer).join(separator)), +); diff --git a/packages/shared/src/strings/toKebabCase.ts b/packages/shared/src/strings/toKebabCase.ts deleted file mode 100644 index 0af18aaf..00000000 --- a/packages/shared/src/strings/toKebabCase.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default (value: string): string => { - const matches = value.match( - /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g, - ); - if (matches === null) { - return value; - } - - return matches.map((x) => x.toLowerCase()).join('-'); -}; diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index d8f8028a..8e5c0822 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -1,35 +1,151 @@ +/** + * Dictionary of type. + * + * @internal + */ export type Dictionary = { [K: string]: T }; +/** + * Constructor of type. + * + * @internal + */ export type Constructor = new (...args: any[]) => T; +/** + * Utility for recursive types. + * + * @internal + */ export type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; +/** + * Infer type from function return value if it is a function. + * + * @internal + */ export type Value = T extends (...args: any[]) => any ? ReturnType : T; +/** + * Type which can be awaited or not. + * + * @internal + */ export type Awaitable = T | Promise; +/** + * Type which can be an array or not. + * + * @internal + */ export type Arrayable = T[] | T; +/** + * Type which can be an array or not, as a variadic parameter. + * + * @internal + */ export type ArrayableVariadic = T[] | [T[]]; +/** + * Type which can an item of an array if the original type is an array. + */ +export type Itemable = T extends any[] ? (T[number] | T) : T; + +/** + * Type which can be null or undefined. + * + * @internal + */ export type Optional = T | null | undefined; +/** + * Types considered `false` by JavaScript. + * + * @internal + */ export type Falsy = null | undefined | false | 0 | -0 | 0n | ''; +/** + * Only truthy types from `T`. + * + * @internal + */ export type OnlyTruthy = T extends Falsy ? never : T; +/** + * Only falsy types from `T`. + * + * @internal + */ export type OnlyFalsy = T extends Falsy ? T : never; -export type Transformer = (value: T) => U; - +/** + * Exclude properties with types `never` from object type. + * + * @internal + */ export type OmitNever = { [K in keyof T as T[K] extends never ? never : K]: T[K] }; +/** + * Convert a union type to intersection. + * + * @internal + */ export type UnionToIntersection = (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never; +/** + * Define a type whether the original type is any or not. + * + * @internal + */ +export type IfAny = 0 extends (1 & T) ? Y : N; + +/** + * Define a type whether the original types are equal or not. + */ +export type IfEquals = + (() => T extends X ? 1 : 2) extends (() => T extends Y ? 1 : 2) + ? A : B; + +/** + * Get the writable (not readonly) keys of a type. + */ +export type WritableKeys = { + [P in keyof T]-?: IfEquals<{ [Q in P]: T[P]; }, { -readonly [Q in P]: T[P]; }, P>; +}[keyof T]; + +/** + * Transformer function from a type to another. + * + * @internal + */ +export type Transformer = (value: T) => U; + +/** + * Middleware next callback. + * + * @internal + */ +export type MiddlewareNext = (value: V) => R; + +/** + * Middleware callback. + * + * @internal + */ +export type Middleware = (value: V, next: MiddlewareNext) => R; + +/** + * Map identifier with type. + * + * @internal + */ export type IdentifiersMap = { + all: () => T[]; find: (type: Type, id: Id) => T | null; put: (type: Type, id: Id, value: T) => void; forget: (type: Type, id: Id) => void; @@ -37,6 +153,30 @@ export type IdentifiersMap = { clear: () => void; }; -export type FosciaObject = { - $FOSCIA_TYPE: Symbol; +/** + * Foscia object which can be identified by a unique symbol. + * + * @internal + */ +export type FosciaObject = { + /** + * Type of the Foscia object. + * + * @internal + */ + readonly $FOSCIA_TYPE: S; +}; + +/** + * Foscia object which can be flagged using bit flags. + * + * @internal + */ +export type FosciaFlaggedObject = { + /** + * Flags of the Foscia object. + * + * @internal + */ + readonly $FOSCIA_FLAGS: number; }; diff --git a/packages/shared/tests/unit/checks/flags.test.ts b/packages/shared/tests/unit/checks/flags.test.ts new file mode 100644 index 00000000..7cc7e37e --- /dev/null +++ b/packages/shared/tests/unit/checks/flags.test.ts @@ -0,0 +1,32 @@ +import { isFosciaFlag } from '@foscia/shared'; +import { describe, expect, it } from 'vitest'; + +describe.concurrent('unit: flags', () => { + it('should ensure object is flagged with flag', () => { + const fooFlag = 1; + const barFlag = 2; + const bazFlag = 4; + + const nonObject = {}; + const nonFlaggedObject = {}; + const flaggedObjectFoo = { $FOSCIA_FLAGS: fooFlag }; + const flaggedObjectBar = { $FOSCIA_FLAGS: barFlag }; + // eslint-disable-next-line no-bitwise + const flaggedObjectFooBar = { $FOSCIA_FLAGS: fooFlag | barFlag }; + + expect(isFosciaFlag(nonObject, fooFlag)).toBe(false); + expect(isFosciaFlag(nonFlaggedObject, fooFlag)).toBe(false); + + expect(isFosciaFlag(flaggedObjectFoo, fooFlag)).toBe(true); + expect(isFosciaFlag(flaggedObjectBar, fooFlag)).toBe(false); + expect(isFosciaFlag(flaggedObjectFooBar, fooFlag)).toBe(true); + + expect(isFosciaFlag(flaggedObjectFoo, barFlag)).toBe(false); + expect(isFosciaFlag(flaggedObjectBar, barFlag)).toBe(true); + expect(isFosciaFlag(flaggedObjectFooBar, barFlag)).toBe(true); + + expect(isFosciaFlag(flaggedObjectFoo, bazFlag)).toBe(false); + expect(isFosciaFlag(flaggedObjectBar, bazFlag)).toBe(false); + expect(isFosciaFlag(flaggedObjectFooBar, bazFlag)).toBe(false); + }); +}); diff --git a/packages/shared/tests/unit/strings/case.test.ts b/packages/shared/tests/unit/strings/case.test.ts new file mode 100644 index 00000000..3d143282 --- /dev/null +++ b/packages/shared/tests/unit/strings/case.test.ts @@ -0,0 +1,28 @@ +import { camelCase, kebabCase } from '@foscia/shared'; +import { describe, expect, it } from 'vitest'; + +describe.concurrent('unit: case', () => { + it.each([ + ['', ''], + ['foo', 'foo'], + ['foo-bar', 'foo-bar'], + ['foo_bar', 'foo-bar'], + ['foo bar', 'foo-bar'], + ['Foo Bar', 'foo-bar'], + ['FOO BAR', 'foo-bar'], + ])('should convert to kebab case', (value, kebab) => { + expect(kebabCase(value)).toStrictEqual(kebab); + }); + + it.each([ + ['', ''], + ['foo', 'foo'], + ['foo-bar', 'fooBar'], + ['foo_bar', 'fooBar'], + ['foo bar', 'fooBar'], + ['Foo Bar', 'fooBar'], + ['FOO BAR', 'fooBar'], + ])('should convert to camel case', (value, kebab) => { + expect(camelCase(value)).toStrictEqual(kebab); + }); +}); diff --git a/packages/shared/tests/unit/strings/pluralization.test.ts b/packages/shared/tests/unit/strings/pluralization.test.ts new file mode 100644 index 00000000..997a1a1b --- /dev/null +++ b/packages/shared/tests/unit/strings/pluralization.test.ts @@ -0,0 +1,41 @@ +import { pluralize, singularize } from '@foscia/shared'; +import { describe, expect, it } from 'vitest'; + +describe.concurrent('unit: pluralization', () => { + const dataset = [ + // Classic words. + ['car', 'cars'], + ['apple', 'apples'], + ['bus', 'buses'], + ['dish', 'dishes'], + ['leaf', 'leaves'], + ['life', 'lives'], + ['knife', 'knives'], + ['day', 'days'], + ['donkey', 'donkeys'], + ['city', 'cities'], + ['country', 'countries'], + ['zoo', 'zoos'], + ['video', 'videos'], + ['hero', 'heroes'], + ['potato', 'potatoes'], + // Classic relations. + ['author', 'authors'], + ['owner', 'owners'], + ['post', 'posts'], + ['user', 'users'], + ['parent', 'parents'], + ['child', 'children'], + ['tag', 'tags'], + ['category', 'categories'], + ['product', 'products'], + ]; + + it.each(dataset)('should pluralize word', (singular, plural) => { + expect(pluralize(singular)).toStrictEqual(plural); + }); + + it.each(dataset)('should singularize word', (singular, plural) => { + expect(singularize(plural)).toStrictEqual(singular); + }); +}); diff --git a/packages/shared/tests/unit/strings/pluralize.test.ts b/packages/shared/tests/unit/strings/pluralize.test.ts deleted file mode 100644 index 036f019d..00000000 --- a/packages/shared/tests/unit/strings/pluralize.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { pluralize } from '@foscia/shared'; -import { describe, expect, it } from 'vitest'; - -describe.concurrent('unit: pluralize', () => { - it.each([ - ['car', 'cars'], - ['apple', 'apples'], - ['bus', 'buses'], - ['dish', 'dishes'], - ['leaf', 'leaves'], - ['knife', 'knives'], - ['day', 'days'], - ['donkey', 'donkeys'], - ['city', 'cities'], - ['country', 'countries'], - ['zoo', 'zoos'], - ['video', 'videos'], - ['hero', 'heroes'], - ['potato', 'potatoes'], - ])('should pluralize word', (value, kebab) => { - expect(pluralize(value)).toStrictEqual(kebab); - }); -}); diff --git a/packages/shared/tests/unit/strings/toKebabCase.test.ts b/packages/shared/tests/unit/strings/toKebabCase.test.ts deleted file mode 100644 index f3c4b7d8..00000000 --- a/packages/shared/tests/unit/strings/toKebabCase.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { toKebabCase } from '@foscia/shared'; -import { describe, expect, it } from 'vitest'; - -describe.concurrent('unit: toKebabCase', () => { - it.each([ - ['', ''], - ['foo', 'foo'], - ['foo-bar', 'foo-bar'], - ['foo_bar', 'foo-bar'], - ['foo bar', 'foo-bar'], - ['Foo Bar', 'foo-bar'], - ['FOO BAR', 'foo-bar'], - ])('should convert to kebab case', (value, kebab) => { - expect(toKebabCase(value)).toStrictEqual(kebab); - }); -}); diff --git a/packages/test/src/fosciaTestError.ts b/packages/test/src/fosciaTestError.ts new file mode 100644 index 00000000..18bc051b --- /dev/null +++ b/packages/test/src/fosciaTestError.ts @@ -0,0 +1,11 @@ +import { FosciaError } from '@foscia/core'; + +/** + * Error which occurs during testing code using a Foscia feature. + * + * @group Errors + * + * @internal + */ +export default class FosciaTestError extends FosciaError { +} diff --git a/packages/test/src/index.ts b/packages/test/src/index.ts index 84feaee9..f133ebc6 100644 --- a/packages/test/src/index.ts +++ b/packages/test/src/index.ts @@ -1,15 +1,13 @@ -import makeActionFactoryMock from '@foscia/test/makeActionFactoryMock'; import makeActionFactoryMockable from '@foscia/test/makeActionFactoryMockable'; import mockAction from '@foscia/test/mockAction'; -import UnexpectedMockedRunError from '@foscia/test/unexpectedMockedRunError'; +import UnexpectedActionError from '@foscia/test/unexpectedActionError'; import unmockAction from '@foscia/test/unmockAction'; export * from '@foscia/test/types'; export { makeActionFactoryMockable, - makeActionFactoryMock, mockAction, unmockAction, - UnexpectedMockedRunError, + UnexpectedActionError, }; diff --git a/packages/test/src/makeActionFactoryMock.ts b/packages/test/src/makeActionFactoryMock.ts index d09830af..adca672c 100644 --- a/packages/test/src/makeActionFactoryMock.ts +++ b/packages/test/src/makeActionFactoryMock.ts @@ -1,167 +1,150 @@ -import { Action, ActionFactory } from '@foscia/core'; -import { sequentialTransform } from '@foscia/shared'; -import makeActionMockedHistoryItem from '@foscia/test/makeActionMockedHistoryItem'; -import makeActionMockedRun from '@foscia/test/makeActionMockedRun'; import { - ActionFactoryMock, - ActionMockedHistoryItem, - ActionMockedPredicate, - ActionMockedResult, - ActionMockedRun, - ActionMockedRunOptions, -} from '@foscia/test/types'; -import UnexpectedMockedRunError from '@foscia/test/unexpectedMockedRunError'; - -export default ( - factory: ActionFactory, -): ActionFactoryMock => { - const mocks = [] as ActionMockedRun[]; - const history = [] as ActionMockedHistoryItem[]; - - const makeMockedRun = ( - result?: ActionMockedResult | ActionMockedRunOptions, - predicate?: ActionMockedPredicate, - ) => { - const options = result !== null && typeof result === 'object' ? result : { result, predicate }; - - return makeActionMockedRun(options); - }; - - const findMockedRun = (context: RC) => sequentialTransform( - mocks.map((mockedRun) => async (prev: ActionMockedRun | null) => { - if (prev) { - return prev; + Action, + ActionFactory, + ContextEnhancer, + ContextRunner, + FosciaError, + isWhenContextFunction, + runHooks, + withoutHooks, +} from '@foscia/core'; +import { + Dictionary, + Middleware, + sequentialTransform, + throughMiddlewares, + value, +} from '@foscia/shared'; +import makeActionMock from '@foscia/test/makeActionMock'; +import makeActionTestContext from '@foscia/test/makeActionTestContext'; +import { ActionFactoryMock, ActionFactoryMockHistoryItem, ActionMock } from '@foscia/test/types'; +import UnexpectedActionError from '@foscia/test/unexpectedActionError'; + +/** + * Create an action factory mock. + * + * @internal + */ +export default ( + factory: ActionFactory, +) => { + const mocks = [] as ActionMock[]; + const history = [] as ActionFactoryMockHistoryItem[]; + + const unnestRunners = async ( + action: Action, + runners: ContextRunner[], + ): Promise[]> => { + const runner = runners[runners.length - 1]; + if (isWhenContextFunction(runner)) { + const callback = await value(runner.meta.args[0] as Function) + ? runner.meta.args[1] + : runner.meta.args[2]; + if (!callback) { + return [...runners, () => undefined]; } - return await mockedRun.shouldRun(context) ? mockedRun : null; - }), - null, - ); - - const forgetMockedRun = async (mockedRun: ActionMockedRun) => { - if (await mockedRun.shouldForget()) { - const index = mocks.indexOf(mockedRun); - if (index !== -1) { - mocks.splice(index, 1); + if (isWhenContextFunction(callback)) { + return unnestRunners(action, [...runners, callback]); } + + return [...runners, callback]; } + + return runners; }; - const runMockedRun = async ( - action: Action, - enhancers: any[], + const run = async ( + action: Action, + enhancers: (ContextEnhancer | ContextRunner)[], ) => { - // Drop runner. - enhancers.pop(); + if (enhancers.length === 0) { + throw new FosciaError('`run` must be called with at least one runner function.'); + } + + const rootRunner = enhancers.pop() as ContextRunner; - (action.use as any)(...enhancers); + (action as any).use(...enhancers); - const context = await action.useContext(); + const { middlewares, ...context } = await action.useContext() as Dictionary; + action.updateContext(context); - history.push(makeActionMockedHistoryItem(context)); + const runners = await unnestRunners(action, [rootRunner]); + const calls = action.calls(); + const testContext = makeActionTestContext(context, calls, runners); - const mockedRun = await findMockedRun(context); - if (!mockedRun) { - throw new UnexpectedMockedRunError(context); + const mock = await sequentialTransform(mocks.map((next) => async (prev: ActionMock | null) => { + if (prev) { + return prev; + } + + return await next.shouldRun(testContext) ? next : null; + }), null); + if (!mock) { + throw new UnexpectedActionError(testContext); } + await runHooks(action, 'running', { action, runner: rootRunner }); + try { - const result = await mockedRun.run(context); + const result = await throughMiddlewares( + (middlewares ?? []) as Middleware[], + async (a) => withoutHooks(a, async () => mock.run(testContext)), + )(action); - await forgetMockedRun(mockedRun); + await runHooks(action, 'success', { action, result }); + + history.push({ context: testContext, mock, result, error: undefined }); return result; } catch (error) { - await forgetMockedRun(mockedRun); + await runHooks(action, 'error', { action, error }); + + history.push({ context: testContext, mock, result: undefined, error }); throw error; + } finally { + if (await mock.shouldRemove(testContext)) { + const mockIndex = mocks.indexOf(mock); + if (mockIndex !== -1) { + mocks.splice(mockIndex, 1); + } + } + + await runHooks(action, 'finally', { action }); } }; - const makeAction = (...args: A) => new Proxy(factory(...args), { - get: (target, property) => ( - property === 'run' - ? (...enhancers: any[]) => runMockedRun(target, enhancers) - : target[property as keyof Action] - ), - }); - - const mockResult = ( - result?: ActionMockedResult | ActionMockedRunOptions, - predicate?: ActionMockedPredicate, + const make = ( + ...immediateEnhancers: (ContextEnhancer | ContextRunner)[] ) => { - const mockedRun = makeMockedRun(result, predicate); - - mocks.push(mockedRun); + const action = new Proxy(factory(), { + get: (target, property) => ( + property === 'run' + ? ( + ...enhancers: (ContextEnhancer | ContextRunner)[] + ) => run(target, enhancers) + : target[property as keyof Action] + ), + }); + + return immediateEnhancers.length + ? (action.run as any)(...immediateEnhancers) + : action; }; - const mockResultOnce = ( - result?: ActionMockedResult, - predicate?: ActionMockedPredicate, - options?: ActionMockedRunOptions, - ) => mockResult({ ...options, result, predicate, times: 1 }); - - /** - * Mock result to given value for only 2 times. - * - * @param result - * @param predicate - * @param options - */ - const mockResultTwice = ( - result?: ActionMockedResult, - predicate?: ActionMockedPredicate, - options?: ActionMockedRunOptions, - ) => mockResult({ ...options, result, predicate, times: 2 }); - - /** - * Mock result to given value for only "n" times. - * - * @param times - * @param result - * @param predicate - * @param options - */ - const mockResultTimes = ( - times: number, - result?: ActionMockedResult, - predicate?: ActionMockedPredicate, - options?: ActionMockedRunOptions, - ) => mockResult({ ...options, result, predicate, times }); - - /** - * Reset the given mocks. - */ - const resetMocks = () => { - mocks.length = 0; - }; + const mock = (result?: unknown) => { + const newMock = makeActionMock().return(result); - /** - * Reset the ran contexts history. - */ - const resetHistory = () => { - history.length = 0; + mocks.push(newMock); + + return newMock; }; - /** - * Reset both mocked runs and ran contexts history. - */ const reset = () => { - resetMocks(); - resetHistory(); + mocks.length = 0; + history.length = 0; }; - return { - get history(): readonly ActionMockedHistoryItem[] { - return history; - }, - makeAction, - mockResult, - mockResultOnce, - mockResultTwice, - mockResultTimes, - resetMocks, - resetHistory, - reset, - }; + return { history, make, mock, reset } as ActionFactoryMock; }; diff --git a/packages/test/src/makeActionFactoryMockable.ts b/packages/test/src/makeActionFactoryMockable.ts index 8263a8b9..21b7e4e9 100644 --- a/packages/test/src/makeActionFactoryMockable.ts +++ b/packages/test/src/makeActionFactoryMockable.ts @@ -1,21 +1,26 @@ -import type { ActionFactory } from '@foscia/core'; +import { ActionFactory, ContextEnhancer, ContextRunner } from '@foscia/core'; import type { ActionFactoryMock, ActionMockableFactory } from '@foscia/test/types'; /** * Creates a proxy of an action factory which can be mocked. * * @param factory + * + * @category Factories + * @experimental */ -export default ( - factory: ActionFactory, -): ActionMockableFactory => { - const mockableFactory = (...args: A) => ( +export default ( + factory: ActionFactory, +): ActionMockableFactory => { + const mockableFactory = ( + ...immediateEnhancers: (ContextEnhancer | ContextRunner)[] + ) => ( mockableFactory.$mock - ? mockableFactory.$mock.makeAction(...args) - : mockableFactory.$real(...args) + ? mockableFactory.$mock.make(...immediateEnhancers) + : (mockableFactory.$real as any)(...immediateEnhancers) ); - mockableFactory.$mock = null as ActionFactoryMock | null; + mockableFactory.$mock = null as ActionFactoryMock | null; mockableFactory.$real = factory; return mockableFactory; diff --git a/packages/test/src/makeActionMock.ts b/packages/test/src/makeActionMock.ts new file mode 100644 index 00000000..e0022c99 --- /dev/null +++ b/packages/test/src/makeActionMock.ts @@ -0,0 +1,59 @@ +import { ActionMock, ActionTestContext } from '@foscia/test/types'; + +/** + * Create an action mock. + * + * @internal + */ +export default () => { + const options = { + remaining: null as number | null, + predicate: (() => true) as (context: ActionTestContext) => unknown, + expectation: (() => undefined) as (context: ActionTestContext) => unknown, + result: undefined as ((context: ActionTestContext) => unknown) | unknown, + }; + + return { + once() { + return this.times(1); + }, + twice() { + return this.times(2); + }, + times(times: number) { + options.remaining = times; + + return this; + }, + when(predicate) { + options.predicate = predicate; + + return this; + }, + return(value) { + options.result = value; + + return this; + }, + expect(expectation) { + options.expectation = expectation; + + return this; + }, + shouldRun: async (testContext) => options.predicate(testContext), + shouldRemove: async () => (options.remaining !== null && options.remaining < 1), + run: async (testContext) => { + if (options.remaining !== null) { + options.remaining -= 1; + } + + if (options.expectation) { + await options.expectation(testContext); + } + + return typeof options.result === 'function' + ? options.result(testContext) + : options.result; + }, + } as ActionMock; +}; diff --git a/packages/test/src/makeActionMockedHistoryItem.ts b/packages/test/src/makeActionMockedHistoryItem.ts deleted file mode 100644 index a6ee11fd..00000000 --- a/packages/test/src/makeActionMockedHistoryItem.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Create a history item wrapper for a ran context. - * - * @param context - */ -export default (context: any) => ({ context }); diff --git a/packages/test/src/makeActionMockedRun.ts b/packages/test/src/makeActionMockedRun.ts deleted file mode 100644 index 2e937bc1..00000000 --- a/packages/test/src/makeActionMockedRun.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ActionMockedRunOptions } from '@foscia/test/types'; - -/** - * Create a new mocked action run. - * - * @param options - */ -export default ( - options: ActionMockedRunOptions, -) => { - let remaining = options.times ?? null; - - const shouldRun = async (context: C) => ( - options.predicate ? (await options.predicate(context) ?? true) : true - ); - - const shouldForget = async () => (remaining !== null && remaining < 1); - - const run = async (context: C) => { - if (remaining !== null) { - remaining -= 1; - } - - if (options.expectation) { - await options.expectation(context); - } - - return typeof options.result === 'function' - ? options.result(context) - : options.result; - }; - - return { - shouldRun, - shouldForget, - run, - }; -}; diff --git a/packages/test/src/makeActionTestContext.ts b/packages/test/src/makeActionTestContext.ts new file mode 100644 index 00000000..6f9acf1f --- /dev/null +++ b/packages/test/src/makeActionTestContext.ts @@ -0,0 +1,85 @@ +import { ActionCall, ContextRunner, isContextFunction } from '@foscia/core'; +import { Dictionary } from '@foscia/shared'; +import { ActionTestCall, ActionTestContext } from '@foscia/test/types'; + +export default ( + context: Dictionary, + calls: ActionCall[], + runners: ContextRunner[], +) => { + const makeRunnerCall = (r: ContextRunner[]): ActionCall => ({ + call: r[0], + calls: (r.length > 1 ? [makeRunnerCall(r.slice(1))] : []), + }); + + const allCalls = [...calls, makeRunnerCall(runners)]; + + const makeTestCall = (call: ActionCall, depth = 0): ActionTestCall => ({ + ...(isContextFunction(call.call) ? { + name: call.call.meta.factory.meta.name, + args: call.call.meta.args, + } : { name: null, args: null }), + depth, + calls: call.calls.map((c) => makeTestCall(c, depth + 1)), + original: call, + }); + + const testCalls = allCalls.map((c) => makeTestCall(c, 0)); + + const flattenedTestCalls = [] as ActionTestCall[]; + const appendToFlattenedCalls = (call: ActionTestCall) => { + flattenedTestCalls.push(call); + call.calls.forEach(appendToFlattenedCalls); + }; + testCalls.forEach(appendToFlattenedCalls); + + const makeTestCallPredicate = ( + predicate: ((call: ActionTestCall) => boolean) | string, + ) => (testCall: ActionTestCall) => ( + typeof predicate === 'string' ? testCall.name === predicate : predicate(testCall) + ); + + const has = ( + predicate: ((call: ActionTestCall) => boolean) | string, + ) => flattenedTestCalls.some(makeTestCallPredicate(predicate)); + + const find = ( + predicate: ((call: ActionTestCall) => boolean) | string, + ) => { + const testCall = flattenedTestCalls.find(makeTestCallPredicate(predicate)); + if (!testCall) { + return null; + } + + return testCall; + }; + + const args = ( + predicate: ((call: ActionTestCall) => boolean) | string, + ) => { + const testCall = find(predicate); + if (!testCall || testCall.name === null) { + return []; + } + + return testCall.args; + }; + + const findAll = ( + predicate: ((call: ActionTestCall) => boolean) | string, + ) => flattenedTestCalls.filter(makeTestCallPredicate(predicate)); + + return { + context, + calls: { + has, + args, + find, + findAll, + size: () => testCalls.length, + tree: () => testCalls, + all: () => flattenedTestCalls, + originals: () => allCalls, + }, + } as ActionTestContext; +}; diff --git a/packages/test/src/mockAction.ts b/packages/test/src/mockAction.ts index a9f7d1ee..b68b9624 100644 --- a/packages/test/src/mockAction.ts +++ b/packages/test/src/mockAction.ts @@ -5,9 +5,12 @@ import { ActionMockableFactory } from '@foscia/test/types'; * Starts mocking a mockable action factory. * * @param factory + * + * @category Utilities + * @experimental */ -export default ( - factory: ActionMockableFactory, +export default ( + factory: ActionMockableFactory, ) => { // eslint-disable-next-line no-param-reassign factory.$mock = makeActionFactoryMock(factory.$real); diff --git a/packages/test/src/types.ts b/packages/test/src/types.ts index 6071caea..07e313a8 100644 --- a/packages/test/src/types.ts +++ b/packages/test/src/types.ts @@ -1,86 +1,291 @@ -import { Action, ActionFactory } from '@foscia/core'; -import { Awaitable } from '@foscia/shared'; +import { Action, ActionCall, ActionFactory, ContextEnhancer, ContextRunner } from '@foscia/core'; +import { Dictionary } from '@foscia/shared'; /** - * Mocked action run result definition (factory function or raw value). + * Action test call with parsed properties from enhancer or runner + * (name, arguments). + * + * @internal */ -export type ActionMockedResult = - | unknown - | ((context: Context) => Awaitable); +export type ActionTestNamedCall = { + name: string; + args: any[]; + depth: number; + calls: ActionTestCall[]; + original: ActionCall; +}; /** - * Mocked action run predicate to ensure the right context is intercepted. + * Action test call without parsed properties from anonymous enhancer or runner. + * + * @internal */ -export type ActionMockedPredicate = - (context: Context) => Awaitable; +export type ActionTestAnonymousCall = { + name: null; + args: null; + depth: number; + calls: ActionTestCall[]; + original: ActionCall; +}; /** - * Mocked action run expectation to run before returning result. + * Action test call with parsed properties when available (name, arguments). + * + * @internal */ -export type ActionMockedExpectation = - (context: Context) => Awaitable; +export type ActionTestCall = + | ActionTestNamedCall + | ActionTestAnonymousCall; /** - * Options to configure a mocked action run. + * Test context wrapper used to create callbacks, predicates and expectations. + * + * @internal */ -export type ActionMockedRunOptions = { - result?: ActionMockedResult; - predicate?: ActionMockedPredicate; - expectation?: ActionMockedExpectation; - times?: number; +export type ActionTestContext = { + /** + * Context which was computed just before action run. + * + * @experimental + */ + context: Dictionary; + /** + * Helper to inspect enhancers and runners calls. + * + * @experimental + */ + calls: { + /** + * Check if call stack contain a given enhancer or runner call. + * + * @param predicate + * + * @experimental + */ + has(predicate: ((testCall: ActionTestCall) => boolean) | string): boolean; + /** + * Get arguments for a given enhancer or runner call. + * + * @param predicate + * + * @experimental + */ + args(predicate: ((testCall: ActionTestCall) => boolean) | string): any[]; + /** + * Find a named enhancer or runner call using its name. + * + * @param predicate + * + * @experimental + */ + find(predicate: string): ActionTestNamedCall | null; + /** + * Find an enhancer or runner call using a predicate. + * + * @param predicate + * + * @experimental + */ + find(predicate: (testCall: ActionTestCall) => boolean): ActionTestCall | null; + /** + * Find named enhancers or runners calls using their common name. + * + * @param predicate + * + * @experimental + */ + findAll(predicate: string): ActionTestNamedCall[]; + /** + * Find enhancers or runners calls using a predicate. + * + * @param predicate + * + * @experimental + */ + findAll(predicate: (testCall: ActionTestCall) => boolean): ActionTestCall[]; + /** + * Get the size of the root calls stack. + * + * @experimental + */ + size(): number; + /** + * Get the root calls stack. + * + * @experimental + */ + tree(): ActionTestCall[]; + /** + * Get the flattened calls stack. + * + * @experimental + */ + all(): ActionTestCall[]; + /** + * Get the original action calls. + * + * @experimental + */ + originals(): ActionCall[]; + }; }; /** - * Mocked action run configuration for mocked action factory. + * Mock of one or many action. + * + * @internal */ -export type ActionMockedRun = { - shouldRun: (context: Context) => Promise; - shouldForget: () => Promise; - run: (context: Context) => Promise; +export type ActionMock = { + /** + * Tells the mock to only run once. + * + * @experimental + */ + once(): ActionMock; + /** + * Tells the mock to only run twice. + * + * @experimental + */ + twice(): ActionMock; + /** + * Tells the mock to only run `n` times. + * + * @experimental + */ + times(n: number): ActionMock; + /** + * Tells the mock to only run when predicate is truthy. + * + * @param predicate + * + * @experimental + */ + when(predicate: (context: ActionTestContext) => unknown): ActionMock; + /** + * Tells the mock to return given value when action runs. + * + * @param value + * + * @experimental + */ + return(value: unknown | ((context: ActionTestContext) => unknown)): ActionMock; + /** + * Schedule expectation callback to run on action run. + * + * @param expectation + * + * @experimental + */ + expect(expectation: (context: ActionTestContext) => unknown): ActionMock; + /** + * Check if the mock should run. + * + * @param context + * + * @internal + */ + shouldRun(context: ActionTestContext): Promise; + /** + * Check if the mock should be removed from available action mocks. + * + * @param context + * + * @internal + */ + shouldRemove(context: ActionTestContext): Promise; + /** + * Run the mock. + * + * @param context + * + * @internal + */ + run(context: ActionTestContext): Promise; }; /** - * History item wrapper for a ran context. + * Action factory mock history item. + * + * @internal */ -export type ActionMockedHistoryItem = { - context: any; +export type ActionFactoryMockHistoryItem = { + /** + * Action test context which was created for this run. + * + * @experimental + */ + context: ActionTestContext; + /** + * Action mock which handled this run. + * + * @experimental + */ + mock: ActionMock; + /** + * Result returned by the action when it succeeded. + * + * @experimental + */ + result?: unknown; + /** + * Error thrown by the action when it failed. + * + * @experimental + */ + error?: unknown; }; /** - * Mock for an action factory with mocked results and ran context history. + * Action factory mock. + * + * @experimental */ -export interface ActionFactoryMock { - readonly history: readonly ActionMockedHistoryItem[]; - makeAction: (...args: Args) => Action; - mockResult: ( - result?: ActionMockedResult | ActionMockedRunOptions, - predicate?: ActionMockedPredicate, - ) => void; - mockResultOnce: ( - result?: ActionMockedResult, - predicate?: ActionMockedPredicate, - options?: ActionMockedRunOptions, - ) => void; - mockResultTwice: ( - result?: ActionMockedResult, - predicate?: ActionMockedPredicate, - options?: ActionMockedRunOptions, - ) => void; - mockResultTimes: ( - times: number, - result?: ActionMockedResult, - predicate?: ActionMockedPredicate, - options?: ActionMockedRunOptions, - ) => void; - resetMocks: () => void; - resetHistory: () => void; - reset: () => void; -} +export type ActionFactoryMock = { + /** + * History of action test context which were ran. + * + * @experimental + */ + readonly history: ActionFactoryMockHistoryItem[]; + /** + * Make an action object. + * + * @internal + */ + make(...immediateEnhancers: (ContextEnhancer | ContextRunner)[]): Action; + /** + * Make an action mock. + * + * @param value + * + * @experimental + */ + mock(value?: unknown | ((context: ActionTestContext) => unknown)): ActionMock; + /** + * Reset all configured mocks and history. + * + * @experimental + */ + reset(): void; +}; /** * Proxy of an action factory which can easily be mocked. + * + * @internal */ -export type ActionMockableFactory = { - $mock: ActionFactoryMock | null; - $real: ActionFactory; -} & ActionFactory; +export type ActionMockableFactory = { + /** + * Mock when mocking actions is activated. + * + * @internal + */ + $mock: ActionFactoryMock | null; + /** + * Real implementation of action factory. + * + * @internal + */ + $real: ActionFactory; +} & ActionFactory; diff --git a/packages/test/src/unexpectedActionError.ts b/packages/test/src/unexpectedActionError.ts new file mode 100644 index 00000000..4aaa5f60 --- /dev/null +++ b/packages/test/src/unexpectedActionError.ts @@ -0,0 +1,22 @@ +import FosciaTestError from '@foscia/test/fosciaTestError'; +import { ActionTestContext } from '@foscia/test/types'; + +/** + * Error which occurs when a mocked action is ran but no mock matching test + * context could be found to mock the result. + * + * @group Errors + * + * @internal + */ +export default class UnexpectedActionError extends FosciaTestError { + public readonly context: ActionTestContext; + + public constructor(context: ActionTestContext) { + super( + 'Unexpected mocked action run. Either you didn\'t called `mock` on your factory mock or your predicate does not match test context.', + ); + + this.context = context; + } +} diff --git a/packages/test/src/unexpectedMockedRunError.ts b/packages/test/src/unexpectedMockedRunError.ts deleted file mode 100644 index 6f9cdf21..00000000 --- a/packages/test/src/unexpectedMockedRunError.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { FosciaError } from '@foscia/core'; - -/** - * Error which occurs when a mock action is ran but no mocked result were - * defined on the mock or their predicates does not match executed context. - */ -export default class UnexpectedMockedRunError extends FosciaError { - public readonly context: any; - - public constructor(context: any) { - super( - 'Unexpected mocked action run. Either you didn\'t called `mockResult` methods or your predicate does not match ran context.', - ); - - this.context = context; - } -} diff --git a/packages/test/src/unmockAction.ts b/packages/test/src/unmockAction.ts index 3e0c48c6..6414e437 100644 --- a/packages/test/src/unmockAction.ts +++ b/packages/test/src/unmockAction.ts @@ -4,9 +4,12 @@ import { ActionMockableFactory } from '@foscia/test/types'; * Stops mocking a mockable action factory. * * @param factory + * + * @category Utilities + * @experimental */ -export default ( - factory: ActionMockableFactory, +export default ( + factory: ActionMockableFactory, ) => { // eslint-disable-next-line no-param-reassign factory.$mock = null; diff --git a/packages/test/tests/unit/mocking.test.ts b/packages/test/tests/unit/mocking.test.ts index 8a6d7a10..99037d2f 100644 --- a/packages/test/tests/unit/mocking.test.ts +++ b/packages/test/tests/unit/mocking.test.ts @@ -1,24 +1,22 @@ -import { context, makeActionClass, AdapterI, raw } from '@foscia/core'; +import { Adapter, context, makeActionFactory, makeModel, query, raw, when } from '@foscia/core'; import { makeActionFactoryMockable, mockAction, unmockAction } from '@foscia/test'; import { describe, expect, it, vi } from 'vitest'; describe.concurrent('unit: mocking', () => { const makeAction = () => { - const ActionClass = makeActionClass(); - const fetch = vi.fn(); return { fetch, - action: makeActionFactoryMockable((() => new ActionClass().use(context({ + action: makeActionFactoryMockable(makeActionFactory({ adapter: { execute: async () => { const rawData = fetch(); return { raw: rawData, read: () => rawData.json() }; }, - } as AdapterI, - })))), + } as Adapter, + })), }; }; @@ -26,7 +24,7 @@ describe.concurrent('unit: mocking', () => { const { action, fetch } = makeAction(); const mock = mockAction(action); - mock.mockResult('foo'); + mock.mock('foo'); expect(await action().run(raw())).toStrictEqual('foo'); expect(await action().run(raw())).toStrictEqual('foo'); @@ -38,9 +36,9 @@ describe.concurrent('unit: mocking', () => { const { action, fetch } = makeAction(); const mock = mockAction(action); - mock.mockResultOnce('foo'); - mock.mockResultTwice('bar'); - mock.mockResultTimes(1, 'baz'); + mock.mock('foo').once(); + mock.mock('bar').twice(); + mock.mock('baz').times(1); expect(await action().run(raw())).toStrictEqual('foo'); expect(await action().run(raw())).toStrictEqual('bar'); @@ -56,8 +54,8 @@ describe.concurrent('unit: mocking', () => { const { action, fetch } = makeAction(); const mock = mockAction(action); - mock.mockResult('foo', ({ value }) => value === 'foo'); - mock.mockResult('bar', ({ value }) => value === 'bar'); + mock.mock('foo').when((ctx) => ctx.context.value === 'foo'); + mock.mock('bar').when((ctx) => ctx.context.value === 'bar'); expect(await action().use(context({ value: 'foo' })).run(raw())).toStrictEqual('foo'); expect(await action().use(context({ value: 'bar' })).run(raw())).toStrictEqual('bar'); @@ -71,8 +69,8 @@ describe.concurrent('unit: mocking', () => { const { action, fetch } = makeAction(); const mock = mockAction(action); - mock.mockResult('foo', ({ value }) => value === 'foo'); - mock.mockResult('bar', ({ value }) => value === 'bar'); + mock.mock('foo').when((ctx) => ctx.context.value === 'foo'); + mock.mock('bar').when((ctx) => ctx.context.value === 'bar'); expect(await action().run(context({ value: 'foo' }), raw())).toStrictEqual('foo'); expect(await action().run(context({ value: 'bar' }), raw())).toStrictEqual('bar'); @@ -89,12 +87,9 @@ describe.concurrent('unit: mocking', () => { let expectContext: any; const mock = mockAction(action); - mock.mockResult({ - result: 'foo', - expectation: (c: any) => { - expectationFn(c); - expectContext = c; - }, + mock.mock('foo').expect((ctx) => { + expectationFn(ctx.context); + expectContext = ctx.context; }); expect(await action().use(context({ value: 'foo' })).run(raw())).toStrictEqual('foo'); @@ -108,7 +103,7 @@ describe.concurrent('unit: mocking', () => { const { action, fetch } = makeAction(); const mock = mockAction(action); - mock.mockResult(() => 'foo'); + mock.mock(() => 'foo'); expect(await action().run(raw())).toStrictEqual('foo'); @@ -119,7 +114,7 @@ describe.concurrent('unit: mocking', () => { const { action, fetch } = makeAction(); const mock = mockAction(action); - mock.mockResult(() => { + mock.mock(() => { throw new Error('dummy error'); }); @@ -135,7 +130,7 @@ describe.concurrent('unit: mocking', () => { fetch.mockImplementation(() => responseMock); const mock = mockAction(action); - mock.mockResult('foo'); + mock.mock('foo'); expect(mock.history.length).toStrictEqual(0); @@ -143,8 +138,8 @@ describe.concurrent('unit: mocking', () => { await action().use(context({ foo: 'baz' })).run(raw()); expect(mock.history.length).toStrictEqual(2); - expect(mock.history[0].context.foo).toStrictEqual('bar'); - expect(mock.history[1].context.foo).toStrictEqual('baz'); + expect(mock.history[0].context.context.foo).toStrictEqual('bar'); + expect(mock.history[1].context.context.foo).toStrictEqual('baz'); mock.reset(); @@ -159,4 +154,64 @@ describe.concurrent('unit: mocking', () => { expect(fetchValue).toStrictEqual(responseMock); expect(fetch).toHaveBeenCalledOnce(); }); + + it('should support enhancers and runners inspection', async () => { + const Post = makeModel('posts'); + let expectContext: any; + + const { action, fetch } = makeAction(); + + const mock = mockAction(action); + mock.mock(() => 'foo').expect((ctx) => { + expect(ctx.calls.size()).toStrictEqual(4); + + expect(ctx.calls.has('create')).toStrictEqual(false); + expect(ctx.calls.find('create')).toStrictEqual(null); + expect(ctx.calls.args('create')).toStrictEqual([]); + + expect(ctx.calls.has('query')).toStrictEqual(true); + expect(ctx.calls.find('query')!.depth).toStrictEqual(0); + expect(ctx.calls.find('query')!.args).toStrictEqual([Post, 1]); + expect(ctx.calls.find((c) => c.name === 'query')) + .toStrictEqual(ctx.calls.find('query')); + + expect(ctx.calls.has('context')).toStrictEqual(true); + expect(ctx.calls.findAll('context').length).toStrictEqual(1); + expect(ctx.calls.find('context')!.depth).toStrictEqual(3); + expect(ctx.calls.args('context')).toStrictEqual([{ bar: 'bar' }]); + + expect(ctx.calls.findAll('when').length).toStrictEqual(5); + expect(ctx.calls.findAll('when')[0].depth).toStrictEqual(0); + expect(ctx.calls.findAll('when')[1].depth).toStrictEqual(0); + expect(ctx.calls.findAll('when')[2].depth).toStrictEqual(1); + expect(ctx.calls.findAll('when')[3].depth).toStrictEqual(0); + expect(ctx.calls.findAll('when')[4].depth).toStrictEqual(1); + + expect(ctx.calls.find('raw')!.args).toStrictEqual([]); + expect(ctx.calls.find('raw')!.depth).toStrictEqual(2); + + expect(ctx.calls.tree().length).toStrictEqual(4); + expect(ctx.calls.all().length).toStrictEqual(9); + expect(ctx.calls.originals().length).toStrictEqual(4); + + expectContext = ctx.context; + }); + + expect( + await action().run( + query(Post, 1), + when(async () => false, context({ foo: 'foo' })), + when(true, when(() => true, (a) => a.use(context({ bar: 'bar' })))), + when(async () => false, () => null, when(() => true, raw())), + ), + ).toStrictEqual('foo'); + + expect(fetch).not.toHaveBeenCalled(); + expect(expectContext).toStrictEqual({ + adapter: expectContext.adapter, + bar: 'bar', + model: Post, + id: 1, + }); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6399c195..4a05e3ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,44 +9,44 @@ importers: .: devDependencies: '@commitlint/cli': - specifier: ^19.3.0 - version: 19.3.0(@types/node@18.19.39)(typescript@5.5.2) + specifier: ^19.6.1 + version: 19.6.1(@types/node@18.19.70)(typescript@5.7.3) '@commitlint/config-conventional': - specifier: ^19.2.2 - version: 19.2.2 + specifier: ^19.6.0 + version: 19.6.0 '@release-it/bumper': specifier: ^6.0.1 - version: 6.0.1(release-it@17.4.0(typescript@5.5.2)) + version: 6.0.1(release-it@17.11.0(typescript@5.7.3)) '@release-it/conventional-changelog': - specifier: ^8.0.1 - version: 8.0.1(release-it@17.4.0(typescript@5.5.2)) + specifier: ^8.0.2 + version: 8.0.2(release-it@17.11.0(typescript@5.7.3)) '@rollup/plugin-commonjs': - specifier: ^26.0.1 - version: 26.0.1(rollup@4.18.0) + specifier: ^26.0.3 + version: 26.0.3(rollup@4.30.1) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.18.0) + version: 6.1.0(rollup@4.30.1) '@rollup/plugin-node-resolve': - specifier: ^15.2.3 - version: 15.2.3(rollup@4.18.0) + specifier: ^15.3.1 + version: 15.3.1(rollup@4.30.1) '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@4.18.0) + version: 0.4.4(rollup@4.30.1) '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@4.18.0)(tslib@2.6.3)(typescript@5.5.2) + version: 11.1.6(rollup@4.30.1)(tslib@2.8.1)(typescript@5.7.3) '@types/node': - specifier: ^18.19.39 - version: 18.19.39 + specifier: ^18.19.70 + version: 18.19.70 '@typescript-eslint/eslint-plugin': - specifier: ^7.14.1 - version: 7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) + specifier: ^7.18.0 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3) '@typescript-eslint/parser': - specifier: ^7.14.1 - version: 7.14.1(eslint@8.57.0)(typescript@5.5.2) + specifier: ^7.18.0 + version: 7.18.0(eslint@8.57.1)(typescript@5.7.3) '@vitest/coverage-istanbul': specifier: ^1.6.0 - version: 1.6.0(vitest@1.6.0(@types/node@18.19.39)(jsdom@24.1.0)(terser@5.31.1)) + version: 1.6.0(vitest@1.6.0(@types/node@18.19.70)(jsdom@24.1.3)(terser@5.37.0)) ansi-colors: specifier: ^4.1.3 version: 4.1.3 @@ -54,56 +54,56 @@ importers: specifier: ^8.2.2 version: 8.2.2 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^8.57.1 + version: 8.57.1 eslint-config-airbnb-base: specifier: ^15.0.0 - version: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0) + version: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1))(eslint@8.57.1) eslint-config-airbnb-typescript: specifier: ^18.0.0 - version: 18.0.0(@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0) + version: 18.0.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1))(eslint@8.57.1) eslint-import-resolver-typescript: - specifier: ^3.6.1 - version: 3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + specifier: ^3.7.0 + version: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1) eslint-plugin-import: - specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + specifier: ^2.31.0 + version: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) execa: - specifier: ^9.3.0 - version: 9.3.0 + specifier: ^9.5.2 + version: 9.5.2 husky: - specifier: ^9.0.11 - version: 9.0.11 + specifier: ^9.1.7 + version: 9.1.7 jsdom: - specifier: ^24.1.0 - version: 24.1.0 + specifier: ^24.1.3 + version: 24.1.3 minimist: specifier: ^1.2.8 version: 1.2.8 ora: - specifier: ^8.0.1 - version: 8.0.1 + specifier: ^8.1.1 + version: 8.1.1 release-it: - specifier: ^17.4.0 - version: 17.4.0(typescript@5.5.2) + specifier: ^17.11.0 + version: 17.11.0(typescript@5.7.3) rimraf: - specifier: ^5.0.7 - version: 5.0.7 + specifier: ^5.0.10 + version: 5.0.10 rollup: - specifier: ^4.18.0 - version: 4.18.0 + specifier: ^4.30.1 + version: 4.30.1 tsc-alias: specifier: ^1.8.10 version: 1.8.10 tslib: - specifier: ^2.6.3 - version: 2.6.3 + specifier: ^2.8.1 + version: 2.8.1 typescript: - specifier: ^5.5.2 - version: 5.5.2 + specifier: ^5.7.3 + version: 5.7.3 vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@18.19.39)(jsdom@24.1.0)(terser@5.31.1) + version: 1.6.0(@types/node@18.19.70)(jsdom@24.1.3)(terser@5.37.0) packages/cli: devDependencies: @@ -224,35 +224,35 @@ importers: website: dependencies: '@docusaurus/core': - specifier: ^3.4.0 - version: 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) + specifier: ^3.7.0 + version: 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) '@docusaurus/preset-classic': - specifier: ^3.4.0 - version: 3.4.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.4.5) + specifier: ^3.7.0 + version: 3.7.0(@algolia/client-search@5.19.0)(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(@types/react@19.0.7)(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.7.3) '@docusaurus/theme-common': - specifier: ^3.4.0 - version: 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) + specifier: ^3.7.0 + version: 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@fontsource-variable/onest': - specifier: ^5.0.4 - version: 5.0.4 + specifier: ^5.1.1 + version: 5.1.1 '@fontsource/fira-mono': - specifier: ^5.0.13 - version: 5.0.13 + specifier: ^5.1.1 + version: 5.1.1 '@mdx-js/react': - specifier: ^3.0.1 - version: 3.0.1(@types/react@18.3.3)(react@18.3.1) + specifier: ^3.1.0 + version: 3.1.0(@types/react@19.0.7)(react@18.3.1) clsx: specifier: ^2.1.1 version: 2.1.1 docusaurus-plugin-typedoc: - specifier: ^1.0.1 - version: 1.0.1(typedoc-plugin-markdown@4.0.3(typedoc@0.25.13(typescript@5.4.5))) + specifier: ^1.2.1 + version: 1.2.1(typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.7.3))) dotenv: - specifier: ^16.4.5 - version: 16.4.5 + specifier: ^16.4.7 + version: 16.4.7 prism-react-renderer: - specifier: ^2.3.1 - version: 2.3.1(react@18.3.1) + specifier: ^2.4.1 + version: 2.4.1(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -260,254 +260,253 @@ importers: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) typedoc: - specifier: ^0.25.13 - version: 0.25.13(typescript@5.4.5) + specifier: ^0.27.6 + version: 0.27.6(typescript@5.7.3) typedoc-plugin-markdown: - specifier: ^4.0.3 - version: 4.0.3(typedoc@0.25.13(typescript@5.4.5)) + specifier: ^4.4.1 + version: 4.4.1(typedoc@0.27.6(typescript@5.7.3)) + typedoc-plugin-mdn-links: + specifier: ^4.0.8 + version: 4.0.8(typedoc@0.27.6(typescript@5.7.3)) typescript: - specifier: ^5.4.5 - version: 5.4.5 + specifier: ^5.7.3 + version: 5.7.3 devDependencies: '@docusaurus/module-type-aliases': - specifier: ^3.4.0 - version: 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^3.7.0 + version: 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) docusaurus-mdx-checker: specifier: ^3.0.0 version: 3.0.0 prettier: - specifier: ^3.3.1 - version: 3.3.2 + specifier: ^3.4.2 + version: 3.4.2 rimraf: - specifier: ^5.0.7 - version: 5.0.7 + specifier: ^5.0.10 + version: 5.0.10 packages: - '@algolia/autocomplete-core@1.9.3': - resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==} + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} - '@algolia/autocomplete-plugin-algolia-insights@1.9.3': - resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==} + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} peerDependencies: search-insights: '>= 1 < 3' - '@algolia/autocomplete-preset-algolia@1.9.3': - resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==} + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} peerDependencies: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' - '@algolia/autocomplete-shared@1.9.3': - resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==} + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} peerDependencies: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' - '@algolia/cache-browser-local-storage@4.23.3': - resolution: {integrity: sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==} + '@algolia/client-abtesting@5.19.0': + resolution: {integrity: sha512-dMHwy2+nBL0SnIsC1iHvkBao64h4z+roGelOz11cxrDBrAdASxLxmfVMop8gmodQ2yZSacX0Rzevtxa+9SqxCw==} + engines: {node: '>= 14.0.0'} - '@algolia/cache-common@4.23.3': - resolution: {integrity: sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==} + '@algolia/client-analytics@5.19.0': + resolution: {integrity: sha512-CDW4RwnCHzU10upPJqS6N6YwDpDHno7w6/qXT9KPbPbt8szIIzCHrva4O9KIfx1OhdsHzfGSI5hMAiOOYl4DEQ==} + engines: {node: '>= 14.0.0'} - '@algolia/cache-in-memory@4.23.3': - resolution: {integrity: sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==} + '@algolia/client-common@5.19.0': + resolution: {integrity: sha512-2ERRbICHXvtj5kfFpY5r8qu9pJII/NAHsdgUXnUitQFwPdPL7wXiupcvZJC7DSntOnE8AE0lM7oDsPhrJfj5nQ==} + engines: {node: '>= 14.0.0'} - '@algolia/client-account@4.23.3': - resolution: {integrity: sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==} + '@algolia/client-insights@5.19.0': + resolution: {integrity: sha512-xPOiGjo6I9mfjdJO7Y+p035aWePcbsItizIp+qVyfkfZiGgD+TbNxM12g7QhFAHIkx/mlYaocxPY/TmwPzTe+A==} + engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@4.23.3': - resolution: {integrity: sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==} + '@algolia/client-personalization@5.19.0': + resolution: {integrity: sha512-B9eoce/fk8NLboGje+pMr72pw+PV7c5Z01On477heTZ7jkxoZ4X92dobeGuEQop61cJ93Gaevd1of4mBr4hu2A==} + engines: {node: '>= 14.0.0'} - '@algolia/client-common@4.23.3': - resolution: {integrity: sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==} + '@algolia/client-query-suggestions@5.19.0': + resolution: {integrity: sha512-6fcP8d4S8XRDtVogrDvmSM6g5g6DndLc0pEm1GCKe9/ZkAzCmM3ZmW1wFYYPxdjMeifWy1vVEDMJK7sbE4W7MA==} + engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@4.23.3': - resolution: {integrity: sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==} - - '@algolia/client-search@4.23.3': - resolution: {integrity: sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==} + '@algolia/client-search@5.19.0': + resolution: {integrity: sha512-Ctg3xXD/1VtcwmkulR5+cKGOMj4r0wC49Y/KZdGQcqpydKn+e86F6l3tb3utLJQVq4lpEJud6kdRykFgcNsp8Q==} + engines: {node: '>= 14.0.0'} '@algolia/events@4.0.1': resolution: {integrity: sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==} - '@algolia/logger-common@4.23.3': - resolution: {integrity: sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==} - - '@algolia/logger-console@4.23.3': - resolution: {integrity: sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==} + '@algolia/ingestion@1.19.0': + resolution: {integrity: sha512-LO7w1MDV+ZLESwfPmXkp+KLeYeFrYEgtbCZG6buWjddhYraPQ9MuQWLhLLiaMlKxZ/sZvFTcZYuyI6Jx4WBhcg==} + engines: {node: '>= 14.0.0'} - '@algolia/recommend@4.23.3': - resolution: {integrity: sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==} + '@algolia/monitoring@1.19.0': + resolution: {integrity: sha512-Mg4uoS0aIKeTpu6iv6O0Hj81s8UHagi5TLm9k2mLIib4vmMtX7WgIAHAcFIaqIZp5D6s5EVy1BaDOoZ7buuJHA==} + engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@4.23.3': - resolution: {integrity: sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==} + '@algolia/recommend@5.19.0': + resolution: {integrity: sha512-PbgrMTbUPlmwfJsxjFhal4XqZO2kpBNRjemLVTkUiti4w/+kzcYO4Hg5zaBgVqPwvFDNQ8JS4SS3TBBem88u+g==} + engines: {node: '>= 14.0.0'} - '@algolia/requester-common@4.23.3': - resolution: {integrity: sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==} + '@algolia/requester-browser-xhr@5.19.0': + resolution: {integrity: sha512-GfnhnQBT23mW/VMNs7m1qyEyZzhZz093aY2x8p0era96MMyNv8+FxGek5pjVX0b57tmSCZPf4EqNCpkGcGsmbw==} + engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@4.23.3': - resolution: {integrity: sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==} + '@algolia/requester-fetch@5.19.0': + resolution: {integrity: sha512-oyTt8ZJ4T4fYvW5avAnuEc6Laedcme9fAFryMD9ndUTIUe/P0kn3BuGcCLFjN3FDmdrETHSFkgPPf1hGy3sLCw==} + engines: {node: '>= 14.0.0'} - '@algolia/transporter@4.23.3': - resolution: {integrity: sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==} + '@algolia/requester-node-http@5.19.0': + resolution: {integrity: sha512-p6t8ue0XZNjcRiqNkb5QAM0qQRAKsCiebZ6n9JjWA+p8fWf8BvnhO55y2fO28g3GW0Imj7PrAuyBuxq8aDVQwQ==} + engines: {node: '>= 14.0.0'} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@asamuzakjp/css-color@2.8.3': + resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.24.7': - resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/core@7.24.7': - resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} + '@babel/compat-data@7.26.5': + resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.24.7': - resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.24.7': - resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} + '@babel/generator@7.26.5': + resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} engines: {node: '>=6.9.0'} - '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': - resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==} + '@babel/helper-annotate-as-pure@7.25.9': + resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.24.7': - resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + '@babel/helper-compilation-targets@7.26.5': + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.24.7': - resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==} + '@babel/helper-create-class-features-plugin@7.25.9': + resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.24.7': - resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==} + '@babel/helper-create-regexp-features-plugin@7.26.3': + resolution: {integrity: sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.2': - resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==} + '@babel/helper-define-polyfill-provider@0.6.3': + resolution: {integrity: sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - '@babel/helper-environment-visitor@7.24.7': - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-function-name@7.24.7': - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-hoist-variables@7.24.7': - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-member-expression-to-functions@7.24.7': - resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==} + '@babel/helper-member-expression-to-functions@7.25.9': + resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.24.7': - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.24.7': - resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.24.7': - resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==} + '@babel/helper-optimise-call-expression@7.25.9': + resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.24.7': - resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} + '@babel/helper-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} engines: {node: '>=6.9.0'} - '@babel/helper-remap-async-to-generator@7.24.7': - resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==} + '@babel/helper-remap-async-to-generator@7.25.9': + resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.24.7': - resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==} + '@babel/helper-replace-supers@7.26.5': + resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-simple-access@7.24.7': - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-skip-transparent-expression-wrappers@7.24.7': - resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-split-export-declaration@7.24.7': - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.24.7': - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.24.7': - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.24.7': - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.24.7': - resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} + '@babel/helper-wrap-function@7.25.9': + resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.24.7': - resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} engines: {node: '>=6.9.0'} '@babel/highlight@7.24.7': resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.24.7': - resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + '@babel/parser@7.26.5': + resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7': - resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': + resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9': + resolution: {integrity: sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7': - resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==} + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9': + resolution: {integrity: sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7': - resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==} + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9': + resolution: {integrity: sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7': - resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9': + resolution: {integrity: sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -518,104 +517,31 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-dynamic-import@7.8.3': resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-export-namespace-from@7.8.3': - resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-assertions@7.24.7': - resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.24.7': - resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.24.7': - resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} + '@babel/plugin-syntax-import-assertions@7.26.0': + resolution: {integrity: sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.24.7': - resolution: {integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==} + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -626,338 +552,350 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-arrow-functions@7.24.7': - resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==} + '@babel/plugin-transform-arrow-functions@7.25.9': + resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.24.7': - resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==} + '@babel/plugin-transform-async-generator-functions@7.25.9': + resolution: {integrity: sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.24.7': - resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==} + '@babel/plugin-transform-async-to-generator@7.25.9': + resolution: {integrity: sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoped-functions@7.24.7': - resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==} + '@babel/plugin-transform-block-scoped-functions@7.26.5': + resolution: {integrity: sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.24.7': - resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==} + '@babel/plugin-transform-block-scoping@7.25.9': + resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.24.7': - resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==} + '@babel/plugin-transform-class-properties@7.25.9': + resolution: {integrity: sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.24.7': - resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==} + '@babel/plugin-transform-class-static-block@7.26.0': + resolution: {integrity: sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.24.7': - resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==} + '@babel/plugin-transform-classes@7.25.9': + resolution: {integrity: sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.24.7': - resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==} + '@babel/plugin-transform-computed-properties@7.25.9': + resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.24.7': - resolution: {integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==} + '@babel/plugin-transform-destructuring@7.25.9': + resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dotall-regex@7.24.7': - resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==} + '@babel/plugin-transform-dotall-regex@7.25.9': + resolution: {integrity: sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-keys@7.24.7': - resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==} + '@babel/plugin-transform-duplicate-keys@7.25.9': + resolution: {integrity: sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dynamic-import@7.24.7': - resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9': + resolution: {integrity: sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.25.9': + resolution: {integrity: sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.24.7': - resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==} + '@babel/plugin-transform-exponentiation-operator@7.26.3': + resolution: {integrity: sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-export-namespace-from@7.24.7': - resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==} + '@babel/plugin-transform-export-namespace-from@7.25.9': + resolution: {integrity: sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-for-of@7.24.7': - resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==} + '@babel/plugin-transform-for-of@7.25.9': + resolution: {integrity: sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-function-name@7.24.7': - resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==} + '@babel/plugin-transform-function-name@7.25.9': + resolution: {integrity: sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-json-strings@7.24.7': - resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==} + '@babel/plugin-transform-json-strings@7.25.9': + resolution: {integrity: sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-literals@7.24.7': - resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==} + '@babel/plugin-transform-literals@7.25.9': + resolution: {integrity: sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.24.7': - resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==} + '@babel/plugin-transform-logical-assignment-operators@7.25.9': + resolution: {integrity: sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-member-expression-literals@7.24.7': - resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==} + '@babel/plugin-transform-member-expression-literals@7.25.9': + resolution: {integrity: sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-amd@7.24.7': - resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==} + '@babel/plugin-transform-modules-amd@7.25.9': + resolution: {integrity: sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.24.7': - resolution: {integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==} + '@babel/plugin-transform-modules-commonjs@7.26.3': + resolution: {integrity: sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.24.7': - resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==} + '@babel/plugin-transform-modules-systemjs@7.25.9': + resolution: {integrity: sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-umd@7.24.7': - resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==} + '@babel/plugin-transform-modules-umd@7.25.9': + resolution: {integrity: sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-named-capturing-groups-regex@7.24.7': - resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==} + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9': + resolution: {integrity: sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-new-target@7.24.7': - resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==} + '@babel/plugin-transform-new-target@7.25.9': + resolution: {integrity: sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7': - resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==} + '@babel/plugin-transform-nullish-coalescing-operator@7.26.6': + resolution: {integrity: sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.24.7': - resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==} + '@babel/plugin-transform-numeric-separator@7.25.9': + resolution: {integrity: sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.24.7': - resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==} + '@babel/plugin-transform-object-rest-spread@7.25.9': + resolution: {integrity: sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-super@7.24.7': - resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==} + '@babel/plugin-transform-object-super@7.25.9': + resolution: {integrity: sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-catch-binding@7.24.7': - resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==} + '@babel/plugin-transform-optional-catch-binding@7.25.9': + resolution: {integrity: sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.24.7': - resolution: {integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==} + '@babel/plugin-transform-optional-chaining@7.25.9': + resolution: {integrity: sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.24.7': - resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==} + '@babel/plugin-transform-parameters@7.25.9': + resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-methods@7.24.7': - resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==} + '@babel/plugin-transform-private-methods@7.25.9': + resolution: {integrity: sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-property-in-object@7.24.7': - resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==} + '@babel/plugin-transform-private-property-in-object@7.25.9': + resolution: {integrity: sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-property-literals@7.24.7': - resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==} + '@babel/plugin-transform-property-literals@7.25.9': + resolution: {integrity: sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-constant-elements@7.24.7': - resolution: {integrity: sha512-7LidzZfUXyfZ8/buRW6qIIHBY8wAZ1OrY9c/wTr8YhZ6vMPo+Uc/CVFLYY1spZrEQlD4w5u8wjqk5NQ3OVqQKA==} + '@babel/plugin-transform-react-constant-elements@7.25.9': + resolution: {integrity: sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-display-name@7.24.7': - resolution: {integrity: sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==} + '@babel/plugin-transform-react-display-name@7.25.9': + resolution: {integrity: sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-development@7.24.7': - resolution: {integrity: sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==} + '@babel/plugin-transform-react-jsx-development@7.25.9': + resolution: {integrity: sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.24.7': - resolution: {integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==} + '@babel/plugin-transform-react-jsx@7.25.9': + resolution: {integrity: sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-pure-annotations@7.24.7': - resolution: {integrity: sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==} + '@babel/plugin-transform-react-pure-annotations@7.25.9': + resolution: {integrity: sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.24.7': - resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==} + '@babel/plugin-transform-regenerator@7.25.9': + resolution: {integrity: sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-reserved-words@7.24.7': - resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==} + '@babel/plugin-transform-regexp-modifiers@7.26.0': + resolution: {integrity: sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.25.9': + resolution: {integrity: sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-runtime@7.24.7': - resolution: {integrity: sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==} + '@babel/plugin-transform-runtime@7.25.9': + resolution: {integrity: sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-shorthand-properties@7.24.7': - resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==} + '@babel/plugin-transform-shorthand-properties@7.25.9': + resolution: {integrity: sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.24.7': - resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==} + '@babel/plugin-transform-spread@7.25.9': + resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-sticky-regex@7.24.7': - resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==} + '@babel/plugin-transform-sticky-regex@7.25.9': + resolution: {integrity: sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-template-literals@7.24.7': - resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==} + '@babel/plugin-transform-template-literals@7.25.9': + resolution: {integrity: sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typeof-symbol@7.24.7': - resolution: {integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==} + '@babel/plugin-transform-typeof-symbol@7.25.9': + resolution: {integrity: sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.24.7': - resolution: {integrity: sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==} + '@babel/plugin-transform-typescript@7.26.5': + resolution: {integrity: sha512-GJhPO0y8SD5EYVCy2Zr+9dSZcEgaSmq5BLR0Oc25TOEhC+ba49vUAGZFjy8v79z9E1mdldq4x9d1xgh4L1d5dQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-escapes@7.24.7': - resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==} + '@babel/plugin-transform-unicode-escapes@7.25.9': + resolution: {integrity: sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-property-regex@7.24.7': - resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==} + '@babel/plugin-transform-unicode-property-regex@7.25.9': + resolution: {integrity: sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-regex@7.24.7': - resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==} + '@babel/plugin-transform-unicode-regex@7.25.9': + resolution: {integrity: sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-sets-regex@7.24.7': - resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==} + '@babel/plugin-transform-unicode-sets-regex@7.25.9': + resolution: {integrity: sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.24.7': - resolution: {integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==} + '@babel/preset-env@7.26.0': + resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -967,123 +905,384 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/preset-react@7.24.7': - resolution: {integrity: sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==} + '@babel/preset-react@7.26.3': + resolution: {integrity: sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/preset-typescript@7.24.7': - resolution: {integrity: sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==} + '@babel/preset-typescript@7.26.0': + resolution: {integrity: sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/regjsgen@0.8.0': - resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} - - '@babel/runtime-corejs3@7.24.7': - resolution: {integrity: sha512-eytSX6JLBY6PVAeQa2bFlDx/7Mmln/gaEpsit5a3WEvjGfiIytEsgAwuIXCPM0xvw0v0cJn3ilq0/TvXrW0kgA==} + '@babel/runtime-corejs3@7.26.0': + resolution: {integrity: sha512-YXHu5lN8kJCb1LOb9PgV6pvak43X2h4HvRApcN5SdWeaItQOzfn1hgP6jasD6KWQyJDBxrVmA9o9OivlnNJK/w==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.24.7': - resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} engines: {node: '>=6.9.0'} - '@babel/template@7.24.7': - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.24.7': - resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + '@babel/traverse@7.26.5': + resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.24.7': - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + '@babel/types@7.26.5': + resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} engines: {node: '>=6.9.0'} '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@commitlint/cli@19.3.0': - resolution: {integrity: sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==} + '@commitlint/cli@19.6.1': + resolution: {integrity: sha512-8hcyA6ZoHwWXC76BoC8qVOSr8xHy00LZhZpauiD0iO0VYbVhMnED0da85lTfIULxl7Lj4c6vZgF0Wu/ed1+jlQ==} engines: {node: '>=v18'} hasBin: true - '@commitlint/config-conventional@19.2.2': - resolution: {integrity: sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==} + '@commitlint/config-conventional@19.6.0': + resolution: {integrity: sha512-DJT40iMnTYtBtUfw9ApbsLZFke1zKh6llITVJ+x9mtpHD08gsNXaIRqHTmwTZL3dNX5+WoyK7pCN/5zswvkBCQ==} engines: {node: '>=v18'} - '@commitlint/config-validator@19.0.3': - resolution: {integrity: sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==} + '@commitlint/config-validator@19.5.0': + resolution: {integrity: sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==} engines: {node: '>=v18'} - '@commitlint/ensure@19.0.3': - resolution: {integrity: sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==} + '@commitlint/ensure@19.5.0': + resolution: {integrity: sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==} engines: {node: '>=v18'} - '@commitlint/execute-rule@19.0.0': - resolution: {integrity: sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==} + '@commitlint/execute-rule@19.5.0': + resolution: {integrity: sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==} engines: {node: '>=v18'} - '@commitlint/format@19.3.0': - resolution: {integrity: sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==} + '@commitlint/format@19.5.0': + resolution: {integrity: sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==} engines: {node: '>=v18'} - '@commitlint/is-ignored@19.2.2': - resolution: {integrity: sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==} + '@commitlint/is-ignored@19.6.0': + resolution: {integrity: sha512-Ov6iBgxJQFR9koOupDPHvcHU9keFupDgtB3lObdEZDroiG4jj1rzky60fbQozFKVYRTUdrBGICHG0YVmRuAJmw==} engines: {node: '>=v18'} - '@commitlint/lint@19.2.2': - resolution: {integrity: sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==} + '@commitlint/lint@19.6.0': + resolution: {integrity: sha512-LRo7zDkXtcIrpco9RnfhOKeg8PAnE3oDDoalnrVU/EVaKHYBWYL1DlRR7+3AWn0JiBqD8yKOfetVxJGdEtZ0tg==} engines: {node: '>=v18'} - '@commitlint/load@19.2.0': - resolution: {integrity: sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==} + '@commitlint/load@19.6.1': + resolution: {integrity: sha512-kE4mRKWWNju2QpsCWt428XBvUH55OET2N4QKQ0bF85qS/XbsRGG1MiTByDNlEVpEPceMkDr46LNH95DtRwcsfA==} engines: {node: '>=v18'} - '@commitlint/message@19.0.0': - resolution: {integrity: sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==} + '@commitlint/message@19.5.0': + resolution: {integrity: sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==} engines: {node: '>=v18'} - '@commitlint/parse@19.0.3': - resolution: {integrity: sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==} + '@commitlint/parse@19.5.0': + resolution: {integrity: sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==} engines: {node: '>=v18'} - '@commitlint/read@19.2.1': - resolution: {integrity: sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw==} + '@commitlint/read@19.5.0': + resolution: {integrity: sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==} engines: {node: '>=v18'} - '@commitlint/resolve-extends@19.1.0': - resolution: {integrity: sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==} + '@commitlint/resolve-extends@19.5.0': + resolution: {integrity: sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==} engines: {node: '>=v18'} - '@commitlint/rules@19.0.3': - resolution: {integrity: sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==} + '@commitlint/rules@19.6.0': + resolution: {integrity: sha512-1f2reW7lbrI0X0ozZMesS/WZxgPa4/wi56vFuJENBmed6mWq5KsheN/nxqnl/C23ioxpPO/PL6tXpiiFy5Bhjw==} engines: {node: '>=v18'} - '@commitlint/to-lines@19.0.0': - resolution: {integrity: sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==} + '@commitlint/to-lines@19.5.0': + resolution: {integrity: sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==} engines: {node: '>=v18'} - '@commitlint/top-level@19.0.0': - resolution: {integrity: sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==} + '@commitlint/top-level@19.5.0': + resolution: {integrity: sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==} engines: {node: '>=v18'} - '@commitlint/types@19.0.3': - resolution: {integrity: sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==} + '@commitlint/types@19.5.0': + resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} engines: {node: '>=v18'} + '@conventional-changelog/git-client@1.0.1': + resolution: {integrity: sha512-PJEqBwAleffCMETaVm/fUgHldzBE35JFk3/9LL6NUA5EXa3qednu+UT6M7E5iBu3zIQZCULYIiZ90fBYHt6xUw==} + engines: {node: '>=18'} + peerDependencies: + conventional-commits-filter: ^5.0.0 + conventional-commits-parser: ^6.0.0 + peerDependenciesMeta: + conventional-commits-filter: + optional: true + conventional-commits-parser: + optional: true + + '@csstools/cascade-layer-name-parser@2.0.4': + resolution: {integrity: sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/color-helpers@5.0.1': + resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.1': + resolution: {integrity: sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-color-parser@3.0.7': + resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-tokenizer@3.0.3': + resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@4.0.2': + resolution: {integrity: sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/postcss-cascade-layers@5.0.1': + resolution: {integrity: sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-function@4.0.7': + resolution: {integrity: sha512-aDHYmhNIHR6iLw4ElWhf+tRqqaXwKnMl0YsQ/X105Zc4dQwe6yJpMrTN6BwOoESrkDjOYMOfORviSSLeDTJkdQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-mix-function@3.0.7': + resolution: {integrity: sha512-e68Nev4CxZYCLcrfWhHH4u/N1YocOfTmw67/kVX5Rb7rnguqqLyxPjhHWjSBX8o4bmyuukmNf3wrUSU3//kT7g==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-content-alt-text@2.0.4': + resolution: {integrity: sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-exponential-functions@2.0.6': + resolution: {integrity: sha512-IgJA5DQsQLu/upA3HcdvC6xEMR051ufebBTIXZ5E9/9iiaA7juXWz1ceYj814lnDYP/7eWjZnw0grRJlX4eI6g==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-font-format-keywords@4.0.0': + resolution: {integrity: sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gamut-mapping@2.0.7': + resolution: {integrity: sha512-gzFEZPoOkY0HqGdyeBXR3JP218Owr683u7KOZazTK7tQZBE8s2yhg06W1tshOqk7R7SWvw9gkw2TQogKpIW8Xw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gradients-interpolation-method@5.0.7': + resolution: {integrity: sha512-WgEyBeg6glUeTdS2XT7qeTFBthTJuXlS9GFro/DVomj7W7WMTamAwpoP4oQCq/0Ki2gvfRYFi/uZtmRE14/DFA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-hwb-function@4.0.7': + resolution: {integrity: sha512-LKYqjO+wGwDCfNIEllessCBWfR4MS/sS1WXO+j00KKyOjm7jDW2L6jzUmqASEiv/kkJO39GcoIOvTTfB3yeBUA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-ic-unit@4.0.0': + resolution: {integrity: sha512-9QT5TDGgx7wD3EEMN3BSUG6ckb6Eh5gSPT5kZoVtUuAonfPmLDJyPhqR4ntPpMYhUKAMVKAg3I/AgzqHMSeLhA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-initial@2.0.0': + resolution: {integrity: sha512-dv2lNUKR+JV+OOhZm9paWzYBXOCi+rJPqJ2cJuhh9xd8USVrd0cBEPczla81HNOyThMQWeCcdln3gZkQV2kYxA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-is-pseudo-class@5.0.1': + resolution: {integrity: sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-light-dark-function@2.0.7': + resolution: {integrity: sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-float-and-clear@3.0.0': + resolution: {integrity: sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overflow@2.0.0': + resolution: {integrity: sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overscroll-behavior@2.0.0': + resolution: {integrity: sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-resize@3.0.0': + resolution: {integrity: sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-viewport-units@3.0.3': + resolution: {integrity: sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-minmax@2.0.6': + resolution: {integrity: sha512-J1+4Fr2W3pLZsfxkFazK+9kr96LhEYqoeBszLmFjb6AjYs+g9oDAw3J5oQignLKk3rC9XHW+ebPTZ9FaW5u5pg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.4': + resolution: {integrity: sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-nested-calc@4.0.0': + resolution: {integrity: sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-normalize-display-values@4.0.0': + resolution: {integrity: sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-oklab-function@4.0.7': + resolution: {integrity: sha512-I6WFQIbEKG2IO3vhaMGZDkucbCaUSXMxvHNzDdnfsTCF5tc0UlV3Oe2AhamatQoKFjBi75dSEMrgWq3+RegsOQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-progressive-custom-properties@4.0.0': + resolution: {integrity: sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-random-function@1.0.2': + resolution: {integrity: sha512-vBCT6JvgdEkvRc91NFoNrLjgGtkLWt47GKT6E2UDn3nd8ZkMBiziQ1Md1OiKoSsgzxsSnGKG3RVdhlbdZEkHjA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-relative-color-syntax@3.0.7': + resolution: {integrity: sha512-apbT31vsJVd18MabfPOnE977xgct5B1I+Jpf+Munw3n6kKb1MMuUmGGH+PT9Hm/fFs6fe61Q/EWnkrb4bNoNQw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-scope-pseudo-class@4.0.1': + resolution: {integrity: sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-sign-functions@1.1.1': + resolution: {integrity: sha512-MslYkZCeMQDxetNkfmmQYgKCy4c+w9pPDfgOBCJOo/RI1RveEUdZQYtOfrC6cIZB7sD7/PHr2VGOcMXlZawrnA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-stepped-value-functions@4.0.6': + resolution: {integrity: sha512-/dwlO9w8vfKgiADxpxUbZOWlL5zKoRIsCymYoh1IPuBsXODKanKnfuZRr32DEqT0//3Av1VjfNZU9yhxtEfIeA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-text-decoration-shorthand@4.0.1': + resolution: {integrity: sha512-xPZIikbx6jyzWvhms27uugIc0I4ykH4keRvoa3rxX5K7lEhkbd54rjj/dv60qOCTisoS+3bmwJTeyV1VNBrXaw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-trigonometric-functions@4.0.6': + resolution: {integrity: sha512-c4Y1D2Why/PeccaSouXnTt6WcNHJkoJRidV2VW9s5gJ97cNxnLgQ4Qj8qOqkIR9VmTQKJyNcbF4hy79ZQnWD7A==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-unset-value@4.0.0': + resolution: {integrity: sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/selector-resolve-nested@3.0.0': + resolution: {integrity: sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@csstools/utilities@2.0.0': + resolution: {integrity: sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + '@discoveryjs/json-ext@0.5.7': resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} - '@docsearch/css@3.6.0': - resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} - '@docsearch/react@3.6.0': - resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' react: '>= 16.8.0 < 19.0.0' @@ -1099,155 +1298,168 @@ packages: search-insights: optional: true - '@docusaurus/core@3.4.0': - resolution: {integrity: sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==} + '@docusaurus/babel@3.7.0': + resolution: {integrity: sha512-0H5uoJLm14S/oKV3Keihxvh8RV+vrid+6Gv+2qhuzbqHanawga8tYnsdpjEyt36ucJjqlby2/Md2ObWjA02UXQ==} + engines: {node: '>=18.0'} + + '@docusaurus/bundler@3.7.0': + resolution: {integrity: sha512-CUUT9VlSGukrCU5ctZucykvgCISivct+cby28wJwCC/fkQFgAHRp/GKv2tx38ZmXb7nacrKzFTcp++f9txUYGg==} + engines: {node: '>=18.0'} + peerDependencies: + '@docusaurus/faster': '*' + peerDependenciesMeta: + '@docusaurus/faster': + optional: true + + '@docusaurus/core@3.7.0': + resolution: {integrity: sha512-b0fUmaL+JbzDIQaamzpAFpTviiaU4cX3Qz8cuo14+HGBCwa0evEK0UYCBFY3n4cLzL8Op1BueeroUD2LYAIHbQ==} engines: {node: '>=18.0'} hasBin: true peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + '@mdx-js/react': ^3.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/cssnano-preset@3.4.0': - resolution: {integrity: sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==} + '@docusaurus/cssnano-preset@3.7.0': + resolution: {integrity: sha512-X9GYgruZBSOozg4w4dzv9uOz8oK/EpPVQXkp0MM6Tsgp/nRIU9hJzJ0Pxg1aRa3xCeEQTOimZHcocQFlLwYajQ==} engines: {node: '>=18.0'} - '@docusaurus/logger@3.4.0': - resolution: {integrity: sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==} + '@docusaurus/logger@3.7.0': + resolution: {integrity: sha512-z7g62X7bYxCYmeNNuO9jmzxLQG95q9QxINCwpboVcNff3SJiHJbGrarxxOVMVmAh1MsrSfxWkVGv4P41ktnFsA==} engines: {node: '>=18.0'} - '@docusaurus/mdx-loader@3.4.0': - resolution: {integrity: sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==} + '@docusaurus/mdx-loader@3.7.0': + resolution: {integrity: sha512-OFBG6oMjZzc78/U3WNPSHs2W9ZJ723ewAcvVJaqS0VgyeUfmzUV8f1sv+iUHA0DtwiR5T5FjOxj6nzEE8LY6VA==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/module-type-aliases@3.4.0': - resolution: {integrity: sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==} + '@docusaurus/module-type-aliases@3.7.0': + resolution: {integrity: sha512-g7WdPqDNaqA60CmBrr0cORTrsOit77hbsTj7xE2l71YhBn79sxdm7WMK7wfhcaafkbpIh7jv5ef5TOpf1Xv9Lg==} peerDependencies: react: '*' react-dom: '*' - '@docusaurus/plugin-content-blog@3.4.0': - resolution: {integrity: sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==} + '@docusaurus/plugin-content-blog@3.7.0': + resolution: {integrity: sha512-EFLgEz6tGHYWdPU0rK8tSscZwx+AsyuBW/r+tNig2kbccHYGUJmZtYN38GjAa3Fda4NU+6wqUO5kTXQSRBQD3g==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + '@docusaurus/plugin-content-docs': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-content-docs@3.4.0': - resolution: {integrity: sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==} + '@docusaurus/plugin-content-docs@3.7.0': + resolution: {integrity: sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-content-pages@3.4.0': - resolution: {integrity: sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==} + '@docusaurus/plugin-content-pages@3.7.0': + resolution: {integrity: sha512-YJSU3tjIJf032/Aeao8SZjFOrXJbz/FACMveSMjLyMH4itQyZ2XgUIzt4y+1ISvvk5zrW4DABVT2awTCqBkx0Q==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-debug@3.4.0': - resolution: {integrity: sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==} + '@docusaurus/plugin-debug@3.7.0': + resolution: {integrity: sha512-Qgg+IjG/z4svtbCNyTocjIwvNTNEwgRjSXXSJkKVG0oWoH0eX/HAPiu+TS1HBwRPQV+tTYPWLrUypYFepfujZA==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-google-analytics@3.4.0': - resolution: {integrity: sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==} + '@docusaurus/plugin-google-analytics@3.7.0': + resolution: {integrity: sha512-otIqiRV/jka6Snjf+AqB360XCeSv7lQC+DKYW+EUZf6XbuE8utz5PeUQ8VuOcD8Bk5zvT1MC4JKcd5zPfDuMWA==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-google-gtag@3.4.0': - resolution: {integrity: sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==} + '@docusaurus/plugin-google-gtag@3.7.0': + resolution: {integrity: sha512-M3vrMct1tY65ModbyeDaMoA+fNJTSPe5qmchhAbtqhDD/iALri0g9LrEpIOwNaoLmm6lO88sfBUADQrSRSGSWA==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-google-tag-manager@3.4.0': - resolution: {integrity: sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==} + '@docusaurus/plugin-google-tag-manager@3.7.0': + resolution: {integrity: sha512-X8U78nb8eiMiPNg3jb9zDIVuuo/rE1LjGDGu+5m5CX4UBZzjMy+klOY2fNya6x8ACyE/L3K2erO1ErheP55W/w==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-sitemap@3.4.0': - resolution: {integrity: sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==} + '@docusaurus/plugin-sitemap@3.7.0': + resolution: {integrity: sha512-bTRT9YLZ/8I/wYWKMQke18+PF9MV8Qub34Sku6aw/vlZ/U+kuEuRpQ8bTcNOjaTSfYsWkK4tTwDMHK2p5S86cA==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/preset-classic@3.4.0': - resolution: {integrity: sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==} + '@docusaurus/plugin-svgr@3.7.0': + resolution: {integrity: sha512-HByXIZTbc4GV5VAUkZ2DXtXv1Qdlnpk3IpuImwSnEzCDBkUMYcec5282hPjn6skZqB25M1TYCmWS91UbhBGxQg==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/preset-classic@3.7.0': + resolution: {integrity: sha512-nPHj8AxDLAaQXs+O6+BwILFuhiWbjfQWrdw2tifOClQoNfuXDjfjogee6zfx6NGHWqshR23LrcN115DmkHC91Q==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 '@docusaurus/react-loadable@6.0.0': resolution: {integrity: sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==} peerDependencies: react: '*' - '@docusaurus/theme-classic@3.4.0': - resolution: {integrity: sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==} + '@docusaurus/theme-classic@3.7.0': + resolution: {integrity: sha512-MnLxG39WcvLCl4eUzHr0gNcpHQfWoGqzADCly54aqCofQX6UozOS9Th4RK3ARbM9m7zIRv3qbhggI53dQtx/hQ==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/theme-common@3.4.0': - resolution: {integrity: sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==} + '@docusaurus/theme-common@3.7.0': + resolution: {integrity: sha512-8eJ5X0y+gWDsURZnBfH0WabdNm8XMCXHv8ENy/3Z/oQKwaB/EHt5lP9VsTDTf36lKEp0V6DjzjFyFIB+CetL0A==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + '@docusaurus/plugin-content-docs': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/theme-search-algolia@3.4.0': - resolution: {integrity: sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==} + '@docusaurus/theme-search-algolia@3.7.0': + resolution: {integrity: sha512-Al/j5OdzwRU1m3falm+sYy9AaB93S1XF1Lgk9Yc6amp80dNxJVplQdQTR4cYdzkGtuQqbzUA8+kaoYYO0RbK6g==} engines: {node: '>=18.0'} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/theme-translations@3.4.0': - resolution: {integrity: sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==} + '@docusaurus/theme-translations@3.7.0': + resolution: {integrity: sha512-Ewq3bEraWDmienM6eaNK7fx+/lHMtGDHQyd1O+4+3EsDxxUmrzPkV7Ct3nBWTuE0MsoZr3yNwQVKjllzCMuU3g==} engines: {node: '>=18.0'} - '@docusaurus/types@3.4.0': - resolution: {integrity: sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==} + '@docusaurus/types@3.7.0': + resolution: {integrity: sha512-kOmZg5RRqJfH31m+6ZpnwVbkqMJrPOG5t0IOl4i/+3ruXyNfWzZ0lVtVrD0u4ONc/0NOsS9sWYaxxWNkH1LdLQ==} peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/utils-common@3.4.0': - resolution: {integrity: sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==} + '@docusaurus/utils-common@3.7.0': + resolution: {integrity: sha512-IZeyIfCfXy0Mevj6bWNg7DG7B8G+S6o6JVpddikZtWyxJguiQ7JYr0SIZ0qWd8pGNuMyVwriWmbWqMnK7Y5PwA==} engines: {node: '>=18.0'} - peerDependencies: - '@docusaurus/types': '*' - peerDependenciesMeta: - '@docusaurus/types': - optional: true - '@docusaurus/utils-validation@3.4.0': - resolution: {integrity: sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==} + '@docusaurus/utils-validation@3.7.0': + resolution: {integrity: sha512-w8eiKk8mRdN+bNfeZqC4nyFoxNyI1/VExMKAzD9tqpJfLLbsa46Wfn5wcKH761g9WkKh36RtFV49iL9lh1DYBA==} engines: {node: '>=18.0'} - '@docusaurus/utils@3.4.0': - resolution: {integrity: sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==} + '@docusaurus/utils@3.7.0': + resolution: {integrity: sha512-e7zcB6TPnVzyUaHMJyLSArKa2AG3h9+4CfvKXKKWNx6hRs+p0a+u7HHTJBgo6KW2m+vqDnuIHK4X+bhmoghAFA==} engines: {node: '>=18.0'} - peerDependencies: - '@docusaurus/types': '*' - peerDependenciesMeta: - '@docusaurus/types': - optional: true '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} @@ -1387,29 +1599,29 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.11.0': - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@fontsource-variable/onest@5.0.4': - resolution: {integrity: sha512-AgdtsALwfkuLuL3dMCCX/ag27ho7yMKNIrkX5AKYKsPDm5u6mrTZ0LcO9jtMI7Z4ant7Huf9c2QtPdK3az/mow==} + '@fontsource-variable/onest@5.1.1': + resolution: {integrity: sha512-Lb1hWJL7z7VZ9oDiHp5/O4tq/tzSQ0md1Luv+RPRnw3WalFoRBdjE9Xw9ibgteEeVlv8fYQ3UTR6ALDwdWxgzQ==} - '@fontsource/fira-mono@5.0.13': - resolution: {integrity: sha512-fZDjR2BdAqmauEbTjcIT62zYzbOgDa5+IQH34D2k8Pxmy1T815mAqQkZciWZVQ9dc/BgdTtTUV9HJ2ulBNwchg==} + '@fontsource/fira-mono@5.1.1': + resolution: {integrity: sha512-JDZUQuQQFTvPJgwOftXg44LOa7XYNqwTHHquEpi8rY6SK8m7Q5rWxmMX7nUOvRF2g+jc02mqmK2Y+WmviF9+VQ==} '@foscia/core@0.12.1': resolution: {integrity: sha512-Hgn7TQr9ANpNAR/vQpEP2yu0c4/eCjGLBtc6GLryu2J8ruzbCcLe4oiY0B5auHtir6OpM8WHKIoIN3Ez30gpMw==} @@ -1429,14 +1641,17 @@ packages: '@foscia/test@0.12.1': resolution: {integrity: sha512-Rji9GuNUsKr/dMvzurGiJUQFIk7yjFwyu8SxDurxTwz7oIE6/2x7mx6g9xLt6V+jPLVMNgmHnm+Os6O0VNST4g==} + '@gerrit0/mini-shiki@1.27.0': + resolution: {integrity: sha512-nFZkbq4/wU+GxkyJVrXeMIS9SEcHI1HzJtK3EDtMQy16nNs1LNaI0dZTLAP2EuK/QSTYLo/Zaabm6Phxlmra1A==} + '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} deprecated: Use @eslint/config-array instead @@ -1455,8 +1670,8 @@ packages: '@iarna/toml@2.2.5': resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} - '@inquirer/figures@1.0.3': - resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} + '@inquirer/figures@1.0.9': + resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==} engines: {node: '>=18'} '@isaacs/cliui@8.0.2': @@ -1479,6 +1694,10 @@ packages: resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1493,21 +1712,20 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@ljharb/through@2.3.13': - resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} - engines: {node: '>= 0.4'} - - '@mdx-js/mdx@3.0.1': - resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==} + '@mdx-js/mdx@3.1.0': + resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} - '@mdx-js/react@3.0.1': - resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==} + '@mdx-js/react@3.1.0': + resolution: {integrity: sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==} peerDependencies: '@types/react': '>=16' react: '>=16' @@ -1524,6 +1742,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + '@octokit/auth-token@4.0.0': resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} engines: {node: '>= 18'} @@ -1540,8 +1762,8 @@ packages: resolution: {integrity: sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==} engines: {node: '>= 18'} - '@octokit/openapi-types@22.2.0': - resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} + '@octokit/openapi-types@23.0.1': + resolution: {integrity: sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==} '@octokit/plugin-paginate-rest@11.3.1': resolution: {integrity: sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==} @@ -1573,8 +1795,8 @@ packages: resolution: {integrity: sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==} engines: {node: '>= 18'} - '@octokit/types@13.5.0': - resolution: {integrity: sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==} + '@octokit/types@13.7.0': + resolution: {integrity: sha512-BXfRP+3P3IN6fd4uF3SniaHKOO4UXWBfkdR3vA8mIvaoO/wLjGN5qivUtW0QRitBHHMcfC41SLhNVYIZZE+wkA==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -1588,12 +1810,12 @@ packages: resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} engines: {node: '>=12.22.0'} - '@pnpm/npm-conf@2.2.2': - resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + '@pnpm/npm-conf@2.3.1': + resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} - '@polka/url@1.0.0-next.25': - resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} '@release-it/bumper@6.0.1': resolution: {integrity: sha512-yeQsbGNMzzN0c/5JV1awXP6UHX/kJamXCKR6/daS0YQfj98SZXAcLn3JEq+qfK/Jq/cnATnlz5r6UY0cfBkm1A==} @@ -1601,14 +1823,14 @@ packages: peerDependencies: release-it: ^17.0.0 - '@release-it/conventional-changelog@8.0.1': - resolution: {integrity: sha512-pwc9jaBYDaSX5TXw6rEnPfqDkKJN2sFBhYpON1kBi9T3sA9EOBncC4ed0Bv3L1ciNb6eqEJXPfp+tQMqVlv/eg==} - engines: {node: '>=18'} + '@release-it/conventional-changelog@8.0.2': + resolution: {integrity: sha512-WpnWWRr7O0JeLoiejLrPEWnnwFhCscBn1wBTAXeitiz2/Ifaol0s+t8otf/HYq/OiQOri2iH8d0CnVb72tBdIQ==} + engines: {node: ^18.18.0 || ^20.9.0 || ^22.0.0} peerDependencies: release-it: ^17.0.0 - '@rollup/plugin-commonjs@26.0.1': - resolution: {integrity: sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==} + '@rollup/plugin-commonjs@26.0.3': + resolution: {integrity: sha512-2BJcolt43MY+y5Tz47djHkodCC3c1VKVrBDKpVqHKpQ9z9S158kCCqB8NF6/gzxLdNlYW9abB3Ibh+kOWLp8KQ==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -1625,8 +1847,8 @@ packages: rollup: optional: true - '@rollup/plugin-node-resolve@15.2.3': - resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} + '@rollup/plugin-node-resolve@15.3.1': + resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.78.0||^3.0.0||^4.0.0 @@ -1656,8 +1878,8 @@ packages: tslib: optional: true - '@rollup/pluginutils@5.1.0': - resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -1665,89 +1887,116 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.18.0': - resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} + '@rollup/rollup-android-arm-eabi@4.30.1': + resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.18.0': - resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} + '@rollup/rollup-android-arm64@4.30.1': + resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.18.0': - resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} + '@rollup/rollup-darwin-arm64@4.30.1': + resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.18.0': - resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} + '@rollup/rollup-darwin-x64@4.30.1': + resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} + '@rollup/rollup-freebsd-arm64@4.30.1': + resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.30.1': + resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} + '@rollup/rollup-linux-arm-musleabihf@4.30.1': + resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.18.0': - resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} + '@rollup/rollup-linux-arm64-gnu@4.30.1': + resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.18.0': - resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} + '@rollup/rollup-linux-arm64-musl@4.30.1': + resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} + '@rollup/rollup-linux-riscv64-gnu@4.30.1': + resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.18.0': - resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} + '@rollup/rollup-linux-s390x-gnu@4.30.1': + resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.18.0': - resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} + '@rollup/rollup-linux-x64-gnu@4.30.1': + resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.18.0': - resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} + '@rollup/rollup-linux-x64-musl@4.30.1': + resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.18.0': - resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} + '@rollup/rollup-win32-arm64-msvc@4.30.1': + resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.18.0': - resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} + '@rollup/rollup-win32-ia32-msvc@4.30.1': + resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.18.0': - resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} + '@rollup/rollup-win32-x64-msvc@4.30.1': + resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} cpu: [x64] os: [win32] + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@shikijs/engine-oniguruma@1.27.2': + resolution: {integrity: sha512-FZYKD1KN7srvpkz4lbGLOYWlyDU4Rd+2RtuKfABTkafAPOFr+J6umfIwY/TzOQqfNtWjL7SAwPAO0dcOraRLaQ==} + + '@shikijs/types@1.27.2': + resolution: {integrity: sha512-DM9OWUyjmdYdnKDpaGB/GEn9XkToyK1tqxuqbmc5PV+5K8WjjwfygL3+cIvbkSw2v1ySwHDgqATq/+98pJ4Kyg==} + + '@shikijs/vscode-textmate@10.0.1': + resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} + '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -1776,6 +2025,12 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@slorber/react-helmet-async@1.3.0': + resolution: {integrity: sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@slorber/remark-comment@1.0.0': resolution: {integrity: sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==} @@ -1883,8 +2138,8 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/conventional-commits-parser@5.0.0': - resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} + '@types/conventional-commits-parser@5.0.1': + resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1892,8 +2147,8 @@ packages: '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - '@types/eslint@8.56.10': - resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1901,8 +2156,14 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/express-serve-static-core@4.19.3': - resolution: {integrity: sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/express-serve-static-core@4.19.6': + resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} + + '@types/express-serve-static-core@5.0.5': + resolution: {integrity: sha512-GLZPrd9ckqEBFMcVM/qRFAP0Hg3qiVEojgEFsx/N/zKXsBzbGF6z5FBDpZ0+Xhp1xr+qRZYjfGr1cWHB9oFHSA==} '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} @@ -1925,8 +2186,8 @@ packages: '@types/http-errors@2.0.4': resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - '@types/http-proxy@1.17.14': - resolution: {integrity: sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==} + '@types/http-proxy@1.17.15': + resolution: {integrity: sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==} '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -1970,11 +2231,11 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - '@types/node@18.19.39': - resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} + '@types/node@18.19.70': + resolution: {integrity: sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==} - '@types/node@20.14.9': - resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==} + '@types/node@22.10.6': + resolution: {integrity: sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1985,14 +2246,11 @@ packages: '@types/pluralize@0.0.33': resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==} - '@types/prismjs@1.26.4': - resolution: {integrity: sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==} - - '@types/prop-types@15.7.12': - resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + '@types/prismjs@1.26.5': + resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} - '@types/qs@6.9.15': - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + '@types/qs@6.9.18': + resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} @@ -2006,8 +2264,8 @@ packages: '@types/react-router@5.1.20': resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} - '@types/react@18.3.3': - resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + '@types/react@19.0.7': + resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2018,6 +2276,9 @@ packages: '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + '@types/send@0.17.4': resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} @@ -2030,23 +2291,23 @@ packages: '@types/sockjs@0.3.36': resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} - '@types/unist@2.0.10': - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - '@types/unist@3.0.2': - resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/ws@8.5.10': - resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} + '@types/ws@8.5.13': + resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.32': - resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@7.14.1': - resolution: {integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==} + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -2056,8 +2317,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@7.14.1': - resolution: {integrity: sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==} + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -2066,12 +2327,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@7.14.1': - resolution: {integrity: sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==} + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/type-utils@7.14.1': - resolution: {integrity: sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==} + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -2080,12 +2341,12 @@ packages: typescript: optional: true - '@typescript-eslint/types@7.14.1': - resolution: {integrity: sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==} + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/typescript-estree@7.14.1': - resolution: {integrity: sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==} + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' @@ -2093,18 +2354,18 @@ packages: typescript: optional: true - '@typescript-eslint/utils@7.14.1': - resolution: {integrity: sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==} + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/visitor-keys@7.14.1': - resolution: {integrity: sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==} + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@ungap/structured-clone@1.2.1': + resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} '@vitest/coverage-istanbul@1.6.0': resolution: {integrity: sha512-h/BwpXehkkS0qsNCS00QxiupAqVkNi0WT19BR0dQvlge5oHghoSVLx63fABYFoKxVb7Ue7+k6V2KokmQ1zdMpg==} @@ -2126,50 +2387,50 @@ packages: '@vitest/utils@1.6.0': resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} - '@webassemblyjs/ast@1.12.1': - resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} - '@webassemblyjs/floating-point-hex-parser@1.11.6': - resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} - '@webassemblyjs/helper-api-error@1.11.6': - resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} - '@webassemblyjs/helper-buffer@1.12.1': - resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} - '@webassemblyjs/helper-numbers@1.11.6': - resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} - '@webassemblyjs/helper-wasm-bytecode@1.11.6': - resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - '@webassemblyjs/helper-wasm-section@1.12.1': - resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - '@webassemblyjs/ieee754@1.11.6': - resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - '@webassemblyjs/leb128@1.11.6': - resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - '@webassemblyjs/utf8@1.11.6': - resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} - '@webassemblyjs/wasm-edit@1.12.1': - resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} - '@webassemblyjs/wasm-gen@1.12.1': - resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} - '@webassemblyjs/wasm-opt@1.12.1': - resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} - '@webassemblyjs/wasm-parser@1.12.1': - resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} - '@webassemblyjs/wast-printer@1.12.1': - resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -2185,22 +2446,17 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - acorn@8.12.0: - resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true @@ -2211,8 +2467,8 @@ packages: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} - agent-base@7.1.1: - resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} aggregate-error@3.1.0: @@ -2240,16 +2496,17 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - algoliasearch-helper@3.21.0: - resolution: {integrity: sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==} + algoliasearch-helper@3.23.0: + resolution: {integrity: sha512-8CK4Gb/ju4OesAYcS+mjBpNiVA7ILWpg7D2vhBZohh0YkG8QT1KZ9LG+8+EntQBUGoKtPy06OFhiwP4f5zzAQg==} peerDependencies: algoliasearch: '>= 3.1 < 6' - algoliasearch@4.23.3: - resolution: {integrity: sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==} + algoliasearch@5.19.0: + resolution: {integrity: sha512-zrLtGhC63z3sVLDDKGW+SlCRN9eJHFTgdEmoAOpsVh6wgGL1GgTTDou7tpCBjevzgIvi3AIyDAQO3Xjbg5eqZg==} + engines: {node: '>= 14.0.0'} ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -2279,9 +2536,6 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - ansi-sequence-parser@1.1.1: - resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==} - ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -2314,8 +2568,8 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} array-flatten@1.1.1: @@ -2336,20 +2590,16 @@ packages: resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} engines: {node: '>= 0.4'} - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} engines: {node: '>= 0.4'} - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} engines: {node: '>= 0.4'} - array.prototype.map@1.0.7: - resolution: {integrity: sha512-XpcFfLoBEAhezrrNw1V+yLXkE7M6uR7xJEsxbG6c/V9v043qurwVJB9r9UTnoSioFDoz1i1VOydpWGmJpfVZbg==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} assertion-error@1.1.0: @@ -2359,8 +2609,8 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} - astring@1.8.6: - resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true async-retry@1.3.3: @@ -2373,8 +2623,11 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} - autoprefixer@10.4.19: - resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + atomically@2.0.3: + resolution: {integrity: sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -2384,8 +2637,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - babel-loader@9.1.3: - resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} engines: {node: '>= 14.15.0'} peerDependencies: '@babel/core': ^7.12.0 @@ -2394,18 +2647,18 @@ packages: babel-plugin-dynamic-import-node@2.3.3: resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} - babel-plugin-polyfill-corejs2@0.4.11: - resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} + babel-plugin-polyfill-corejs2@0.4.12: + resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-corejs3@0.10.4: - resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==} + babel-plugin-polyfill-corejs3@0.10.6: + resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.2: - resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==} + babel-plugin-polyfill-regenerator@0.6.3: + resolution: {integrity: sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -2438,12 +2691,12 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@1.20.2: - resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - bonjour-service@1.2.1: - resolution: {integrity: sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==} + bonjour-service@1.3.0: + resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -2456,7 +2709,11 @@ packages: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} - brace-expansion@1.1.11: + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} brace-expansion@2.0.1: @@ -2466,8 +2723,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.23.1: - resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -2477,10 +2734,6 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -2505,8 +2758,16 @@ packages: resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} engines: {node: '>=14.16'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + call-bind-apply-helpers@1.0.1: + resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.3: + resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} callsites@3.1.0: @@ -2524,17 +2785,21 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001636: - resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} + caniuse-lite@1.0.30001692: + resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} engines: {node: '>=4'} chalk@2.4.2: @@ -2549,6 +2814,10 @@ packages: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -2590,6 +2859,10 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + ci-info@4.1.0: + resolution: {integrity: sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==} + engines: {node: '>=8'} + clean-css@5.3.3: resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} @@ -2610,6 +2883,10 @@ packages: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + cli-highlight@2.1.11: resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} engines: {node: '>=8.0.0', npm: '>=5.0.0'} @@ -2723,8 +3000,8 @@ packages: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} - compression@1.7.4: - resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + compression@1.7.5: + resolution: {integrity: sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==} engines: {node: '>= 0.8.0'} concat-map@0.0.1: @@ -2739,8 +3016,8 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -2749,6 +3026,10 @@ packages: resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} engines: {node: '>=12'} + configstore@7.0.0: + resolution: {integrity: sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ==} + engines: {node: '>=18'} + confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} @@ -2756,8 +3037,9 @@ packages: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} engines: {node: '>=0.8'} - consola@2.15.3: - resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + engines: {node: ^14.18.0 || >=16.10.0} content-disposition@0.5.2: resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} @@ -2844,8 +3126,8 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} copy-text-to-clipboard@3.2.0: @@ -2858,25 +3140,25 @@ packages: peerDependencies: webpack: ^5.1.0 - core-js-compat@3.37.1: - resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} + core-js-compat@3.40.0: + resolution: {integrity: sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==} - core-js-pure@3.37.1: - resolution: {integrity: sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==} + core-js-pure@3.40.0: + resolution: {integrity: sha512-AtDzVIgRrmRKQai62yuSIN5vNiQjcJakJb4fbhVw3ehxx7Lohphvw9SGNWKhLFqSxC4ilD0g/L1huAYFQU3Q6A==} - core-js@3.37.1: - resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==} + core-js@3.40.0: + resolution: {integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cosmiconfig-typescript-loader@5.0.0: - resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} - engines: {node: '>=v16'} + cosmiconfig-typescript-loader@6.1.0: + resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==} + engines: {node: '>=v18'} peerDependencies: '@types/node': '*' - cosmiconfig: '>=8.2' - typescript: '>=4' + cosmiconfig: '>=9' + typescript: '>=5' cosmiconfig@6.0.0: resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} @@ -2904,16 +3186,32 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + crypto-random-string@4.0.0: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} + css-blank-pseudo@7.0.1: + resolution: {integrity: sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + css-declaration-sorter@7.2.0: resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.0.9 + css-has-pseudo@7.0.2: + resolution: {integrity: sha512-nzol/h+E0bId46Kn2dQH5VElaknX2Sr0hFuB/1EomdC7j+OISt2ZzK7EHX9DZDY53WbIVAR7FYKSO2XnSf07MQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + css-loader@6.11.0: resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} engines: {node: '>= 12.13.0'} @@ -2951,6 +3249,12 @@ packages: lightningcss: optional: true + css-prefers-color-scheme@10.0.0: + resolution: {integrity: sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} @@ -2969,6 +3273,9 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} + cssdb@8.2.3: + resolution: {integrity: sha512-9BDG5XmJrJQQnJ51VFxXCAtpZ5ebDlAREmO8sxMOVU0aSxN/gocbctjIG5LMh3WBUq+xTlb/jw2LoljBEqraTA==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -3002,8 +3309,8 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - cssstyle@4.0.1: - resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + cssstyle@4.2.1: + resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==} engines: {node: '>=18'} csstype@3.1.3: @@ -3013,10 +3320,6 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - data-uri-to-buffer@6.0.2: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} engines: {node: '>= 14'} @@ -3025,16 +3328,16 @@ packages: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} engines: {node: '>= 0.4'} - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} date-fns@2.30.0: @@ -3060,8 +3363,8 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -3204,10 +3507,10 @@ packages: resolution: {integrity: sha512-xNZcOTPGfhYXoAA5htdmNH9iSUlwu3m9BC5prLA158ZsgB35rsQ9DeWAYRvq7qy0wJ8cwMaqYfh/1HxJpP2r/Q==} hasBin: true - docusaurus-plugin-typedoc@1.0.1: - resolution: {integrity: sha512-q3e/XHmnMNdP361/0SMBbCQyr7oUwbSs5QMhu1BkEUvM45oKG7i1qxcKKabOefFDVDW9cmbBISWxB8XZGJAVFg==} + docusaurus-plugin-typedoc@1.2.1: + resolution: {integrity: sha512-4GlqhT1btGEjB94RzIRbkgsGtENvOMKnliWEXcPIUcbaV6zzq4py4GsLoD67QrLtAFAx6W9N1CbVh6yJ7ZXrDA==} peerDependencies: - typedoc-plugin-markdown: '>=4.0.0' + typedoc-plugin-markdown: '>=4.4.0' dom-converter@0.2.0: resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} @@ -3232,8 +3535,8 @@ packages: domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -3246,10 +3549,18 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + dot-prop@9.0.0: + resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} + engines: {node: '>=18'} + + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} engines: {node: '>=12'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -3259,12 +3570,15 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.4.803: - resolution: {integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==} + electron-to-chromium@1.5.83: + resolution: {integrity: sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==} emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3278,15 +3592,19 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} - emoticon@4.0.1: - resolution: {integrity: sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==} + emoticon@4.1.0: + resolution: {integrity: sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==} encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} - enhanced-resolve@5.17.0: - resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + enhanced-resolve@5.18.0: + resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -3307,42 +3625,42 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} engines: {node: '>= 0.4'} - es-array-method-boxes-properly@1.0.0: - resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} - - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} - es-module-lexer@1.5.3: - resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==} - - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -3352,6 +3670,10 @@ packages: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-goat@4.0.0: resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} engines: {node: '>=12'} @@ -3393,15 +3715,21 @@ packages: eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - eslint-import-resolver-typescript@3.6.1: - resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + eslint-import-resolver-typescript@3.7.0: + resolution: {integrity: sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true - eslint-module-utils@2.8.1: - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -3421,12 +3749,12 @@ packages: eslint-import-resolver-webpack: optional: true - eslint-plugin-import@2.29.1: - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 peerDependenciesMeta: '@typescript-eslint/parser': optional: true @@ -3443,9 +3771,10 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@9.6.1: @@ -3457,8 +3786,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -3482,11 +3811,14 @@ packages: estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + estree-util-to-js@2.0.0: resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} - estree-util-value-to-estree@3.1.1: - resolution: {integrity: sha512-5mvUrF2suuv5f5cGDnDphIy4/gW86z82kl5qG6mM9z04SEQI4FB5Apmaw/TGEf3l55nLtMs5s51dmhUzvAHQCA==} + estree-util-value-to-estree@3.2.1: + resolution: {integrity: sha512-Vt2UOjyPbNQQgT5eJh+K5aATti0OjCIAGc9SgMdOFYbohuifsWclR74l0iZTJwePMgWYdX1hlVS+dedH9XV8kw==} estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} @@ -3524,16 +3856,20 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} + execa@8.0.0: + resolution: {integrity: sha512-CTNS0BcKBcoOsawKBlpcKNmK4Kjuyz5jVLhf+PUsHGMqiKMVTa4cN3U7r7bRY8KTpfOGpXMo27fdy0dYVg2pqA==} + engines: {node: '>=16.17'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} - execa@9.3.0: - resolution: {integrity: sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==} + execa@9.5.2: + resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} - express@4.19.2: - resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} extend-shallow@2.0.1: @@ -3550,8 +3886,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: @@ -3560,8 +3896,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-url-parser@1.1.3: - resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -3577,9 +3913,9 @@ packages: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} @@ -3603,8 +3939,8 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} find-cache-dir@4.0.0: @@ -3635,11 +3971,11 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} - follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -3650,8 +3986,8 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} fork-ts-checker-webpack-plugin@6.5.3: @@ -3672,18 +4008,14 @@ packages: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} - form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} engines: {node: '>= 6'} format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3695,8 +4027,8 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + fs-extra@11.3.0: + resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} fs-extra@9.1.0: @@ -3717,8 +4049,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -3736,16 +4068,24 @@ packages: resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} engines: {node: '>=18'} + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + get-intrinsic@1.2.7: + resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} engines: {node: '>= 0.4'} get-own-enumerable-property-symbols@3.0.2: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -3758,15 +4098,15 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.7.5: - resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - get-uri@6.0.3: - resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} + get-uri@6.0.4: + resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} engines: {node: '>= 14'} git-raw-commits@4.0.0: @@ -3779,6 +4119,11 @@ packages: engines: {node: '>=16'} hasBin: true + git-semver-tags@8.0.0: + resolution: {integrity: sha512-N7YRIklvPH3wYWAR2vysaqGLPRcpwQ0GKdlqTiVN5w1UmCdaeY3K8s6DMKRCh54DDdzyt/OAB6C8jgVtb7Y2Fg==} + engines: {node: '>=18'} + hasBin: true + git-up@7.0.0: resolution: {integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==} @@ -3799,14 +4144,8 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.1: - resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} - engines: {node: '>=16 || 14 >=14.18'} - hasBin: true - - glob@10.4.2: - resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==} - engines: {node: '>=16 || 14 >=14.18'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true glob@7.2.3: @@ -3849,21 +4188,18 @@ packages: resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - globby@14.0.1: - resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==} + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} engines: {node: '>=18'} - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} got@12.6.1: resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} engines: {node: '>=14.16'} - got@13.0.0: - resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} - engines: {node: '>=16'} - graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} @@ -3889,8 +4225,9 @@ packages: engines: {node: '>=0.4.7'} hasBin: true - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} @@ -3903,12 +4240,12 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} has-tostringtag@1.0.2: @@ -3923,20 +4260,20 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hast-util-from-parse5@8.0.1: - resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} + hast-util-from-parse5@8.0.2: + resolution: {integrity: sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A==} hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - hast-util-raw@9.0.4: - resolution: {integrity: sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==} + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} - hast-util-to-estree@3.1.0: - resolution: {integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==} + hast-util-to-estree@3.1.1: + resolution: {integrity: sha512-IWtwwmPskfSmma9RpzCappDUitC8t5jhAynHhc1m2+5trOgsrp7txscUSavc5Ic8PATyAjfrCK1wgtxh2cICVQ==} - hast-util-to-jsx-runtime@2.3.0: - resolution: {integrity: sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==} + hast-util-to-jsx-runtime@2.3.2: + resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==} hast-util-to-parse5@8.0.0: resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} @@ -3944,8 +4281,8 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - hastscript@8.0.0: - resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} + hastscript@9.0.0: + resolution: {integrity: sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==} he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} @@ -3994,8 +4331,8 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - html-webpack-plugin@5.6.0: - resolution: {integrity: sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==} + html-webpack-plugin@5.6.3: + resolution: {integrity: sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==} engines: {node: '>=10.13.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -4026,15 +4363,15 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} - http-parser-js@0.5.8: - resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + http-parser-js@0.5.9: + resolution: {integrity: sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==} http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - http-proxy-middleware@2.0.6: - resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} + http-proxy-middleware@2.0.7: + resolution: {integrity: sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==} engines: {node: '>=12.0.0'} peerDependencies: '@types/express': ^4.17.13 @@ -4050,8 +4387,8 @@ packages: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} - https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} human-signals@2.1.0: @@ -4062,12 +4399,12 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - human-signals@7.0.0: - resolution: {integrity: sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==} + human-signals@8.0.0: + resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==} engines: {node: '>=18.18.0'} - husky@9.0.11: - resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} hasBin: true @@ -4088,12 +4425,12 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - image-size@1.1.1: - resolution: {integrity: sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==} + image-size@1.2.0: + resolution: {integrity: sha512-4S8fwbO6w3GeCVN6OPtA9I5IGKkcDMPcKndtUlpJuCwu7JLjtj7JZpwqLuyY2nrmQT3AWsCJLSKPsc2mPBSl3w==} engines: {node: '>=16.x'} hasBin: true @@ -4119,8 +4456,8 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - infima@0.2.0-alpha.43: - resolution: {integrity: sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==} + infima@0.2.0-alpha.45: + resolution: {integrity: sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==} engines: {node: '>=12'} inflight@1.0.6: @@ -4148,18 +4485,15 @@ packages: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - inline-style-parser@0.1.1: - resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} - - inline-style-parser@0.2.3: - resolution: {integrity: sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==} + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} - inquirer@9.2.23: - resolution: {integrity: sha512-kod5s+FBPIDM2xiy9fu+6wdU/SkK5le5GS9lh4FEBjBHqiMgD9lLFbCbuqFNAjNL2ZOy9Wd9F694IOzN9pZHBA==} + inquirer@9.3.2: + resolution: {integrity: sha512-+ynEbhWKhyomnaX0n2aLIMSkgSlGB5RrWbNXnEqj6mdaIydu6y40MdBjL38SAB0JcdmOaIaMua1azdjLEr3sdw==} engines: {node: '>=18'} - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} interpret@1.4.0: @@ -4187,31 +4521,31 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-async-function@2.1.0: + resolution: {integrity: sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + is-boolean-object@1.2.1: + resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} engines: {node: '>= 0.4'} - is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} + is-bun-module@1.3.0: + resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} @@ -4221,16 +4555,16 @@ packages: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true - is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} is-decimal@2.0.1: @@ -4254,10 +4588,18 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -4265,8 +4607,8 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-in-ci@0.1.0: - resolution: {integrity: sha512-d9PXLEY0v1iJ64xLiQMJ51J128EYHAaOR4yZqQi8aHGfw6KgifM3/Viw1oZZ1GCVmb3gBuyhLyHj0HgR2DhSXQ==} + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} engines: {node: '>=18'} hasBin: true @@ -4279,6 +4621,10 @@ packages: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -4294,16 +4640,12 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - is-npm@6.0.0: resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} is-number@7.0.0: @@ -4326,6 +4668,10 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + is-plain-obj@3.0.0: resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} engines: {node: '>=10'} @@ -4344,11 +4690,11 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-reference@3.0.2: - resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} is-regexp@1.0.0: @@ -4363,8 +4709,8 @@ packages: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} is-ssh@1.4.0: @@ -4382,20 +4728,20 @@ packages: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} is-text-path@2.0.0: resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} engines: {node: '>=8'} - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} is-typedarray@1.0.0: @@ -4413,8 +4759,21 @@ packages: resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} engines: {node: '>=18'} - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.0: + resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} @@ -4452,31 +4811,24 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - istanbul-lib-instrument@6.0.2: - resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@5.0.4: - resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==} + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} - iterate-iterator@1.0.2: - resolution: {integrity: sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==} - - iterate-value@1.0.2: - resolution: {integrity: sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==} - - jackspeak@3.4.0: - resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==} - engines: {node: '>=14'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} jest-util@29.7.0: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} @@ -4490,18 +4842,22 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jiti@1.21.6: - resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - joi@17.13.1: - resolution: {integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==} + joi@17.13.3: + resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -4514,8 +4870,8 @@ packages: jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - jsdom@24.1.0: - resolution: {integrity: sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==} + jsdom@24.1.3: + resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==} engines: {node: '>=18'} peerDependencies: canvas: ^2.11.2 @@ -4523,13 +4879,14 @@ packages: canvas: optional: true - jsesc@0.5.0: - resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} hasBin: true - jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: @@ -4563,9 +4920,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} - jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -4573,8 +4927,8 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} - katex@0.16.10: - resolution: {integrity: sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==} + katex@0.16.20: + resolution: {integrity: sha512-jjuLaMGD/7P8jUTpdKhA9IoqnH+yMFB3sdAFtq5QdAqeP2PjiSbnC3EaguKPNtv6dXXanHxp1ckwvF4a86LBig==} hasBin: true keyv@4.5.4: @@ -4588,12 +4942,20 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + ky@1.7.4: + resolution: {integrity: sha512-zYEr/gh7uLW2l4su11bmQ2M9xLgQLjyvx58UyNM/6nuqyWFHPX5ktMjvpev3F8QWdjSsHUpnWew4PBCswBNuMQ==} + engines: {node: '>=18'} + latest-version@7.0.0: resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} engines: {node: '>=14.16'} - launch-editor@2.7.0: - resolution: {integrity: sha512-KAc66u6LxWL8MifQ94oG3YGKYWDwz/Gi0T15lN//GaQoZe08vQGFJxrXkPAeu50UXgvJPPaRKVGuP1TRUm/aHQ==} + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} + + launch-editor@2.9.1: + resolution: {integrity: sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -4603,8 +4965,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lilconfig@3.1.2: - resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} lines-and-columns@1.2.4: @@ -4614,6 +4976,9 @@ packages: resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} @@ -4626,8 +4991,8 @@ packages: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} - local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} engines: {node: '>=14'} locate-path@3.0.0: @@ -4722,9 +5087,8 @@ packages: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} - lru-cache@10.3.0: - resolution: {integrity: sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==} - engines: {node: 14 || >=16.14} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -4736,15 +5100,15 @@ packages: lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - macos-release@3.2.0: - resolution: {integrity: sha512-fSErXALFNsnowREYZ49XCdOHF8wOPWuFOGQrAhP7x5J/BqQv+B02cNsTykGpDgRVx43EKg++6ANmTaGTtW+hUA==} + macos-release@3.3.0: + resolution: {integrity: sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magicast@0.3.4: - resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} @@ -4754,28 +5118,34 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} - markdown-table@3.0.3: - resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} - - marked@4.3.0: - resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} - engines: {node: '>= 12'} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true + markdown-table@2.0.0: + resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdast-util-directive@3.0.0: resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} - mdast-util-find-and-replace@3.0.1: - resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - mdast-util-from-markdown@2.0.1: - resolution: {integrity: sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==} + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} mdast-util-frontmatter@2.0.1: resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} - mdast-util-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} mdast-util-gfm-footnote@2.0.0: resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} @@ -4795,11 +5165,11 @@ packages: mdast-util-math@3.0.0: resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} - mdast-util-mdx-expression@2.0.0: - resolution: {integrity: sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==} + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - mdast-util-mdx-jsx@3.1.2: - resolution: {integrity: sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==} + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} mdast-util-mdx@3.0.0: resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} @@ -4813,8 +5183,8 @@ packages: mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} - mdast-util-to-markdown@2.1.0: - resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} @@ -4825,6 +5195,9 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -4837,8 +5210,12 @@ packages: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} - merge-descriptors@1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4851,44 +5228,44 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - micromark-core-commonmark@2.0.1: - resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} + micromark-core-commonmark@2.0.2: + resolution: {integrity: sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==} - micromark-extension-directive@3.0.0: - resolution: {integrity: sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==} + micromark-extension-directive@3.0.2: + resolution: {integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==} micromark-extension-frontmatter@2.0.0: resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} - micromark-extension-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - micromark-extension-gfm-footnote@2.0.0: - resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - micromark-extension-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - micromark-extension-gfm-table@2.0.0: - resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + micromark-extension-gfm-table@2.1.0: + resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==} micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - micromark-extension-gfm-task-list-item@2.0.1: - resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - micromark-extension-math@3.0.0: - resolution: {integrity: sha512-iJ2Q28vBoEovLN5o3GO12CpqorQRYDPT+p4zW50tGwTfJB+iv/VnB6Ini+gqa24K97DwptMBBIvVX6Bjk49oyQ==} + micromark-extension-math@3.1.0: + resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} micromark-extension-mdx-expression@3.0.0: resolution: {integrity: sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==} - micromark-extension-mdx-jsx@3.0.0: - resolution: {integrity: sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==} + micromark-extension-mdx-jsx@3.0.1: + resolution: {integrity: sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg==} micromark-extension-mdx-md@2.0.0: resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} @@ -4899,86 +5276,86 @@ packages: micromark-extension-mdxjs@3.0.0: resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} - micromark-factory-destination@2.0.0: - resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - micromark-factory-label@2.0.0: - resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - micromark-factory-mdx-expression@2.0.1: - resolution: {integrity: sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==} + micromark-factory-mdx-expression@2.0.2: + resolution: {integrity: sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw==} micromark-factory-space@1.1.0: resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} - micromark-factory-space@2.0.0: - resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - micromark-factory-title@2.0.0: - resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - micromark-factory-whitespace@2.0.0: - resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} micromark-util-character@1.2.0: resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} - micromark-util-character@2.1.0: - resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - micromark-util-chunked@2.0.0: - resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - micromark-util-classify-character@2.0.0: - resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - micromark-util-combine-extensions@2.0.0: - resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - micromark-util-decode-numeric-character-reference@2.0.1: - resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - micromark-util-decode-string@2.0.0: - resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - micromark-util-encode@2.0.0: - resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} micromark-util-events-to-acorn@2.0.2: resolution: {integrity: sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==} - micromark-util-html-tag-name@2.0.0: - resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - micromark-util-normalize-identifier@2.0.0: - resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - micromark-util-resolve-all@2.0.0: - resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - micromark-util-sanitize-uri@2.0.0: - resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - micromark-util-subtokenize@2.0.1: - resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} + micromark-util-subtokenize@2.0.3: + resolution: {integrity: sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==} micromark-util-symbol@1.1.0: resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} - micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} micromark-util-types@1.1.0: resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} - micromark-util-types@2.0.0: - resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + micromark-util-types@2.0.1: + resolution: {integrity: sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==} - micromark@4.0.0: - resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + micromark@4.0.1: + resolution: {integrity: sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==} - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} mime-db@1.33.0: @@ -4989,6 +5366,10 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.53.0: + resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} + engines: {node: '>= 0.6'} + mime-types@2.1.18: resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} engines: {node: '>= 0.6'} @@ -5010,6 +5391,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -5018,8 +5403,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - mini-css-extract-plugin@2.9.0: - resolution: {integrity: sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==} + mini-css-extract-plugin@2.9.2: + resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 @@ -5030,10 +5415,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -5045,8 +5426,8 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} @@ -5055,9 +5436,6 @@ packages: ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -5076,8 +5454,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -5088,6 +5466,10 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -5102,24 +5484,16 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - - node-emoji@2.1.3: - resolution: {integrity: sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==} + node-emoji@2.2.0: + resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} normalize-package-data@6.0.2: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} @@ -5145,32 +5519,39 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + nprogress@0.2.0: resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - nwsapi@2.2.10: - resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} + null-loader@4.0.1: + resolution: {integrity: sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + nwsapi@2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - - object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} object.entries@1.1.8: @@ -5185,8 +5566,8 @@ packages: resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} engines: {node: '>= 0.4'} - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} obuf@1.1.2: @@ -5211,6 +5592,10 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + open@10.1.0: resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} @@ -5235,6 +5620,10 @@ packages: resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==} engines: {node: '>=18'} + ora@8.1.1: + resolution: {integrity: sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==} + engines: {node: '>=18'} + os-name@5.1.0: resolution: {integrity: sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5243,6 +5632,10 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -5287,8 +5680,8 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - pac-proxy-agent@7.0.2: - resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} + pac-proxy-agent@7.1.0: + resolution: {integrity: sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==} engines: {node: '>= 14'} pac-resolver@7.0.1: @@ -5298,6 +5691,10 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} + package-json@8.1.1: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} @@ -5309,8 +5706,8 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-entities@4.0.1: - resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} @@ -5336,8 +5733,8 @@ packages: parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} - parse5-htmlparser2-tree-adapter@7.0.0: - resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} parse5@5.1.1: resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} @@ -5345,8 +5742,8 @@ packages: parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} @@ -5389,14 +5786,14 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} - path-to-regexp@1.8.0: - resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==} + path-to-regexp@1.9.0: + resolution: {integrity: sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==} - path-to-regexp@2.2.1: - resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} + path-to-regexp@3.3.0: + resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -5409,25 +5806,32 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.1: + resolution: {integrity: sha512-6jpjMpOth5S9ITVu5clZ7NOgHNsv5vRQdheL9ztp2vZmM6fRbLvyua1tiBIL4lk8SAe3ARzeXEly6siXCjDHDw==} + pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pkg-dir@7.0.0: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} - pkg-types@1.1.1: - resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} pkg-up@3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} @@ -5445,12 +5849,42 @@ packages: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} + postcss-attribute-case-insensitive@7.0.1: + resolution: {integrity: sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-calc@9.0.1: resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.2.2 + postcss-clamp@4.1.0: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + + postcss-color-functional-notation@7.0.7: + resolution: {integrity: sha512-EZvAHsvyASX63vXnyXOIynkxhaHRSsdb7z6yiXKIovGXAolW4cMZ3qoh7k3VdTsLBS6VGdksGfIo3r6+waLoOw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-color-hex-alpha@10.0.0: + resolution: {integrity: sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-color-rebeccapurple@10.0.0: + resolution: {integrity: sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-colormin@6.1.0: resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} engines: {node: ^14 || ^16 || >=18.0} @@ -5463,6 +5897,30 @@ packages: peerDependencies: postcss: ^8.4.31 + postcss-custom-media@11.0.5: + resolution: {integrity: sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-properties@14.0.4: + resolution: {integrity: sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-selectors@8.0.4: + resolution: {integrity: sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-dir-pseudo-class@9.0.1: + resolution: {integrity: sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-discard-comments@6.0.2: resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} engines: {node: ^14 || ^16 || >=18.0} @@ -5493,6 +5951,47 @@ packages: peerDependencies: postcss: ^8.4.31 + postcss-double-position-gradients@6.0.0: + resolution: {integrity: sha512-JkIGah3RVbdSEIrcobqj4Gzq0h53GG4uqDPsho88SgY84WnpkTpI0k50MFK/sX7XqVisZ6OqUfFnoUO6m1WWdg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-focus-visible@10.0.1: + resolution: {integrity: sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-focus-within@9.0.1: + resolution: {integrity: sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-font-variant@5.0.0: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + + postcss-gap-properties@6.0.0: + resolution: {integrity: sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-image-set-function@7.0.0: + resolution: {integrity: sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-lab-function@7.0.7: + resolution: {integrity: sha512-+ONj2bpOQfsCKZE2T9VGMyVVdGcGUpr7u3SVfvkJlvhTRmDCfY25k4Jc8fubB9DclAPR4+w8uVtDZmdRgdAHig==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-loader@7.3.4: resolution: {integrity: sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==} engines: {node: '>= 14.15.0'} @@ -5500,6 +5999,12 @@ packages: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 + postcss-logical@8.0.0: + resolution: {integrity: sha512-HpIdsdieClTjXLOyYdUPAX/XQASNIwdKt5hoZW08ZOAiI+tbV0ta1oclkpVkW5ANU+xJvk3KkA0FejkjGLXUkg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-merge-idents@6.0.3: resolution: {integrity: sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==} engines: {node: ^14 || ^16 || >=18.0} @@ -5548,14 +6053,14 @@ packages: peerDependencies: postcss: ^8.1.0 - postcss-modules-local-by-default@4.0.5: - resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==} + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 - postcss-modules-scope@3.2.0: - resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==} + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 @@ -5566,6 +6071,12 @@ packages: peerDependencies: postcss: ^8.1.0 + postcss-nesting@13.0.1: + resolution: {integrity: sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-normalize-charset@6.0.2: resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} engines: {node: ^14 || ^16 || >=18.0} @@ -5620,12 +6131,47 @@ packages: peerDependencies: postcss: ^8.4.31 + postcss-opacity-percentage@3.0.0: + resolution: {integrity: sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-ordered-values@6.0.2: resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-overflow-shorthand@6.0.0: + resolution: {integrity: sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-page-break@3.0.4: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + + postcss-place@10.0.0: + resolution: {integrity: sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-preset-env@10.1.3: + resolution: {integrity: sha512-9qzVhcMFU/MnwYHyYpJz4JhGku/4+xEiPTmhn0hj3IxnUYlEF9vbh7OC1KoLAnenS6Fgg43TKNp9xcuMeAi4Zw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-pseudo-class-any-link@10.0.1: + resolution: {integrity: sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + postcss-reduce-idents@6.0.3: resolution: {integrity: sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==} engines: {node: ^14 || ^16 || >=18.0} @@ -5644,8 +6190,23 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-selector-parser@6.1.0: - resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} + postcss-replace-overflow-wrap@4.0.0: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + + postcss-selector-not@8.0.1: + resolution: {integrity: sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.0.0: + resolution: {integrity: sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==} engines: {node: '>=4'} postcss-sort-media-queries@5.2.0: @@ -5675,16 +6236,16 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.3.2: - resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==} + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} engines: {node: '>=14'} hasBin: true @@ -5695,16 +6256,16 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - pretty-ms@9.0.0: - resolution: {integrity: sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==} + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} engines: {node: '>=18'} pretty-time@1.1.0: resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==} engines: {node: '>=4'} - prism-react-renderer@2.3.1: - resolution: {integrity: sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw==} + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} peerDependencies: react: '>=16.0.0' @@ -5715,10 +6276,6 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - promise.allsettled@1.0.7: - resolution: {integrity: sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==} - engines: {node: '>= 0.4'} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -5739,18 +6296,19 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - proxy-agent@6.4.0: - resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} engines: {node: '>= 14'} proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - psl@1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -5760,8 +6318,8 @@ packages: resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} engines: {node: '>=12.20'} - qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} querystringify@2.2.0: @@ -5821,25 +6379,14 @@ packages: react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - react-helmet-async@1.3.0: - resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==} - peerDependencies: - react: ^16.6.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 - - react-helmet-async@2.0.5: - resolution: {integrity: sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==} - peerDependencies: - react: ^16.6.0 || ^17.0.0 || ^18.0.0 - react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-json-view-lite@1.4.0: - resolution: {integrity: sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==} + react-json-view-lite@1.5.0: + resolution: {integrity: sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==} engines: {node: '>=14'} peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -5897,12 +6444,28 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.0: + resolution: {integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==} + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + recursive-readdir@2.2.3: resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} engines: {node: '>=6.0.0'} - regenerate-unicode-properties@10.1.1: - resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.0: + resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} engines: {node: '>=4'} regenerate@1.4.2: @@ -5914,35 +6477,41 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - regexpu-core@5.3.2: - resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} + regexpu-core@6.2.0: + resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} engines: {node: '>=4'} - registry-auth-token@5.0.2: - resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + registry-auth-token@5.0.3: + resolution: {integrity: sha512-1bpc9IyC+e+CNFRaWyn77tk4xGG4PPUyfakSmA6F6cvUDjrm58dfyJ3II+9yb10EDkHoy1LaPSmHaWLOH3m6HA==} engines: {node: '>=14'} registry-url@6.0.1: resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} engines: {node: '>=12'} - regjsparser@0.9.1: - resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + relateurl@0.2.7: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} - release-it@17.4.0: - resolution: {integrity: sha512-tYnk1tS530TLQtV8UQ+6OCiV3opVtkgwmLOpjXeV63ZtlZpSAGLZCXrA/I6ywiYKcEQWxW8WV7YJQvdxxGNZSg==} + release-it@17.11.0: + resolution: {integrity: sha512-qQGgfMbUZ3/vpXUPmngsgjFObOLjlkwtiozHUYen9fo9AEGciXjG1ZpGr+FNmuBT8R7TOSY+x/s84wOCRKJjbA==} engines: {node: ^18.18.0 || ^20.9.0 || ^22.0.0} hasBin: true @@ -5962,14 +6531,14 @@ packages: remark-math@6.0.0: resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} - remark-mdx@3.0.1: - resolution: {integrity: sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==} + remark-mdx@3.1.0: + resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==} remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - remark-rehype@11.1.0: - resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==} + remark-rehype@11.1.1: + resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==} remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} @@ -5977,6 +6546,10 @@ packages: renderkid@3.0.0: resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -6008,8 +6581,9 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} hasBin: true responselike@3.0.0: @@ -6024,6 +6598,10 @@ packages: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -6037,27 +6615,23 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@5.0.7: - resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} - engines: {node: '>=14.18'} + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rollup@4.18.0: - resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} + rollup@4.30.1: + resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rrweb-cssom@0.6.0: - resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} - rrweb-cssom@0.7.1: resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} - rtl-detect@1.1.2: - resolution: {integrity: sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==} + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - rtlcss@4.1.1: - resolution: {integrity: sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==} + rtlcss@4.3.0: + resolution: {integrity: sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==} engines: {node: '>=12.0.0'} hasBin: true @@ -6075,8 +6649,8 @@ packages: rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} safe-buffer@5.1.2: @@ -6085,8 +6659,12 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} safer-buffer@2.1.2: @@ -6110,9 +6688,9 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} - schema-utils@4.2.0: - resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} - engines: {node: '>= 12.13.0'} + schema-utils@4.3.0: + resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==} + engines: {node: '>= 10.13.0'} search-insights@2.14.0: resolution: {integrity: sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==} @@ -6136,27 +6714,27 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true - send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serve-handler@6.1.5: - resolution: {integrity: sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==} + serve-handler@6.1.6: + resolution: {integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==} serve-index@1.9.1: resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} engines: {node: '>= 0.8.0'} - serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} set-function-length@1.2.2: @@ -6167,6 +6745,10 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + setprototypeof@1.1.0: resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} @@ -6188,19 +6770,29 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + shell-quote@1.8.2: + resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + engines: {node: '>= 0.4'} shelljs@0.8.5: resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} engines: {node: '>=4'} hasBin: true - shiki@0.14.7: - resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} siginfo@2.0.0: @@ -6254,8 +6846,8 @@ packages: sockjs@0.3.24: resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} - socks-proxy-agent@8.0.4: - resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} socks@2.8.3: @@ -6266,8 +6858,8 @@ packages: resolution: {integrity: sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==} engines: {node: '>= 6.3.0'} - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} source-map-support@0.5.21: @@ -6296,8 +6888,8 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} spdy-transport@3.0.0: resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} @@ -6320,6 +6912,9 @@ packages: resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} engines: {node: '>=12'} + stable-hash@0.0.4: + resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -6331,17 +6926,13 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} - stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -6354,12 +6945,17 @@ packages: resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} engines: {node: '>=18'} - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} @@ -6414,14 +7010,14 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + strip-literal@2.1.1: + resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} - style-to-object@0.4.4: - resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} + stubborn-fs@1.2.5: + resolution: {integrity: sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==} - style-to-object@1.0.6: - resolution: {integrity: sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==} + style-to-object@1.0.8: + resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} stylehacks@6.1.1: resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} @@ -6472,8 +7068,8 @@ packages: resolution: {integrity: sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==} engines: {node: '>=12'} - terser-webpack-plugin@5.3.10: - resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + terser-webpack-plugin@5.3.11: + resolution: {integrity: sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -6488,8 +7084,8 @@ packages: uglify-js: optional: true - terser@5.31.1: - resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==} + terser@5.37.0: + resolution: {integrity: sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==} engines: {node: '>=10'} hasBin: true @@ -6523,8 +7119,11 @@ packages: tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} tinypool@0.8.4: resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} @@ -6538,10 +7137,6 @@ packages: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -6572,8 +7167,8 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' @@ -6585,15 +7180,15 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} engines: {node: '>=4'} type-fest@0.20.2: @@ -6616,28 +7211,28 @@ packages: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} - type-fest@4.20.1: - resolution: {integrity: sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg==} + type-fest@4.32.0: + resolution: {integrity: sha512-rfgpoi08xagF3JSdtJlCwMq9DGNDE0IMh3Mkpc1wUypg9vPi786AiqeBBKcqvIkq42azsBM85N490fyZjeUftw==} engines: {node: '>=16'} type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} typedarray-to-buffer@3.1.5: @@ -6646,44 +7241,52 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typedoc-plugin-markdown@4.0.3: - resolution: {integrity: sha512-0tZbeVGGCd4+lpoIX+yHWgUfyaLZCQCgJOpuVdTtOtD3+jKaedJ4sl/tkNaYBPeWVKiyDkSHfGuHkq53jlzIFg==} + typedoc-plugin-markdown@4.4.1: + resolution: {integrity: sha512-fx23nSCvewI9IR8lzIYtzDphETcgTDuxKcmHKGD4lo36oexC+B1k4NaCOY58Snqb4OlE8OXDAGVcQXYYuLRCNw==} + engines: {node: '>= 18'} peerDependencies: - typedoc: 0.25.x + typedoc: 0.27.x - typedoc@0.25.13: - resolution: {integrity: sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==} - engines: {node: '>= 16'} - hasBin: true + typedoc-plugin-mdn-links@4.0.8: + resolution: {integrity: sha512-HQqGiO1zIHYgWKPkfutYKqnUeDCdf/3SfSz2mgjQnD4/S7NxqemKZbeAZy5Cp2+1qvyo32sf9bN8SmUTB0f6mA==} peerDependencies: - typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x + typedoc: 0.26.x || 0.27.x - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} + typedoc@0.27.6: + resolution: {integrity: sha512-oBFRoh2Px6jFx366db0lLlihcalq/JzyCVp7Vaq1yphL/tbgx2e+bkpkCgJPunaPvPwoTOXSwasfklWHm7GfAw==} + engines: {node: '>= 18'} hasBin: true + peerDependencies: + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x - typescript@5.5.2: - resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} - uglify-js@3.18.0: - resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} hasBin: true - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - unicode-canonical-property-names-ecmascript@2.0.0: - resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} unicode-emoji-modifier-base@1.0.0: @@ -6694,8 +7297,8 @@ packages: resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} engines: {node: '>=4'} - unicode-match-property-value-ecmascript@2.1.0: - resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + unicode-match-property-value-ecmascript@2.2.0: + resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} engines: {node: '>=4'} unicode-property-aliases-ecmascript@2.1.0: @@ -6706,8 +7309,12 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - unified@11.0.4: - resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} @@ -6749,8 +7356,8 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - update-browserslist-db@1.0.16: - resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + update-browserslist-db@1.1.2: + resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -6759,8 +7366,8 @@ packages: resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} engines: {node: '>=14.16'} - update-notifier@7.0.0: - resolution: {integrity: sha512-Hv25Bh+eAbOLlsjJreVPOs4vd51rrtCrmhyOJtbpAojro34jS4KQaEp4/EvlHJX7jSO42VvEFpkastVyXyIsdQ==} + update-notifier@7.3.1: + resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} engines: {node: '>=18'} uri-js@4.4.1: @@ -6811,22 +7418,22 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vfile-location@5.0.2: - resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==} + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - vfile@6.0.1: - resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} vite-node@1.6.0: resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - vite@5.3.2: - resolution: {integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==} + vite@5.4.11: + resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -6834,6 +7441,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -6846,6 +7454,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -6878,18 +7488,12 @@ packages: jsdom: optional: true - vscode-oniguruma@1.7.0: - resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - - vscode-textmate@8.0.0: - resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} - w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - watchpack@2.4.1: - resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} + watchpack@2.4.2: + resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} engines: {node: '>=10.13.0'} wbuf@1.7.3: @@ -6901,10 +7505,6 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -6937,12 +7537,16 @@ packages: resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} engines: {node: '>=10.0.0'} + webpack-merge@6.0.1: + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + engines: {node: '>=18.0.0'} + webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} - webpack@5.92.0: - resolution: {integrity: sha512-Bsw2X39MYIgxouNATyVpCNVWBCuUwDgWtN78g6lSdPJRLaQ/PUVm/oXcaRAyY/sMFoKFQrsPeqvTizWtq7QPCA==} + webpack@5.97.1: + resolution: {integrity: sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -6951,9 +7555,9 @@ packages: webpack-cli: optional: true - webpackbar@5.0.2: - resolution: {integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==} - engines: {node: '>=12'} + webpackbar@6.0.1: + resolution: {integrity: sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==} + engines: {node: '>=14.21.3'} peerDependencies: webpack: 3 || 4 || 5 @@ -6973,15 +7577,27 @@ packages: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} - whatwg-url@14.0.0: - resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + whatwg-url@14.1.0: + resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==} engines: {node: '>=18'} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + when-exit@2.1.4: + resolution: {integrity: sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + which-typed-array@1.1.18: + resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} engines: {node: '>= 0.4'} which@1.3.1: @@ -6993,8 +7609,8 @@ packages: engines: {node: '>= 8'} hasBin: true - why-is-node-running@2.2.2: - resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true @@ -7002,8 +7618,12 @@ packages: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} - wildcard-match@5.1.3: - resolution: {integrity: sha512-a95hPUk+BNzSGLntNXYxsjz2Hooi5oL7xOfJR6CKwSsSALh7vUNuTlzsrZowtYy38JNduYFRVhFv19ocqNOZlg==} + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + wildcard-match@5.1.4: + resolution: {integrity: sha512-wldeCaczs8XXq7hj+5d/F38JE2r7EXgb6WQDM84RVwxy81T/sxB5e9+uZLK9Q9oNz1mlvjut+QtvgaOQFPVq/g==} wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} @@ -7031,6 +7651,10 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -7049,8 +7673,8 @@ packages: utf-8-validate: optional: true - ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -7087,6 +7711,11 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -7111,8 +7740,16 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} - yoctocolors@2.0.2: - resolution: {integrity: sha512-Ct97huExsu7cWeEjmrXlofevF8CvzUglJ4iGUet5B8xn1oumtAZBpHU4GzYuoE6PVqcZ5hghtBrSlhwHuR1Jmw==} + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} zwitch@2.0.4: @@ -7120,1042 +7757,940 @@ packages: snapshots: - '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.14.0)': + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.14.0)': dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.14.0) - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.14.0) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights - '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.14.0)': + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.14.0)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) search-insights: 2.14.0 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) - '@algolia/client-search': 4.23.3 - algoliasearch: 4.23.3 + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) + '@algolia/client-search': 5.19.0 + algoliasearch: 5.19.0 - '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)': dependencies: - '@algolia/client-search': 4.23.3 - algoliasearch: 4.23.3 + '@algolia/client-search': 5.19.0 + algoliasearch: 5.19.0 - '@algolia/cache-browser-local-storage@4.23.3': + '@algolia/client-abtesting@5.19.0': dependencies: - '@algolia/cache-common': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/cache-common@4.23.3': {} - - '@algolia/cache-in-memory@4.23.3': + '@algolia/client-analytics@5.19.0': dependencies: - '@algolia/cache-common': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/client-account@4.23.3': - dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common@5.19.0': {} - '@algolia/client-analytics@4.23.3': + '@algolia/client-insights@5.19.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/client-common@4.23.3': + '@algolia/client-personalization@5.19.0': dependencies: - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/client-personalization@4.23.3': + '@algolia/client-query-suggestions@5.19.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/client-search@4.23.3': + '@algolia/client-search@5.19.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 '@algolia/events@4.0.1': {} - '@algolia/logger-common@4.23.3': {} - - '@algolia/logger-console@4.23.3': + '@algolia/ingestion@1.19.0': dependencies: - '@algolia/logger-common': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/recommend@4.23.3': + '@algolia/monitoring@1.19.0': dependencies: - '@algolia/cache-browser-local-storage': 4.23.3 - '@algolia/cache-common': 4.23.3 - '@algolia/cache-in-memory': 4.23.3 - '@algolia/client-common': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/logger-common': 4.23.3 - '@algolia/logger-console': 4.23.3 - '@algolia/requester-browser-xhr': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/requester-node-http': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/requester-browser-xhr@4.23.3': + '@algolia/recommend@5.19.0': dependencies: - '@algolia/requester-common': 4.23.3 + '@algolia/client-common': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 - '@algolia/requester-common@4.23.3': {} + '@algolia/requester-browser-xhr@5.19.0': + dependencies: + '@algolia/client-common': 5.19.0 - '@algolia/requester-node-http@4.23.3': + '@algolia/requester-fetch@5.19.0': dependencies: - '@algolia/requester-common': 4.23.3 + '@algolia/client-common': 5.19.0 - '@algolia/transporter@4.23.3': + '@algolia/requester-node-http@5.19.0': dependencies: - '@algolia/cache-common': 4.23.3 - '@algolia/logger-common': 4.23.3 - '@algolia/requester-common': 4.23.3 + '@algolia/client-common': 5.19.0 '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 + '@asamuzakjp/css-color@2.8.3': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + lru-cache: 10.4.3 + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 - picocolors: 1.0.1 + picocolors: 1.1.1 + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 - '@babel/compat-data@7.24.7': {} + '@babel/compat-data@7.26.5': {} - '@babel/core@7.24.7': + '@babel/core@7.26.0': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helpers': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.5 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.24.7': + '@babel/generator@7.26.5': dependencies: - '@babel/types': 7.24.7 - '@jridgewell/gen-mapping': 0.3.5 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - - '@babel/helper-annotate-as-pure@7.24.7': - dependencies: - '@babel/types': 7.24.7 + jsesc: 3.1.0 - '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': + '@babel/helper-annotate-as-pure@7.25.9': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/types': 7.26.5 - '@babel/helper-compilation-targets@7.24.7': + '@babel/helper-compilation-targets@7.26.5': dependencies: - '@babel/compat-data': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - browserslist: 4.23.1 + '@babel/compat-data': 7.26.5 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 - '@babel/helper-optimise-call-expression': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/traverse': 7.26.5 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)': + '@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - regexpu-core: 5.3.2 + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + regexpu-core: 6.2.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)': + '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.5 + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + debug: 4.4.0 lodash.debounce: 4.0.8 - resolve: 1.22.8 + resolve: 1.22.10 transitivePeerDependencies: - supports-color - '@babel/helper-environment-visitor@7.24.7': + '@babel/helper-member-expression-to-functions@7.25.9': dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-function-name@7.24.7': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 - - '@babel/helper-hoist-variables@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-member-expression-to-functions@7.24.7': - dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.24.7': + '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.24.7': + '@babel/helper-optimise-call-expression@7.25.9': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.26.5 - '@babel/helper-plugin-utils@7.24.7': {} - - '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-wrap-function': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/helper-plugin-utils@7.26.5': {} - '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)': + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 - '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-wrap-function': 7.25.9 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helper-simple-access@7.24.7': + '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.0)': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helper-split-export-declaration@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-string-parser@7.24.7': {} + '@babel/helper-string-parser@7.25.9': {} - '@babel/helper-validator-identifier@7.24.7': {} + '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-option@7.24.7': {} + '@babel/helper-validator-option@7.25.9': {} - '@babel/helper-wrap-function@7.24.7': + '@babel/helper-wrap-function@7.25.9': dependencies: - '@babel/helper-function-name': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helpers@7.24.7': + '@babel/helpers@7.26.0': dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 '@babel/highlight@7.24.7': dependencies: - '@babel/helper-validator-identifier': 7.24.7 + '@babel/helper-validator-identifier': 7.25.9 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.0.1 - - '@babel/parser@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + picocolors: 1.1.1 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)': + '@babel/parser@7.26.5': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/types': 7.26.5 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.5 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)': + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-block-scoped-functions@7.26.5(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) + '@babel/traverse': 7.26.5 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/template': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/template': 7.25.9 - '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.5 + transitivePeerDependencies: + - supports-color - '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-simple-access': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-nullish-coalescing-operator@7.26.6(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-constant-elements@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-constant-elements@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-display-name@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) - '@babel/types': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-pure-annotations@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 regenerator-transform: 0.15.2 - '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-runtime@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7) - babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-runtime@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 + babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) + babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color - - '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-typescript@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/preset-env@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/compat-data': 7.24.7 - '@babel/core': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7) - babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7) - babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7) - core-js-compat: 3.37.1 + + '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-typescript@7.26.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/preset-env@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/compat-data': 7.26.5 + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0) + '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoped-functions': 7.26.5(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-exponentiation-operator': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.26.6(@babel/core@7.26.0) + '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.0) + babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) + babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) + core-js-compat: 3.40.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/types': 7.24.7 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/types': 7.26.5 esutils: 2.0.3 - '@babel/preset-react@7.24.7(@babel/core@7.24.7)': + '@babel/preset-react@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-react-pure-annotations': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-pure-annotations': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.24.7(@babel/core@7.24.7)': + '@babel/preset-typescript@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-typescript': 7.26.5(@babel/core@7.26.0) transitivePeerDependencies: - supports-color - '@babel/regjsgen@0.8.0': {} - - '@babel/runtime-corejs3@7.24.7': + '@babel/runtime-corejs3@7.26.0': dependencies: - core-js-pure: 3.37.1 + core-js-pure: 3.40.0 regenerator-runtime: 0.14.1 - '@babel/runtime@7.24.7': + '@babel/runtime@7.26.0': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.24.7': + '@babel/template@7.25.9': dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 - '@babel/traverse@7.24.7': + '@babel/traverse@7.26.5': dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - debug: 4.3.5 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.5 + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.24.7': + '@babel/types@7.26.5': dependencies: - '@babel/helper-string-parser': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 '@colors/colors@1.5.0': optional: true - '@commitlint/cli@19.3.0(@types/node@18.19.39)(typescript@5.5.2)': + '@commitlint/cli@19.6.1(@types/node@18.19.70)(typescript@5.7.3)': dependencies: - '@commitlint/format': 19.3.0 - '@commitlint/lint': 19.2.2 - '@commitlint/load': 19.2.0(@types/node@18.19.39)(typescript@5.5.2) - '@commitlint/read': 19.2.1 - '@commitlint/types': 19.0.3 - execa: 8.0.1 + '@commitlint/format': 19.5.0 + '@commitlint/lint': 19.6.0 + '@commitlint/load': 19.6.1(@types/node@18.19.70)(typescript@5.7.3) + '@commitlint/read': 19.5.0 + '@commitlint/types': 19.5.0 + tinyexec: 0.3.2 yargs: 17.7.2 transitivePeerDependencies: - '@types/node' - typescript - '@commitlint/config-conventional@19.2.2': + '@commitlint/config-conventional@19.6.0': dependencies: - '@commitlint/types': 19.0.3 + '@commitlint/types': 19.5.0 conventional-changelog-conventionalcommits: 7.0.2 - '@commitlint/config-validator@19.0.3': + '@commitlint/config-validator@19.5.0': dependencies: - '@commitlint/types': 19.0.3 - ajv: 8.16.0 + '@commitlint/types': 19.5.0 + ajv: 8.17.1 - '@commitlint/ensure@19.0.3': + '@commitlint/ensure@19.5.0': dependencies: - '@commitlint/types': 19.0.3 + '@commitlint/types': 19.5.0 lodash.camelcase: 4.3.0 lodash.kebabcase: 4.1.1 lodash.snakecase: 4.1.1 lodash.startcase: 4.4.0 lodash.upperfirst: 4.3.1 - '@commitlint/execute-rule@19.0.0': {} + '@commitlint/execute-rule@19.5.0': {} - '@commitlint/format@19.3.0': + '@commitlint/format@19.5.0': dependencies: - '@commitlint/types': 19.0.3 - chalk: 5.3.0 + '@commitlint/types': 19.5.0 + chalk: 5.4.1 - '@commitlint/is-ignored@19.2.2': + '@commitlint/is-ignored@19.6.0': dependencies: - '@commitlint/types': 19.0.3 - semver: 7.6.2 + '@commitlint/types': 19.5.0 + semver: 7.6.3 - '@commitlint/lint@19.2.2': + '@commitlint/lint@19.6.0': dependencies: - '@commitlint/is-ignored': 19.2.2 - '@commitlint/parse': 19.0.3 - '@commitlint/rules': 19.0.3 - '@commitlint/types': 19.0.3 + '@commitlint/is-ignored': 19.6.0 + '@commitlint/parse': 19.5.0 + '@commitlint/rules': 19.6.0 + '@commitlint/types': 19.5.0 - '@commitlint/load@19.2.0(@types/node@18.19.39)(typescript@5.5.2)': + '@commitlint/load@19.6.1(@types/node@18.19.70)(typescript@5.7.3)': dependencies: - '@commitlint/config-validator': 19.0.3 - '@commitlint/execute-rule': 19.0.0 - '@commitlint/resolve-extends': 19.1.0 - '@commitlint/types': 19.0.3 - chalk: 5.3.0 - cosmiconfig: 9.0.0(typescript@5.5.2) - cosmiconfig-typescript-loader: 5.0.0(@types/node@18.19.39)(cosmiconfig@9.0.0(typescript@5.5.2))(typescript@5.5.2) + '@commitlint/config-validator': 19.5.0 + '@commitlint/execute-rule': 19.5.0 + '@commitlint/resolve-extends': 19.5.0 + '@commitlint/types': 19.5.0 + chalk: 5.4.1 + cosmiconfig: 9.0.0(typescript@5.7.3) + cosmiconfig-typescript-loader: 6.1.0(@types/node@18.19.70)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -8163,146 +8698,450 @@ snapshots: - '@types/node' - typescript - '@commitlint/message@19.0.0': {} + '@commitlint/message@19.5.0': {} - '@commitlint/parse@19.0.3': + '@commitlint/parse@19.5.0': dependencies: - '@commitlint/types': 19.0.3 + '@commitlint/types': 19.5.0 conventional-changelog-angular: 7.0.0 conventional-commits-parser: 5.0.0 - '@commitlint/read@19.2.1': + '@commitlint/read@19.5.0': dependencies: - '@commitlint/top-level': 19.0.0 - '@commitlint/types': 19.0.3 - execa: 8.0.1 + '@commitlint/top-level': 19.5.0 + '@commitlint/types': 19.5.0 git-raw-commits: 4.0.0 minimist: 1.2.8 + tinyexec: 0.3.2 - '@commitlint/resolve-extends@19.1.0': + '@commitlint/resolve-extends@19.5.0': dependencies: - '@commitlint/config-validator': 19.0.3 - '@commitlint/types': 19.0.3 + '@commitlint/config-validator': 19.5.0 + '@commitlint/types': 19.5.0 global-directory: 4.0.1 import-meta-resolve: 4.1.0 lodash.mergewith: 4.6.2 resolve-from: 5.0.0 - '@commitlint/rules@19.0.3': + '@commitlint/rules@19.6.0': dependencies: - '@commitlint/ensure': 19.0.3 - '@commitlint/message': 19.0.0 - '@commitlint/to-lines': 19.0.0 - '@commitlint/types': 19.0.3 - execa: 8.0.1 + '@commitlint/ensure': 19.5.0 + '@commitlint/message': 19.5.0 + '@commitlint/to-lines': 19.5.0 + '@commitlint/types': 19.5.0 - '@commitlint/to-lines@19.0.0': {} + '@commitlint/to-lines@19.5.0': {} - '@commitlint/top-level@19.0.0': + '@commitlint/top-level@19.5.0': dependencies: find-up: 7.0.0 - '@commitlint/types@19.0.3': + '@commitlint/types@19.5.0': dependencies: - '@types/conventional-commits-parser': 5.0.0 - chalk: 5.3.0 + '@types/conventional-commits-parser': 5.0.1 + chalk: 5.4.1 + + '@conventional-changelog/git-client@1.0.1': + dependencies: + '@types/semver': 7.5.8 + semver: 7.6.3 + + '@csstools/cascade-layer-name-parser@2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/color-helpers@5.0.1': {} + + '@csstools/css-calc@2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/color-helpers': 5.0.1 + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-tokenizer@3.0.3': {} + + '@csstools/media-query-list-parser@4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/postcss-cascade-layers@5.0.1(postcss@8.5.1)': + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.0.0) + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + '@csstools/postcss-color-function@4.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-color-mix-function@3.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-content-alt-text@2.0.4(postcss@8.5.1)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-exponential-functions@2.0.6(postcss@8.5.1)': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + postcss: 8.5.1 + + '@csstools/postcss-font-format-keywords@4.0.0(postcss@8.5.1)': + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-gamut-mapping@2.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + postcss: 8.5.1 + + '@csstools/postcss-gradients-interpolation-method@5.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-hwb-function@4.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-ic-unit@4.0.0(postcss@8.5.1)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-initial@2.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + + '@csstools/postcss-is-pseudo-class@5.0.1(postcss@8.5.1)': + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.0.0) + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + '@csstools/postcss-light-dark-function@2.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-logical-float-and-clear@3.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + + '@csstools/postcss-logical-overflow@2.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + + '@csstools/postcss-logical-overscroll-behavior@2.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + + '@csstools/postcss-logical-resize@3.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-logical-viewport-units@3.0.3(postcss@8.5.1)': + dependencies: + '@csstools/css-tokenizer': 3.0.3 + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-media-minmax@2.0.6(postcss@8.5.1)': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + postcss: 8.5.1 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.4(postcss@8.5.1)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + postcss: 8.5.1 + + '@csstools/postcss-nested-calc@4.0.0(postcss@8.5.1)': + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-normalize-display-values@4.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-oklab-function@4.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-progressive-custom-properties@4.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-random-function@1.0.2(postcss@8.5.1)': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + postcss: 8.5.1 + + '@csstools/postcss-relative-color-syntax@3.0.7(postcss@8.5.1)': + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + '@csstools/postcss-scope-pseudo-class@4.0.1(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + '@csstools/postcss-sign-functions@1.1.1(postcss@8.5.1)': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + postcss: 8.5.1 + + '@csstools/postcss-stepped-value-functions@4.0.6(postcss@8.5.1)': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + postcss: 8.5.1 + + '@csstools/postcss-text-decoration-shorthand@4.0.1(postcss@8.5.1)': + dependencies: + '@csstools/color-helpers': 5.0.1 + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-trigonometric-functions@4.0.6(postcss@8.5.1)': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + postcss: 8.5.1 + + '@csstools/postcss-unset-value@4.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 + + '@csstools/selector-resolve-nested@3.0.0(postcss-selector-parser@7.0.0)': + dependencies: + postcss-selector-parser: 7.0.0 + + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.0.0)': + dependencies: + postcss-selector-parser: 7.0.0 + + '@csstools/utilities@2.0.0(postcss@8.5.1)': + dependencies: + postcss: 8.5.1 '@discoveryjs/json-ext@0.5.7': {} - '@docsearch/css@3.6.0': {} + '@docsearch/css@3.8.2': {} - '@docsearch/react@3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)': + '@docsearch/react@3.8.2(@algolia/client-search@5.19.0)(@types/react@19.0.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)': dependencies: - '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.14.0) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3) - '@docsearch/css': 3.6.0 - algoliasearch: 4.23.3 + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.14.0) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.19.0 optionalDependencies: - '@types/react': 18.3.3 + '@types/react': 19.0.7 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) search-insights: 2.14.0 transitivePeerDependencies: - '@algolia/client-search' - '@docusaurus/core@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': - dependencies: - '@babel/core': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-transform-runtime': 7.24.7(@babel/core@7.24.7) - '@babel/preset-env': 7.24.7(@babel/core@7.24.7) - '@babel/preset-react': 7.24.7(@babel/core@7.24.7) - '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) - '@babel/runtime': 7.24.7 - '@babel/runtime-corejs3': 7.24.7 - '@babel/traverse': 7.24.7 - '@docusaurus/cssnano-preset': 3.4.0 - '@docusaurus/logger': 3.4.0 - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - autoprefixer: 10.4.19(postcss@8.4.38) - babel-loader: 9.1.3(@babel/core@7.24.7)(webpack@5.92.0) + '@docusaurus/babel@3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.26.5 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-transform-runtime': 7.25.9(@babel/core@7.26.0) + '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/preset-react': 7.26.3(@babel/core@7.26.0) + '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) + '@babel/runtime': 7.26.0 + '@babel/runtime-corejs3': 7.26.0 + '@babel/traverse': 7.26.5 + '@docusaurus/logger': 3.7.0 + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) babel-plugin-dynamic-import-node: 2.3.3 + fs-extra: 11.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - react + - react-dom + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/bundler@3.7.0(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + dependencies: + '@babel/core': 7.26.0 + '@docusaurus/babel': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/cssnano-preset': 3.7.0 + '@docusaurus/logger': 3.7.0 + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + babel-loader: 9.2.1(@babel/core@7.26.0)(webpack@5.97.1) + clean-css: 5.3.3 + copy-webpack-plugin: 11.0.0(webpack@5.97.1) + css-loader: 6.11.0(webpack@5.97.1) + css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.97.1) + cssnano: 6.1.2(postcss@8.5.1) + file-loader: 6.2.0(webpack@5.97.1) + html-minifier-terser: 7.2.0 + mini-css-extract-plugin: 2.9.2(webpack@5.97.1) + null-loader: 4.0.1(webpack@5.97.1) + postcss: 8.5.1 + postcss-loader: 7.3.4(postcss@8.5.1)(typescript@5.7.3)(webpack@5.97.1) + postcss-preset-env: 10.1.3(postcss@8.5.1) + react-dev-utils: 12.0.1(eslint@8.57.1)(typescript@5.7.3)(webpack@5.97.1) + terser-webpack-plugin: 5.3.11(webpack@5.97.1) + tslib: 2.8.1 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.97.1))(webpack@5.97.1) + webpack: 5.97.1 + webpackbar: 6.0.1(webpack@5.97.1) + transitivePeerDependencies: + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - csso + - esbuild + - eslint + - lightningcss + - react + - react-dom + - supports-color + - typescript + - uglify-js + - vue-template-compiler + - webpack-cli + + '@docusaurus/core@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + dependencies: + '@docusaurus/babel': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/bundler': 3.7.0(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/logger': 3.7.0 + '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/react': 3.1.0(@types/react@19.0.7)(react@18.3.1) boxen: 6.2.1 chalk: 4.1.2 chokidar: 3.6.0 - clean-css: 5.3.3 cli-table3: 0.6.5 combine-promises: 1.2.0 commander: 5.1.0 - copy-webpack-plugin: 11.0.0(webpack@5.92.0) - core-js: 3.37.1 - css-loader: 6.11.0(webpack@5.92.0) - css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.92.0) - cssnano: 6.1.2(postcss@8.4.38) + core-js: 3.40.0 del: 6.1.1 detect-port: 1.6.1 escape-html: 1.0.3 eta: 2.2.0 eval: 0.1.8 - file-loader: 6.2.0(webpack@5.92.0) - fs-extra: 11.2.0 - html-minifier-terser: 7.2.0 + fs-extra: 11.3.0 html-tags: 3.3.1 - html-webpack-plugin: 5.6.0(webpack@5.92.0) + html-webpack-plugin: 5.6.3(webpack@5.97.1) leven: 3.1.0 lodash: 4.17.21 - mini-css-extract-plugin: 2.9.0(webpack@5.92.0) p-map: 4.0.0 - postcss: 8.4.38 - postcss-loader: 7.3.4(postcss@8.4.38)(typescript@5.4.5)(webpack@5.92.0) prompts: 2.4.2 react: 18.3.1 - react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.4.5)(webpack@5.92.0) + react-dev-utils: 12.0.1(eslint@8.57.1)(typescript@5.7.3)(webpack@5.97.1) react-dom: 18.3.1(react@18.3.1) - react-helmet-async: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.92.0) + react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.97.1) react-router: 5.3.4(react@18.3.1) react-router-config: 5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1) react-router-dom: 5.3.4(react@18.3.1) - rtl-detect: 1.1.2 - semver: 7.6.2 - serve-handler: 6.1.5 + semver: 7.6.3 + serve-handler: 6.1.6 shelljs: 0.8.5 - terser-webpack-plugin: 5.3.10(webpack@5.92.0) - tslib: 2.6.3 + tslib: 2.8.1 update-notifier: 6.0.2 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.92.0))(webpack@5.92.0) - webpack: 5.92.0 + webpack: 5.97.1 webpack-bundle-analyzer: 4.10.2 - webpack-dev-server: 4.15.2(webpack@5.92.0) - webpack-merge: 5.10.0 - webpackbar: 5.0.2(webpack@5.92.0) + webpack-dev-server: 4.15.2(webpack@5.97.1) + webpack-merge: 6.0.1 transitivePeerDependencies: - - '@docusaurus/types' + - '@docusaurus/faster' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8316,30 +9155,30 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/cssnano-preset@3.4.0': + '@docusaurus/cssnano-preset@3.7.0': dependencies: - cssnano-preset-advanced: 6.1.2(postcss@8.4.38) - postcss: 8.4.38 - postcss-sort-media-queries: 5.2.0(postcss@8.4.38) - tslib: 2.6.3 + cssnano-preset-advanced: 6.1.2(postcss@8.5.1) + postcss: 8.5.1 + postcss-sort-media-queries: 5.2.0(postcss@8.5.1) + tslib: 2.8.1 - '@docusaurus/logger@3.4.0': + '@docusaurus/logger@3.7.0': dependencies: chalk: 4.1.2 - tslib: 2.6.3 + tslib: 2.8.1 - '@docusaurus/mdx-loader@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/mdx-loader@3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@mdx-js/mdx': 3.0.1 + '@docusaurus/logger': 3.7.0 + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/mdx': 3.1.0(acorn@8.14.0) '@slorber/remark-comment': 1.0.0 escape-html: 1.0.3 - estree-util-value-to-estree: 3.1.1 - file-loader: 6.2.0(webpack@5.92.0) - fs-extra: 11.2.0 - image-size: 1.1.1 + estree-util-value-to-estree: 3.2.1 + file-loader: 6.2.0(webpack@5.97.1) + fs-extra: 11.3.0 + image-size: 1.2.0 mdast-util-mdx: 3.0.0 mdast-util-to-string: 4.0.0 react: 18.3.1 @@ -8350,65 +9189,70 @@ snapshots: remark-frontmatter: 5.0.0 remark-gfm: 4.0.0 stringify-object: 3.3.0 - tslib: 2.6.3 - unified: 11.0.4 + tslib: 2.8.1 + unified: 11.0.5 unist-util-visit: 5.0.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.92.0))(webpack@5.92.0) - vfile: 6.0.1 - webpack: 5.92.0 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.97.1))(webpack@5.97.1) + vfile: 6.0.3 + webpack: 5.97.1 transitivePeerDependencies: - - '@docusaurus/types' - '@swc/core' + - acorn - esbuild - supports-color - - typescript - uglify-js - webpack-cli - '@docusaurus/module-type-aliases@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/module-type-aliases@3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 19.0.7 '@types/react-router-config': 5.0.11 '@types/react-router-dom': 5.3.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-helmet-async: 2.0.5(react@18.3.1) + react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' transitivePeerDependencies: - '@swc/core' + - acorn - esbuild - supports-color - uglify-js - webpack-cli - '@docusaurus/plugin-content-blog@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': - dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/logger': 3.4.0 - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) + '@docusaurus/plugin-content-blog@3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + dependencies: + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/logger': 3.7.0 + '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) cheerio: 1.0.0-rc.12 feed: 4.2.2 - fs-extra: 11.2.0 + fs-extra: 11.3.0 lodash: 4.17.21 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) reading-time: 1.5.0 srcset: 4.0.0 - tslib: 2.6.3 + tslib: 2.8.1 unist-util-visit: 5.0.0 utility-types: 3.11.0 - webpack: 5.92.0 + webpack: 5.97.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8422,31 +9266,35 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-docs@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': - dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/logger': 3.4.0 - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/module-type-aliases': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) + '@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + dependencies: + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/logger': 3.7.0 + '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/react-router-config': 5.0.11 combine-promises: 1.2.0 - fs-extra: 11.2.0 + fs-extra: 11.3.0 js-yaml: 4.1.0 lodash: 4.17.21 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.8.1 utility-types: 3.11.0 - webpack: 5.92.0 + webpack: 5.97.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8460,23 +9308,26 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-pages@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/plugin-content-pages@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - fs-extra: 11.2.0 + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 - webpack: 5.92.0 + tslib: 2.8.1 + webpack: 5.97.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8490,21 +9341,24 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-debug@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/plugin-debug@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - fs-extra: 11.2.0 + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-json-view-lite: 1.4.0(react@18.3.1) - tslib: 2.6.3 + react-json-view-lite: 1.5.0(react@18.3.1) + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8518,19 +9372,22 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-analytics@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/plugin-google-analytics@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8544,20 +9401,23 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-gtag@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/plugin-google-gtag@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/gtag.js': 0.0.12 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8571,19 +9431,22 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-tag-manager@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/plugin-google-tag-manager@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8597,24 +9460,60 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-sitemap@3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/plugin-sitemap@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/logger': 3.4.0 - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - fs-extra: 11.2.0 + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/logger': 3.7.0 + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) sitemap: 7.1.2 - tslib: 2.6.3 + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - eslint + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - vue-template-compiler + - webpack-cli + + '@docusaurus/plugin-svgr@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + dependencies: + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@svgr/core': 8.1.0(typescript@5.7.3) + '@svgr/webpack': 8.1.0(typescript@5.7.3) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + webpack: 5.97.1 transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8628,30 +9527,34 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/preset-classic@3.4.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.4.5)': - dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-content-blog': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-content-pages': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-debug': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-google-analytics': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-google-gtag': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-google-tag-manager': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-sitemap': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/theme-classic': 3.4.0(@types/react@18.3.3)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/theme-search-algolia': 3.4.0(@algolia/client-search@4.23.3)(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.4.5) - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/preset-classic@3.7.0(@algolia/client-search@5.19.0)(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(@types/react@19.0.7)(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.7.3)': + dependencies: + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-content-blog': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-content-pages': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-debug': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-google-analytics': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-google-gtag': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-google-tag-manager': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-sitemap': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-svgr': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/theme-classic': 3.7.0(@types/react@19.0.7)(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-search-algolia': 3.7.0(@algolia/client-search@5.19.0)(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(@types/react@19.0.7)(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.7.3) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@algolia/client-search' + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' - '@types/react' + - acorn - bufferutil - csso - debug @@ -8668,44 +9571,47 @@ snapshots: '@docusaurus/react-loadable@6.0.0(react@18.3.1)': dependencies: - '@types/react': 18.3.3 + '@types/react': 19.0.7 react: 18.3.1 - '@docusaurus/theme-classic@3.4.0(@types/react@18.3.3)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': - dependencies: - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/module-type-aliases': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-blog': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-content-pages': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/theme-translations': 3.4.0 - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@mdx-js/react': 3.0.1(@types/react@18.3.3)(react@18.3.1) + '@docusaurus/theme-classic@3.7.0(@types/react@19.0.7)(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': + dependencies: + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/logger': 3.7.0 + '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-blog': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/plugin-content-pages': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-translations': 3.7.0 + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/react': 3.1.0(@types/react@19.0.7)(react@18.3.1) clsx: 2.1.1 copy-text-to-clipboard: 3.2.0 - infima: 0.2.0-alpha.43 + infima: 0.2.0-alpha.45 lodash: 4.17.21 nprogress: 0.2.0 - postcss: 8.4.38 - prism-react-renderer: 2.3.1(react@18.3.1) + postcss: 8.5.1 + prism-react-renderer: 2.4.1(react@18.3.1) prismjs: 1.29.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-router-dom: 5.3.4(react@18.3.1) - rtlcss: 4.1.1 - tslib: 2.6.3 + rtlcss: 4.3.0 + tslib: 2.8.1 utility-types: 3.11.0 transitivePeerDependencies: + - '@docusaurus/faster' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' - '@types/react' + - acorn - bufferutil - csso - debug @@ -8719,72 +9625,61 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)': + '@docusaurus/theme-common@3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/mdx-loader': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/module-type-aliases': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-blog': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/plugin-content-pages': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 19.0.7 '@types/react-router-config': 5.0.11 clsx: 2.1.1 parse-numeric-range: 1.3.0 - prism-react-renderer: 2.3.1(react@18.3.1) + prism-react-renderer: 2.4.1(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.8.1 utility-types: 3.11.0 transitivePeerDependencies: - - '@docusaurus/types' - - '@parcel/css' - - '@rspack/core' - '@swc/core' - - '@swc/css' - - bufferutil - - csso - - debug + - acorn - esbuild - - eslint - - lightningcss - supports-color - - typescript - uglify-js - - utf-8-validate - - vue-template-compiler - webpack-cli - '@docusaurus/theme-search-algolia@3.4.0(@algolia/client-search@4.23.3)(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.4.5)': - dependencies: - '@docsearch/react': 3.6.0(@algolia/client-search@4.23.3)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0) - '@docusaurus/core': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/logger': 3.4.0 - '@docusaurus/plugin-content-docs': 3.4.0(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/theme-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5) - '@docusaurus/theme-translations': 3.4.0 - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-validation': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - algoliasearch: 4.23.3 - algoliasearch-helper: 3.21.0(algoliasearch@4.23.3) + '@docusaurus/theme-search-algolia@3.7.0(@algolia/client-search@5.19.0)(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(@types/react@19.0.7)(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.7.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.19.0)(@types/react@19.0.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0) + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/logger': 3.7.0 + '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) + '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1))(acorn@8.14.0)(eslint@8.57.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-translations': 3.7.0 + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + algoliasearch: 5.19.0 + algoliasearch-helper: 3.23.0(algoliasearch@5.19.0) clsx: 2.1.1 eta: 2.2.0 - fs-extra: 11.2.0 + fs-extra: 11.3.0 lodash: 4.17.21 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.8.1 utility-types: 3.11.0 transitivePeerDependencies: - '@algolia/client-search' - - '@docusaurus/types' + - '@docusaurus/faster' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' - '@types/react' + - acorn - bufferutil - csso - debug @@ -8799,85 +9694,95 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-translations@3.4.0': + '@docusaurus/theme-translations@3.7.0': dependencies: - fs-extra: 11.2.0 - tslib: 2.6.3 + fs-extra: 11.3.0 + tslib: 2.8.1 - '@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/types@3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@mdx-js/mdx': 3.0.1 + '@mdx-js/mdx': 3.1.0(acorn@8.14.0) '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 19.0.7 commander: 5.1.0 - joi: 17.13.1 + joi: 17.13.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-helmet-async: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' utility-types: 3.11.0 - webpack: 5.92.0 + webpack: 5.97.1 webpack-merge: 5.10.0 transitivePeerDependencies: - '@swc/core' + - acorn - esbuild - supports-color - uglify-js - webpack-cli - '@docusaurus/utils-common@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@docusaurus/utils-common@3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - tslib: 2.6.3 - optionalDependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - react + - react-dom + - supports-color + - uglify-js + - webpack-cli - '@docusaurus/utils-validation@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5)': + '@docusaurus/utils-validation@3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5) - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - fs-extra: 11.2.0 - joi: 17.13.1 + '@docusaurus/logger': 3.7.0 + '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 + joi: 17.13.3 js-yaml: 4.1.0 lodash: 4.17.21 - tslib: 2.6.3 + tslib: 2.8.1 transitivePeerDependencies: - - '@docusaurus/types' - '@swc/core' + - acorn - esbuild + - react + - react-dom - supports-color - - typescript - uglify-js - webpack-cli - '@docusaurus/utils@3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.4.5)': + '@docusaurus/utils@3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@docusaurus/logger': 3.4.0 - '@docusaurus/utils-common': 3.4.0(@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@svgr/webpack': 8.1.0(typescript@5.4.5) + '@docusaurus/logger': 3.7.0 + '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) escape-string-regexp: 4.0.0 - file-loader: 6.2.0(webpack@5.92.0) - fs-extra: 11.2.0 + file-loader: 6.2.0(webpack@5.97.1) + fs-extra: 11.3.0 github-slugger: 1.5.0 globby: 11.1.0 gray-matter: 4.0.3 - jiti: 1.21.6 + jiti: 1.21.7 js-yaml: 4.1.0 lodash: 4.17.21 - micromatch: 4.0.7 + micromatch: 4.0.8 prompts: 2.4.2 resolve-pathname: 3.0.0 shelljs: 0.8.5 - tslib: 2.6.3 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.92.0))(webpack@5.92.0) + tslib: 2.8.1 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.97.1))(webpack@5.97.1) utility-types: 3.11.0 - webpack: 5.92.0 - optionalDependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + webpack: 5.97.1 transitivePeerDependencies: - '@swc/core' + - acorn - esbuild + - react + - react-dom - supports-color - - typescript - uglify-js - webpack-cli @@ -8950,20 +9855,20 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)': dependencies: - eslint: 8.57.0 + eslint: 8.57.1 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.11.0': {} + '@eslint-community/regexpp@4.12.1': {} '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.5 + debug: 4.4.0 espree: 9.6.1 globals: 13.24.0 - ignore: 5.3.1 + ignore: 5.3.2 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -8971,11 +9876,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} + '@eslint/js@8.57.1': {} - '@fontsource-variable/onest@5.0.4': {} + '@fontsource-variable/onest@5.1.1': {} - '@fontsource/fira-mono@5.0.13': {} + '@fontsource/fira-mono@5.1.1': {} '@foscia/core@0.12.1': dependencies: @@ -9006,16 +9911,22 @@ snapshots: '@foscia/core': 0.12.1 '@foscia/shared': 0.12.1 + '@gerrit0/mini-shiki@1.27.0': + dependencies: + '@shikijs/engine-oniguruma': 1.27.2 + '@shikijs/types': 1.27.2 + '@shikijs/vscode-textmate': 10.0.1 + '@hapi/hoek@9.3.0': {} '@hapi/topo@5.1.0': dependencies: '@hapi/hoek': 9.3.0 - '@humanwhocodes/config-array@0.11.14': + '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.5 + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -9028,7 +9939,7 @@ snapshots: '@iarna/toml@2.2.5': {} - '@inquirer/figures@1.0.3': {} + '@inquirer/figures@1.0.9': {} '@isaacs/cliui@8.0.2': dependencies: @@ -9050,8 +9961,8 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.14.9 - '@types/yargs': 17.0.32 + '@types/node': 22.10.6 + '@types/yargs': 17.0.33 chalk: 4.1.2 '@jridgewell/gen-mapping@0.3.5': @@ -9060,17 +9971,25 @@ snapshots: '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} '@jridgewell/source-map@0.3.6': dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -9078,42 +9997,40 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@ljharb/through@2.3.13': - dependencies: - call-bind: 1.0.7 - - '@mdx-js/mdx@3.0.1': + '@mdx-js/mdx@3.1.0(acorn@8.14.0)': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdx': 2.0.13 collapse-white-space: 2.1.0 devlop: 1.1.0 - estree-util-build-jsx: 3.0.1 estree-util-is-identifier-name: 3.0.0 - estree-util-to-js: 2.0.0 + estree-util-scope: 1.0.0 estree-walker: 3.0.3 - hast-util-to-estree: 3.1.0 - hast-util-to-jsx-runtime: 2.3.0 + hast-util-to-jsx-runtime: 2.3.2 markdown-extensions: 2.0.0 - periscopic: 3.1.0 - remark-mdx: 3.0.1 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.14.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 remark-parse: 11.0.0 - remark-rehype: 11.1.0 + remark-rehype: 11.1.1 source-map: 0.7.4 - unified: 11.0.4 + unified: 11.0.5 unist-util-position-from-estree: 2.0.0 unist-util-stringify-position: 4.0.0 unist-util-visit: 5.0.0 - vfile: 6.0.1 + vfile: 6.0.3 transitivePeerDependencies: + - acorn - supports-color - '@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1)': + '@mdx-js/react@3.1.0(@types/react@19.0.7)(react@18.3.1)': dependencies: '@types/mdx': 2.0.13 - '@types/react': 18.3.3 + '@types/react': 19.0.7 react: 18.3.1 '@nodelib/fs.scandir@2.1.5': @@ -9128,6 +10045,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@nolyfill/is-core-module@1.0.39': {} + '@octokit/auth-token@4.0.0': {} '@octokit/core@5.2.0': @@ -9136,27 +10055,27 @@ snapshots: '@octokit/graphql': 7.1.0 '@octokit/request': 8.4.0 '@octokit/request-error': 5.1.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 before-after-hook: 2.2.3 universal-user-agent: 6.0.1 '@octokit/endpoint@9.0.5': dependencies: - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 universal-user-agent: 6.0.1 '@octokit/graphql@7.1.0': dependencies: '@octokit/request': 8.4.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 universal-user-agent: 6.0.1 - '@octokit/openapi-types@22.2.0': {} + '@octokit/openapi-types@23.0.1': {} '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@5.2.0)': dependencies: '@octokit/core': 5.2.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 '@octokit/plugin-request-log@4.0.1(@octokit/core@5.2.0)': dependencies: @@ -9165,11 +10084,11 @@ snapshots: '@octokit/plugin-rest-endpoint-methods@13.2.2(@octokit/core@5.2.0)': dependencies: '@octokit/core': 5.2.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 '@octokit/request-error@5.1.0': dependencies: - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 deprecation: 2.3.1 once: 1.4.0 @@ -9177,7 +10096,7 @@ snapshots: dependencies: '@octokit/endpoint': 9.0.5 '@octokit/request-error': 5.1.0 - '@octokit/types': 13.5.0 + '@octokit/types': 13.7.0 universal-user-agent: 6.0.1 '@octokit/rest@20.1.1': @@ -9187,9 +10106,9 @@ snapshots: '@octokit/plugin-request-log': 4.0.1(@octokit/core@5.2.0) '@octokit/plugin-rest-endpoint-methods': 13.2.2(@octokit/core@5.2.0) - '@octokit/types@13.5.0': + '@octokit/types@13.7.0': dependencies: - '@octokit/openapi-types': 22.2.0 + '@octokit/openapi-types': 23.0.1 '@pkgjs/parseargs@0.11.0': optional: true @@ -9200,136 +10119,162 @@ snapshots: dependencies: graceful-fs: 4.2.10 - '@pnpm/npm-conf@2.2.2': + '@pnpm/npm-conf@2.3.1': dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@polka/url@1.0.0-next.25': {} + '@polka/url@1.0.0-next.28': {} - '@release-it/bumper@6.0.1(release-it@17.4.0(typescript@5.5.2))': + '@release-it/bumper@6.0.1(release-it@17.11.0(typescript@5.7.3))': dependencies: '@iarna/toml': 2.2.5 detect-indent: 7.0.1 - fast-glob: 3.3.2 + fast-glob: 3.3.3 ini: 4.1.3 js-yaml: 4.1.0 lodash-es: 4.17.21 - release-it: 17.4.0(typescript@5.5.2) - semver: 7.6.2 + release-it: 17.11.0(typescript@5.7.3) + semver: 7.6.3 - '@release-it/conventional-changelog@8.0.1(release-it@17.4.0(typescript@5.5.2))': + '@release-it/conventional-changelog@8.0.2(release-it@17.11.0(typescript@5.7.3))': dependencies: concat-stream: 2.0.0 conventional-changelog: 5.1.0 conventional-recommended-bump: 9.0.0 - release-it: 17.4.0(typescript@5.5.2) - semver: 7.6.2 + git-semver-tags: 8.0.0 + release-it: 17.11.0(typescript@5.7.3) + semver: 7.6.3 + transitivePeerDependencies: + - conventional-commits-filter + - conventional-commits-parser - '@rollup/plugin-commonjs@26.0.1(rollup@4.18.0)': + '@rollup/plugin-commonjs@26.0.3(rollup@4.30.1)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) commondir: 1.0.1 estree-walker: 2.0.2 - glob: 10.4.2 + glob: 10.4.5 is-reference: 1.2.1 - magic-string: 0.30.10 + magic-string: 0.30.17 optionalDependencies: - rollup: 4.18.0 + rollup: 4.30.1 - '@rollup/plugin-json@6.1.0(rollup@4.18.0)': + '@rollup/plugin-json@6.1.0(rollup@4.30.1)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) optionalDependencies: - rollup: 4.18.0 + rollup: 4.30.1 - '@rollup/plugin-node-resolve@15.2.3(rollup@4.18.0)': + '@rollup/plugin-node-resolve@15.3.1(rollup@4.30.1)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) '@types/resolve': 1.20.2 deepmerge: 4.3.1 - is-builtin-module: 3.2.1 is-module: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.10 optionalDependencies: - rollup: 4.18.0 + rollup: 4.30.1 - '@rollup/plugin-terser@0.4.4(rollup@4.18.0)': + '@rollup/plugin-terser@0.4.4(rollup@4.30.1)': dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 - terser: 5.31.1 + terser: 5.37.0 optionalDependencies: - rollup: 4.18.0 + rollup: 4.30.1 - '@rollup/plugin-typescript@11.1.6(rollup@4.18.0)(tslib@2.6.3)(typescript@5.5.2)': + '@rollup/plugin-typescript@11.1.6(rollup@4.30.1)(tslib@2.8.1)(typescript@5.7.3)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) - resolve: 1.22.8 - typescript: 5.5.2 + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + resolve: 1.22.10 + typescript: 5.7.3 optionalDependencies: - rollup: 4.18.0 - tslib: 2.6.3 + rollup: 4.30.1 + tslib: 2.8.1 - '@rollup/pluginutils@5.1.0(rollup@4.18.0)': + '@rollup/pluginutils@5.1.4(rollup@4.30.1)': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-walker: 2.0.2 - picomatch: 2.3.1 + picomatch: 4.0.2 optionalDependencies: - rollup: 4.18.0 + rollup: 4.30.1 + + '@rollup/rollup-android-arm-eabi@4.30.1': + optional: true + + '@rollup/rollup-android-arm64@4.30.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.30.1': + optional: true - '@rollup/rollup-android-arm-eabi@4.18.0': + '@rollup/rollup-darwin-x64@4.30.1': optional: true - '@rollup/rollup-android-arm64@4.18.0': + '@rollup/rollup-freebsd-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-arm64@4.18.0': + '@rollup/rollup-freebsd-x64@4.30.1': optional: true - '@rollup/rollup-darwin-x64@4.18.0': + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + '@rollup/rollup-linux-arm-musleabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.18.0': + '@rollup/rollup-linux-arm64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.18.0': + '@rollup/rollup-linux-arm64-musl@4.30.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.18.0': + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.18.0': + '@rollup/rollup-linux-riscv64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.18.0': + '@rollup/rollup-linux-s390x-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.18.0': + '@rollup/rollup-linux-x64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-musl@4.18.0': + '@rollup/rollup-linux-x64-musl@4.30.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.18.0': + '@rollup/rollup-win32-arm64-msvc@4.30.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.18.0': + '@rollup/rollup-win32-ia32-msvc@4.30.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.18.0': + '@rollup/rollup-win32-x64-msvc@4.30.1': optional: true + '@rtsao/scc@1.1.0': {} + '@sec-ant/readable-stream@0.4.1': {} + '@shikijs/engine-oniguruma@1.27.2': + dependencies: + '@shikijs/types': 1.27.2 + '@shikijs/vscode-textmate': 10.0.1 + + '@shikijs/types@1.27.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.1 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.1': {} + '@sideway/address@4.1.5': dependencies: '@hapi/hoek': 9.3.0 @@ -9348,62 +10293,72 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + invariant: 2.2.4 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-fast-compare: 3.2.2 + shallowequal: 1.1.0 + '@slorber/remark-comment@1.0.0': dependencies: micromark-factory-space: 1.1.0 micromark-util-character: 1.2.0 micromark-util-symbol: 1.1.0 - '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.24.7)': + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 - '@svgr/babel-preset@8.1.0(@babel/core@7.24.7)': + '@svgr/babel-preset@8.1.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.24.7 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.24.7) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.24.7) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.24.7) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.24.7) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.24.7) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.24.7) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.24.7) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.26.0) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.26.0) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.26.0) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.26.0) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.26.0) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.26.0) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.26.0) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.26.0) - '@svgr/core@8.1.0(typescript@5.4.5)': + '@svgr/core@8.1.0(typescript@5.7.3)': dependencies: - '@babel/core': 7.24.7 - '@svgr/babel-preset': 8.1.0(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.26.0) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.4.5) + cosmiconfig: 8.3.6(typescript@5.7.3) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -9411,38 +10366,38 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.26.5 entities: 4.5.0 - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.4.5))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.7.3))': dependencies: - '@babel/core': 7.24.7 - '@svgr/babel-preset': 8.1.0(@babel/core@7.24.7) - '@svgr/core': 8.1.0(typescript@5.4.5) + '@babel/core': 7.26.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.26.0) + '@svgr/core': 8.1.0(typescript@5.7.3) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.4.5))(typescript@5.4.5)': + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.7.3))(typescript@5.7.3)': dependencies: - '@svgr/core': 8.1.0(typescript@5.4.5) - cosmiconfig: 8.3.6(typescript@5.4.5) + '@svgr/core': 8.1.0(typescript@5.7.3) + cosmiconfig: 8.3.6(typescript@5.7.3) deepmerge: 4.3.1 svgo: 3.3.2 transitivePeerDependencies: - typescript - '@svgr/webpack@8.1.0(typescript@5.4.5)': + '@svgr/webpack@8.1.0(typescript@5.7.3)': dependencies: - '@babel/core': 7.24.7 - '@babel/plugin-transform-react-constant-elements': 7.24.7(@babel/core@7.24.7) - '@babel/preset-env': 7.24.7(@babel/core@7.24.7) - '@babel/preset-react': 7.24.7(@babel/core@7.24.7) - '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) - '@svgr/core': 8.1.0(typescript@5.4.5) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5)) - '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.4.5))(typescript@5.4.5) + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-constant-elements': 7.25.9(@babel/core@7.26.0) + '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/preset-react': 7.26.3(@babel/core@7.26.0) + '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) + '@svgr/core': 8.1.0(typescript@5.7.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.7.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.7.3))(typescript@5.7.3) transitivePeerDependencies: - supports-color - typescript @@ -9457,29 +10412,29 @@ snapshots: '@types/acorn@4.0.6': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.14.9 + '@types/node': 22.10.6 '@types/bonjour@3.5.13': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 '@types/connect-history-api-fallback@1.5.4': dependencies: - '@types/express-serve-static-core': 4.19.3 - '@types/node': 20.14.9 + '@types/express-serve-static-core': 5.0.5 + '@types/node': 22.10.6 '@types/connect@3.4.38': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 - '@types/conventional-commits-parser@5.0.0': + '@types/conventional-commits-parser@5.0.1': dependencies: - '@types/node': 18.19.39 + '@types/node': 18.19.70 '@types/debug@4.1.12': dependencies: @@ -9487,39 +10442,48 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: - '@types/eslint': 8.56.10 - '@types/estree': 1.0.5 + '@types/eslint': 9.6.1 + '@types/estree': 1.0.6 - '@types/eslint@8.56.10': + '@types/eslint@9.6.1': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/estree@1.0.5': {} - '@types/express-serve-static-core@4.19.3': + '@types/estree@1.0.6': {} + + '@types/express-serve-static-core@4.19.6': + dependencies: + '@types/node': 22.10.6 + '@types/qs': 6.9.18 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express-serve-static-core@5.0.5': dependencies: - '@types/node': 20.14.9 - '@types/qs': 6.9.15 + '@types/node': 22.10.6 + '@types/qs': 6.9.18 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.3 - '@types/qs': 6.9.15 + '@types/express-serve-static-core': 4.19.6 + '@types/qs': 6.9.18 '@types/serve-static': 1.15.7 '@types/gtag.js@0.0.12': {} '@types/hast@3.0.4': dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 '@types/history@4.7.11': {} @@ -9529,9 +10493,9 @@ snapshots: '@types/http-errors@2.0.4': {} - '@types/http-proxy@1.17.14': + '@types/http-proxy@1.17.15': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 '@types/istanbul-lib-coverage@2.0.6': {} @@ -9557,7 +10521,7 @@ snapshots: '@types/mdast@4.0.4': dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 '@types/mdx@2.0.13': {} @@ -9567,17 +10531,17 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 '@types/node@17.0.45': {} - '@types/node@18.19.39': + '@types/node@18.19.70': dependencies: undici-types: 5.26.5 - '@types/node@20.14.9': + '@types/node@22.10.6': dependencies: - undici-types: 5.26.5 + undici-types: 6.20.0 '@types/normalize-package-data@2.4.4': {} @@ -9585,34 +10549,31 @@ snapshots: '@types/pluralize@0.0.33': {} - '@types/prismjs@1.26.4': {} + '@types/prismjs@1.26.5': {} - '@types/prop-types@15.7.12': {} - - '@types/qs@6.9.15': {} + '@types/qs@6.9.18': {} '@types/range-parser@1.2.7': {} '@types/react-router-config@5.0.11': dependencies: '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 19.0.7 '@types/react-router': 5.1.20 '@types/react-router-dom@5.3.3': dependencies: '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 19.0.7 '@types/react-router': 5.1.20 '@types/react-router@5.1.20': dependencies: '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 19.0.7 - '@types/react@18.3.3': + '@types/react@19.0.7': dependencies: - '@types/prop-types': 15.7.12 csstype: 3.1.3 '@types/resolve@1.20.2': {} @@ -9623,10 +10584,12 @@ snapshots: dependencies: '@types/node': 17.0.45 + '@types/semver@7.5.8': {} + '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.14.9 + '@types/node': 22.10.6 '@types/serve-index@1.9.4': dependencies: @@ -9635,122 +10598,122 @@ snapshots: '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 20.14.9 + '@types/node': 22.10.6 '@types/send': 0.17.4 '@types/sockjs@0.3.36': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 - '@types/unist@2.0.10': {} + '@types/unist@2.0.11': {} - '@types/unist@3.0.2': {} + '@types/unist@3.0.3': {} - '@types/ws@8.5.10': + '@types/ws@8.5.13': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.32': + '@types/yargs@17.0.33': dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)': dependencies: - '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/type-utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.14.1 - eslint: 8.57.0 + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 8.57.1 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.2) + ts-api-utils: 1.4.3(typescript@5.7.3) optionalDependencies: - typescript: 5.5.2 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3)': dependencies: - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.14.1 - debug: 4.3.5 - eslint: 8.57.0 + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 + eslint: 8.57.1 optionalDependencies: - typescript: 5.5.2 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.14.1': + '@typescript-eslint/scope-manager@7.18.0': dependencies: - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/visitor-keys': 7.14.1 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 - '@typescript-eslint/type-utils@7.14.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - debug: 4.3.5 - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.5.2) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.7.3) + debug: 4.4.0 + eslint: 8.57.1 + ts-api-utils: 1.4.3(typescript@5.7.3) optionalDependencies: - typescript: 5.5.2 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.14.1': {} + '@typescript-eslint/types@7.18.0': {} - '@typescript-eslint/typescript-estree@7.14.1(typescript@5.5.2)': + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.7.3)': dependencies: - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/visitor-keys': 7.14.1 - debug: 4.3.5 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.2) + semver: 7.6.3 + ts-api-utils: 1.4.3(typescript@5.7.3) optionalDependencies: - typescript: 5.5.2 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.14.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) - eslint: 8.57.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3) + eslint: 8.57.1 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@7.14.1': + '@typescript-eslint/visitor-keys@7.18.0': dependencies: - '@typescript-eslint/types': 7.14.1 + '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 - '@ungap/structured-clone@1.2.0': {} + '@ungap/structured-clone@1.2.1': {} - '@vitest/coverage-istanbul@1.6.0(vitest@1.6.0(@types/node@18.19.39)(jsdom@24.1.0)(terser@5.31.1))': + '@vitest/coverage-istanbul@1.6.0(vitest@1.6.0(@types/node@18.19.70)(jsdom@24.1.3)(terser@5.37.0))': dependencies: - debug: 4.3.5 + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.2 + istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.4 + istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - magicast: 0.3.4 - picocolors: 1.0.1 + magicast: 0.3.5 + picocolors: 1.1.1 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@18.19.39)(jsdom@24.1.0)(terser@5.31.1) + vitest: 1.6.0(@types/node@18.19.70)(jsdom@24.1.3)(terser@5.37.0) transitivePeerDependencies: - supports-color @@ -9758,7 +10721,7 @@ snapshots: dependencies: '@vitest/spy': 1.6.0 '@vitest/utils': 1.6.0 - chai: 4.4.1 + chai: 4.5.0 '@vitest/runner@1.6.0': dependencies: @@ -9768,7 +10731,7 @@ snapshots: '@vitest/snapshot@1.6.0': dependencies: - magic-string: 0.30.10 + magic-string: 0.30.17 pathe: 1.1.2 pretty-format: 29.7.0 @@ -9783,80 +10746,80 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 - '@webassemblyjs/ast@1.12.1': + '@webassemblyjs/ast@1.14.1': dependencies: - '@webassemblyjs/helper-numbers': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/floating-point-hex-parser@1.11.6': {} + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} - '@webassemblyjs/helper-api-error@1.11.6': {} + '@webassemblyjs/helper-api-error@1.13.2': {} - '@webassemblyjs/helper-buffer@1.12.1': {} + '@webassemblyjs/helper-buffer@1.14.1': {} - '@webassemblyjs/helper-numbers@1.11.6': + '@webassemblyjs/helper-numbers@1.13.2': dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.6 - '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 '@xtuc/long': 4.2.2 - '@webassemblyjs/helper-wasm-bytecode@1.11.6': {} + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} - '@webassemblyjs/helper-wasm-section@1.12.1': + '@webassemblyjs/helper-wasm-section@1.14.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/ieee754@1.11.6': + '@webassemblyjs/ieee754@1.13.2': dependencies: '@xtuc/ieee754': 1.2.0 - '@webassemblyjs/leb128@1.11.6': + '@webassemblyjs/leb128@1.13.2': dependencies: '@xtuc/long': 4.2.2 - '@webassemblyjs/utf8@1.11.6': {} + '@webassemblyjs/utf8@1.13.2': {} - '@webassemblyjs/wasm-edit@1.12.1': + '@webassemblyjs/wasm-edit@1.14.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/helper-wasm-section': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-opt': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - '@webassemblyjs/wast-printer': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 - '@webassemblyjs/wasm-gen@1.12.1': + '@webassemblyjs/wasm-gen@1.14.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 - '@webassemblyjs/wasm-opt@1.12.1': + '@webassemblyjs/wasm-opt@1.14.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-buffer': 1.12.1 - '@webassemblyjs/wasm-gen': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 - '@webassemblyjs/wasm-parser@1.12.1': + '@webassemblyjs/wasm-parser@1.14.1': dependencies: - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/helper-api-error': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 - '@webassemblyjs/wast-printer@1.12.1': + '@webassemblyjs/wast-printer@1.14.1': dependencies: - '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 '@xtuc/ieee754@1.2.0': {} @@ -9873,46 +10836,38 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-attributes@1.9.5(acorn@8.12.0): + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: - acorn: 8.12.0 + acorn: 8.14.0 - acorn-jsx@5.3.2(acorn@8.12.0): + acorn-walk@8.3.4: dependencies: - acorn: 8.12.0 + acorn: 8.14.0 - acorn-walk@8.3.3: - dependencies: - acorn: 8.12.0 - - acorn@8.12.0: {} + acorn@8.14.0: {} add-stream@1.0.0: {} address@1.2.2: {} - agent-base@7.1.1: - dependencies: - debug: 4.3.5 - transitivePeerDependencies: - - supports-color + agent-base@7.1.3: {} aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - ajv-formats@2.1.1(ajv@8.16.0): + ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: - ajv: 8.16.0 + ajv: 8.17.1 ajv-keywords@3.5.2(ajv@6.12.6): dependencies: ajv: 6.12.6 - ajv-keywords@5.1.0(ajv@8.16.0): + ajv-keywords@5.1.0(ajv@8.17.1): dependencies: - ajv: 8.16.0 + ajv: 8.17.1 fast-deep-equal: 3.1.3 ajv@6.12.6: @@ -9922,35 +10877,33 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.16.0: + ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - uri-js: 4.4.1 - algoliasearch-helper@3.21.0(algoliasearch@4.23.3): + algoliasearch-helper@3.23.0(algoliasearch@5.19.0): dependencies: '@algolia/events': 4.0.1 - algoliasearch: 4.23.3 - - algoliasearch@4.23.3: - dependencies: - '@algolia/cache-browser-local-storage': 4.23.3 - '@algolia/cache-common': 4.23.3 - '@algolia/cache-in-memory': 4.23.3 - '@algolia/client-account': 4.23.3 - '@algolia/client-analytics': 4.23.3 - '@algolia/client-common': 4.23.3 - '@algolia/client-personalization': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/logger-common': 4.23.3 - '@algolia/logger-console': 4.23.3 - '@algolia/recommend': 4.23.3 - '@algolia/requester-browser-xhr': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/requester-node-http': 4.23.3 - '@algolia/transporter': 4.23.3 + algoliasearch: 5.19.0 + + algoliasearch@5.19.0: + dependencies: + '@algolia/client-abtesting': 5.19.0 + '@algolia/client-analytics': 5.19.0 + '@algolia/client-common': 5.19.0 + '@algolia/client-insights': 5.19.0 + '@algolia/client-personalization': 5.19.0 + '@algolia/client-query-suggestions': 5.19.0 + '@algolia/client-search': 5.19.0 + '@algolia/ingestion': 1.19.0 + '@algolia/monitoring': 1.19.0 + '@algolia/recommend': 5.19.0 + '@algolia/requester-browser-xhr': 5.19.0 + '@algolia/requester-fetch': 5.19.0 + '@algolia/requester-node-http': 5.19.0 ansi-align@3.0.1: dependencies: @@ -9972,8 +10925,6 @@ snapshots: ansi-regex@6.0.1: {} - ansi-sequence-parser@1.1.1: {} - ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -10001,10 +10952,10 @@ snapshots: argparse@2.0.1: {} - array-buffer-byte-length@1.0.1: + array-buffer-byte-length@1.0.2: dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 + call-bound: 1.0.3 + is-array-buffer: 3.0.5 array-flatten@1.1.1: {} @@ -10012,65 +10963,55 @@ snapshots: array-includes@3.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - is-string: 1.0.7 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + get-intrinsic: 1.2.7 + is-string: 1.1.1 array-union@2.1.0: {} array.prototype.findlastindex@1.2.5: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 es-shim-unscopables: 1.0.2 - array.prototype.flat@1.3.2: + array.prototype.flat@1.3.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 es-shim-unscopables: 1.0.2 - array.prototype.flatmap@1.3.2: + array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 es-shim-unscopables: 1.0.2 - array.prototype.map@1.0.7: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-array-method-boxes-properly: 1.0.0 - es-object-atoms: 1.0.0 - is-string: 1.0.7 - - arraybuffer.prototype.slice@1.0.3: + arraybuffer.prototype.slice@1.0.4: dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 + get-intrinsic: 1.2.7 + is-array-buffer: 3.0.5 assertion-error@1.1.0: {} ast-types@0.13.4: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 - astring@1.8.6: {} + astring@1.9.0: {} async-retry@1.3.3: dependencies: @@ -10080,52 +11021,57 @@ snapshots: at-least-node@1.0.0: {} - autoprefixer@10.4.19(postcss@8.4.38): + atomically@2.0.3: + dependencies: + stubborn-fs: 1.2.5 + when-exit: 2.1.4 + + autoprefixer@10.4.20(postcss@8.5.1): dependencies: - browserslist: 4.23.1 - caniuse-lite: 1.0.30001636 + browserslist: 4.24.4 + caniuse-lite: 1.0.30001692 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.0.1 - postcss: 8.4.38 + picocolors: 1.1.1 + postcss: 8.5.1 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - babel-loader@9.1.3(@babel/core@7.24.7)(webpack@5.92.0): + babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.97.1): dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.26.0 find-cache-dir: 4.0.0 - schema-utils: 4.2.0 - webpack: 5.92.0 + schema-utils: 4.3.0 + webpack: 5.97.1 babel-plugin-dynamic-import-node@2.3.3: dependencies: - object.assign: 4.1.5 + object.assign: 4.1.7 - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7): + babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): dependencies: - '@babel/compat-data': 7.24.7 - '@babel/core': 7.24.7 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + '@babel/compat-data': 7.26.5 + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7): + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0): dependencies: - '@babel/core': 7.24.7 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) - core-js-compat: 3.37.1 + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + core-js-compat: 3.40.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7): + babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0): dependencies: - '@babel/core': 7.24.7 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) transitivePeerDependencies: - supports-color @@ -10151,7 +11097,7 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@1.20.2: + body-parser@1.20.3: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -10161,14 +11107,14 @@ snapshots: http-errors: 2.0.0 iconv-lite: 0.4.24 on-finished: 2.4.1 - qs: 6.11.0 + qs: 6.13.0 raw-body: 2.5.2 type-is: 1.6.18 unpipe: 1.0.0 transitivePeerDependencies: - supports-color - bonjour-service@1.2.1: + bonjour-service@1.3.0: dependencies: fast-deep-equal: 3.1.3 multicast-dns: 7.2.5 @@ -10197,6 +11143,17 @@ snapshots: widest-line: 4.0.1 wrap-ansi: 8.1.0 + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.4.1 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.32.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -10210,12 +11167,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.23.1: + browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001636 - electron-to-chromium: 1.4.803 - node-releases: 2.0.14 - update-browserslist-db: 1.0.16(browserslist@4.23.1) + caniuse-lite: 1.0.30001692 + electron-to-chromium: 1.5.83 + node-releases: 2.0.19 + update-browserslist-db: 1.1.2(browserslist@4.24.4) buffer-from@1.1.2: {} @@ -10224,8 +11181,6 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - builtin-modules@3.3.0: {} - bundle-name@4.1.0: dependencies: run-applescript: 7.0.0 @@ -10248,37 +11203,48 @@ snapshots: normalize-url: 8.0.1 responselike: 3.0.0 - call-bind@1.0.7: + call-bind-apply-helpers@1.0.1: dependencies: - es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-define-property: 1.0.1 + get-intrinsic: 1.2.7 set-function-length: 1.2.2 + call-bound@1.0.3: + dependencies: + call-bind-apply-helpers: 1.0.1 + get-intrinsic: 1.2.7 + callsites@3.1.0: {} camel-case@4.1.2: dependencies: pascal-case: 3.1.2 - tslib: 2.6.3 + tslib: 2.8.1 camelcase@6.3.0: {} camelcase@7.0.1: {} + camelcase@8.0.0: {} + caniuse-api@3.0.0: dependencies: - browserslist: 4.23.1 - caniuse-lite: 1.0.30001636 + browserslist: 4.24.4 + caniuse-lite: 1.0.30001692 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001636: {} + caniuse-lite@1.0.30001692: {} ccount@2.0.1: {} - chai@4.4.1: + chai@4.5.0: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -10286,7 +11252,7 @@ snapshots: get-func-name: 2.0.2 loupe: 2.3.7 pathval: 1.1.1 - type-detect: 4.0.8 + type-detect: 4.1.0 chalk@2.4.2: dependencies: @@ -10301,6 +11267,8 @@ snapshots: chalk@5.3.0: {} + chalk@5.4.1: {} + char-regex@1.0.2: {} character-entities-html4@2.1.0: {} @@ -10324,17 +11292,17 @@ snapshots: css-what: 6.1.0 domelementtype: 2.3.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 cheerio@1.0.0-rc.12: dependencies: cheerio-select: 2.1.0 dom-serializer: 2.0.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 htmlparser2: 8.0.2 - parse5: 7.1.2 - parse5-htmlparser2-tree-adapter: 7.0.0 + parse5: 7.2.1 + parse5-htmlparser2-tree-adapter: 7.1.0 chokidar@3.6.0: dependencies: @@ -10352,6 +11320,8 @@ snapshots: ci-info@3.9.0: {} + ci-info@4.1.0: {} + clean-css@5.3.3: dependencies: source-map: 0.6.1 @@ -10368,6 +11338,10 @@ snapshots: dependencies: restore-cursor: 4.0.0 + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + cli-highlight@2.1.11: dependencies: chalk: 4.1.2 @@ -10462,16 +11436,16 @@ snapshots: compressible@2.0.18: dependencies: - mime-db: 1.52.0 + mime-db: 1.53.0 - compression@1.7.4: + compression@1.7.5: dependencies: - accepts: 1.3.8 - bytes: 3.0.0 + bytes: 3.1.2 compressible: 2.0.18 debug: 2.6.9 + negotiator: 0.6.4 on-headers: 1.0.2 - safe-buffer: 5.1.2 + safe-buffer: 5.2.1 vary: 1.1.2 transitivePeerDependencies: - supports-color @@ -10491,13 +11465,13 @@ snapshots: date-fns: 2.30.0 lodash: 4.17.21 rxjs: 7.8.1 - shell-quote: 1.8.1 + shell-quote: 1.8.2 spawn-command: 0.0.2 supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 - confbox@0.1.7: {} + confbox@0.1.8: {} config-chain@1.1.13: dependencies: @@ -10512,11 +11486,18 @@ snapshots: write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 + configstore@7.0.0: + dependencies: + atomically: 2.0.3 + dot-prop: 9.0.0 + graceful-fs: 4.2.11 + xdg-basedir: 5.1.0 + confusing-browser-globals@1.0.11: {} connect-history-api-fallback@2.0.0: {} - consola@2.15.3: {} + consola@3.4.0: {} content-disposition@0.5.2: {} @@ -10571,7 +11552,7 @@ snapshots: handlebars: 4.7.8 json-stringify-safe: 5.0.1 meow: 12.1.1 - semver: 7.6.2 + semver: 7.6.3 split2: 4.2.0 conventional-changelog@5.1.0: @@ -10610,36 +11591,36 @@ snapshots: cookie-signature@1.0.6: {} - cookie@0.6.0: {} + cookie@0.7.1: {} copy-text-to-clipboard@3.2.0: {} - copy-webpack-plugin@11.0.0(webpack@5.92.0): + copy-webpack-plugin@11.0.0(webpack@5.97.1): dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.3 glob-parent: 6.0.2 globby: 13.2.2 normalize-path: 3.0.0 - schema-utils: 4.2.0 + schema-utils: 4.3.0 serialize-javascript: 6.0.2 - webpack: 5.92.0 + webpack: 5.97.1 - core-js-compat@3.37.1: + core-js-compat@3.40.0: dependencies: - browserslist: 4.23.1 + browserslist: 4.24.4 - core-js-pure@3.37.1: {} + core-js-pure@3.40.0: {} - core-js@3.37.1: {} + core-js@3.40.0: {} core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@5.0.0(@types/node@18.19.39)(cosmiconfig@9.0.0(typescript@5.5.2))(typescript@5.5.2): + cosmiconfig-typescript-loader@6.1.0(@types/node@18.19.70)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3): dependencies: - '@types/node': 18.19.39 - cosmiconfig: 9.0.0(typescript@5.5.2) - jiti: 1.21.6 - typescript: 5.5.2 + '@types/node': 18.19.70 + cosmiconfig: 9.0.0(typescript@5.7.3) + jiti: 2.4.2 + typescript: 5.7.3 cosmiconfig@6.0.0: dependencies: @@ -10649,23 +11630,23 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 - cosmiconfig@8.3.6(typescript@5.4.5): + cosmiconfig@8.3.6(typescript@5.7.3): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.4.5 + typescript: 5.7.3 - cosmiconfig@9.0.0(typescript@5.5.2): + cosmiconfig@9.0.0(typescript@5.7.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 optionalDependencies: - typescript: 5.5.2 + typescript: 5.7.3 cross-spawn@7.0.3: dependencies: @@ -10673,39 +11654,61 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 - css-declaration-sorter@7.2.0(postcss@8.4.38): + css-blank-pseudo@7.0.1(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + css-declaration-sorter@7.2.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + css-has-pseudo@7.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.0.0) + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + postcss-value-parser: 4.2.0 - css-loader@6.11.0(webpack@5.92.0): + css-loader@6.11.0(webpack@5.97.1): dependencies: - icss-utils: 5.1.0(postcss@8.4.38) - postcss: 8.4.38 - postcss-modules-extract-imports: 3.1.0(postcss@8.4.38) - postcss-modules-local-by-default: 4.0.5(postcss@8.4.38) - postcss-modules-scope: 3.2.0(postcss@8.4.38) - postcss-modules-values: 4.0.0(postcss@8.4.38) + icss-utils: 5.1.0(postcss@8.5.1) + postcss: 8.5.1 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.1) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.1) + postcss-modules-scope: 3.2.1(postcss@8.5.1) + postcss-modules-values: 4.0.0(postcss@8.5.1) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 optionalDependencies: - webpack: 5.92.0 + webpack: 5.97.1 - css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.92.0): + css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.97.1): dependencies: '@jridgewell/trace-mapping': 0.3.25 - cssnano: 6.1.2(postcss@8.4.38) + cssnano: 6.1.2(postcss@8.5.1) jest-worker: 29.7.0 - postcss: 8.4.38 - schema-utils: 4.2.0 + postcss: 8.5.1 + schema-utils: 4.3.0 serialize-javascript: 6.0.2 - webpack: 5.92.0 + webpack: 5.97.1 optionalDependencies: clean-css: 5.3.3 + css-prefers-color-scheme@10.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + css-select@4.3.0: dependencies: boolbase: 1.0.0 @@ -10719,120 +11722,121 @@ snapshots: boolbase: 1.0.0 css-what: 6.1.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 nth-check: 2.1.1 css-tree@2.2.1: dependencies: mdn-data: 2.0.28 - source-map-js: 1.2.0 + source-map-js: 1.2.1 css-tree@2.3.1: dependencies: mdn-data: 2.0.30 - source-map-js: 1.2.0 + source-map-js: 1.2.1 css-what@6.1.0: {} + cssdb@8.2.3: {} + cssesc@3.0.0: {} - cssnano-preset-advanced@6.1.2(postcss@8.4.38): - dependencies: - autoprefixer: 10.4.19(postcss@8.4.38) - browserslist: 4.23.1 - cssnano-preset-default: 6.1.2(postcss@8.4.38) - postcss: 8.4.38 - postcss-discard-unused: 6.0.5(postcss@8.4.38) - postcss-merge-idents: 6.0.3(postcss@8.4.38) - postcss-reduce-idents: 6.0.3(postcss@8.4.38) - postcss-zindex: 6.0.2(postcss@8.4.38) - - cssnano-preset-default@6.1.2(postcss@8.4.38): - dependencies: - browserslist: 4.23.1 - css-declaration-sorter: 7.2.0(postcss@8.4.38) - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 - postcss-calc: 9.0.1(postcss@8.4.38) - postcss-colormin: 6.1.0(postcss@8.4.38) - postcss-convert-values: 6.1.0(postcss@8.4.38) - postcss-discard-comments: 6.0.2(postcss@8.4.38) - postcss-discard-duplicates: 6.0.3(postcss@8.4.38) - postcss-discard-empty: 6.0.3(postcss@8.4.38) - postcss-discard-overridden: 6.0.2(postcss@8.4.38) - postcss-merge-longhand: 6.0.5(postcss@8.4.38) - postcss-merge-rules: 6.1.1(postcss@8.4.38) - postcss-minify-font-values: 6.1.0(postcss@8.4.38) - postcss-minify-gradients: 6.0.3(postcss@8.4.38) - postcss-minify-params: 6.1.0(postcss@8.4.38) - postcss-minify-selectors: 6.0.4(postcss@8.4.38) - postcss-normalize-charset: 6.0.2(postcss@8.4.38) - postcss-normalize-display-values: 6.0.2(postcss@8.4.38) - postcss-normalize-positions: 6.0.2(postcss@8.4.38) - postcss-normalize-repeat-style: 6.0.2(postcss@8.4.38) - postcss-normalize-string: 6.0.2(postcss@8.4.38) - postcss-normalize-timing-functions: 6.0.2(postcss@8.4.38) - postcss-normalize-unicode: 6.1.0(postcss@8.4.38) - postcss-normalize-url: 6.0.2(postcss@8.4.38) - postcss-normalize-whitespace: 6.0.2(postcss@8.4.38) - postcss-ordered-values: 6.0.2(postcss@8.4.38) - postcss-reduce-initial: 6.1.0(postcss@8.4.38) - postcss-reduce-transforms: 6.0.2(postcss@8.4.38) - postcss-svgo: 6.0.3(postcss@8.4.38) - postcss-unique-selectors: 6.0.4(postcss@8.4.38) - - cssnano-utils@4.0.2(postcss@8.4.38): - dependencies: - postcss: 8.4.38 - - cssnano@6.1.2(postcss@8.4.38): - dependencies: - cssnano-preset-default: 6.1.2(postcss@8.4.38) - lilconfig: 3.1.2 - postcss: 8.4.38 + cssnano-preset-advanced@6.1.2(postcss@8.5.1): + dependencies: + autoprefixer: 10.4.20(postcss@8.5.1) + browserslist: 4.24.4 + cssnano-preset-default: 6.1.2(postcss@8.5.1) + postcss: 8.5.1 + postcss-discard-unused: 6.0.5(postcss@8.5.1) + postcss-merge-idents: 6.0.3(postcss@8.5.1) + postcss-reduce-idents: 6.0.3(postcss@8.5.1) + postcss-zindex: 6.0.2(postcss@8.5.1) + + cssnano-preset-default@6.1.2(postcss@8.5.1): + dependencies: + browserslist: 4.24.4 + css-declaration-sorter: 7.2.0(postcss@8.5.1) + cssnano-utils: 4.0.2(postcss@8.5.1) + postcss: 8.5.1 + postcss-calc: 9.0.1(postcss@8.5.1) + postcss-colormin: 6.1.0(postcss@8.5.1) + postcss-convert-values: 6.1.0(postcss@8.5.1) + postcss-discard-comments: 6.0.2(postcss@8.5.1) + postcss-discard-duplicates: 6.0.3(postcss@8.5.1) + postcss-discard-empty: 6.0.3(postcss@8.5.1) + postcss-discard-overridden: 6.0.2(postcss@8.5.1) + postcss-merge-longhand: 6.0.5(postcss@8.5.1) + postcss-merge-rules: 6.1.1(postcss@8.5.1) + postcss-minify-font-values: 6.1.0(postcss@8.5.1) + postcss-minify-gradients: 6.0.3(postcss@8.5.1) + postcss-minify-params: 6.1.0(postcss@8.5.1) + postcss-minify-selectors: 6.0.4(postcss@8.5.1) + postcss-normalize-charset: 6.0.2(postcss@8.5.1) + postcss-normalize-display-values: 6.0.2(postcss@8.5.1) + postcss-normalize-positions: 6.0.2(postcss@8.5.1) + postcss-normalize-repeat-style: 6.0.2(postcss@8.5.1) + postcss-normalize-string: 6.0.2(postcss@8.5.1) + postcss-normalize-timing-functions: 6.0.2(postcss@8.5.1) + postcss-normalize-unicode: 6.1.0(postcss@8.5.1) + postcss-normalize-url: 6.0.2(postcss@8.5.1) + postcss-normalize-whitespace: 6.0.2(postcss@8.5.1) + postcss-ordered-values: 6.0.2(postcss@8.5.1) + postcss-reduce-initial: 6.1.0(postcss@8.5.1) + postcss-reduce-transforms: 6.0.2(postcss@8.5.1) + postcss-svgo: 6.0.3(postcss@8.5.1) + postcss-unique-selectors: 6.0.4(postcss@8.5.1) + + cssnano-utils@4.0.2(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + cssnano@6.1.2(postcss@8.5.1): + dependencies: + cssnano-preset-default: 6.1.2(postcss@8.5.1) + lilconfig: 3.1.3 + postcss: 8.5.1 csso@5.0.5: dependencies: css-tree: 2.2.1 - cssstyle@4.0.1: + cssstyle@4.2.1: dependencies: - rrweb-cssom: 0.6.0 + '@asamuzakjp/css-color': 2.8.3 + rrweb-cssom: 0.8.0 csstype@3.1.3: {} dargs@8.1.0: {} - data-uri-to-buffer@4.0.1: {} - data-uri-to-buffer@6.0.2: {} data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 + whatwg-url: 14.1.0 - data-view-buffer@1.0.1: + data-view-buffer@1.0.2: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 - data-view-byte-length@1.0.1: + data-view-byte-length@1.0.2: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 - data-view-byte-offset@1.0.0: + data-view-byte-offset@1.0.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-data-view: 1.0.1 + is-data-view: 1.0.2 date-fns@2.30.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.26.0 debounce@1.2.1: {} @@ -10844,9 +11848,9 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.5: + debug@4.4.0: dependencies: - ms: 2.1.2 + ms: 2.1.3 decimal.js@10.4.3: {} @@ -10860,7 +11864,7 @@ snapshots: deep-eql@4.1.4: dependencies: - type-detect: 4.0.8 + type-detect: 4.1.0 deep-extend@0.6.0: {} @@ -10887,9 +11891,9 @@ snapshots: define-data-property@1.1.4: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.2.0 define-lazy-prop@2.0.0: {} @@ -10944,7 +11948,7 @@ snapshots: detect-port@1.6.1: dependencies: address: 1.2.2 - debug: 4.3.5 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -10972,10 +11976,10 @@ snapshots: docusaurus-mdx-checker@3.0.0: dependencies: - '@mdx-js/mdx': 3.0.1 + '@mdx-js/mdx': 3.1.0(acorn@8.14.0) '@slorber/remark-comment': 1.0.0 - acorn: 8.12.0 - chalk: 5.3.0 + acorn: 8.14.0 + chalk: 5.4.1 commander: 11.1.0 globby: 13.2.2 lodash: 4.17.21 @@ -10987,9 +11991,9 @@ snapshots: transitivePeerDependencies: - supports-color - docusaurus-plugin-typedoc@1.0.1(typedoc-plugin-markdown@4.0.3(typedoc@0.25.13(typescript@5.4.5))): + docusaurus-plugin-typedoc@1.2.1(typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.7.3))): dependencies: - typedoc-plugin-markdown: 4.0.3(typedoc@0.25.13(typescript@5.4.5)) + typedoc-plugin-markdown: 4.4.1(typedoc@0.27.6(typescript@5.7.3)) dom-converter@0.2.0: dependencies: @@ -11023,7 +12027,7 @@ snapshots: domelementtype: 2.3.0 domhandler: 4.3.1 - domutils@3.1.0: + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 @@ -11032,7 +12036,7 @@ snapshots: dot-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.8.1 dot-prop@5.3.0: dependencies: @@ -11042,7 +12046,17 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@16.4.5: {} + dot-prop@9.0.0: + dependencies: + type-fest: 4.32.0 + + dotenv@16.4.7: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 duplexer@0.1.2: {} @@ -11050,10 +12064,12 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.4.803: {} + electron-to-chromium@1.5.83: {} emoji-regex@10.3.0: {} + emoji-regex@10.4.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -11062,11 +12078,13 @@ snapshots: emojis-list@3.0.0: {} - emoticon@4.0.1: {} + emoticon@4.1.0: {} encodeurl@1.0.2: {} - enhanced-resolve@5.17.0: + encodeurl@2.0.0: {} + + enhanced-resolve@5.18.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -11086,84 +12104,74 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.23.3: + es-abstract@1.23.9: dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 + call-bind: 1.0.8 + call-bound: 1.0.3 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.2.7 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 globalthis: 1.0.4 - gopd: 1.0.1 + gopd: 1.2.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.2 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.0 + math-intrinsics: 1.1.0 + object-inspect: 1.13.3 object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-array-method-boxes-properly@1.0.0: {} + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.18 - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 + es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-get-iterator@1.1.3: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.3 - is-set: 2.0.3 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 - - es-module-lexer@1.5.3: {} + es-module-lexer@1.6.0: {} - es-object-atoms@1.0.0: + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 - es-set-tostringtag@2.0.3: + es-set-tostringtag@2.1.0: dependencies: - get-intrinsic: 1.2.4 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 has-tostringtag: 1.0.2 hasown: 2.0.2 @@ -11171,11 +12179,25 @@ snapshots: dependencies: hasown: 2.0.2 - es-to-primitive@1.2.1: + es-to-primitive@1.3.0: dependencies: is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.14.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.2 esbuild@0.21.5: optionalDependencies: @@ -11205,6 +12227,8 @@ snapshots: escalade@3.1.2: {} + escalade@3.2.0: {} + escape-goat@4.0.0: {} escape-html@1.0.3: {} @@ -11223,82 +12247,83 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: confusing-browser-globals: 1.0.11 - eslint: 8.57.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - object.assign: 4.1.5 + eslint: 8.57.1 + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) + object.assign: 4.1.7 object.entries: 1.1.8 semver: 6.3.1 - eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: - '@typescript-eslint/eslint-plugin': 7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - eslint: 8.57.0 - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0) + '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.7.3) + eslint: 8.57.1 + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - eslint-plugin-import eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 - is-core-module: 2.14.0 - resolve: 1.22.8 + is-core-module: 2.16.1 + resolve: 1.22.10 transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: - debug: 4.3.5 - enhanced-resolve: 5.17.0 - eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - fast-glob: 3.3.2 - get-tsconfig: 4.7.5 - is-core-module: 2.14.0 + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.0 + enhanced-resolve: 5.18.0 + eslint: 8.57.1 + fast-glob: 3.3.3 + get-tsconfig: 4.8.1 + is-bun-module: 1.3.0 is-glob: 4.0.3 + stable-hash: 0.0.4 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - eslint: 8.57.0 + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.7.3) + eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1): dependencies: + '@rtsao/scc': 1.1.0 array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.57.0 + eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 - is-core-module: 2.14.0 + is-core-module: 2.16.1 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 object.groupby: 1.0.3 - object.values: 1.2.0 + object.values: 1.2.1 semver: 6.3.1 + string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -11316,26 +12341,26 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint@8.57.0: + eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.11.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.1 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 + '@ungap/structured-clone': 1.2.1 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.5 + cross-spawn: 7.0.6 + debug: 4.4.0 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - esquery: 1.5.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 @@ -11343,7 +12368,7 @@ snapshots: glob-parent: 6.0.2 globals: 13.24.0 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -11361,13 +12386,13 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.12.0 - acorn-jsx: 5.3.2(acorn@8.12.0) + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} - esquery@1.5.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -11381,7 +12406,7 @@ snapshots: estree-util-attach-comments@3.0.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-util-build-jsx@3.0.1: dependencies: @@ -11392,21 +12417,25 @@ snapshots: estree-util-is-identifier-name@3.0.0: {} + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.6 + devlop: 1.1.0 + estree-util-to-js@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 - astring: 1.8.6 + astring: 1.9.0 source-map: 0.7.4 - estree-util-value-to-estree@3.1.1: + estree-util-value-to-estree@3.2.1: dependencies: - '@types/estree': 1.0.5 - is-plain-obj: 4.1.0 + '@types/estree': 1.0.6 estree-util-visit@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 estree-walker@2.0.2: {} @@ -11422,7 +12451,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 require-like: 0.1.2 eventemitter3@4.0.7: {} @@ -11431,7 +12460,7 @@ snapshots: execa@5.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -11441,6 +12470,18 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + execa@8.0.0: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + execa@8.0.1: dependencies: cross-spawn: 7.0.3 @@ -11453,49 +12494,49 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 - execa@9.3.0: + execa@9.5.2: dependencies: '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 figures: 6.1.0 get-stream: 9.0.1 - human-signals: 7.0.0 + human-signals: 8.0.0 is-plain-obj: 4.1.0 is-stream: 4.0.1 - npm-run-path: 5.3.0 - pretty-ms: 9.0.0 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 signal-exit: 4.1.0 strip-final-newline: 4.0.0 - yoctocolors: 2.0.2 + yoctocolors: 2.1.1 - express@4.19.2: + express@4.21.2: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.2 + body-parser: 1.20.3 content-disposition: 0.5.4 content-type: 1.0.5 - cookie: 0.6.0 + cookie: 0.7.1 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.2.0 + finalhandler: 1.3.1 fresh: 0.5.2 http-errors: 2.0.0 - merge-descriptors: 1.0.1 + merge-descriptors: 1.0.3 methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 - path-to-regexp: 0.1.7 + path-to-regexp: 0.1.12 proxy-addr: 2.0.7 - qs: 6.11.0 + qs: 6.13.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 + send: 0.19.0 + serve-static: 1.16.2 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -11518,21 +12559,19 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.7 + micromatch: 4.0.8 fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fast-url-parser@1.1.3: - dependencies: - punycode: 1.4.1 + fast-uri@3.0.1: {} fastq@1.17.1: dependencies: @@ -11550,24 +12589,23 @@ snapshots: dependencies: xml-js: 1.6.11 - fetch-blob@3.2.0: + figures@3.2.0: dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 + escape-string-regexp: 1.0.5 figures@6.1.0: dependencies: - is-unicode-supported: 2.0.0 + is-unicode-supported: 2.1.0 file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 - file-loader@6.2.0(webpack@5.92.0): + file-loader@6.2.0(webpack@5.97.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.92.0 + webpack: 5.97.1 filesize@8.0.7: {} @@ -11575,10 +12613,10 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@1.2.0: + finalhandler@1.3.1: dependencies: debug: 2.6.9 - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 @@ -11614,28 +12652,28 @@ snapshots: flat-cache@3.2.0: dependencies: - flatted: 3.3.1 + flatted: 3.3.2 keyv: 4.5.4 rimraf: 3.0.2 flat@5.0.2: {} - flatted@3.3.1: {} + flatted@3.3.2: {} - follow-redirects@1.15.6: {} + follow-redirects@1.15.9: {} for-each@0.3.3: dependencies: is-callable: 1.2.7 - foreground-child@3.2.1: + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.0)(typescript@5.4.5)(webpack@5.92.0): + fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.1)(typescript@5.7.3)(webpack@5.97.1): dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 '@types/json-schema': 7.0.15 chalk: 4.1.2 chokidar: 3.6.0 @@ -11646,16 +12684,16 @@ snapshots: memfs: 3.5.3 minimatch: 3.1.2 schema-utils: 2.7.0 - semver: 7.6.2 + semver: 7.6.3 tapable: 1.1.3 - typescript: 5.4.5 - webpack: 5.92.0 + typescript: 5.7.3 + webpack: 5.97.1 optionalDependencies: - eslint: 8.57.0 + eslint: 8.57.1 form-data-encoder@2.1.4: {} - form-data@4.0.0: + form-data@4.0.1: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -11663,17 +12701,13 @@ snapshots: format@0.2.2: {} - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - forwarded@0.2.0: {} fraction.js@4.3.7: {} fresh@0.5.2: {} - fs-extra@11.2.0: + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 @@ -11695,12 +12729,14 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.6: + function.prototype.name@1.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-abstract: 1.23.3 functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 functions-have-names@1.2.3: {} @@ -11710,18 +12746,30 @@ snapshots: get-east-asian-width@1.2.0: {} + get-east-asian-width@1.3.0: {} + get-func-name@2.0.2: {} - get-intrinsic@1.2.4: + get-intrinsic@1.2.7: dependencies: + call-bind-apply-helpers: 1.0.1 + es-define-property: 1.0.1 es-errors: 1.3.0 + es-object-atoms: 1.1.1 function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 + math-intrinsics: 1.1.0 get-own-enumerable-property-symbols@3.0.2: {} + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-stream@6.0.1: {} get-stream@8.0.1: {} @@ -11731,22 +12779,21 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 - get-symbol-description@1.0.2: + get-symbol-description@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.7 - get-tsconfig@4.7.5: + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 - get-uri@6.0.3: + get-uri@6.0.4: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.3.5 - fs-extra: 11.2.0 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -11759,7 +12806,15 @@ snapshots: git-semver-tags@7.0.1: dependencies: meow: 12.1.1 - semver: 7.6.2 + semver: 7.6.3 + + git-semver-tags@8.0.0: + dependencies: + '@conventional-changelog/git-client': 1.0.1 + meow: 13.2.0 + transitivePeerDependencies: + - conventional-commits-filter + - conventional-commits-parser git-up@7.0.0: dependencies: @@ -11782,18 +12837,10 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.4.1: - dependencies: - foreground-child: 3.2.1 - jackspeak: 3.4.0 - minimatch: 9.0.4 - minipass: 7.1.2 - path-scurry: 1.11.1 - - glob@10.4.2: + glob@10.4.5: dependencies: - foreground-child: 3.2.1 - jackspeak: 3.4.0 + foreground-child: 3.3.0 + jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 package-json-from-dist: 1.0.0 @@ -11835,37 +12882,35 @@ snapshots: globalthis@1.0.4: dependencies: define-properties: 1.2.1 - gopd: 1.0.1 + gopd: 1.2.0 globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 + fast-glob: 3.3.3 + ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 globby@13.2.2: dependencies: dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 + fast-glob: 3.3.3 + ignore: 5.3.2 merge2: 1.4.1 slash: 4.0.0 - globby@14.0.1: + globby@14.0.2: dependencies: '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.2 - ignore: 5.3.1 + fast-glob: 3.3.3 + ignore: 5.3.2 path-type: 5.0.0 slash: 5.1.0 unicorn-magic: 0.1.0 - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 + gopd@1.2.0: {} got@12.6.1: dependencies: @@ -11881,20 +12926,6 @@ snapshots: p-cancelable: 3.0.0 responselike: 3.0.0 - got@13.0.0: - dependencies: - '@sindresorhus/is': 5.6.0 - '@szmarczak/http-timer': 5.0.1 - cacheable-lookup: 7.0.0 - cacheable-request: 10.2.14 - decompress-response: 6.0.0 - form-data-encoder: 2.1.4 - get-stream: 6.0.1 - http2-wrapper: 2.2.1 - lowercase-keys: 3.0.0 - p-cancelable: 3.0.0 - responselike: 3.0.0 - graceful-fs@4.2.10: {} graceful-fs@4.2.11: {} @@ -11921,9 +12952,9 @@ snapshots: source-map: 0.6.1 wordwrap: 1.0.0 optionalDependencies: - uglify-js: 3.18.0 + uglify-js: 3.19.3 - has-bigints@1.0.2: {} + has-bigints@1.1.0: {} has-flag@3.0.0: {} @@ -11931,15 +12962,17 @@ snapshots: has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 - has-proto@1.0.3: {} + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 - has-symbols@1.0.3: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 has-yarn@3.0.0: {} @@ -11947,40 +12980,40 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-from-parse5@8.0.1: + hast-util-from-parse5@8.0.2: dependencies: '@types/hast': 3.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 devlop: 1.1.0 - hastscript: 8.0.0 + hastscript: 9.0.0 property-information: 6.5.0 - vfile: 6.0.1 - vfile-location: 5.0.2 + vfile: 6.0.3 + vfile-location: 5.0.3 web-namespaces: 2.0.1 hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 - hast-util-raw@9.0.4: + hast-util-raw@9.1.0: dependencies: '@types/hast': 3.0.4 - '@types/unist': 3.0.2 - '@ungap/structured-clone': 1.2.0 - hast-util-from-parse5: 8.0.1 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.2.1 + hast-util-from-parse5: 8.0.2 hast-util-to-parse5: 8.0.0 html-void-elements: 3.0.0 mdast-util-to-hast: 13.2.0 - parse5: 7.1.2 + parse5: 7.2.1 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 - vfile: 6.0.1 + vfile: 6.0.3 web-namespaces: 2.0.1 zwitch: 2.0.4 - hast-util-to-estree@3.1.0: + hast-util-to-estree@3.1.1: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 @@ -11988,32 +13021,32 @@ snapshots: estree-util-attach-comments: 3.0.0 estree-util-is-identifier-name: 3.0.0 hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 property-information: 6.5.0 space-separated-tokens: 2.0.2 - style-to-object: 0.4.4 + style-to-object: 1.0.8 unist-util-position: 5.0.0 zwitch: 2.0.4 transitivePeerDependencies: - supports-color - hast-util-to-jsx-runtime@2.3.0: + hast-util-to-jsx-runtime@2.3.2: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/hast': 3.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 hast-util-whitespace: 3.0.0 - mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 property-information: 6.5.0 space-separated-tokens: 2.0.2 - style-to-object: 1.0.6 + style-to-object: 1.0.8 unist-util-position: 5.0.0 vfile-message: 4.0.2 transitivePeerDependencies: @@ -12033,7 +13066,7 @@ snapshots: dependencies: '@types/hast': 3.0.4 - hastscript@8.0.0: + hastscript@9.0.0: dependencies: '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 @@ -12047,7 +13080,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.26.0 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -12060,7 +13093,7 @@ snapshots: hosted-git-info@7.0.2: dependencies: - lru-cache: 10.3.0 + lru-cache: 10.4.3 hpack.js@2.1.6: dependencies: @@ -12085,7 +13118,7 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.31.1 + terser: 5.37.0 html-minifier-terser@7.2.0: dependencies: @@ -12095,13 +13128,13 @@ snapshots: entities: 4.5.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.31.1 + terser: 5.37.0 html-tags@3.3.1: {} html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.0(webpack@5.92.0): + html-webpack-plugin@5.6.3(webpack@5.97.1): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -12109,7 +13142,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.2.1 optionalDependencies: - webpack: 5.92.0 + webpack: 5.97.1 htmlparser2@6.1.0: dependencies: @@ -12122,7 +13155,7 @@ snapshots: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 entities: 4.5.0 http-cache-semantics@4.1.1: {} @@ -12144,22 +13177,22 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 - http-parser-js@0.5.8: {} + http-parser-js@0.5.9: {} http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.1 - debug: 4.3.5 + agent-base: 7.1.3 + debug: 4.4.0 transitivePeerDependencies: - supports-color - http-proxy-middleware@2.0.6(@types/express@4.17.21): + http-proxy-middleware@2.0.7(@types/express@4.17.21): dependencies: - '@types/http-proxy': 1.17.14 + '@types/http-proxy': 1.17.15 http-proxy: 1.18.1 is-glob: 4.0.3 is-plain-obj: 3.0.0 - micromatch: 4.0.7 + micromatch: 4.0.8 optionalDependencies: '@types/express': 4.17.21 transitivePeerDependencies: @@ -12168,7 +13201,7 @@ snapshots: http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.6 + follow-redirects: 1.15.9 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -12178,10 +13211,10 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - https-proxy-agent@7.0.5: + https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.1 - debug: 4.3.5 + agent-base: 7.1.3 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -12189,9 +13222,9 @@ snapshots: human-signals@5.0.0: {} - human-signals@7.0.0: {} + human-signals@8.0.0: {} - husky@9.0.11: {} + husky@9.1.7: {} iconv-lite@0.4.24: dependencies: @@ -12201,15 +13234,15 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.4.38): + icss-utils@5.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 ieee754@1.2.1: {} - ignore@5.3.1: {} + ignore@5.3.2: {} - image-size@1.1.1: + image-size@1.2.0: dependencies: queue: 6.0.2 @@ -12228,7 +13261,7 @@ snapshots: indent-string@4.0.0: {} - infima@0.2.0-alpha.43: {} + infima@0.2.0-alpha.45: {} inflight@1.0.6: dependencies: @@ -12247,20 +13280,14 @@ snapshots: ini@4.1.3: {} - inline-style-parser@0.1.1: {} - - inline-style-parser@0.2.3: {} + inline-style-parser@0.2.4: {} - inquirer@9.2.23: + inquirer@9.3.2: dependencies: - '@inquirer/figures': 1.0.3 - '@ljharb/through': 2.3.13 + '@inquirer/figures': 1.0.9 ansi-escapes: 4.3.2 - chalk: 5.3.0 - cli-cursor: 3.1.0 cli-width: 4.1.0 external-editor: 3.1.0 - lodash: 4.17.21 mute-stream: 1.0.0 ora: 5.4.1 run-async: 3.0.0 @@ -12268,12 +13295,13 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 - internal-slot@1.0.7: + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 - side-channel: 1.0.6 + side-channel: 1.1.0 interpret@1.4.0: {} @@ -12297,34 +13325,37 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - is-arguments@1.1.1: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-array-buffer@3.0.4: + is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.7 is-arrayish@0.2.1: {} - is-bigint@1.0.4: + is-async-function@2.1.0: dependencies: - has-bigints: 1.0.2 + call-bound: 1.0.3 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.1.2: + is-boolean-object@1.2.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 has-tostringtag: 1.0.2 - is-builtin-module@3.2.1: + is-bun-module@1.3.0: dependencies: - builtin-modules: 3.3.0 + semver: 7.6.3 is-callable@1.2.7: {} @@ -12332,16 +13363,19 @@ snapshots: dependencies: ci-info: 3.9.0 - is-core-module@2.14.0: + is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-data-view@1.0.1: + is-data-view@1.0.2: dependencies: - is-typed-array: 1.1.13 + call-bound: 1.0.3 + get-intrinsic: 1.2.7 + is-typed-array: 1.1.15 - is-date-object@1.0.5: + is-date-object@1.1.0: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 is-decimal@2.0.1: {} @@ -12354,15 +13388,26 @@ snapshots: is-extglob@2.1.1: {} + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.3 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.3 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-hexadecimal@2.0.1: {} - is-in-ci@0.1.0: {} + is-in-ci@1.0.0: {} is-inside-container@1.0.0: dependencies: @@ -12373,6 +13418,11 @@ snapshots: global-dirs: 3.0.1 is-path-inside: 3.0.3 + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + is-interactive@1.0.0: {} is-interactive@2.0.0: {} @@ -12381,12 +13431,11 @@ snapshots: is-module@1.0.0: {} - is-negative-zero@2.0.3: {} - is-npm@6.0.0: {} - is-number-object@1.0.7: + is-number-object@1.1.1: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 is-number@7.0.0: {} @@ -12399,6 +13448,8 @@ snapshots: is-path-inside@3.0.3: {} + is-path-inside@4.0.0: {} + is-plain-obj@3.0.0: {} is-plain-obj@4.1.0: {} @@ -12411,16 +13462,18 @@ snapshots: is-reference@1.2.1: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 - is-reference@3.0.2: + is-reference@3.0.3: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 - is-regex@1.1.4: + is-regex@1.2.1: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 + gopd: 1.2.0 has-tostringtag: 1.0.2 + hasown: 2.0.2 is-regexp@1.0.0: {} @@ -12428,9 +13481,9 @@ snapshots: is-set@2.0.3: {} - is-shared-array-buffer@1.0.3: + is-shared-array-buffer@1.0.4: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 is-ssh@1.4.0: dependencies: @@ -12442,21 +13495,24 @@ snapshots: is-stream@4.0.1: {} - is-string@1.0.7: + is-string@1.1.1: dependencies: + call-bound: 1.0.3 has-tostringtag: 1.0.2 - is-symbol@1.0.4: + is-symbol@1.1.1: dependencies: - has-symbols: 1.0.3 + call-bound: 1.0.3 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 is-text-path@2.0.0: dependencies: text-extensions: 2.4.0 - is-typed-array@1.1.13: + is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.15 + which-typed-array: 1.1.18 is-typedarray@1.0.0: {} @@ -12466,9 +13522,18 @@ snapshots: is-unicode-supported@2.0.0: {} - is-weakref@1.0.2: + is-unicode-supported@2.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.0: + dependencies: + call-bound: 1.0.3 + + is-weakset@2.0.4: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 + get-intrinsic: 1.2.7 is-wsl@2.2.0: dependencies: @@ -12500,13 +13565,13 @@ snapshots: istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@6.0.2: + istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.24.7 - '@babel/parser': 7.24.7 + '@babel/core': 7.26.0 + '@babel/parser': 7.26.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -12516,10 +13581,10 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@5.0.4: + istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.5 + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -12529,14 +13594,7 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - iterate-iterator@1.0.2: {} - - iterate-value@1.0.2: - dependencies: - es-get-iterator: 1.1.3 - iterate-iterator: 1.0.2 - - jackspeak@3.4.0: + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: @@ -12545,7 +13603,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.9 + '@types/node': 22.10.6 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -12553,20 +13611,22 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 20.14.9 + '@types/node': 22.10.6 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jiti@1.21.6: {} + jiti@1.21.7: {} + + jiti@2.4.2: {} - joi@17.13.1: + joi@17.13.3: dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 @@ -12576,7 +13636,7 @@ snapshots: js-tokens@4.0.0: {} - js-tokens@9.0.0: {} + js-tokens@9.0.1: {} js-yaml@3.14.1: dependencies: @@ -12589,18 +13649,18 @@ snapshots: jsbn@1.1.0: {} - jsdom@24.1.0: + jsdom@24.1.3: dependencies: - cssstyle: 4.0.1 + cssstyle: 4.2.1 data-urls: 5.0.0 decimal.js: 10.4.3 - form-data: 4.0.0 + form-data: 4.0.1 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.10 - parse5: 7.1.2 + nwsapi: 2.2.16 + parse5: 7.2.1 rrweb-cssom: 0.7.1 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -12609,17 +13669,17 @@ snapshots: webidl-conversions: 7.0.0 whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 - ws: 8.17.1 + whatwg-url: 14.1.0 + ws: 8.18.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - jsesc@0.5.0: {} + jsesc@3.0.2: {} - jsesc@2.5.2: {} + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -12641,8 +13701,6 @@ snapshots: json5@2.2.3: {} - jsonc-parser@3.2.1: {} - jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -12651,7 +13709,7 @@ snapshots: jsonparse@1.3.1: {} - katex@0.16.10: + katex@0.16.20: dependencies: commander: 8.3.0 @@ -12663,14 +13721,20 @@ snapshots: kleur@3.0.3: {} + ky@1.7.4: {} + latest-version@7.0.0: dependencies: package-json: 8.1.1 - launch-editor@2.7.0: + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + + launch-editor@2.9.1: dependencies: - picocolors: 1.0.1 - shell-quote: 1.8.1 + picocolors: 1.1.1 + shell-quote: 1.8.2 leven@3.1.0: {} @@ -12679,12 +13743,16 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lilconfig@3.1.2: {} + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} lines-and-columns@2.0.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + loader-runner@4.3.0: {} loader-utils@2.0.4: @@ -12695,10 +13763,10 @@ snapshots: loader-utils@3.3.1: {} - local-pkg@0.5.0: + local-pkg@0.5.1: dependencies: - mlly: 1.7.1 - pkg-types: 1.1.1 + mlly: 1.7.4 + pkg-types: 1.3.1 locate-path@3.0.0: dependencies: @@ -12769,13 +13837,13 @@ snapshots: lower-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 lowercase-keys@3.0.0: {} lru-cache@10.2.2: {} - lru-cache@10.3.0: {} + lru-cache@10.4.3: {} lru-cache@5.1.1: dependencies: @@ -12785,61 +13853,74 @@ snapshots: lunr@2.3.9: {} - macos-release@3.2.0: {} + macos-release@3.3.0: {} - magic-string@0.30.10: + magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 - magicast@0.3.4: + magicast@0.3.5: dependencies: - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - source-map-js: 1.2.0 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + source-map-js: 1.2.1 make-dir@4.0.0: dependencies: - semver: 7.6.2 + semver: 7.6.3 markdown-extensions@2.0.0: {} - markdown-table@3.0.3: {} + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + markdown-table@2.0.0: + dependencies: + repeat-string: 1.6.1 + + markdown-table@3.0.4: {} - marked@4.3.0: {} + math-intrinsics@1.1.0: {} mdast-util-directive@3.0.0: dependencies: '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 - parse-entities: 4.0.1 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 stringify-entities: 4.0.4 unist-util-visit-parents: 6.0.1 transitivePeerDependencies: - supports-color - mdast-util-find-and-replace@3.0.1: + mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - mdast-util-from-markdown@2.0.1: + mdast-util-from-markdown@2.0.2: dependencies: '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 decode-named-character-reference: 1.0.2 devlop: 1.1.0 mdast-util-to-string: 4.0.0 - micromark: 4.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-decode-string: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark: 4.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 unist-util-stringify-position: 4.0.0 transitivePeerDependencies: - supports-color @@ -12849,35 +13930,35 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 escape-string-regexp: 5.0.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 micromark-extension-frontmatter: 2.0.0 transitivePeerDependencies: - supports-color - mdast-util-gfm-autolink-literal@2.0.0: + mdast-util-gfm-autolink-literal@2.0.1: dependencies: '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 mdast-util-gfm-footnote@2.0.0: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color mdast-util-gfm-strikethrough@2.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -12885,9 +13966,9 @@ snapshots: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - markdown-table: 3.0.3 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -12895,20 +13976,20 @@ snapshots: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color mdast-util-gfm@3.0.0: dependencies: - mdast-util-from-markdown: 2.0.1 - mdast-util-gfm-autolink-literal: 2.0.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 mdast-util-gfm-footnote: 2.0.0 mdast-util-gfm-strikethrough: 2.0.0 mdast-util-gfm-table: 2.0.0 mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -12918,36 +13999,35 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 longest-streak: 3.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 unist-util-remove-position: 5.0.0 transitivePeerDependencies: - supports-color - mdast-util-mdx-expression@2.0.0: + mdast-util-mdx-expression@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-mdx-jsx@3.1.2: + mdast-util-mdx-jsx@3.2.0: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 - parse-entities: 4.0.1 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 stringify-entities: 4.0.4 - unist-util-remove-position: 5.0.0 unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 transitivePeerDependencies: @@ -12955,11 +14035,11 @@ snapshots: mdast-util-mdx@3.0.0: dependencies: - mdast-util-from-markdown: 2.0.1 - mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.1.2 + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -12969,8 +14049,8 @@ snapshots: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.1 - mdast-util-to-markdown: 2.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -12983,22 +14063,23 @@ snapshots: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.2.0 + '@ungap/structured-clone': 1.2.1 devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.0 + micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 - vfile: 6.0.1 + vfile: 6.0.3 - mdast-util-to-markdown@2.1.0: + mdast-util-to-markdown@2.1.2: dependencies: '@types/mdast': 4.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 - micromark-util-decode-string: 2.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 unist-util-visit: 5.0.0 zwitch: 2.0.4 @@ -13010,6 +14091,8 @@ snapshots: mdn-data@2.0.30: {} + mdurl@2.0.0: {} + media-typer@0.3.0: {} memfs@3.5.3: @@ -13018,7 +14101,9 @@ snapshots: meow@12.1.1: {} - merge-descriptors@1.0.1: {} + meow@13.2.0: {} + + merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -13026,182 +14111,184 @@ snapshots: methods@1.1.2: {} - micromark-core-commonmark@2.0.1: + micromark-core-commonmark@2.0.2: dependencies: decode-named-character-reference: 1.0.2 devlop: 1.1.0 - micromark-factory-destination: 2.0.0 - micromark-factory-label: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-factory-title: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-html-tag-name: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-extension-directive@3.0.0: + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-extension-directive@3.0.2: dependencies: devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - parse-entities: 4.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + parse-entities: 4.0.2 micromark-extension-frontmatter@2.0.0: dependencies: fault: 2.0.1 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-extension-gfm-autolink-literal@2.0.0: + micromark-extension-gfm-autolink-literal@2.1.0: dependencies: - micromark-util-character: 2.1.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-extension-gfm-footnote@2.0.0: + micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 - micromark-core-commonmark: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-core-commonmark: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-extension-gfm-strikethrough@2.0.0: + micromark-extension-gfm-strikethrough@2.1.0: dependencies: devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-extension-gfm-table@2.0.0: + micromark-extension-gfm-table@2.1.0: dependencies: devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 micromark-extension-gfm-tagfilter@2.0.0: dependencies: - micromark-util-types: 2.0.0 + micromark-util-types: 2.0.1 - micromark-extension-gfm-task-list-item@2.0.1: + micromark-extension-gfm-task-list-item@2.1.0: dependencies: devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 micromark-extension-gfm@3.0.0: dependencies: - micromark-extension-gfm-autolink-literal: 2.0.0 - micromark-extension-gfm-footnote: 2.0.0 - micromark-extension-gfm-strikethrough: 2.0.0 - micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.0 micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.0.1 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.1 - micromark-extension-math@3.0.0: + micromark-extension-math@3.1.0: dependencies: '@types/katex': 0.16.7 devlop: 1.1.0 - katex: 0.16.10 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + katex: 0.16.20 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 micromark-extension-mdx-expression@3.0.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 - micromark-factory-mdx-expression: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 + micromark-factory-mdx-expression: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 micromark-util-events-to-acorn: 2.0.2 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-extension-mdx-jsx@3.0.0: + micromark-extension-mdx-jsx@3.0.1: dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 - micromark-factory-mdx-expression: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-mdx-expression: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 vfile-message: 4.0.2 micromark-extension-mdx-md@2.0.0: dependencies: - micromark-util-types: 2.0.0 + micromark-util-types: 2.0.1 micromark-extension-mdxjs-esm@3.0.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 - micromark-core-commonmark: 2.0.1 - micromark-util-character: 2.1.0 + micromark-core-commonmark: 2.0.2 + micromark-util-character: 2.1.1 micromark-util-events-to-acorn: 2.0.2 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 unist-util-position-from-estree: 2.0.0 vfile-message: 4.0.2 micromark-extension-mdxjs@3.0.0: dependencies: - acorn: 8.12.0 - acorn-jsx: 5.3.2(acorn@8.12.0) + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) micromark-extension-mdx-expression: 3.0.0 - micromark-extension-mdx-jsx: 3.0.0 + micromark-extension-mdx-jsx: 3.0.1 micromark-extension-mdx-md: 2.0.0 micromark-extension-mdxjs-esm: 3.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.1 - micromark-factory-destination@2.0.0: + micromark-factory-destination@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-factory-label@2.0.0: + micromark-factory-label@2.0.1: dependencies: devlop: 1.1.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-factory-mdx-expression@2.0.1: + micromark-factory-mdx-expression@2.0.2: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 - micromark-util-character: 2.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 micromark-util-events-to-acorn: 2.0.2 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 unist-util-position-from-estree: 2.0.0 vfile-message: 4.0.2 @@ -13210,128 +14297,128 @@ snapshots: micromark-util-character: 1.2.0 micromark-util-types: 1.1.0 - micromark-factory-space@2.0.0: + micromark-factory-space@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.1 - micromark-factory-title@2.0.0: + micromark-factory-title@2.0.1: dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-factory-whitespace@2.0.0: + micromark-factory-whitespace@2.0.1: dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 micromark-util-character@1.2.0: dependencies: micromark-util-symbol: 1.1.0 micromark-util-types: 1.1.0 - micromark-util-character@2.1.0: + micromark-util-character@2.1.1: dependencies: - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-util-chunked@2.0.0: + micromark-util-chunked@2.0.1: dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 - micromark-util-classify-character@2.0.0: + micromark-util-classify-character@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 - micromark-util-combine-extensions@2.0.0: + micromark-util-combine-extensions@2.0.1: dependencies: - micromark-util-chunked: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.1 - micromark-util-decode-numeric-character-reference@2.0.1: + micromark-util-decode-numeric-character-reference@2.0.2: dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 - micromark-util-decode-string@2.0.0: + micromark-util-decode-string@2.0.1: dependencies: decode-named-character-reference: 1.0.2 - micromark-util-character: 2.1.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 - micromark-util-encode@2.0.0: {} + micromark-util-encode@2.0.1: {} micromark-util-events-to-acorn@2.0.2: dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.5 - '@types/unist': 3.0.2 + '@types/estree': 1.0.6 + '@types/unist': 3.0.3 devlop: 1.1.0 estree-util-visit: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 vfile-message: 4.0.2 - micromark-util-html-tag-name@2.0.0: {} + micromark-util-html-tag-name@2.0.1: {} - micromark-util-normalize-identifier@2.0.0: + micromark-util-normalize-identifier@2.0.1: dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 - micromark-util-resolve-all@2.0.0: + micromark-util-resolve-all@2.0.1: dependencies: - micromark-util-types: 2.0.0 + micromark-util-types: 2.0.1 - micromark-util-sanitize-uri@2.0.0: + micromark-util-sanitize-uri@2.0.1: dependencies: - micromark-util-character: 2.1.0 - micromark-util-encode: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@2.0.1: + micromark-util-subtokenize@2.0.3: dependencies: devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 micromark-util-symbol@1.1.0: {} - micromark-util-symbol@2.0.0: {} + micromark-util-symbol@2.0.1: {} micromark-util-types@1.1.0: {} - micromark-util-types@2.0.0: {} + micromark-util-types@2.0.1: {} - micromark@4.0.0: + micromark@4.0.1: dependencies: '@types/debug': 4.1.12 - debug: 4.3.5 + debug: 4.4.0 decode-named-character-reference: 1.0.2 devlop: 1.1.0 - micromark-core-commonmark: 2.0.1 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-encode: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + micromark-core-commonmark: 2.0.2 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 transitivePeerDependencies: - supports-color - micromatch@4.0.7: + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 @@ -13340,6 +14427,8 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.53.0: {} + mime-types@2.1.18: dependencies: mime-db: 1.33.0 @@ -13354,15 +14443,17 @@ snapshots: mimic-fn@4.0.0: {} + mimic-function@5.0.1: {} + mimic-response@3.1.0: {} mimic-response@4.0.0: {} - mini-css-extract-plugin@2.9.0(webpack@5.92.0): + mini-css-extract-plugin@2.9.2(webpack@5.97.1): dependencies: - schema-utils: 4.2.0 + schema-utils: 4.3.0 tapable: 2.2.1 - webpack: 5.92.0 + webpack: 5.97.1 minimalistic-assert@1.0.1: {} @@ -13370,10 +14461,6 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@9.0.4: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -13382,19 +14469,17 @@ snapshots: minipass@7.1.2: {} - mlly@1.7.1: + mlly@1.7.4: dependencies: - acorn: 8.12.0 - pathe: 1.1.2 - pkg-types: 1.1.1 - ufo: 1.5.3 + acorn: 8.14.0 + pathe: 2.0.1 + pkg-types: 1.3.1 + ufo: 1.5.4 mrmime@2.0.0: {} ms@2.0.0: {} - ms@2.1.2: {} - ms@2.1.3: {} multicast-dns@7.2.5: @@ -13412,12 +14497,14 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.7: {} + nanoid@3.3.8: {} natural-compare@1.4.0: {} negotiator@0.6.3: {} + negotiator@0.6.4: {} + neo-async@2.6.2: {} netmask@2.0.2: {} @@ -13429,31 +14516,23 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.6.3 + tslib: 2.8.1 - node-domexception@1.0.0: {} - - node-emoji@2.1.3: + node-emoji@2.2.0: dependencies: '@sindresorhus/is': 4.6.0 char-regex: 1.0.2 emojilib: 2.4.0 skin-tone: 2.0.0 - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - node-forge@1.3.1: {} - node-releases@2.0.14: {} + node-releases@2.0.19: {} normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.6.2 + semver: 7.6.3 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -13470,53 +14549,65 @@ snapshots: dependencies: path-key: 4.0.0 + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + nprogress@0.2.0: {} nth-check@2.1.1: dependencies: boolbase: 1.0.0 - nwsapi@2.2.10: {} + null-loader@4.0.1(webpack@5.97.1): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.97.1 - object-assign@4.1.1: {} + nwsapi@2.2.16: {} - object-inspect@1.13.1: {} + object-assign@4.1.1: {} - object-inspect@1.13.2: {} + object-inspect@1.13.3: {} object-keys@1.1.1: {} - object.assign@4.1.5: + object.assign@4.1.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - has-symbols: 1.0.3 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 object-keys: 1.1.1 object.entries@1.1.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.23.9 - object.values@1.2.0: + object.values@1.2.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 obuf@1.1.2: {} @@ -13538,6 +14629,10 @@ snapshots: dependencies: mimic-fn: 4.0.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + open@10.1.0: dependencies: default-browser: 5.2.1 @@ -13586,13 +14681,31 @@ snapshots: string-width: 7.1.0 strip-ansi: 7.1.0 + ora@8.1.1: + dependencies: + chalk: 5.4.1 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + os-name@5.1.0: dependencies: - macos-release: 3.2.0 + macos-release: 3.3.0 windows-release: 5.1.1 os-tmpdir@1.0.2: {} + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.2.7 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + p-cancelable@3.0.0: {} p-limit@2.3.0: @@ -13609,7 +14722,7 @@ snapshots: p-limit@5.0.0: dependencies: - yocto-queue: 1.0.0 + yocto-queue: 1.1.1 p-locate@3.0.0: dependencies: @@ -13634,16 +14747,16 @@ snapshots: p-try@2.2.0: {} - pac-proxy-agent@7.0.2: + pac-proxy-agent@7.1.0: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.1 - debug: 4.3.5 - get-uri: 6.0.3 + agent-base: 7.1.3 + debug: 4.4.0 + get-uri: 6.0.4 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.4 + socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -13654,26 +14767,32 @@ snapshots: package-json-from-dist@1.0.0: {} + package-json@10.0.1: + dependencies: + ky: 1.7.4 + registry-auth-token: 5.0.3 + registry-url: 6.0.1 + semver: 7.6.3 + package-json@8.1.1: dependencies: got: 12.6.1 - registry-auth-token: 5.0.2 + registry-auth-token: 5.0.3 registry-url: 6.0.1 - semver: 7.6.2 + semver: 7.6.3 param-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.8.1 parent-module@1.0.1: dependencies: callsites: 3.1.0 - parse-entities@4.0.1: + parse-entities@4.0.2: dependencies: - '@types/unist': 2.0.10 - character-entities: 2.0.2 + '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 decode-named-character-reference: 1.0.2 @@ -13690,7 +14809,7 @@ snapshots: parse-json@7.1.1: dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 error-ex: 1.3.2 json-parse-even-better-errors: 3.0.2 lines-and-columns: 2.0.4 @@ -13712,16 +14831,16 @@ snapshots: dependencies: parse5: 6.0.1 - parse5-htmlparser2-tree-adapter@7.0.0: + parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 - parse5: 7.1.2 + parse5: 7.2.1 parse5@5.1.1: {} parse5@6.0.1: {} - parse5@7.1.2: + parse5@7.2.1: dependencies: entities: 4.5.0 @@ -13730,7 +14849,7 @@ snapshots: pascal-case@3.1.2: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.8.1 path-exists@3.0.0: {} @@ -13753,13 +14872,13 @@ snapshots: lru-cache: 10.2.2 minipass: 7.1.2 - path-to-regexp@0.1.7: {} + path-to-regexp@0.1.12: {} - path-to-regexp@1.8.0: + path-to-regexp@1.9.0: dependencies: isarray: 0.0.1 - path-to-regexp@2.2.1: {} + path-to-regexp@3.3.0: {} path-type@4.0.0: {} @@ -13767,27 +14886,31 @@ snapshots: pathe@1.1.2: {} + pathe@2.0.1: {} + pathval@1.1.1: {} periscopic@3.1.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-walker: 3.0.3 - is-reference: 3.0.2 + is-reference: 3.0.3 - picocolors@1.0.1: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.2: {} + pkg-dir@7.0.0: dependencies: find-up: 6.3.0 - pkg-types@1.1.1: + pkg-types@1.3.1: dependencies: - confbox: 0.1.7 - mlly: 1.7.1 - pathe: 1.1.2 + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.1 pkg-up@3.1.0: dependencies: @@ -13801,225 +14924,442 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-calc@9.0.1(postcss@8.4.38): + postcss-attribute-case-insensitive@7.0.1(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + postcss-calc@9.0.1(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-clamp@4.1.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-color-functional-notation@7.0.7(postcss@8.5.1): + dependencies: + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + + postcss-color-hex-alpha@10.0.0(postcss@8.5.1): + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-color-rebeccapurple@10.0.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-colormin@6.1.0(postcss@8.4.38): + postcss-colormin@6.1.0(postcss@8.5.1): dependencies: - browserslist: 4.23.1 + browserslist: 4.24.4 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.38 + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-convert-values@6.1.0(postcss@8.5.1): + dependencies: + browserslist: 4.24.4 + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-custom-media@11.0.5(postcss@8.5.1): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + postcss: 8.5.1 + + postcss-custom-properties@14.0.4(postcss@8.5.1): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-convert-values@6.1.0(postcss@8.4.38): + postcss-custom-selectors@8.0.4(postcss@8.5.1): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.4(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + postcss-dir-pseudo-class@9.0.1(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + postcss-discard-comments@6.0.2(postcss@8.5.1): dependencies: - browserslist: 4.23.1 - postcss: 8.4.38 + postcss: 8.5.1 + + postcss-discard-duplicates@6.0.3(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-discard-empty@6.0.3(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-discard-overridden@6.0.2(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-discard-unused@6.0.5(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 + + postcss-double-position-gradients@6.0.0(postcss@8.5.1): + dependencies: + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-discard-comments@6.0.2(postcss@8.4.38): + postcss-focus-visible@10.0.1(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + postcss-focus-within@9.0.1(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 - postcss-discard-duplicates@6.0.3(postcss@8.4.38): + postcss-font-variant@5.0.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 - postcss-discard-empty@6.0.3(postcss@8.4.38): + postcss-gap-properties@6.0.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 - postcss-discard-overridden@6.0.2(postcss@8.4.38): + postcss-image-set-function@7.0.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 + postcss-value-parser: 4.2.0 - postcss-discard-unused@6.0.5(postcss@8.4.38): + postcss-lab-function@7.0.7(postcss@8.5.1): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/utilities': 2.0.0(postcss@8.5.1) + postcss: 8.5.1 - postcss-loader@7.3.4(postcss@8.4.38)(typescript@5.4.5)(webpack@5.92.0): + postcss-loader@7.3.4(postcss@8.5.1)(typescript@5.7.3)(webpack@5.97.1): dependencies: - cosmiconfig: 8.3.6(typescript@5.4.5) - jiti: 1.21.6 - postcss: 8.4.38 - semver: 7.6.2 - webpack: 5.92.0 + cosmiconfig: 8.3.6(typescript@5.7.3) + jiti: 1.21.7 + postcss: 8.5.1 + semver: 7.6.3 + webpack: 5.97.1 transitivePeerDependencies: - typescript - postcss-merge-idents@6.0.3(postcss@8.4.38): + postcss-logical@8.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-merge-idents@6.0.3(postcss@8.5.1): dependencies: - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 + cssnano-utils: 4.0.2(postcss@8.5.1) + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-merge-longhand@6.0.5(postcss@8.4.38): + postcss-merge-longhand@6.0.5(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - stylehacks: 6.1.1(postcss@8.4.38) + stylehacks: 6.1.1(postcss@8.5.1) - postcss-merge-rules@6.1.1(postcss@8.4.38): + postcss-merge-rules@6.1.1(postcss@8.5.1): dependencies: - browserslist: 4.23.1 + browserslist: 4.24.4 caniuse-api: 3.0.0 - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + cssnano-utils: 4.0.2(postcss@8.5.1) + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 - postcss-minify-font-values@6.1.0(postcss@8.4.38): + postcss-minify-font-values@6.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-minify-gradients@6.0.3(postcss@8.4.38): + postcss-minify-gradients@6.0.3(postcss@8.5.1): dependencies: colord: 2.9.3 - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 + cssnano-utils: 4.0.2(postcss@8.5.1) + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-minify-params@6.1.0(postcss@8.4.38): + postcss-minify-params@6.1.0(postcss@8.5.1): dependencies: - browserslist: 4.23.1 - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 + browserslist: 4.24.4 + cssnano-utils: 4.0.2(postcss@8.5.1) + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-minify-selectors@6.0.4(postcss@8.4.38): + postcss-minify-selectors@6.0.4(postcss@8.5.1): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 - postcss-modules-extract-imports@3.1.0(postcss@8.4.38): + postcss-modules-extract-imports@3.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 - postcss-modules-local-by-default@4.0.5(postcss@8.4.38): + postcss-modules-local-by-default@4.2.0(postcss@8.5.1): dependencies: - icss-utils: 5.1.0(postcss@8.4.38) - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + icss-utils: 5.1.0(postcss@8.5.1) + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.0(postcss@8.4.38): + postcss-modules-scope@3.2.1(postcss@8.5.1): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 - postcss-modules-values@4.0.0(postcss@8.4.38): + postcss-modules-values@4.0.0(postcss@8.5.1): dependencies: - icss-utils: 5.1.0(postcss@8.4.38) - postcss: 8.4.38 + icss-utils: 5.1.0(postcss@8.5.1) + postcss: 8.5.1 - postcss-normalize-charset@6.0.2(postcss@8.4.38): + postcss-nesting@13.0.1(postcss@8.5.1): dependencies: - postcss: 8.4.38 + '@csstools/selector-resolve-nested': 3.0.0(postcss-selector-parser@7.0.0) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.0.0) + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 - postcss-normalize-display-values@6.0.2(postcss@8.4.38): + postcss-normalize-charset@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 + + postcss-normalize-display-values@6.0.2(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@6.0.2(postcss@8.5.1): + dependencies: + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-positions@6.0.2(postcss@8.4.38): + postcss-normalize-repeat-style@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@6.0.2(postcss@8.4.38): + postcss-normalize-string@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-string@6.0.2(postcss@8.4.38): + postcss-normalize-timing-functions@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@6.0.2(postcss@8.4.38): + postcss-normalize-unicode@6.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + browserslist: 4.24.4 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@6.1.0(postcss@8.4.38): + postcss-normalize-url@6.0.2(postcss@8.5.1): dependencies: - browserslist: 4.23.1 - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-url@6.0.2(postcss@8.4.38): + postcss-normalize-whitespace@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@6.0.2(postcss@8.4.38): + postcss-opacity-percentage@3.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-ordered-values@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + cssnano-utils: 4.0.2(postcss@8.5.1) + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-ordered-values@6.0.2(postcss@8.4.38): + postcss-overflow-shorthand@6.0.0(postcss@8.5.1): dependencies: - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-reduce-idents@6.0.3(postcss@8.4.38): + postcss-page-break@3.0.4(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-place@10.0.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-preset-env@10.1.3(postcss@8.5.1): + dependencies: + '@csstools/postcss-cascade-layers': 5.0.1(postcss@8.5.1) + '@csstools/postcss-color-function': 4.0.7(postcss@8.5.1) + '@csstools/postcss-color-mix-function': 3.0.7(postcss@8.5.1) + '@csstools/postcss-content-alt-text': 2.0.4(postcss@8.5.1) + '@csstools/postcss-exponential-functions': 2.0.6(postcss@8.5.1) + '@csstools/postcss-font-format-keywords': 4.0.0(postcss@8.5.1) + '@csstools/postcss-gamut-mapping': 2.0.7(postcss@8.5.1) + '@csstools/postcss-gradients-interpolation-method': 5.0.7(postcss@8.5.1) + '@csstools/postcss-hwb-function': 4.0.7(postcss@8.5.1) + '@csstools/postcss-ic-unit': 4.0.0(postcss@8.5.1) + '@csstools/postcss-initial': 2.0.0(postcss@8.5.1) + '@csstools/postcss-is-pseudo-class': 5.0.1(postcss@8.5.1) + '@csstools/postcss-light-dark-function': 2.0.7(postcss@8.5.1) + '@csstools/postcss-logical-float-and-clear': 3.0.0(postcss@8.5.1) + '@csstools/postcss-logical-overflow': 2.0.0(postcss@8.5.1) + '@csstools/postcss-logical-overscroll-behavior': 2.0.0(postcss@8.5.1) + '@csstools/postcss-logical-resize': 3.0.0(postcss@8.5.1) + '@csstools/postcss-logical-viewport-units': 3.0.3(postcss@8.5.1) + '@csstools/postcss-media-minmax': 2.0.6(postcss@8.5.1) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 3.0.4(postcss@8.5.1) + '@csstools/postcss-nested-calc': 4.0.0(postcss@8.5.1) + '@csstools/postcss-normalize-display-values': 4.0.0(postcss@8.5.1) + '@csstools/postcss-oklab-function': 4.0.7(postcss@8.5.1) + '@csstools/postcss-progressive-custom-properties': 4.0.0(postcss@8.5.1) + '@csstools/postcss-random-function': 1.0.2(postcss@8.5.1) + '@csstools/postcss-relative-color-syntax': 3.0.7(postcss@8.5.1) + '@csstools/postcss-scope-pseudo-class': 4.0.1(postcss@8.5.1) + '@csstools/postcss-sign-functions': 1.1.1(postcss@8.5.1) + '@csstools/postcss-stepped-value-functions': 4.0.6(postcss@8.5.1) + '@csstools/postcss-text-decoration-shorthand': 4.0.1(postcss@8.5.1) + '@csstools/postcss-trigonometric-functions': 4.0.6(postcss@8.5.1) + '@csstools/postcss-unset-value': 4.0.0(postcss@8.5.1) + autoprefixer: 10.4.20(postcss@8.5.1) + browserslist: 4.24.4 + css-blank-pseudo: 7.0.1(postcss@8.5.1) + css-has-pseudo: 7.0.2(postcss@8.5.1) + css-prefers-color-scheme: 10.0.0(postcss@8.5.1) + cssdb: 8.2.3 + postcss: 8.5.1 + postcss-attribute-case-insensitive: 7.0.1(postcss@8.5.1) + postcss-clamp: 4.1.0(postcss@8.5.1) + postcss-color-functional-notation: 7.0.7(postcss@8.5.1) + postcss-color-hex-alpha: 10.0.0(postcss@8.5.1) + postcss-color-rebeccapurple: 10.0.0(postcss@8.5.1) + postcss-custom-media: 11.0.5(postcss@8.5.1) + postcss-custom-properties: 14.0.4(postcss@8.5.1) + postcss-custom-selectors: 8.0.4(postcss@8.5.1) + postcss-dir-pseudo-class: 9.0.1(postcss@8.5.1) + postcss-double-position-gradients: 6.0.0(postcss@8.5.1) + postcss-focus-visible: 10.0.1(postcss@8.5.1) + postcss-focus-within: 9.0.1(postcss@8.5.1) + postcss-font-variant: 5.0.0(postcss@8.5.1) + postcss-gap-properties: 6.0.0(postcss@8.5.1) + postcss-image-set-function: 7.0.0(postcss@8.5.1) + postcss-lab-function: 7.0.7(postcss@8.5.1) + postcss-logical: 8.0.0(postcss@8.5.1) + postcss-nesting: 13.0.1(postcss@8.5.1) + postcss-opacity-percentage: 3.0.0(postcss@8.5.1) + postcss-overflow-shorthand: 6.0.0(postcss@8.5.1) + postcss-page-break: 3.0.4(postcss@8.5.1) + postcss-place: 10.0.0(postcss@8.5.1) + postcss-pseudo-class-any-link: 10.0.1(postcss@8.5.1) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.1) + postcss-selector-not: 8.0.1(postcss@8.5.1) + + postcss-pseudo-class-any-link@10.0.1(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + postcss-reduce-idents@6.0.3(postcss@8.5.1): + dependencies: + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-reduce-initial@6.1.0(postcss@8.4.38): + postcss-reduce-initial@6.1.0(postcss@8.5.1): dependencies: - browserslist: 4.23.1 + browserslist: 4.24.4 caniuse-api: 3.0.0 - postcss: 8.4.38 + postcss: 8.5.1 - postcss-reduce-transforms@6.0.2(postcss@8.4.38): + postcss-reduce-transforms@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-selector-parser@6.1.0: + postcss-replace-overflow-wrap@4.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-selector-not@8.0.1(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.0.0: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-sort-media-queries@5.2.0(postcss@8.4.38): + postcss-sort-media-queries@5.2.0(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 sort-css-media-queries: 2.2.0 - postcss-svgo@6.0.3(postcss@8.4.38): + postcss-svgo@6.0.3(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 postcss-value-parser: 4.2.0 svgo: 3.3.2 - postcss-unique-selectors@6.0.4(postcss@8.4.38): + postcss-unique-selectors@6.0.4(postcss@8.5.1): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 postcss-value-parser@4.2.0: {} - postcss-zindex@6.0.2(postcss@8.4.38): + postcss-zindex@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.38 + postcss: 8.5.1 - postcss@8.4.38: + postcss@8.5.1: dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 prelude-ls@1.2.1: {} - prettier@3.3.2: {} + prettier@3.4.2: {} pretty-error@4.0.0: dependencies: @@ -14032,15 +15372,15 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - pretty-ms@9.0.0: + pretty-ms@9.2.0: dependencies: parse-ms: 4.0.0 pretty-time@1.1.0: {} - prism-react-renderer@2.3.1(react@18.3.1): + prism-react-renderer@2.4.1(react@18.3.1): dependencies: - '@types/prismjs': 1.26.4 + '@types/prismjs': 1.26.5 clsx: 2.1.1 react: 18.3.1 @@ -14048,15 +15388,6 @@ snapshots: process-nextick-args@2.0.1: {} - promise.allsettled@1.0.7: - dependencies: - array.prototype.map: 1.0.7 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - get-intrinsic: 1.2.4 - iterate-value: 1.0.2 - prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -14079,24 +15410,26 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - proxy-agent@6.4.0: + proxy-agent@6.5.0: dependencies: - agent-base: 7.1.1 - debug: 4.3.5 + agent-base: 7.1.3 + debug: 4.4.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 lru-cache: 7.18.3 - pac-proxy-agent: 7.0.2 + pac-proxy-agent: 7.1.0 proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.4 + socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color proxy-from-env@1.1.0: {} - psl@1.9.0: {} + psl@1.15.0: + dependencies: + punycode: 2.3.1 - punycode@1.4.1: {} + punycode.js@2.3.1: {} punycode@2.3.1: {} @@ -14104,9 +15437,9 @@ snapshots: dependencies: escape-goat: 4.0.0 - qs@6.11.0: + qs@6.13.0: dependencies: - side-channel: 1.0.6 + side-channel: 1.1.0 querystringify@2.2.0: {} @@ -14142,18 +15475,18 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-dev-utils@12.0.1(eslint@8.57.0)(typescript@5.4.5)(webpack@5.92.0): + react-dev-utils@12.0.1(eslint@8.57.1)(typescript@5.7.3)(webpack@5.97.1): dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.26.2 address: 1.2.2 - browserslist: 4.23.1 + browserslist: 4.24.4 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 detect-port-alt: 1.1.6 escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.0)(typescript@5.4.5)(webpack@5.92.0) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.1)(typescript@5.7.3)(webpack@5.97.1) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -14165,12 +15498,12 @@ snapshots: prompts: 2.4.2 react-error-overlay: 6.0.11 recursive-readdir: 2.2.3 - shell-quote: 1.8.1 + shell-quote: 1.8.2 strip-ansi: 6.0.1 text-table: 0.2.0 - webpack: 5.92.0 + webpack: 5.97.1 optionalDependencies: - typescript: 5.4.5 + typescript: 5.7.3 transitivePeerDependencies: - eslint - supports-color @@ -14186,46 +15519,29 @@ snapshots: react-fast-compare@3.2.2: {} - react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.24.7 - invariant: 2.2.4 - prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-fast-compare: 3.2.2 - shallowequal: 1.1.0 - - react-helmet-async@2.0.5(react@18.3.1): - dependencies: - invariant: 2.2.4 - react: 18.3.1 - react-fast-compare: 3.2.2 - shallowequal: 1.1.0 - react-is@16.13.1: {} react-is@18.3.1: {} - react-json-view-lite@1.4.0(react@18.3.1): + react-json-view-lite@1.5.0(react@18.3.1): dependencies: react: 18.3.1 - react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.92.0): + react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.97.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.26.0 react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - webpack: 5.92.0 + webpack: 5.97.1 react-router-config@5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.26.0 react: 18.3.1 react-router: 5.3.4(react@18.3.1) react-router-dom@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.26.0 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -14236,11 +15552,11 @@ snapshots: react-router@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.26.0 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 - path-to-regexp: 1.8.0 + path-to-regexp: 1.9.0 prop-types: 15.8.1 react: 18.3.1 react-is: 16.13.1 @@ -14255,14 +15571,14 @@ snapshots: dependencies: find-up: 6.3.0 read-pkg: 8.1.0 - type-fest: 4.20.1 + type-fest: 4.32.0 read-pkg@8.1.0: dependencies: '@types/normalize-package-data': 2.4.4 normalize-package-data: 6.0.2 parse-json: 7.1.1 - type-fest: 4.20.1 + type-fest: 4.32.0 readable-stream@2.3.8: dependencies: @@ -14288,13 +15604,54 @@ snapshots: rechoir@0.6.2: dependencies: - resolve: 1.22.8 + resolve: 1.22.10 + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.6 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.0(acorn@8.14.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.14.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.6 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.6 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 recursive-readdir@2.2.3: dependencies: minimatch: 3.1.2 - regenerate-unicode-properties@10.1.1: + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.2.7 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.0: dependencies: regenerate: 1.4.2 @@ -14304,72 +15661,81 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.26.0 - regexp.prototype.flags@1.5.2: + regexp.prototype.flags@1.5.4: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 set-function-name: 2.0.2 - regexpu-core@5.3.2: + regexpu-core@6.2.0: dependencies: - '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 - regenerate-unicode-properties: 10.1.1 - regjsparser: 0.9.1 + regenerate-unicode-properties: 10.2.0 + regjsgen: 0.8.0 + regjsparser: 0.12.0 unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.1.0 + unicode-match-property-value-ecmascript: 2.2.0 - registry-auth-token@5.0.2: + registry-auth-token@5.0.3: dependencies: - '@pnpm/npm-conf': 2.2.2 + '@pnpm/npm-conf': 2.3.1 registry-url@6.0.1: dependencies: rc: 1.2.8 - regjsparser@0.9.1: + regjsgen@0.8.0: {} + + regjsparser@0.12.0: dependencies: - jsesc: 0.5.0 + jsesc: 3.0.2 rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.4 - hast-util-raw: 9.0.4 - vfile: 6.0.1 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.6 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.1 + transitivePeerDependencies: + - supports-color relateurl@0.2.7: {} - release-it@17.4.0(typescript@5.5.2): + release-it@17.11.0(typescript@5.7.3): dependencies: '@iarna/toml': 2.2.5 '@octokit/rest': 20.1.1 async-retry: 1.3.3 - chalk: 5.3.0 - cosmiconfig: 9.0.0(typescript@5.5.2) - execa: 8.0.1 + chalk: 5.4.1 + ci-info: 4.1.0 + cosmiconfig: 9.0.0(typescript@5.7.3) + execa: 8.0.0 git-url-parse: 14.0.0 - globby: 14.0.1 - got: 13.0.0 - inquirer: 9.2.23 - is-ci: 3.0.1 + globby: 14.0.2 + inquirer: 9.3.2 issue-parser: 7.0.1 lodash: 4.17.21 mime-types: 2.1.35 new-github-release-url: 2.0.0 - node-fetch: 3.3.2 open: 10.1.0 - ora: 8.0.1 + ora: 8.1.1 os-name: 5.1.0 - promise.allsettled: 1.0.7 - proxy-agent: 6.4.0 - semver: 7.6.2 + proxy-agent: 6.5.0 + semver: 7.6.3 shelljs: 0.8.5 - update-notifier: 7.0.0 + update-notifier: 7.3.1 url-join: 5.0.0 - wildcard-match: 5.1.3 + wildcard-match: 5.1.4 yargs-parser: 21.1.1 transitivePeerDependencies: - supports-color @@ -14379,25 +15745,25 @@ snapshots: dependencies: '@types/mdast': 4.0.4 mdast-util-directive: 3.0.0 - micromark-extension-directive: 3.0.0 - unified: 11.0.4 + micromark-extension-directive: 3.0.2 + unified: 11.0.5 transitivePeerDependencies: - supports-color remark-emoji@4.0.1: dependencies: '@types/mdast': 4.0.4 - emoticon: 4.0.1 - mdast-util-find-and-replace: 3.0.1 - node-emoji: 2.1.3 - unified: 11.0.4 + emoticon: 4.1.0 + mdast-util-find-and-replace: 3.0.2 + node-emoji: 2.2.0 + unified: 11.0.5 remark-frontmatter@5.0.0: dependencies: '@types/mdast': 4.0.4 mdast-util-frontmatter: 2.0.1 micromark-extension-frontmatter: 2.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -14408,7 +15774,7 @@ snapshots: micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0 remark-stringify: 11.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -14416,12 +15782,12 @@ snapshots: dependencies: '@types/mdast': 4.0.4 mdast-util-math: 3.0.0 - micromark-extension-math: 3.0.0 - unified: 11.0.4 + micromark-extension-math: 3.1.0 + unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-mdx@3.0.1: + remark-mdx@3.1.0: dependencies: mdast-util-mdx: 3.0.0 micromark-extension-mdxjs: 3.0.0 @@ -14431,25 +15797,25 @@ snapshots: remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.1 - micromark-util-types: 2.0.0 - unified: 11.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.1 + unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-rehype@11.1.0: + remark-rehype@11.1.1: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 mdast-util-to-hast: 13.2.0 - unified: 11.0.4 - vfile: 6.0.1 + unified: 11.0.5 + vfile: 6.0.3 remark-stringify@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.0 - unified: 11.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 renderkid@3.0.0: dependencies: @@ -14459,6 +15825,8 @@ snapshots: lodash: 4.17.21 strip-ansi: 6.0.1 + repeat-string@1.6.1: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -14477,9 +15845,9 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve@1.22.8: + resolve@1.22.10: dependencies: - is-core-module: 2.14.0 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -14497,6 +15865,11 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + retry@0.13.1: {} reusify@1.0.4: {} @@ -14505,43 +15878,44 @@ snapshots: dependencies: glob: 7.2.3 - rimraf@5.0.7: + rimraf@5.0.10: dependencies: - glob: 10.4.1 + glob: 10.4.5 - rollup@4.18.0: + rollup@4.30.1: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.0 - '@rollup/rollup-android-arm64': 4.18.0 - '@rollup/rollup-darwin-arm64': 4.18.0 - '@rollup/rollup-darwin-x64': 4.18.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 - '@rollup/rollup-linux-arm-musleabihf': 4.18.0 - '@rollup/rollup-linux-arm64-gnu': 4.18.0 - '@rollup/rollup-linux-arm64-musl': 4.18.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 - '@rollup/rollup-linux-riscv64-gnu': 4.18.0 - '@rollup/rollup-linux-s390x-gnu': 4.18.0 - '@rollup/rollup-linux-x64-gnu': 4.18.0 - '@rollup/rollup-linux-x64-musl': 4.18.0 - '@rollup/rollup-win32-arm64-msvc': 4.18.0 - '@rollup/rollup-win32-ia32-msvc': 4.18.0 - '@rollup/rollup-win32-x64-msvc': 4.18.0 + '@rollup/rollup-android-arm-eabi': 4.30.1 + '@rollup/rollup-android-arm64': 4.30.1 + '@rollup/rollup-darwin-arm64': 4.30.1 + '@rollup/rollup-darwin-x64': 4.30.1 + '@rollup/rollup-freebsd-arm64': 4.30.1 + '@rollup/rollup-freebsd-x64': 4.30.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 + '@rollup/rollup-linux-arm-musleabihf': 4.30.1 + '@rollup/rollup-linux-arm64-gnu': 4.30.1 + '@rollup/rollup-linux-arm64-musl': 4.30.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 + '@rollup/rollup-linux-riscv64-gnu': 4.30.1 + '@rollup/rollup-linux-s390x-gnu': 4.30.1 + '@rollup/rollup-linux-x64-gnu': 4.30.1 + '@rollup/rollup-linux-x64-musl': 4.30.1 + '@rollup/rollup-win32-arm64-msvc': 4.30.1 + '@rollup/rollup-win32-ia32-msvc': 4.30.1 + '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 - rrweb-cssom@0.6.0: {} - rrweb-cssom@0.7.1: {} - rtl-detect@1.1.2: {} + rrweb-cssom@0.8.0: {} - rtlcss@4.1.1: + rtlcss@4.3.0: dependencies: - escalade: 3.1.2 - picocolors: 1.0.1 - postcss: 8.4.38 + escalade: 3.2.0 + picocolors: 1.1.1 + postcss: 8.5.1 strip-json-comments: 3.1.1 run-applescript@7.0.0: {} @@ -14554,24 +15928,30 @@ snapshots: rxjs@7.8.1: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 - safe-array-concat@1.1.2: + safe-array-concat@1.1.3: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.7 + has-symbols: 1.1.0 isarray: 2.0.5 safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} - safe-regex-test@1.0.3: + safe-push-apply@1.0.0: dependencies: - call-bind: 1.0.7 es-errors: 1.3.0 - is-regex: 1.1.4 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + is-regex: 1.2.1 safer-buffer@2.1.2: {} @@ -14597,12 +15977,12 @@ snapshots: ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.2.0: + schema-utils@4.3.0: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.16.0 - ajv-formats: 2.1.1(ajv@8.16.0) - ajv-keywords: 5.1.0(ajv@8.16.0) + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) search-insights@2.14.0: {} @@ -14620,13 +16000,13 @@ snapshots: semver-diff@4.0.0: dependencies: - semver: 7.6.2 + semver: 7.6.3 semver@6.3.1: {} - semver@7.6.2: {} + semver@7.6.3: {} - send@0.18.0: + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -14648,15 +16028,14 @@ snapshots: dependencies: randombytes: 2.1.0 - serve-handler@6.1.5: + serve-handler@6.1.6: dependencies: bytes: 3.0.0 content-disposition: 0.5.2 - fast-url-parser: 1.1.3 mime-types: 2.1.18 minimatch: 3.1.2 path-is-inside: 1.0.2 - path-to-regexp: 2.2.1 + path-to-regexp: 3.3.0 range-parser: 1.2.0 serve-index@1.9.1: @@ -14671,12 +16050,12 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.15.0: + serve-static@1.16.2: dependencies: - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0 + send: 0.19.0 transitivePeerDependencies: - supports-color @@ -14685,8 +16064,8 @@ snapshots: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 + get-intrinsic: 1.2.7 + gopd: 1.2.0 has-property-descriptors: 1.0.2 set-function-name@2.0.2: @@ -14696,6 +16075,12 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + setprototypeof@1.1.0: {} setprototypeof@1.2.0: {} @@ -14712,7 +16097,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.1: {} + shell-quote@1.8.2: {} shelljs@0.8.5: dependencies: @@ -14720,19 +16105,33 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 - shiki@0.14.7: + side-channel-list@1.0.0: dependencies: - ansi-sequence-parser: 1.1.1 - jsonc-parser: 3.2.1 - vscode-oniguruma: 1.7.0 - vscode-textmate: 8.0.0 + es-errors: 1.3.0 + object-inspect: 1.13.3 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + object-inspect: 1.13.3 - side-channel@1.0.6: + side-channel-weakmap@1.0.2: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + get-intrinsic: 1.2.7 + object-inspect: 1.13.3 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 siginfo@2.0.0: {} @@ -14742,7 +16141,7 @@ snapshots: sirv@2.0.4: dependencies: - '@polka/url': 1.0.0-next.25 + '@polka/url': 1.0.0-next.28 mrmime: 2.0.0 totalist: 3.0.1 @@ -14772,7 +16171,7 @@ snapshots: snake-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.8.1 sockjs@0.3.24: dependencies: @@ -14780,10 +16179,10 @@ snapshots: uuid: 8.3.2 websocket-driver: 0.7.4 - socks-proxy-agent@8.0.4: + socks-proxy-agent@8.0.5: dependencies: - agent-base: 7.1.1 - debug: 4.3.5 + agent-base: 7.1.3 + debug: 4.4.0 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -14795,7 +16194,7 @@ snapshots: sort-css-media-queries@2.2.0: {} - source-map-js@1.2.0: {} + source-map-js@1.2.1: {} source-map-support@0.5.21: dependencies: @@ -14813,20 +16212,20 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.18 + spdx-license-ids: 3.0.21 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.18 + spdx-license-ids: 3.0.21 - spdx-license-ids@3.0.18: {} + spdx-license-ids@3.0.21: {} spdy-transport@3.0.0: dependencies: - debug: 4.3.5 + debug: 4.4.0 detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -14837,7 +16236,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.3.5 + debug: 4.4.0 handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -14853,20 +16252,18 @@ snapshots: srcset@4.0.0: {} + stable-hash@0.0.4: {} + stackback@0.0.2: {} statuses@1.5.0: {} statuses@2.0.1: {} - std-env@3.7.0: {} + std-env@3.8.0: {} stdin-discarder@0.2.2: {} - stop-iteration-iterator@1.0.0: - dependencies: - internal-slot: 1.0.7 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -14885,24 +16282,34 @@ snapshots: get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 - string.prototype.trim@1.2.9: + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string.prototype.trim@1.2.10: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 + define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 - string.prototype.trimend@1.0.8: + string.prototype.trimend@1.0.9: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 string_decoder@1.1.1: dependencies: @@ -14945,23 +16352,21 @@ snapshots: strip-json-comments@3.1.1: {} - strip-literal@2.1.0: + strip-literal@2.1.1: dependencies: - js-tokens: 9.0.0 + js-tokens: 9.0.1 - style-to-object@0.4.4: - dependencies: - inline-style-parser: 0.1.1 + stubborn-fs@1.2.5: {} - style-to-object@1.0.6: + style-to-object@1.0.8: dependencies: - inline-style-parser: 0.2.3 + inline-style-parser: 0.2.4 - stylehacks@6.1.1(postcss@8.4.38): + stylehacks@6.1.1(postcss@8.5.1): dependencies: - browserslist: 4.23.1 - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + browserslist: 4.24.4 + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 supports-color@5.5.0: dependencies: @@ -14992,7 +16397,7 @@ snapshots: css-tree: 2.3.1 css-what: 6.1.0 csso: 5.0.5 - picocolors: 1.0.1 + picocolors: 1.1.1 symbol-tree@3.2.4: {} @@ -15005,19 +16410,19 @@ snapshots: ansi-escapes: 5.0.0 supports-hyperlinks: 2.3.0 - terser-webpack-plugin@5.3.10(webpack@5.92.0): + terser-webpack-plugin@5.3.11(webpack@5.97.1): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 - schema-utils: 3.3.0 + schema-utils: 4.3.0 serialize-javascript: 6.0.2 - terser: 5.31.1 - webpack: 5.92.0 + terser: 5.37.0 + webpack: 5.97.1 - terser@5.31.1: + terser@5.37.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.12.0 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -15047,7 +16452,9 @@ snapshots: tiny-warning@1.0.3: {} - tinybench@2.8.0: {} + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} tinypool@0.8.4: {} @@ -15057,8 +16464,6 @@ snapshots: dependencies: os-tmpdir: 1.0.2 - to-fast-properties@2.0.0: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -15069,7 +16474,7 @@ snapshots: tough-cookie@4.1.4: dependencies: - psl: 1.9.0 + psl: 1.15.0 punycode: 2.3.1 universalify: 0.2.0 url-parse: 1.5.10 @@ -15084,9 +16489,9 @@ snapshots: trough@2.2.0: {} - ts-api-utils@1.3.0(typescript@5.5.2): + ts-api-utils@1.4.3(typescript@5.7.3): dependencies: - typescript: 5.5.2 + typescript: 5.7.3 tsc-alias@1.8.10: dependencies: @@ -15104,13 +16509,13 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tslib@2.6.3: {} + tslib@2.8.1: {} type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - type-detect@4.0.8: {} + type-detect@4.1.0: {} type-fest@0.20.2: {} @@ -15122,44 +16527,45 @@ snapshots: type-fest@3.13.1: {} - type-fest@4.20.1: {} + type-fest@4.32.0: {} type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - typed-array-buffer@1.0.2: + typed-array-buffer@1.0.3: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-typed-array: 1.1.13 + is-typed-array: 1.1.15 - typed-array-byte-length@1.0.1: + typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 - typed-array-byte-offset@1.0.2: + typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.6: + typed-array-length@1.0.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + gopd: 1.2.0 + is-typed-array: 1.1.15 possible-typed-array-names: 1.0.0 + reflect.getprototypeof: 1.0.10 typedarray-to-buffer@3.1.5: dependencies: @@ -15167,60 +16573,69 @@ snapshots: typedarray@0.0.6: {} - typedoc-plugin-markdown@4.0.3(typedoc@0.25.13(typescript@5.4.5)): + typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.7.3)): + dependencies: + typedoc: 0.27.6(typescript@5.7.3) + + typedoc-plugin-mdn-links@4.0.8(typedoc@0.27.6(typescript@5.7.3)): dependencies: - typedoc: 0.25.13(typescript@5.4.5) + typedoc: 0.27.6(typescript@5.7.3) - typedoc@0.25.13(typescript@5.4.5): + typedoc@0.27.6(typescript@5.7.3): dependencies: + '@gerrit0/mini-shiki': 1.27.0 lunr: 2.3.9 - marked: 4.3.0 - minimatch: 9.0.4 - shiki: 0.14.7 - typescript: 5.4.5 + markdown-it: 14.1.0 + minimatch: 9.0.5 + typescript: 5.7.3 + yaml: 2.7.0 - typescript@5.4.5: {} + typescript@5.7.3: {} - typescript@5.5.2: {} + uc.micro@2.1.0: {} - ufo@1.5.3: {} + ufo@1.5.4: {} - uglify-js@3.18.0: + uglify-js@3.19.3: optional: true - unbox-primitive@1.0.2: + unbox-primitive@1.1.0: dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + call-bound: 1.0.3 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 undici-types@5.26.5: {} - unicode-canonical-property-names-ecmascript@2.0.0: {} + undici-types@6.20.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-emoji-modifier-base@1.0.0: {} unicode-match-property-ecmascript@2.0.0: dependencies: - unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-canonical-property-names-ecmascript: 2.0.1 unicode-property-aliases-ecmascript: 2.1.0 - unicode-match-property-value-ecmascript@2.1.0: {} + unicode-match-property-value-ecmascript@2.2.0: {} unicode-property-aliases-ecmascript@2.1.0: {} unicorn-magic@0.1.0: {} - unified@11.0.4: + unicorn-magic@0.3.0: {} + + unified@11.0.5: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 bail: 2.0.2 devlop: 1.1.0 extend: 3.0.2 is-plain-obj: 4.1.0 trough: 2.2.0 - vfile: 6.0.1 + vfile: 6.0.3 unique-string@3.0.0: dependencies: @@ -15228,33 +16643,33 @@ snapshots: unist-util-is@6.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-position-from-estree@2.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-position@5.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-remove-position@5.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-visit: 5.0.0 unist-util-stringify-position@4.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-visit-parents@6.0.1: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is: 6.0.0 unist-util-visit@5.0.0: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 @@ -15266,16 +16681,16 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.0.16(browserslist@4.23.1): + update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: - browserslist: 4.23.1 - escalade: 3.1.2 - picocolors: 1.0.1 + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 update-notifier@6.0.2: dependencies: boxen: 7.1.1 - chalk: 5.3.0 + chalk: 5.4.1 configstore: 6.0.0 has-yarn: 3.0.0 import-lazy: 4.0.0 @@ -15285,23 +16700,21 @@ snapshots: is-yarn-global: 0.4.1 latest-version: 7.0.0 pupa: 3.1.0 - semver: 7.6.2 + semver: 7.6.3 semver-diff: 4.0.0 xdg-basedir: 5.1.0 - update-notifier@7.0.0: + update-notifier@7.3.1: dependencies: - boxen: 7.1.1 - chalk: 5.3.0 - configstore: 6.0.0 - import-lazy: 4.0.0 - is-in-ci: 0.1.0 - is-installed-globally: 0.4.0 + boxen: 8.0.1 + chalk: 5.4.1 + configstore: 7.0.0 + is-in-ci: 1.0.0 + is-installed-globally: 1.0.0 is-npm: 6.0.0 - latest-version: 7.0.0 + latest-version: 9.0.0 pupa: 3.1.0 - semver: 7.6.2 - semver-diff: 4.0.0 + semver: 7.6.3 xdg-basedir: 5.1.0 uri-js@4.4.1: @@ -15310,14 +16723,14 @@ snapshots: url-join@5.0.0: {} - url-loader@4.1.1(file-loader@6.2.0(webpack@5.92.0))(webpack@5.92.0): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.97.1))(webpack@5.97.1): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.92.0 + webpack: 5.97.1 optionalDependencies: - file-loader: 6.2.0(webpack@5.92.0) + file-loader: 6.2.0(webpack@5.97.1) url-parse@1.5.10: dependencies: @@ -15343,92 +16756,89 @@ snapshots: vary@1.1.2: {} - vfile-location@5.0.2: + vfile-location@5.0.3: dependencies: - '@types/unist': 3.0.2 - vfile: 6.0.1 + '@types/unist': 3.0.3 + vfile: 6.0.3 vfile-message@4.0.2: dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 - vfile@6.0.1: + vfile@6.0.3: dependencies: - '@types/unist': 3.0.2 - unist-util-stringify-position: 4.0.0 + '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@1.6.0(@types/node@18.19.39)(terser@5.31.1): + vite-node@1.6.0(@types/node@18.19.70)(terser@5.37.0): dependencies: cac: 6.7.14 - debug: 4.3.5 + debug: 4.4.0 pathe: 1.1.2 - picocolors: 1.0.1 - vite: 5.3.2(@types/node@18.19.39)(terser@5.31.1) + picocolors: 1.1.1 + vite: 5.4.11(@types/node@18.19.70)(terser@5.37.0) transitivePeerDependencies: - '@types/node' - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color - terser - vite@5.3.2(@types/node@18.19.39)(terser@5.31.1): + vite@5.4.11(@types/node@18.19.70)(terser@5.37.0): dependencies: esbuild: 0.21.5 - postcss: 8.4.38 - rollup: 4.18.0 + postcss: 8.5.1 + rollup: 4.30.1 optionalDependencies: - '@types/node': 18.19.39 + '@types/node': 18.19.70 fsevents: 2.3.3 - terser: 5.31.1 + terser: 5.37.0 - vitest@1.6.0(@types/node@18.19.39)(jsdom@24.1.0)(terser@5.31.1): + vitest@1.6.0(@types/node@18.19.70)(jsdom@24.1.3)(terser@5.37.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 '@vitest/snapshot': 1.6.0 '@vitest/spy': 1.6.0 '@vitest/utils': 1.6.0 - acorn-walk: 8.3.3 - chai: 4.4.1 - debug: 4.3.5 + acorn-walk: 8.3.4 + chai: 4.5.0 + debug: 4.4.0 execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.10 + local-pkg: 0.5.1 + magic-string: 0.30.17 pathe: 1.1.2 - picocolors: 1.0.1 - std-env: 3.7.0 - strip-literal: 2.1.0 - tinybench: 2.8.0 + picocolors: 1.1.1 + std-env: 3.8.0 + strip-literal: 2.1.1 + tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.3.2(@types/node@18.19.39)(terser@5.31.1) - vite-node: 1.6.0(@types/node@18.19.39)(terser@5.31.1) - why-is-node-running: 2.2.2 + vite: 5.4.11(@types/node@18.19.70)(terser@5.37.0) + vite-node: 1.6.0(@types/node@18.19.70)(terser@5.37.0) + why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 18.19.39 - jsdom: 24.1.0 + '@types/node': 18.19.70 + jsdom: 24.1.3 transitivePeerDependencies: - less - lightningcss - sass + - sass-embedded - stylus - sugarss - supports-color - terser - vscode-oniguruma@1.7.0: {} - - vscode-textmate@8.0.0: {} - w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 - watchpack@2.4.1: + watchpack@2.4.2: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -15443,38 +16853,36 @@ snapshots: web-namespaces@2.0.1: {} - web-streams-polyfill@3.3.3: {} - webidl-conversions@7.0.0: {} webpack-bundle-analyzer@4.10.2: dependencies: '@discoveryjs/json-ext': 0.5.7 - acorn: 8.12.0 - acorn-walk: 8.3.3 + acorn: 8.14.0 + acorn-walk: 8.3.4 commander: 7.2.0 debounce: 1.2.1 escape-string-regexp: 4.0.0 gzip-size: 6.0.0 html-escaper: 2.0.2 opener: 1.5.2 - picocolors: 1.0.1 + picocolors: 1.1.1 sirv: 2.0.4 ws: 7.5.10 transitivePeerDependencies: - bufferutil - utf-8-validate - webpack-dev-middleware@5.3.4(webpack@5.92.0): + webpack-dev-middleware@5.3.4(webpack@5.97.1): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 - schema-utils: 4.2.0 - webpack: 5.92.0 + schema-utils: 4.3.0 + webpack: 5.97.1 - webpack-dev-server@4.15.2(webpack@5.92.0): + webpack-dev-server@4.15.2(webpack@5.97.1): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -15482,32 +16890,32 @@ snapshots: '@types/serve-index': 1.9.4 '@types/serve-static': 1.15.7 '@types/sockjs': 0.3.36 - '@types/ws': 8.5.10 + '@types/ws': 8.5.13 ansi-html-community: 0.0.8 - bonjour-service: 1.2.1 + bonjour-service: 1.3.0 chokidar: 3.6.0 colorette: 2.0.20 - compression: 1.7.4 + compression: 1.7.5 connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.19.2 + express: 4.21.2 graceful-fs: 4.2.11 html-entities: 2.5.2 - http-proxy-middleware: 2.0.6(@types/express@4.17.21) + http-proxy-middleware: 2.0.7(@types/express@4.17.21) ipaddr.js: 2.2.0 - launch-editor: 2.7.0 + launch-editor: 2.9.1 open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 - schema-utils: 4.2.0 + schema-utils: 4.3.0 selfsigned: 2.4.1 serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.92.0) - ws: 8.17.1 + webpack-dev-middleware: 5.3.4(webpack@5.97.1) + ws: 8.18.0 optionalDependencies: - webpack: 5.92.0 + webpack: 5.97.1 transitivePeerDependencies: - bufferutil - debug @@ -15520,21 +16928,26 @@ snapshots: flat: 5.0.2 wildcard: 2.0.1 + webpack-merge@6.0.1: + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + webpack-sources@3.2.3: {} - webpack@5.92.0: + webpack@5.97.1: dependencies: '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.0 - acorn-import-attributes: 1.9.5(acorn@8.12.0) - browserslist: 4.23.1 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.4 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.17.0 - es-module-lexer: 1.5.3 + enhanced-resolve: 5.18.0 + es-module-lexer: 1.6.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -15545,25 +16958,29 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(webpack@5.92.0) - watchpack: 2.4.1 + terser-webpack-plugin: 5.3.11(webpack@5.97.1) + watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpackbar@5.0.2(webpack@5.92.0): + webpackbar@6.0.1(webpack@5.97.1): dependencies: + ansi-escapes: 4.3.2 chalk: 4.1.2 - consola: 2.15.3 + consola: 3.4.0 + figures: 3.2.0 + markdown-table: 2.0.0 pretty-time: 1.1.0 - std-env: 3.7.0 - webpack: 5.92.0 + std-env: 3.8.0 + webpack: 5.97.1 + wrap-ansi: 7.0.0 websocket-driver@0.7.4: dependencies: - http-parser-js: 0.5.8 + http-parser-js: 0.5.9 safe-buffer: 5.2.1 websocket-extensions: 0.1.4 @@ -15575,25 +16992,51 @@ snapshots: whatwg-mimetype@4.0.0: {} - whatwg-url@14.0.0: + whatwg-url@14.1.0: dependencies: tr46: 5.0.0 webidl-conversions: 7.0.0 - which-boxed-primitive@1.0.2: + when-exit@2.1.4: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.1 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 + call-bound: 1.0.3 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.0 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.0 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.18 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 - which-typed-array@1.1.15: + which-typed-array@1.1.18: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-tostringtag: 1.0.2 which@1.3.1: @@ -15604,7 +17047,7 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@2.2.2: + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 @@ -15613,7 +17056,11 @@ snapshots: dependencies: string-width: 5.1.2 - wildcard-match@5.1.3: {} + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + wildcard-match@5.1.4: {} wildcard@2.0.1: {} @@ -15643,6 +17090,12 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + wrappy@1.0.2: {} write-file-atomic@3.0.3: @@ -15654,7 +17107,7 @@ snapshots: ws@7.5.10: {} - ws@8.17.1: {} + ws@8.18.0: {} xdg-basedir@5.1.0: {} @@ -15672,6 +17125,8 @@ snapshots: yaml@1.10.2: {} + yaml@2.7.0: {} + yargs-parser@20.2.9: {} yargs-parser@21.1.1: {} @@ -15689,7 +17144,7 @@ snapshots: yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.2 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 @@ -15700,6 +17155,10 @@ snapshots: yocto-queue@1.0.0: {} - yoctocolors@2.0.2: {} + yocto-queue@1.1.1: {} + + yoctocolors-cjs@2.1.2: {} + + yoctocolors@2.1.1: {} zwitch@2.0.4: {} diff --git a/scripts/generate-files.js b/scripts/generate-files.js index 3fff96ad..27946f91 100644 --- a/scripts/generate-files.js +++ b/scripts/generate-files.js @@ -16,83 +16,96 @@ async function run(argv) { await oraPromise(async () => { await generateFile( - 'packages/core/src/actions/actionVariadicUse.ts', + 'packages/core/src/actions/variadic.ts', renderActionVariadicUse(15), options.show, ); }, { - text: 'Generating actionVariadicUse...', - successText: 'Generated actionVariadicUse.', - }); - - await oraPromise(async () => { - await generateFile( - 'packages/core/src/actions/actionVariadicRun.ts', - renderActionVariadicRun(15), - options.show, - ); - }, { - text: 'Generating actionVariadicRun...', - successText: 'Generated actionVariadicRun.', + text: 'Generating variadic...', + successText: 'Generated variadic.', }); } catch (error) { - console.log(error); + console.error(error); process.exit(1); } } function renderActionVariadicUse(size) { - const renderMethod = (index) => { + const renderActionUseFunction = (index) => { const generics = [ 'C1 extends {} = C', ...range(index).map((i) => `C${i + 2} extends C${i + 1} = C${i + 1}`), ].join(', '); const args = [ - 'enhancer1: ContextEnhancer', - ...range(index).map((i) => `enhancer${i + 2}: ContextEnhancer`), + 'enhancer1: ContextEnhancer', + ...range(index).map((i) => `enhancer${i + 2}: ContextEnhancer`), ].join(', '); - return ` use<${generics}>(${args}): Action;`; + return ` use<${generics}>(${args}): Action;`; }; - const content = ` -/* eslint-disable max-len */ -import type { Action, ContextEnhancer } from '@foscia/core/actions/types'; - -// Do not edit this file, it is generated by \`pnpm generate-files\`. - -export type ActionVariadicUse = { -${range(size).map((index) => renderMethod(index + 1)).join('\n')} -}; -`.trim(); + const renderActionRunFunction = (index) => { + const generics = [ + 'R', + 'C1 extends {} = C', + ...range(index - 1).map((i) => `C${i + 2} extends C${i + 1} = C${i + 1}`), + ].join(', '); + const args = [ + 'enhancer1: ContextEnhancer', + ...range(index - 1).map((i) => `enhancer${i + 2}: ContextEnhancer`), + `runner: ContextRunner`, + ].join(', '); - return `${content}\n`; -} + return ` run<${generics}>(${args}): Promise>;`; + }; -function renderActionVariadicRun(size) { - const renderMethod = (index) => { + const renderActionFactoryRunFunction = (index) => { const generics = [ 'R', 'C1 extends {} = C', ...range(index - 1).map((i) => `C${i + 2} extends C${i + 1} = C${i + 1}`), ].join(', '); const args = [ - 'enhancer1: ContextEnhancer', - ...range(index - 1).map((i) => `enhancer${i + 2}: ContextEnhancer`), - `runner: ContextRunner`, + 'enhancer1: ContextEnhancer', + ...range(index - 1).map((i) => `enhancer${i + 2}: ContextEnhancer`), + `runner: ContextRunner`, ].join(', '); - return ` run<${generics}>(${args}): Promise>;`; + return ` <${generics}>(${args}): Promise>;`; }; const content = ` /* eslint-disable max-len */ -import type { ContextEnhancer, ContextRunner } from '@foscia/core/actions/types'; +import type { Action, ContextEnhancer, ContextRunner } from '@foscia/core/actions/types'; // Do not edit this file, it is generated by \`pnpm generate-files\`. -export type ActionVariadicRun = { -${range(size).map((index) => renderMethod(index + 1)).join('\n')} +/** + * Variadic action use function signatures. + * + * @internal + */ +export type ActionVariadicUse = { +${range(size).map((index) => renderActionUseFunction(index + 1)).join('\n')} +}; + +/** + * Variadic action run function signatures. + * + * @internal + */ +export type ActionVariadicRun = { +${range(size).map((index) => renderActionRunFunction(index + 1)).join('\n')} +}; + +/** + * Variadic action factory run function signatures. + * + * @internal + */ +export type ActionFactoryVariadicRun = { + (runner: ContextRunner): Promise>; +${range(size).map((index) => renderActionFactoryRunFunction(index + 1)).join('\n')} }; `.trim(); diff --git a/scripts/lint.js b/scripts/lint.js index 2b9d1906..f95397d8 100644 --- a/scripts/lint.js +++ b/scripts/lint.js @@ -14,6 +14,12 @@ async function run() { text: 'Checking code...', successText: 'Checks OK.', }); + } catch (error) { + console.error(error.message); + process.exit(1); + } + + try { await oraPromise(lint, { text: 'Linting code...', successText: 'Code style OK.', @@ -23,51 +29,53 @@ async function run() { } } -async function lint() { - await execa({ stdio: 'inherit' })`eslint ${[ - '--ext', - '.ts', - 'packages', - ]}`; -} - async function check() { - let containsErrors = false; + const errors = []; // Check for external package use with relative path imports. const rootDirname = useRootDirname(); await Promise.all( (await listFiles(path.resolve(rootDirname, 'packages'))).map(async (file) => { const [_, packageName, context] = file.match(/packages\/([a-z-]+)\/(src|tests)/) ?? []; - if (packageName) { - const errors = []; + if (packageName && (packageName !== 'cli' || context !== 'tests')) { + const fileErrors = []; const fileContent = await readFile(file, { encoding: 'utf8' }); - const matches = fileContent.match(/from '@foscia\/[a-z-]+(\/index|.)/g) ?? []; - matches.forEach((match) => { - const importPackageName = match.substring(14, match.length).replace(/(\/index|.)$/, ''); + const matches = fileContent.matchAll(/[^\n]+(from '@foscia\/[a-z-]+(\/index|.))/g); + [...matches].forEach((match) => { + const importPackageName = match[1].substring(14, match[1].length).replace(/(\/index|.)$/, ''); + if (match[0].match(/^\s+\*/)) { + return; + } - if (match.endsWith('/') && (context !== 'src' || packageName !== importPackageName)) { - errors.push( + if (match[1].endsWith('/') && (context !== 'src' || packageName !== importPackageName)) { + fileErrors.push( `import of ${c.red(`@foscia/${importPackageName}`)} must use root package export (instead of inner package export)`, ); } - if (context === 'src' && packageName === importPackageName && (!match.endsWith('/') || match.endsWith('/index'))) { - errors.push( + if (context === 'src' && packageName === importPackageName && (!match[1].endsWith('/') || match[1].endsWith('/index'))) { + fileErrors.push( `import of ${c.red(`@foscia/${packageName}`)} must use inner package export (instead of root package export)`, ); } }); - if (errors.length) { - containsErrors = true; - console.error(`${c.underline(file)}\n${errors.map((e) => ` - ${e}`).join('\n')}`); + if (fileErrors.length) { + errors.push(`${c.underline(file)}\n${fileErrors.map((e) => ` - ${e}`).join('\n')}`); } } }), ); - if (containsErrors) { - process.exit(1); + if (errors.length) { + throw new Error(errors.join('\n')); } } + +async function lint() { + await execa({ stdio: 'inherit' })`eslint ${[ + '--ext', + '.ts', + 'packages', + ]}`; +} diff --git a/typedoc.json b/typedoc.json index 0f64f169..d07ee3b8 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,14 +1,42 @@ { + "$schema": "https://typedoc.org/schema.json", + "readme": "none", "disableGit": true, "sourceLinkTemplate": "https://github.com/foscia-dev/foscia/tree/main/{path}#L{line}", + "navigation": { + "includeGroups": true, + "includeCategories": true + }, + "categorizeByGroup": true, + "sort": [ + "kind", + "instance-first", + "required-first", + "alphabetical" + ], + "blockTags": [ + "@typeParam", + "@param", + "@returns", + "@example", + "@remarks", + "@see", + "@group", + "@category", + "@deprecated", + "@since", + "@example", + "@provideContext", + "@requireContext" + ], "groupOrder": [ + "Functions", "Classes", - "*", - "Namespaces" - ], - "categoryOrder": [ - "Enhancers", - "Runners", + "Enumerations", + "Interfaces", + "Type Aliases", + "Variables", + "Errors", "*" ] } diff --git a/website/.gitignore b/website/.gitignore index 995eadbd..0379117a 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -4,7 +4,6 @@ # build files /build/ -/docs/reference/api/ .docusaurus .cache-loader @@ -28,4 +27,3 @@ yarn-error.log* *.njsproj *.sln *.sw* - diff --git a/website/.prettierignore b/website/.prettierignore index 8ccb4c28..3d13b253 100644 --- a/website/.prettierignore +++ b/website/.prettierignore @@ -1,5 +1,4 @@ /dist/ /node_modules/ -/docs/reference/api/ /build/ /.docusaurus/ diff --git a/website/docs/.gitignore b/website/docs/.gitignore new file mode 100644 index 00000000..8c6492ca --- /dev/null +++ b/website/docs/.gitignore @@ -0,0 +1 @@ +api/ diff --git a/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index 756b2880..e94ac04f 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -3,7 +3,6 @@ sidebar_position: 200 description: Build actions, registering hooks, discover enhancers and runners. --- -import Link from '@docusaurus/Link'; import FunctionInfo from '@site/src/components/FunctionInfo'; # Actions @@ -12,23 +11,20 @@ import FunctionInfo from '@site/src/components/FunctionInfo'; - Enhancing actions - Running actions -- Extending actions for builder pattern calls - Conditionally enhancing and running actions -- Registering hooks on actions +- Action lifecycle ::: ## Before reading this guide This guide is only about basic usage and lifecycle of actions. +To learn more, you can: -- You can read more about action factories setups in the - [getting started guide](/docs/getting-started#action-factory). -- Advanced usage is different between the various implementations. You can see - advanced usage of actions for your implementation inside the - [digging deeper actions guide](/docs/category/actions). -- If you want to see some concrete examples on how actions could be used, you - can check the [examples documentation section](/docs/category/examples). +- [Check the getting started guide to learn more about action factory setup](/docs/getting-started#first-actions) +- [Read the usages guides to learn more about possibilities with your implementation](/docs/category/usages) +- [Learn advanced capabilities of actions in the dedicated digging deeper guides](/docs/category/actions) +- [Discover actions through examples](/docs/category/examples) ## Usage @@ -37,24 +33,27 @@ through your models. Foscia uses two typologies of functions to run actions, **enhancers** and **runners**, which are described in the [lifecycle guide](#lifecycle). +The next part of this guide focuses on most common actions (e.g. CRUD). -:::info +:::tip -Examples of the documentation commonly use -[**variadic `run` calls**](#variadic-run) on actions, as this is the simplest -way of using actions. +To complete this, you can browse the API documentation to see available +[**enhancers**](/docs/api/@foscia/core/#enhancers) and +[**runners**](/docs/api/@foscia/core/#runners). ::: ### Retrieving records -`query` can target a records index using a model. You can use it in -combination with `all` to retrieve a list of the model instances. +[`query`](/docs/api/@foscia/core/functions/query) can target a records index +using a model. You can use it in combination with +[`all`](/docs/api/@foscia/core/functions/all) to retrieve a list of +the model instances. ```typescript import { query, all } from '@foscia/core'; -const posts = await action().run( +const posts = await action( query(Post), all(), ); @@ -64,54 +63,63 @@ posts.forEach((post) => { }); ``` -In addition, `query` also provide a way to target a record using a model and -an ID. You can use it in combination with `oneOrFail` or `oneOr` to retrieve -a single record and handle not found errors. +In addition, [`query`](/docs/api/@foscia/core/functions/query) also provide a +way to target a record using a model and an ID. You can use it in combination +with [`oneOrFail`](/docs/api/@foscia/core/functions/oneOrFail) or +[`oneOr`](/docs/api/@foscia/core/functions/oneOr) to retrieve a single record +and handle not found errors. ```typescript import { query, oneOrFail } from '@foscia/core'; -const postOrFail = await action().run( +const postOrFail = await action( query(Post, '1'), oneOrFail(), ); -const postOrNull = await action().run( +const postOrNull = await action( query(Post, '2'), oneOr(() => null), ); ``` -Finally, `query` can also be used to target a record using an instance. -If the instance does not exist, this will target the records index, otherwise -it will target the record. This can be used when writing a record (e.g. -create/update) or when [lazy loading a relation](#lazy-loading-relations). +Finally, [`query`](/docs/api/@foscia/core/functions/query) can also be used to +target a record using an instance. If the instance does not exist, this will +target the records index, otherwise it will target the record. +This can be used when writing a record (e.g. create/update) +or when [lazy loading a relation](#lazy-loading-relations). ```typescript import { query, oneOrFail } from '@foscia/core'; -const post = await action().run(query(somePost), oneOrFail()); +const post = await action(query(somePost), oneOrFail()); ``` ### Creating and updating records -You can create or update records using `create` and `update` functions. As a -convenience, Foscia provides a `save` function which will call `create` or -`update` depending on the existence state of your record. - -`oneOrCurrent` is a runner which you can combine with those enhancers to always -retrieve an instance, even if your data source does not return the record on +You can create or update records using +[`create`](/docs/api/@foscia/core/functions/create) and +[`update`](/docs/api/@foscia/core/functions/update) functions. As a convenience, +Foscia provides a [`save`](/docs/api/@foscia/core/functions/save) function which +will call [`create`](/docs/api/@foscia/core/functions/create) or +[`update`](/docs/api/@foscia/core/functions/update) depending on the existence +state of your record. + +[`oneOrCurrent`](/docs/api/@foscia/core/functions/oneOrCurrent) is a runner +which you can combine with those enhancers to always retrieve an instance, +even if your data source does not return the record on those actions (such as a `204 No Content` on update). It will return a record if there is one if your data source response, otherwise it will return the initially provided instance. -You can also combine those functions with `none` runner to not handle the data +You can also combine those functions with +[`none`](/docs/api/@foscia/core/functions/none) runner to not handle the data source response. ```typescript import { create, save, fill, oneOrCurrent, none } from '@foscia/core'; -const post = await action().run( +const post = await action( create(fill(new Post(), { title: 'Hello World!' })), include('author'), oneOrCurrent(), @@ -119,19 +127,35 @@ const post = await action().run( post.title = 'Edited Hello World!'; -await action().run(save(post), none()); +await action(save(post), none()); ``` ### Deleting records -You can use `destroy` to trigger a record deletion. You can combine it with -`none` to ignore the data source's response (errors are still thrown by the -adapter). +#### Deleting by instance + +You can use [`destroy`](/docs/api/@foscia/core/functions/destroy) to trigger +a record deletion. You can combine it with +[`none`](/docs/api/@foscia/core/functions/none) to ignore the data +source's response (errors are still thrown by the adapter). ```typescript import { destroy, none } from '@foscia/core'; -await action().run(destroy(post), none()); +await action(destroy(post), none()); +``` + +#### Deleting by ID + + + +In addition, you can delete a record using only its model and ID, instead of +passing an already retrieved instance. + +```typescript +import { destroy, none } from '@foscia/core'; + +await action(destroy(Post, '1'), none()); ``` ### Relationships @@ -148,12 +172,13 @@ Eager loading provides a way to retrieve records and eager load related models without further requests. It also alleviates the "N + 1" query problem. -To eager load a relation, you can use the `include` function. +To eager load a relation, you can use the +[`include`](/docs/api/@foscia/core/functions/include) function. ```typescript import { query, all, include } from '@foscia/core'; -const posts = await action().run( +const posts = await action( query(Post), include('author'), all(), @@ -169,9 +194,9 @@ relation with an already retrieved a record. import { query, all, oneOrFail } from '@foscia/core'; // Fetch a post without relations. -const post = await action().run(query(Post, 1), oneOrFail()); +const post = await action(query(Post, 1), oneOrFail()); // Fetch "comments" relation. -const comments = await action().run(query(post, 'comments'), all()); +const comments = await action(query(post, 'comments'), all()); ``` :::info @@ -185,21 +210,23 @@ Lazy loading a record's relation won't affect the given record instance ##### To one -For `hasOne` relationship, you can use `associate` and `dissociate` +For [`hasOne`](/docs/api/@foscia/core/functions/hasOne) relationship, +you can use [`associate`](/docs/api/@foscia/core/functions/associate) +and [`dissociate`](/docs/api/@foscia/core/functions/dissociate) to change the relation value. ```typescript import { associate, dissociate, none } from '@foscia/core'; // Associate myUser as myPost's author. -await action().run( +await action( associate(myPost, 'author', myUser), none(), ); console.log(myPost.author); // myUser // Dissociate author of myPost. -await action().run( +await action( dissociate(myPost, 'author'), none(), ); @@ -208,40 +235,44 @@ console.log(myPost.author); // null :::info -Using `associate` and `dissociate` **will update** the model's property and mark -it synced. +Using [`associate`](/docs/api/@foscia/core/functions/associate) and +[`dissociate`](/docs/api/@foscia/core/functions/dissociate) **will update** +the model's property and mark it synced. ::: ##### To many -For `hasMany` relationship, you can use `attach`, `detach` and `updateRelation` +For [`hasMany`](/docs/api/@foscia/core/functions/hasMany) relationship, you can +use [`attach`](/docs/api/@foscia/core/functions/attach), +[`detach`](/docs/api/@foscia/core/functions/detach) and +[`updateRelation`](/docs/api/@foscia/core/functions/updateRelation) to change the relation value: -- `attach` will attach one or many related records, without touching existing - related records; -- `detach` will detach one or many related records, without touching attaching - other records; -- `updateRelation` will sync related records to the given records (detach past - records, attach given records); +- [`attach`](/docs/api/@foscia/core/functions/attach) will attach one or + many related records, without touching existing related records; +- [`detach`](/docs/api/@foscia/core/functions/detach) will detach one or + many related records, without touching attaching other records; +- [`updateRelation`](/docs/api/@foscia/core/functions/updateRelation) will + sync related records to the given records (detach past records, attach given records); ```typescript import { attach, detach, updateRelation, none } from '@foscia/core'; // Attach myUser1 and myUser2 to myTeam's members. -await action().run( +await action( attach(myTeam, 'members', [myUser1, myUser2]), none(), ); // Detach myUser1 and myUser2 from myTeam's members. -await action().run( +await action( detach(myTeam, 'members', [myUser1, myUser2]), none(), ); // Sync myUser1 and myUser2 as myTeam's members. -await action().run( +await action( updateRelation(myTeam, 'members', [myUser1, myUser2]), none(), ); @@ -249,8 +280,10 @@ await action().run( :::warning -Using `attach`, `detach` and `updateRelation` **will not update** the model's -property. +Using [`attach`](/docs/api/@foscia/core/functions/attach), +[`detach`](/docs/api/@foscia/core/functions/detach) and +[`updateRelation`](/docs/api/@foscia/core/functions/updateRelation) +**will not update** the model's property. ::: @@ -262,13 +295,14 @@ In some data source implementation, you may want to create a record "through" a parent record's relation. As an example, creating a comment through a post in a REST API, and using a `POST /posts/1/comments` request. -This can be achieved using the `create` enhancer. +This can be achieved using the +[`create`](/docs/api/@foscia/core/functions/create) enhancer. ```typescript import { create } from '@foscia/core'; // Create a comment through a post. -await action().run( +await action( create(newComment, post, 'comments'), oneOrCurrent(), ); @@ -279,29 +313,32 @@ await action().run( When having a cache enabled on your action, all fetched records are cached when deserializing. You can use the cache to retrieve an already fetched record without interacting with your data source again -using `cached` and `cachedOrFail`. +using [`cached`](/docs/api/@foscia/core/functions/cached) and +[`cachedOrFail`](/docs/api/@foscia/core/functions/cachedOrFail). ```typescript import { query, cached, cachedOrFail } from '@foscia/core'; -const postOrNull = await action().run(query(Post, 1), cached()); -const postOrFail = await action().run(query(Post, 1), cachedOrFail()); +const postOrNull = await action(query(Post, 1), cached()); +const postOrFail = await action(query(Post, 1), cachedOrFail()); ``` Sometimes, you may want to interact with the cache if your record does not -have been cached already. For this particular case, you can use `cachedOr`. +have been cached already. For this particular case, you can use +[`cachedOr`](/docs/api/@foscia/core/functions/cachedOr). ```typescript import { query, cachedOr, oneOrFail } from '@foscia/core'; -const cachedPostOrFetch = await action().run(query(Post, 1), cachedOr(oneOrFail())); +const cachedPostOrFetch = await action(query(Post, 1), cachedOr(oneOrFail())); ``` :::info -`cached` runners will check for loaded relations if you are requesting -relations `include`. If some relations (even deep ones) are not loaded, -it will act as the record is not inside the cache. +[`cached`](/docs/api/@foscia/core/functions/cached) runners will check for +loaded relations if you are requesting relations +[`include`](/docs/api/@foscia/core/functions/include). If some relations +(even deep ones) are not loaded, it will act as the record is not inside the cache. ::: @@ -309,19 +346,21 @@ it will act as the record is not inside the cache. Sometimes, you may need to conditionally apply an enhancer or run an action. As an example, you may want to sort results differently based on the user's defined -sort's direction. This can be done easily using the `when` helper: +sort's direction. This can be done easily using the +[`when`](/docs/api/@foscia/core/functions/when) helper: ```typescript import { query, when } from '@foscia/core'; import { sortByDesc } from '@foscia/jsonapi'; -action().run( +action( query(Post), - when(displayLatestFirst, sortByDesc('createdAt'), + when(displayLatestFirst, sortByDesc('createdAt')), ); ``` -`when` returns a new enhancer or runner based on the given value's _truthiness_. +[`when`](/docs/api/@foscia/core/functions/when) returns a new enhancer or +runner based on the given value's _truthiness_. It will execute the first enhancer/runner only if its value is _truthy_. You may pass the value as a factory function returning the value, and even a promise value. You may also pass a second enhancer/runner which will only execute if the @@ -329,6 +368,15 @@ value is _falsy_. Each callback arguments will receive the action as their first argument and the value as their second argument. Each callback may also be async, as any enhancers and runners. +:::info + +[`when`](/docs/api/@foscia/core/functions/when) will create a new enhancer or +runner which will be **evaluated on run**. +This has benefits, as the condition can be an async value or +callback, but it also means that the condition won't be evaluated immediately. + +::: + Here are further examples: ```typescript @@ -336,7 +384,7 @@ import { changed, create, oneOrFail, when } from '@foscia/core'; const post = fill(new Post(), userInputData); -action().run( +action( create(post), when( () => /* compute a special value */, @@ -351,39 +399,171 @@ action().run( ); ``` +### Errors catching + +Catching special errors is a common use case when dealing with data exchanges. +For this, Foscia provides a [`catchIf`](/docs/api/@foscia/core/functions/catchIf) +runner, allowing you to quickly handle common error management scenarios. + +You can use [`catchIf`](/docs/api/@foscia/core/functions/catchIf) to make your +runner returns `null` on every error. You can compare this code to his +equivalent without using [`catchIf`](/docs/api/@foscia/core/functions/catchIf), +which is more verbose. + +```typescript +const postOrNull = await action( + query(Post, '123'), + catchIf(oneOrFail()), +); +``` + +

+ + + +Equivalent without `catchIf` + + + +```typescript +let postOrNull = null as Post | null; + +try { + postOrNull = await action( + query(Post, '123'), + oneOrFail(), + ); +} catch (error) { + postOrNull = null; +} +``` + +
+ +#### Ignoring specific errors + +In the following example, we would like to create a stat event for each page +viewed, ignoring all "too many requests" errors. + +```typescript +const statEvent = fill(new StatEvent(), { + type: 'page-viewed', + url: 'https://example.com', +}); + +await action( + create(statEvent), + catchIf(none(), (error) => error instanceof HttpTooManyRequestsError), +); +``` + +
+ + + +Equivalent without `catchIf` + + + +```typescript +const statEvent = fill(new StatEvent(), { + type: 'page-viewed', + url: 'https://example.com', +}); + +try { + await action( + create(statEvent), + none(), + ); +} catch (error) { + if (error instanceof HttpTooManyRequestsError) { + // Ignoring too many requests errors. + } else { + // Throwing any other errors. + throw error; + } +} +``` + +
+ +#### Using another runner on specific errors + +Instead of returning `null` on caught errors, you can also use +[`catchIf`](/docs/api/@foscia/core/functions/catchIf) to execute another +runner when catching errors. + +```typescript +// Run a first `oneOrFail`, and another one if an error is thrown. +await action( + query(Post, '123'), + catchIf(oneOrFail(), () => oneOrFail()) +); + +// Run a first `oneOrFail`, and another one only if a "too many requests" +// error is thrown. +await action( + query(Post, '123'), + catchIf(oneOrFail(), (error) => ( + error instanceof HttpTooManyRequestsError && oneOrFail() + )) +); +``` + +Here is a real world example which uses catch runner to retry a request +which has been rejected by the server after a 15sec delay using a hypothetical +`wait` helper function. + +```typescript +await action( + create(statEvent), + catchIf(none(), (error) => ( + error instanceof HttpTooManyRequestsError + && (async (action) => { + await wait(15 * 1000); + + return action.run(none()); + }) + )), +); +``` + ## Lifecycle -When using [variadic `run` calls](#variadic-run), Foscia actions lifecycle +When using variadic factory calls, Foscia actions lifecycle is "hidden" by variadic arguments. In fact, all actions execution have -3 steps: instantiation, enhancements and execution (run). +3 steps: creation, enhancements and execution (run). ```typescript -action().run( // Instantiation. - query(Post), // `query` enhancement. - include('comments'), // `include` enhancement. - all(), // `all` run. +// Creation, context enhancements (`query`, `include`) and run (`all`) in one call. +action( + query(Post), + include('comments'), + all(), ); ``` -The above example is equivalent to and can be decomposed as follows: +The above example is equivalent to the following: ```typescript -action() // Instantiation. +action() // Creation. .use(query(Post)) // `query` enhancement. .use(include('comments')) // `include` enhancement. .run(all()); // `all` run. ``` -### Instantiation +### Creation As stated in the [getting started guide](/docs/getting-started#running-simple-actions), actions -are instantiated through your action factory. In this guide, we'll admit you -have a setup action factory. +are created through your action factory. The creation step can already use +enhancers, generally to provide common dependencies (adapter, serializer, etc.). +In this guide, we'll admit you have a setup action factory. ### Enhancers -An action instance can receive multiple enhancements that will build an +An action can receive multiple enhancements that will build an appropriate context to run requests to your data sources. Each enhancer can be applied using the `use` action method. Note that those @@ -411,15 +591,9 @@ action().use( ); ``` -#### Enhancers API guide - - - Available enhancers API guide - - ### Runners -An action instance can be run using the `run` method. The runner can execute +An action can be run using the `run` method. The runner can execute multiple enhancers or runners internally. When an action run, it does 3 things: @@ -430,7 +604,8 @@ When an action run, it does 3 things: - Return the runner result (might be any value, including void or an error throwing) -Internally, action running will also trigger [actions hooks](#hooks). +Internally, action running will also trigger +[actions hooks](/docs/digging-deeper/actions/actions-hooks). ```typescript action() @@ -442,8 +617,9 @@ action() #### Variadic `run` -`run` also support variadic enhancers. Last argument must be a runner. -Number of arguments might be limited, check the function signature for details. +`run` also support variadic enhancers finishing with a runner, just like the +factory variadic calls. Number of arguments might be limited, +check the function signature for details. ```typescript const posts = await action().run( @@ -453,131 +629,3 @@ const posts = await action().run( all(), ); ``` - -#### Runners API guide - - - Available runners API guide - - -### Hooks - -You may hook on multiple events which occurs on action instance using the hook -registration function: - -- `onRunning`: after context computation, before context runner execution. -- `onSuccess`: after context runner successful execution (no error thrown). -- `onError`: after context runner failed execution (error thrown). -- `onFinally`: after context runner successful or failed execution. - -To register a hook callback, you must use the registration enhancer on your -building action. - -```typescript -import { - onRunning, - onSuccess, - onError, - onFinally, -} from '@foscia/core'; - -action().use(onRunning(({ context }) => /* ... */)); -action().use(onSuccess(({ context, result }) => /* ... */)); -action().use(onError(({ context, error }) => /* ... */)); -action().use(onFinally(({ context }) => /* ... */)); -``` - -:::info - -Hooks' callbacks are async and executed in a sequential fashion (one by one, not -parallelized). - -::: - -You can temporally disable hook execution for a given action by using the -`withoutHooks` function. `withoutHooks` can receive a sync or async -callback: if an async callback is passed, it will return a `Promise`. - -```typescript -import { query, all, withoutHooks } from '@foscia/core'; - -// Retrieve a list of User instances without action hooks running. -const users = await withoutHooks(action(), async (a) => { - return await a.use(query(User)).run(all()); -}); -``` - -:::warning - -**Foscia may also register hooks internally** when using some enhancers. Those -provide some library features -([**models hooks**](/docs/core-concepts/models#hooks), etc.). Be careful running -actions without hooks, as those hooks will also be disable. - -::: - -## Extensions - -Sometimes, functional programming can be frustrating, because you must always -rewrite the same words (e.g. `use`) to keep a builder pattern styled code. - -Extensions provide a set of properties or methods which will be added to your -actions' instances. As an action, extensions can avoid you writing `use` or -`run` by adding enhancers/runners methods on you action. - -The first step to use one or many extensions is to update your action factory in -which you should provide a second parameter. - -```typescript title="action.ts" -import { - makeActionFactory, - query, - include, - all, - hooksExtensions, -} from '@foscia/core'; - -export default makeActionFactory( - { - // makeJsonRestAdapter(), ...etc. - }, - { - ...hooksExtensions(), - ...query.extension(), - ...include.extension(), - ...all.extension(), - }, -); -``` - -You can now use the extended enhancers and runners without calling `use` or -`run`: - -```typescript -import action from './action'; - -await action().query(Post).include('tags').all(); -``` - -Every enhancers and runners of Foscia provide a `.extension()` property which is -extendable by an action instance. - -You may extend your action with any enhancers or runners extensions manually. -Otherwise, you may also use **prebuild extensions packs**. Those provide -multiple extensions in one exported object allowing you to extend multiple -extensions at one time! - - - Available extensions packs API guide - - -:::warning - -Keep in mind that using extensions will avoid tree-shaking the extended -enhancers or runners functions (even when those are unused in your codebase), -because those are imported by their extensions. - -::: diff --git a/website/docs/core-concepts/environment.md b/website/docs/core-concepts/environment.md new file mode 100644 index 00000000..c8654a5e --- /dev/null +++ b/website/docs/core-concepts/environment.md @@ -0,0 +1,69 @@ +--- +sidebar_position: 900 +description: Understand Foscia internal environment and configure the logger. +--- + +# Environment + +:::tip What you'll learn + +- How Foscia detects and uses your environment +- How the logger works and how you can configure the behavior + +::: + +## Environment detection + +Foscia tries to detect the current environment it runs in, between production, +development and testing. For this, it relies on the `process.env.NODE_ENV` +variable. When the environment detection fails, it defaults to production. + +Environment is currently used in Foscia to known which messages +should be logged. + +## Logger + +Foscia uses a [`logger`](/docs/api/@foscia/core/variables/logger) object to output +messages to the console. The logger can be used to display many messages, +from action runs debug information to warning about attribute transformation +failure. + +The logger allows two kind of configuration: minimum log level and output to +write messages on. + +### Configuring minimum log level + +You can configure the minimum log level by changing the +[`level`](/docs/api/@foscia/core/type-aliases/Logger#level) property +of the logger to any level in `error`, `warn`, `info` and `debug`. + +Setting the property to `null` will totally disable logging. + +```typescript +import { logger } from '@foscia/core'; + +logger.level = 'info'; +``` + +:::info + +Foscia will automatically define the minimum log level depending on your +environment (`error` in production, `warn` in development and `null` in testing). + +::: + +### Configuring output + +You can also change the output of the logger. Default behavior is to output +messages to the `console`. + +```typescript +import { logger } from '@foscia/core'; + +logger.output = { + error: (message, ...args) => console.error(message, ...args), + warn: (message, ...args) => console.warn(message, ...args), + info: (message, ...args) => console.info(message, ...args), + debug: (message, ...args) => console.debug(message, ...args), +}; +``` diff --git a/website/docs/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index 87a20125..92f53e87 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -3,10 +3,8 @@ sidebar_position: 100 description: Define models with attributes, relations and hooks. --- -import Link from '@docusaurus/Link'; import TabItem from '@theme/TabItem'; import Tabs from '@theme/Tabs'; -import Chip from '@site/src/components/Chip'; import ShellCommand from '@site/src/components/ShellCommand'; # Models @@ -19,27 +17,36 @@ import ShellCommand from '@site/src/components/ShellCommand'; ::: +## Before reading this guide + +This guide is only about basic usages of models and definition of the model's +schema. +To learn more, you can: + +- [Learn advanced capabilities of models in the dedicated digging deeper guides](/docs/category/models) +- [Discover models through examples](/docs/category/examples) + ## Models ### Using CLI -You can generate a new model using [`@foscia/cli`](/docs/digging-deeper/cli). +You can generate a new model using [`@foscia/cli`](/docs/digging-deeper/usages/cli). ### Model factory -`makeModel` is the default model factory function. It defines a new model using -2 arguments and returns an ES6 class: +To declare a model, you just need to use the +[`makeModel`](/docs/api/@foscia/core/functions/makeModel) function. +This function takes up to 2 arguments and returns a +[`Model`](/docs/api/@foscia/core/interfaces/Model) constructor: -- The string `type` or a +- The `type` string or a [configuration object](/docs/digging-deeper/models/models-configuration). -- The [optional `definition` of the model](#definition): an object map -containing IDs/attributes/relations definitions, custom properties, custom +- The [optional `definition` of the model](#definition): an object +containing property definition factories, custom properties, custom methods and composables. -The attributes and relations definition represents the `schema` of the model. - ```typescript import { makeModel, attr, hasMany, toDateTime } from '@foscia/core'; @@ -57,14 +64,18 @@ export default makeModel('posts', { ### Extending a model class -`makeModel` will return a model class which can be extended by an ES6 class. +[`makeModel`](/docs/api/@foscia/core/functions/makeModel) will return a +[`Model`](/docs/api/@foscia/core/interfaces/Model) constructor +which can be extended by an ES6 class. ```typescript export default class Post extends makeModel('posts') {} ``` -The returned model class also provides static methods to extend the definition -already provided to `makeModel`. +The returned [`Model`](/docs/api/@foscia/core/interfaces/Model) constructor +also implements [`ExtendableModel`](/docs/api/@foscia/core/type-aliases/ExtendableModel) +and provides static methods to extend the definition +already provided to [`makeModel`](/docs/api/@foscia/core/functions/makeModel). ```typescript /* Initial model creation without definition */ @@ -101,8 +112,8 @@ original model class. #### Note on exported value In many Foscia guides and examples, you will see that the ES6 class returned by -`makeModel` is extended before exporting: we use -`export default class Post extends makeModel...` instead of +[`makeModel`](/docs/api/@foscia/core/functions/makeModel) is extended before +exporting: we use `export default class Post extends makeModel...` instead of `export default makeModel...`. This has two benefits: @@ -141,11 +152,12 @@ those. ### Utilities -Foscia proposes you multiple utilities functions to interact with models. - - - Read the models' utilities API guide - +Foscia provides multiple utilities functions to interact with models, such as +[`fill`](/docs/api/@foscia/core/functions/fill) to change an instance's values +or [`changed`](/docs/api/@foscia/core/functions/changed) to check if an +instance's values changed since last data source sync. You can discover +all those utilities functions inside the +[**API documentation utilities category**](/docs/api/@foscia/core/#utilities). ## Definition @@ -153,8 +165,9 @@ Foscia proposes you multiple utilities functions to interact with models. #### Description -`id` is a pending ID definition factory function used to define your model's IDs -properties. You can pass a transformer or a default value to this factory. +[`id`](/docs/api/@foscia/core/functions/id) is an ID definition factory used +to define your model's IDs properties. +You can pass a transformer or a default value to this factory. - Foscia consider your IDs as `string`, `number` or `null` values by default. Each model have `id` and `lid` properties representing record identification. @@ -162,7 +175,7 @@ properties. You can pass a transformer or a default value to this factory. can use the `id` function. - `id` properties can be transformed. [Read more on the transformers guide](/docs/digging-deeper/models/models-transformers). -- `id` pending definition supports [chained modifiers](#ids-chained-modifiers). +- `id` definition factory supports [chained modifiers](#ids-chained-modifiers). :::warning @@ -174,174 +187,332 @@ aliasing an ID in Foscia. ::: -#### Example +#### Examples -```typescript -import { id, toString } from '@foscia/core'; + + -// With TypeScript type. +```typescript id(); -// With default value. -id(null as string | null); -id(() => null as string | null); -// With transformer to infer type. +id(); +``` + + + + +```typescript +id(''); +id(() => uuidV4(), { readOnly: true }); +``` + + + + +```typescript id(toString()); -// With chained modifiers. -id(toString()) +id(toString(), { readOnly: true }); +``` + + + + +```typescript +id() .nullable() .default(() => null) .readOnly(); ``` -#### Chained modifiers {#ids-chained-modifiers} + + + +#### API {#ids-chained-modifiers} -| Name | Parameters | Effect | -| ----------- | ------------------------------------------- | -------------------------------------- | -| `transform` | `ObjectTransformer` | Use a transformer. | -| `default` | unknown | (() => unknown) | Set a default value on `new` instance. | -| `readOnly` | `boolean` | Set read-only state. | -| `nullable` | - | Set current type as nullable. | +[`id`](/docs/api/@foscia/core/functions/id) provides type definition +for the factory and examples. +[`ModelIdFactory`](/docs/api/@foscia/core/type-aliases/ModelIdFactory) +provides type definition of `id` chained modifiers. ### Attributes #### Description -`attr` is an attribute definition factory function used to define your model's +[`attr`](/docs/api/@foscia/core/functions/attr) is an attribute definition +factory used to define your model's attributes. You can pass a transformer or a default value to this factory. - Foscia consider your attributes as non-nullable values by default. - Non-loaded attributes will have a value of `undefined`. - `attr` properties can be transformed. [Read more on the transformers guide](/docs/digging-deeper/models/models-transformers). -- `attr` pending definition supports +- `attr` definition factory supports [chained modifiers](#attributes-chained-modifiers). -#### Example +#### Examples -```typescript -import { attr, toDateTime } from '@foscia/core'; + + -// With TypeScript type. +```typescript attr(); -// With default value. -attr(null as string | null); -attr(() => null as string | null); -// With transformer to infer type. -attr(toDateTime()); -// With chained modifiers. -attr(toDateTime()) +attr(); +``` + + + + +```typescript +attr(''); +attr(() => new Date(), { readOnly: true }); +``` + + + + +```typescript +attr(toString()); +attr(toDateTime(), { readOnly: true }); +``` + + + + +```typescript +attr() .nullable() .default(() => null) - .readOnly() - .alias('published-at') - .sync('pull'); + .readOnly(); ``` -#### Chained modifiers {#attributes-chained-modifiers} + + -| Name | Parameters | Effect | -| ----------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `transform` | `ObjectTransformer` | Use a transformer. | -| `default` | unknown | (() => unknown) | Set a default value on `new` instance. | -| `readOnly` | `boolean` | Set read-only state. | -| `nullable` | - | Set current type as nullable. | -| `alias` | `string` | Set an alias for data source interactions. | -| `sync` | boolean | 'pull' | 'push' | Set sync state: `true` for always (default), `false` for never, `'pull'` to ignore property when serializing, `'push'` to ignore property when deserializing). | +#### API {#attributes-chained-modifiers} + +[`attr`](/docs/api/@foscia/core/functions/attr) provides type definition +for the factory and examples. +[`ModelAttributeFactory`](/docs/api/@foscia/core/type-aliases/ModelAttributeFactory) +provides type definition of `attr` chained modifiers. ### Relations #### Description -`hasMany` and `hasOne` are relation definition factory function used to define -your model's relations. As suggested by their names, `hasMany` represents a +[`hasMany`](/docs/api/@foscia/core/functions/hasMany) and +[`hasOne`](/docs/api/@foscia/core/functions/hasOne) are relation +definition factories used to define your model's relations. +As suggested by their names, `hasMany` represents a relation to a list of models and `hasOne` represents a relation to a single model. You can pass the relation information to this factory. - Foscia consider your relations as non-nullable values by default. - Non-loaded relations will have a value of `undefined`. -- `hasOne` and `hasMany` pending definition supports +- `hasOne` and `hasMany` definition factories supports [chained modifiers](#relations-chained-modifiers). - Depending on your data structure, you should follow one of the recommandation over relations definition to avoid circular dependencies errors: - [Explicit **model** when **not having circular dependencies**](#explicit-model-when-not-having-circular-references). - [Explicit **type** when **having circular dependencies**](#explicit-type-when-having-circular-references). -#### Example +#### Examples - + -```typescript -import { hasOne } from '@foscia/core'; -import User from './user'; + + -// With explicit related model. +```typescript hasOne(() => User); -// With TypeScript type. +hasOne(() => User, { readOnly: true }); +``` + + + + +```typescript hasOne(); -// With explicit related type. hasOne('users'); -// With config object. -hasOne({ type: 'users', path: 'author' }); -// With chained modifiers. -hasOne('users') - .config({ path: 'author' }) +``` + + + + +```typescript +hasOne(() => [Comment, Post]); +hasOne(['comments', 'posts']); +``` + + + + +```typescript +hasOne(() => CreditCard, { inverse: true }); +hasOne(() => CreditCard, { inverse: 'owner' }); +``` + + + + +```typescript +hasMany(() => User, { path: 'author' }); +hasMany({ path: 'author' }); +``` + + + + +```typescript +hasOne(() => User) .nullable() - .default(() => null) .readOnly() .alias('author') - .sync('pull'); + .sync('pull') + .inverse(); ``` - - + + -```typescript -import { hasMany } from '@foscia/core'; -import Comment from './comment'; + + + + + -// With explicit related model. +```typescript hasMany(() => Comment); -// With TypeScript type. +hasMany(() => Comment, { readOnly: true }); +``` + + + + +```typescript hasMany(); -// With explicit related type. hasMany('comments'); -// With config object. -hasMany({ type: 'comments', path: 'comments' }); -// With chained modifiers. -hasMany('comments') - .config({ path: 'comments' }) - .nullable() - .default(() => null) - .readOnly() - .alias('comments') - .sync('pull'); ``` - - + + -#### Polymorphism +```typescript +hasMany(() => [Comment, Post]); +hasMany<(Comment | Post)[]>(['comments', 'posts']); +``` -Defining a polymorphic relation is pretty simple: + + ```typescript -// With explicit related models. -hasOne(() => [Post, Comment]); -hasMany(() => [Post, Comment]); -// With explicit related types. -hasOne(['posts', 'comments']); -hasMany<(Post | Comment)[]>(['posts', 'comments']); +hasMany(() => Post, { inverse: true }); +hasMany(() => Post, { inverse: 'author' }); ``` -:::warning + + + +```typescript +hasMany(() => Comment, { path: 'top-comments' }); +hasMany({ path: 'top-comments' }); +``` + + + + +```typescript +hasMany(() => Comment) + .readOnly() + .sync('pull') + .inverse(); +``` + + + + + + + +#### API {#relations-chained-modifiers} + +[`hasOne`](/docs/api/@foscia/core/functions/hasOne) and +[`hasMany`](/docs/api/@foscia/core/functions/hasMany) provide type definition +for the factories and examples. +[`ModelRelationFactory`](/docs/api/@foscia/core/type-aliases/ModelRelationFactory) +provides type definition of `hasOne` and `hasMany` chained modifiers. + +#### Polymorphism support Polymorphism may require a specific configuration of your action or your data source. @@ -353,18 +524,75 @@ correct model. When implementing polymorphism with a REST data source, your record JSON object must contain a `type` property matching your Foscia models' types. +#### Relations inverse + +Relations inverse allow to automatically fill the related instance inverse +relation with the parent instance. This is particularly useful when fetching +records with their relations, as all related records will get their "inverse" +relation hydrated with the parent record. + +:::info + +Inverse of a relation must target a singular relation (like an `hasOne` +relation). This is because plural inverse's related records cannot be reliably +determined. In addition, be aware that inverse hydration only occurs +during deserialization process. + ::: -#### Chained modifiers {#relations-chained-modifiers} +##### Configuring an inverse + +To enable inverse of a relation, you can define an +[`inverse` option](/docs/api/@foscia/core/interfaces/ModelRelationFactoryConfig#inverse) +to your relation configuration, or call the +[`inverse` chained modifier](/docs/api/@foscia/core/type-aliases/ModelRelationFactory#inverse). +Giving a boolean value, it will enable or disable automatic inverse resolution +(searching for a relation named by the singular camel case type of the parent +model). Giving a string value, it will enable inverse resolution using +the given relation name. + +```typescript +class Post extends makeModel('posts', { + // Automatically define inverse to `post` relation on `Comment` model. + comments: hasMany(() => Comment, { inverse: true }), + // Manually define inverse to `post` relation on `Comment` model. + comments: hasMany(() => Comment, { inverse: 'post' }), +}) { +} +``` + +##### Example -| Name | Parameters | Effect | -| ---------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `config` | string | string[] | ModelRelationConfig | Specify the relation configuration: related types and custom implementations related options, see [relations configuration](#relations-configuration). | -| `default` | unknown | (() => unknown) | Set a default value on `new` instance. | -| `readOnly` | `boolean` | Set read-only state. | -| `nullable` | - | Set current type as nullable. | -| `alias` | `string` | Set an alias for data source interactions. | -| `sync` | boolean | 'pull' | 'push' | Set sync state: `true` for always (default), `false` for never, `'pull'` to ignore property when serializing, `'push'` to ignore property when deserializing). | +To explain this, we will take two models: +a `Comment` model with an `post` "to one" relation to a `Post` model, with +a `comments` "to many" relation to the `Post` model, like this: + +```typescript +class Post extends makeModel('posts', { + comments: hasMany(() => Comment, { inverse: 'post' }), +}) { +} + +class Comment extends makeModel('comments', { + post: hasOne(() => Post), +}) { +} +``` + +With those models and relations definition, when deserializing a `Post` +with included `comments` relation, each `Comment` will have its `post` relation +filled with the parent `Post` instance. + +```typescript +const posts = await action(query(Post), include('comments'), all()); + +posts.forEach((post) => { + post.comments.forEach((comment) => { + // `comment.post` is filled with its parent `post`. + console.log(comment.post); + }); +}); +``` #### Recommandations @@ -419,16 +647,6 @@ import type User from './user'; hasOne('users'); ``` -#### Configuration {#relations-configuration} - -Using `config` chained modifier, you can customize the relation configuration, -which can vary between implementations and used dependencies. - -| Name | Parameters | Effect | -|--------|-------------------------------------|------------------------------------------------------------------------------------------------| -| `type` | string | string[] | Specify related models' types. | -| `path` | string | only: HTTP Specify a path alias for dedicated relation endpoints. | - ### Custom properties In addition to IDs, attributes and relations, you can implement additional @@ -458,229 +676,10 @@ export default class User extends makeModel('users', { } ``` -:::warning - -Please note that when using the object spread syntax, you won't be able to get a -correctly typed `this` context _inside the current definition object_ (it is -still available in next/previous definition or in class body). This is because -of an -[**issue due to a TypeScript limitation**](https://github.com/foscia-dev/foscia/issues/2). - -::: - -## Hooks - -### Using hooks - -You can hook multiple events on model's instances using hook registration -functions, such as `onCreating`. - -To hook on an event, use the dedicated hook registration function. Each -hook registration function will return a callback to unregister the hook. - -```typescript -import { onCreating } from '@foscia/core'; - -// After this, the hook will run on each User instance saving. -const unregisterThisHook = onCreating(User, async (user) => { - // TODO Do something (a)sync with user instance before saving. -}); - -// After this, this hook will never run again. -unregisterThisHook(); -``` - -You can also use `unregisterHook` to remove a registered hook from a model. - -```typescript -import { onCreating, unregisterHook } from '@foscia/core'; - -const myCreatingHook = async (user: User) => { - // TODO Do something (a)sync with user instance before saving. -}; - -// After this, the hook will run on each User instance saving. -onCreating(User, myCreatingHook); - -// After this, this hook will never run again. -unregisterHook(User, 'creating', myCreatingHook); -``` - -Finally, you can temporally disable hook execution for a given model by using -the `withoutHooks` function. `withoutHooks` can receive a sync or async -callback: if an async callback is passed, it will return a `Promise`. - -```typescript -import { withoutHooks } from '@foscia/core'; - -const asyncResultOfYourCallback = await withoutHooks(User, async () => { -// TODO Do something async and return it. -}); -``` - -:::warning - -**Foscia may also register hooks internally** when using some features, such -as relations inverse, etc. Be careful when running a callback -without model's hooks, as those hooks will also be disable. - -::: - -### Instances hooks - -:::info - -Most instances hooks callbacks can be asynchronous and are executed in a -sequential fashion (one by one, not parallelized). Only `init` is a sync -hook callback. - -::: - -You can hook on multiple events on instances: - -- `onInit`: instance was constructed by calling `new` on model class. -- `onRetrieved`: instance was deserialized from a backend response. -- `onCreating`: action to create instance will run soon. -- `onCreated`: action to create instance was ran successfully. -- `onUpdating`: action to update instance will run soon. -- `onUpdated`: action to update instance was ran successfully. -- `onSaving`: action to save (create or update) instance will run soon (always - ran after `onCreating` and `onUpdating`). -- `onSaved`: action to save (create or update) instance was ran successfully - (always ran after `onCreated` and `onUpdated`). -- `onDestroying`: action to destroy instance will run soon. -- `onDestroyed`: action to destroy instance was ran successfully. - -Each of these hooks callback will receive an instance as parameter: - -```typescript -import { onCreating } from '@foscia/core'; - -onCreating(User, async (user) => { -}); -``` - -### Models hooks - -:::info - -Models hooks callbacks are synchronous and are executed in -a sequential fashion (one by one, not parallelized). - -::: - -Only `boot` event can be hooked on a model class, using `onBoot`. -It is like `onInit`, but will be called only once per model and will -receive the model class. - -```typescript -import { onBoot } from '@foscia/core'; - -onBoot(User, async (UserModel) => { -}); -``` - -### Properties hooks - -:::info - -Instances properties hooks callbacks are synchronous and are executed in -a sequential fashion (one by one, not parallelized). - -::: - -You can hook on multiple events on instances' properties: - -- `onPropertyReading`: an instance property getter is called (ran before getting value). -- `onPropertyRead`: an instance property getter is called (ran after getting value). -- `onPropertyWriting`: an instance property setter is called (ran before setting value). -- `onPropertyWrite`: an instance property setter is called (ran after setting value). - -Reading hooks will receive the instance and property key, current value and definition: - -```typescript -import { onPropertyReading, onPropertyRead } from '@foscia/core'; - -// Hook on specific property reading. -onPropertyReading(User, 'email', ({ instance, key, value, def }) => { -}); -onPropertyRead(User, 'email', ({ instance, key, value, def }) => { -}); - -// Hook on any property reading. -onPropertyReading(User, ({ instance, key, value, def }) => { -}); -onPropertyRead(User, ({ instance, key, value, def }) => { -}); -``` - -Writing hooks will receive the instance and property key, previous value, next value and definition: - -```typescript -import { onPropertyWriting, onPropertyWrite } from '@foscia/core'; - -// Hook on specific property reading. -onPropertyWriting(User, 'email', ({ instance, key, prev, next, def }) => { -}); -onPropertyWrite(User, 'email', ({ instance, key, prev, next, def }) => { -}); - -// Hook on any property reading. -onPropertyWriting(User, ({ instance, key, prev, next, def }) => { -}); -onPropertyWrite(User, ({ instance, key, prev, next, def }) => { -}); -``` - -To unregister a property hook callback using `unregisterHook`, you should -pass the event name with or without the property's key, -depending on if it is a specific property hook callback or not: - -```typescript -import { unregisterHook } from '@foscia/core'; - -// Unregister specific property hook. -unregisterHook(User, 'property:reading:email', registeredCallback); -unregisterHook(User, 'property:read:email', registeredCallback); -unregisterHook(User, 'property:writing:email', registeredCallback); -unregisterHook(User, 'property:write:email', registeredCallback); - -// Unregister non-specific properties hook. -unregisterHook(User, 'property:reading', registeredCallback); -unregisterHook(User, 'property:read', registeredCallback); -unregisterHook(User, 'property:writing', registeredCallback); -unregisterHook(User, 'property:write', registeredCallback); -``` - -### Using hooks with composition - -All models, instances and properties hooks can be used on -[composables](/docs/digging-deeper/models/models-composition#composable-using-hooks) -and [models factories](/docs/digging-deeper/models/models-composition#factory-using-hooks). - ## Special properties -### Instances - -Each model's instance have the following special properties: - -| Key | Type | Description | -| ----------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -| `$exists` | `boolean` | Tells if the instance exists in the data source. | -| `$loaded` | `Dictionary` | Dictionary containing `true` for each loaded relation. | -| `$values` | [`Partial`](/docs/reference/api/@foscia/core/type-aliases/ModelValues) | Current values of the instance. | -| `$original` | [`ModelSnapshot`](/docs/reference/api/@foscia/core/type-aliases/ModelSnapshot) | Original snapshot since last sync. | -| `$raw` | `any` | Data source value which was deserialized to create inside the instance. | -| `$model` | [`ModelClass`](/docs/reference/api/@foscia/core/type-aliases/Model) | Base model class. | - -### Models - -Each model have the following special properties: +Foscia models provide special properties on both the model class and its +instances, which you can discover inside the API reference: -| Key | Type | Description | -| -------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------ | -| `$type` | `string` | Unique model type. | -| `$config` | [`ModelConfig`](/docs/reference/api/@foscia/core/type-aliases/ModelConfig) | Configuration options. | -| `$schema` | [`ModelSchema`](/docs/reference/api/@foscia/core/type-aliases/ModelSchema) | Schema containing IDs/attributes/relations definition. | -| `$composables` | [`ModelComposable[]`](/docs/reference/api/@foscia/core/type-aliases/ModelComposable) | Array of used composables. | -| `$booted` | `boolean` | Tells if model is booted (constructed at least once). | +- [`Model`](/docs/api/@foscia/core/interfaces/Model) +- [`ModelInstance`](/docs/api/@foscia/core/interfaces/ModelInstance) diff --git a/website/docs/core-concepts/project.md b/website/docs/core-concepts/project.md index fe039e4d..45783fb9 100644 --- a/website/docs/core-concepts/project.md +++ b/website/docs/core-concepts/project.md @@ -17,15 +17,15 @@ using the CLI, such as: - Suggesting composables or transformers to use when making models or composables using - [`foscia make model`](/docs/digging-deeper/cli#make-model-name) or - [`foscia make composable`](/docs/digging-deeper/cli#make-composable-name). + [`foscia make model`](/docs/digging-deeper/usages/cli#make-model-name) or + [`foscia make composable`](/docs/digging-deeper/usages/cli#make-composable-name). - Importing available files (`models.ts`, `action.ts`, `makeModel.ts`, etc.) when generating other files (such as models, some frameworks integration files). ## Example project structure Your Foscia files are stored in one directory you choose when initiating -your project using [`foscia init`](/docs/digging-deeper/cli#init-path). +your project using [`foscia init`](/docs/digging-deeper/usages/cli#init-path). Here is an example of a Nuxt v4 project using Foscia and storing Foscia's related files into `app/data` directory. @@ -69,6 +69,6 @@ root-path/ | `runners/` | [Action runners](/docs/digging-deeper/actions/custom-action-runners). | | `transformers/` | [Transformers](/docs/digging-deeper/models/models-transformers). | | `utils/` | Other utilities files, such as [model reducer](/docs/digging-deeper/models/models-reduce-revive) and [model reviver](/docs/digging-deeper/models/models-reduce-revive). | -| `action.ts` | Export an [action factory](/docs/getting-started#action-factory). | +| `action.ts` | Export an [action factory](/docs/getting-started#first-actions). | | `models.ts` | Export an array of available models, to be used by registry or other files. | | `makeModel.ts` | Export a [model factory](/docs/digging-deeper/models/models-composition#factory). | diff --git a/website/docs/digging-deeper/actions/_category_.json b/website/docs/digging-deeper/actions/_category_.json index f700a78d..a155df08 100644 --- a/website/docs/digging-deeper/actions/_category_.json +++ b/website/docs/digging-deeper/actions/_category_.json @@ -2,6 +2,7 @@ "label": "Actions", "position": 200, "link": { - "type": "generated-index" + "type": "generated-index", + "description": "Advanced usages of actions with or without models." } } diff --git a/website/docs/digging-deeper/actions/actions-hooks.md b/website/docs/digging-deeper/actions/actions-hooks.md new file mode 100644 index 00000000..e87cb3fc --- /dev/null +++ b/website/docs/digging-deeper/actions/actions-hooks.md @@ -0,0 +1,74 @@ +--- +sidebar_position: 20 +description: Using hooks with actions. +--- + +# Using hooks + +:::tip What you'll learn + +- Registering and unregistering hooks on actions + +::: + +## Usage + +You may hook on multiple events which occurs on action using the hook +registration function: + +- [`onRunning`](/docs/api/@foscia/core/functions/onRunning): + after context computation, before context runner execution. +- [`onSuccess`](/docs/api/@foscia/core/functions/onSuccess): + after context runner successful execution (no error thrown). +- [`onError`](/docs/api/@foscia/core/functions/onError): + after context runner failed execution (error thrown). +- [`onFinally`](/docs/api/@foscia/core/functions/onFinally): + after context runner successful or failed execution. + +To register a hook callback, you must use the registration enhancer on your +building action. + +```typescript +import { + onRunning, + onSuccess, + onError, + onFinally, +} from '@foscia/core'; + +action().use(onRunning(({ action }) => /* ... */)); +action().use(onSuccess(({ action, result }) => /* ... */)); +action().use(onError(({ action, error }) => /* ... */)); +action().use(onFinally(({ action }) => /* ... */)); +``` + +:::info + +Hooks' callbacks are async and executed in a sequential fashion (one by one, not +parallelized). + +::: + +You can temporally disable hook execution for a given action by using the +[`withoutHooks`](/docs/api/@foscia/core/functions/withoutHooks) function. +[`withoutHooks`](/docs/api/@foscia/core/functions/withoutHooks) can receive +a sync or async callback: if an async callback is passed (e.g. returning a +`Promise`), it will also return a `Promise`. + +```typescript +import { query, all, withoutHooks } from '@foscia/core'; + +// Retrieve a list of User instances without action hooks running. +const users = await withoutHooks(action(), async (a) => { + return await a.run(query(User), all()); +}); +``` + +:::warning + +**Foscia may also register hooks internally** when using some enhancers. Those +provide some library features +([**models hooks**](/docs/digging-deeper/models/models-hooks), etc.). Be careful running +actions without hooks, as those hooks will also be disable. + +::: diff --git a/website/docs/digging-deeper/actions/actions-middlewares.mdx b/website/docs/digging-deeper/actions/actions-middlewares.mdx new file mode 100644 index 00000000..f648eff1 --- /dev/null +++ b/website/docs/digging-deeper/actions/actions-middlewares.mdx @@ -0,0 +1,69 @@ +--- +sidebar_position: 30 +description: Using middlewares with actions. +--- +import FunctionInfo from '@site/src/components/FunctionInfo'; + +# Using middlewares + + + +:::tip What you'll learn + +- Registering middlewares on actions +- Replacing middlewares on actions + +::: + +## Usage + +### Registering middlewares + +You can use middlewares on your actions. Those are working the same as in most +web frameworks, and offer an additional way of interacting with actions and +their results. + +To register middlewares, you can use: + +- [`appendActionMiddlewares`](/docs/api/@foscia/core/functions/appendActionMiddlewares) +to add new middlewares after already registered ones +- [`prependActionMiddlewares`](/docs/api/@foscia/core/functions/prependActionMiddlewares) +to add new middlewares before already registered ones + +Each middleware callback will receive two arguments: +the `action` instance and a `next` callback which continues the action's +execution stack. + +Thanks to the access to `next` callback, you can create a middleware which would +execute **before** the action is ran, but also **after**. + +```typescript +import { appendActionMiddlewares } from '@foscia/core'; + +action().use(appendActionMiddlewares([ + (a, next) => { + // Do something before execution. + return next(a); + }, + async (a, next) => { + const result = await next(a); + // Do something after execution. + return result; + }, +])); +``` + +### Replacing middlewares + +Finally, when using [`replaceActionMiddlewares`](/docs/api/@foscia/core/functions/replaceActionMiddlewares), +you can replace the already configured middlewares by passing a factory function. +This allows to replace or merge middlewares manually. + +```typescript +import { replaceActionMiddlewares } from '@foscia/core'; + +action().use(replaceActionMiddlewares((previousActionMiddlewares) => [ + (a, next) => next(a), + ...previousActionMiddlewares, +])); +``` diff --git a/website/docs/digging-deeper/actions/custom-action-enhancers.mdx b/website/docs/digging-deeper/actions/custom-action-enhancers.mdx index 3c476844..6a6dcc4b 100644 --- a/website/docs/digging-deeper/actions/custom-action-enhancers.mdx +++ b/website/docs/digging-deeper/actions/custom-action-enhancers.mdx @@ -1,8 +1,9 @@ --- sidebar_position: 50 -description: Defining your own action enhancers and their associated extension. +description: Defining your own action enhancers. --- + import ShellCommand from '@site/src/components/ShellCommand'; # Creating an action enhancer @@ -10,7 +11,6 @@ import ShellCommand from '@site/src/components/ShellCommand'; :::tip What you'll learn - Defining a custom action enhancer -- Providing an extension property to your enhancer ::: @@ -22,135 +22,58 @@ from any existing Foscia enhancers. ## Goal -Since Foscia is pagination agnostic, providing a `first` enhancer is not -possible. Here is what we want our new `first` enhancer to do: - -- Select the model just like the `query` enhancer, but not a record ID -- Limit the pagination to the first page and one record only +We aim to create a `searchBy` enhancer which will add 2 +query parameters using JSON:API enhancers: a search filter and +a sort by search score. In this example, we will admit a JSON:API is used with the following query parameters working: -- `page[number]` describes the number of the page to fetch -- `page[size]` describes the count of records to fetch (aka. limit) - -:::tip - -If you want to create an enhancer which does not use or expand the action's -context typing (such as defining a query parameter, etc.), you can -ignore generic typing. - -::: +- `filterBy[search]` filters records by a search string +- `sort=searchScore` sorts searched records by their match score -:::info +:::warning -This guide is an enhancer version of the `first` runner described in the -[**custom runners guide**](/docs/digging-deeper/actions/custom-action-runners). +Generics inside enhancers and runners provide context typing propagation +and are required when using TypeScript, otherwise, you may experiment +type inference issues (for returned result) and type error when using +other enhancers and runners. ::: ## Using CLI - + ## Defining the function -Our implementation of `first` will target the model and paginate the context. +Our implementation of `searchBy` will search and sort. -```typescript title="action/enhancers/first.ts" -import { Action, Model, query } from '@foscia/core'; -import { paginate } from '@foscia/jsonapi'; +```typescript title="action/enhancers/searchBy.ts" +import { Action, makeEnhancer } from '@foscia/core'; +import { filterBy, sortByDesc } from '@foscia/jsonapi'; -export default function first(model: M) { - return (action: Action) => action.use( - query(model), - paginate({ number: 1, size: 1 }), - ); -} +export default makeEnhancer('searchBy', ( + search: string, +) => async (action: Action) => action.use( + filterBy({ search }), + sortByDesc('searchScore'), +)); ``` -:::warning - -Please note that when defining custom enhancers or runners, you should always -correctly define generic types. This is very important as it will allow the -context propagation through other enhancers and runners. - -::: - ## Using the function Once your enhancer is ready, you may use it like any other Foscia enhancer. ```typescript -import { one } from '@foscia/core'; +import { query, all } from '@foscia/core'; import action from './action'; -import first from './action/enhancers/first'; +import searchBy from './enhancers/searchBy'; import Post from './models/post'; -const post = await action().run(first(Post), one()); -``` - -## Defining the extension - -Our current enhancer can only be used through an import and the `use` method of -our action. To make it available for the -[builder pattern style calls](/docs/core-concepts/actions#extensions), we must -define an extension for it. - -There is currently a limitation of the TypeScript language (Higher Order types -are not available for now) which forces us to declare each extension manually. -The goal of an extension definition is to get a type safe feature directly -available on our action (and so provide autocomplete, context propagation, -etc.). - -Once your enhancer extension is ready, you will be able to use it -[as any other enhancers of Foscia](/docs/core-concepts/actions#extensions). - -```typescript title="action/enhancers/first.ts" -import { - Action, - ConsumeModel, - Model, - WithParsedExtension, - query, - appendExtension, -} from '@foscia/core'; -import { paginate } from '@foscia/jsonapi'; - -// Our previous enhancer code. -function first(model: M) { - return (action: Action) => action.use( - query(model), - paginate({ number: 1, size: 1 }), - ); -} - -// New default export with typed `extension()`. -export default /* @__PURE__ */ appendExtension( - 'first', - first, - 'use', -) as WithParsedExtension( - this: Action, - model: M, - ): Action, E>; -}>; +const posts = await action( + query(Post), + searchBy('hello'), + all(), +); ``` - -:::warning - -Here again, correctly typing our enhancer extension is really important to get -context and action's extension propagation. - -::: - -## Transforming it to a runner - -Wish `first` was not selecting a model but directly running the action and -fetching a result? - -Check out -[the custom runner guide](/docs/digging-deeper/actions/custom-action-runners) -which describe how to code a `first` action runner. diff --git a/website/docs/digging-deeper/actions/custom-action-runners.mdx b/website/docs/digging-deeper/actions/custom-action-runners.mdx index a655fa1f..24999e14 100644 --- a/website/docs/digging-deeper/actions/custom-action-runners.mdx +++ b/website/docs/digging-deeper/actions/custom-action-runners.mdx @@ -10,7 +10,6 @@ import ShellCommand from '@site/src/components/ShellCommand'; :::tip What you'll learn - Defining a custom action runner -- Providing an extension property to your runner ::: @@ -34,18 +33,12 @@ parameters working: - `page[number]` describes the number of the page to fetch - `page[size]` describes the count of records to fetch (aka. limit) -:::tip - -If you want to create a runner which does not use the action's -context typing (such as defining a query parameter, etc.), you can -ignore generic typing. - -::: - -:::info +:::warning -This guide is a runner version of the `first` enhancer described in the -[**custom enhancers guide**](/docs/digging-deeper/actions/custom-action-enhancers). +Generics inside enhancers and runners provide context typing propagation +and are required when using TypeScript, otherwise, you may experiment +type inference issues (for returned result) and type error when using +other enhancers and runners. ::: @@ -56,31 +49,18 @@ This guide is a runner version of the `first` enhancer described in the ## Defining the function Our implementation of `first` will paginate the context and fetch one instance. +Typing is complex here because we are using `one` runner, which requires to provide +a context containing an adapter and a deserializer. ```typescript title="action/runners/first.ts" -import { - Action, - ConsumeAdapter, - ConsumeDeserializer, - one, -} from '@foscia/core'; +import { Action, ConsumeAdapter, ConsumeDeserializer, makeRunner, one } from '@foscia/core'; import { paginate } from '@foscia/jsonapi'; -export default function first() { - return ( - action: Action & ConsumeDeserializer>, - ) => action.run(paginate({ number: 1, size: 1 }), one()); -} +export default makeRunner('first', () => async ( + action: Action & ConsumeDeserializer>, +) => action.run(paginate({ number: 1, size: 1 }), one())); ``` -:::warning - -Please note that when defining custom enhancers or runners, you should always -correctly define generic types. This is very important as it will allow the -context propagation through other enhancers and runners. - -::: - ## Using the function Once your runner is ready, you may use it like any other Foscia runner. @@ -88,63 +68,8 @@ Once your runner is ready, you may use it like any other Foscia runner. ```typescript import { query } from '@foscia/core'; import action from './action'; -import first from './action/runners/first'; +import first from './runners/first'; import Post from './models/post'; -const post = await action().run(query(Post), first()); -``` - -## Defining the extension - -Our current runner can only be used through an import and the `use` method of -our action. To make it available for the -[builder pattern style calls](/docs/core-concepts/actions#extensions), we must -define an extension for it. - -There is currently a limitation of the TypeScript language (Higher Order types -are not available for now) which forces us to declare each extension manually. -The goal of an extension definition is to get a type safe feature directly -available on our action (and so provide autocomplete, context propagation, -etc.). - -Once your runner extension is ready, you will be able to use it -[as any other runners of Foscia](/docs/core-concepts/actions#extensions). - -```typescript title="action/runners/first.ts" -import { - Action, - ConsumeAdapter, - ConsumeDeserializer, - InferConsumedInstance, - WithParsedExtension, - appendExtension, - one, -} from '@foscia/core'; -import { paginate } from '@foscia/jsonapi'; - -// Our previous enhancer code. -function first() { - return ( - action: Action & ConsumeDeserializer>, - ) => action.run(paginate({ number: 1, size: 1 }), one()); -} - -// New default export with typed `extension()`. -export default /* @__PURE__ */ appendExtension( - 'first', - first, - 'run', -) as WithParsedExtension, RawData, Data, Deserialized>( - this: Action & ConsumeDeserializer>, - ): Promise; -}>; +const post = await action(query(Post), first()); ``` - -:::warning - -Here again, correctly typing our runner extension is really important to get -context and action's extension propagation. - -::: diff --git a/website/docs/digging-deeper/actions/models-registration.mdx b/website/docs/digging-deeper/actions/models-registration.mdx index 3e4e9b7e..09199e17 100644 --- a/website/docs/digging-deeper/actions/models-registration.mdx +++ b/website/docs/digging-deeper/actions/models-registration.mdx @@ -17,8 +17,9 @@ import ShellCommand from '@site/src/components/ShellCommand'; ## When should you use a registry? -A `Registry` is a simple object which will store your models classes and let -Foscia resolves models from their `type` string. +A [`ModelsRegistry`](/docs/api/@foscia/core/interfaces/ModelsRegistry) is a simple +object which will store your models classes and let Foscia resolves models +from their `type` string. As stated in the [models guide](/docs/core-concepts/models#explicit-type-when-having-circular-references), @@ -41,7 +42,7 @@ If you choose the "Manually" option, you can rerun this command to update your models list file. You can also pass the `--models` option when calling -[`foscia make model `](/docs/digging-deeper/cli#make-model-name) to +[`foscia make model `](/docs/digging-deeper/usages/cli#make-model-name) to automatically update the file after creating the model file. #### Manually @@ -59,8 +60,7 @@ export default [ ### Adding registry to action factory Foscia provides a simple implementation for the registry through -[`makeRegistry`](/docs/reference/implementations/core#makemapregistrywith) -(providing using `makeRegistry` factory function). +[`makeRegistry`](/docs/api/@foscia/core/functions/makeRegistry). ```typescript title="action.ts" import { makeActionFactory, makeRegistry } from '@foscia/core'; @@ -73,15 +73,6 @@ export default makeActionFactory({ }); ``` -When adding the registry, you can also provide a type normalization function to -normalize local/data source models' types. - -```typescript -makeRegistry(models, { - normalizeType: (type: string) => pluralize(toCamelCase(type)), -}); -``` - ## Listing models Here are some suggestion on how you can list your models classes easily diff --git a/website/docs/digging-deeper/implementations/_category_.json b/website/docs/digging-deeper/implementations/_category_.json new file mode 100644 index 00000000..ce64ecf7 --- /dev/null +++ b/website/docs/digging-deeper/implementations/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Implementations", + "position": 10000, + "link": { + "type": "generated-index", + "description": "Details on available implementations and how Foscia works internally." + } +} diff --git a/website/docs/digging-deeper/implementations/core.md b/website/docs/digging-deeper/implementations/core.md new file mode 100644 index 00000000..69f7536a --- /dev/null +++ b/website/docs/digging-deeper/implementations/core.md @@ -0,0 +1,158 @@ +--- +sidebar_position: 10 +description: + Specificities of the core implementations and available configuration. +--- + +# Core + +## Introduction + +Core implementations are implementations of actions' dependencies which can be +used when using Foscia for any purpose. + +Since [`ModelsRegistry`](/docs/api/@foscia/core/interfaces/ModelsRegistry) and +[`InstancesCache`](/docs/api/@foscia/core/interfaces/InstancesCache) can be agnostic of +data source you are interacting with, +Foscia proposes core implementations of those dependencies. + +## Implementations + +### `makeCache` + +[`makeCache`](/docs/api/@foscia/core/functions/makeCache) provides a +[`InstancesCache`](/docs/api/@foscia/core/interfaces/InstancesCache) implementation. + +Currently, it uses [`makeRefsCache`](#makerefscache) with +[`makeWeakRefFactory`](/docs/api/@foscia/core/functions/makeWeakRefFactory). +This factory is agnostic of this implementation, so it may change in the future +if a better implementation exists. If you want to lock the used implementation, +prefer using [`makeRefsCache`](#makerefscache) directly. + +#### Example + +```typescript +import { makeCache } from '@foscia/core'; + +const { cache } = makeCache(); + +cache.put('posts', '1', post); +const cachedPost = cache.find('posts', '1'); +``` + +#### Configuration + +Since this factory is agnostic of implementation, no configuration is available. + +#### Defined in + +- [`packages/core/src/cache/makeCache.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/cache/makeCache.ts) + +### `makeRefsCache` + +[`makeRefsCache`](/docs/api/@foscia/core/functions/makeRefsCache) provides a +[`InstancesCache`](/docs/api/@foscia/core/interfaces/InstancesCache) implementation +which stores reference to instances created by a +[`RefFactory`](/docs/api/@foscia/core/type-aliases/RefFactory). + +The [`RefFactory`](/docs/api/@foscia/core/type-aliases/RefFactory) creates +a value reference function which returns a value or `null` if the reference +is expired. + +Foscia proposes two implementations of a +[`RefFactory`](/docs/api/@foscia/core/type-aliases/RefFactory): + +- [`makeWeakRefFactory`](/docs/api/@foscia/core/functions/makeWeakRefFactory), + which will store every instance as a + [`WeakRef`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakRef). + With this implementation, only instance that are still stored in your + application memory (not garbage collected) remains in cache. +- [`makeTimedRefFactory`](/docs/api/@foscia/core/functions/makeTimedRefFactory), + which will store every instance in a special object which expires after a + configured timeout. + +Type and ID normalization functions can be configured +when caching or retrieving instances. This can be useful when your models types +are different from the record types returned inside a JSON:API response. + +#### Example + +```typescript +import { makeRefsCache, makeWeakRefManager } from '@foscia/core'; + +const { cache } = makeRefsCache({ + manager: makeWeakRefManager(), + // or... + // manager: makeTimedRefFactory({ lifetime: 5 * 60 * 1000 }), +}); + +cache.put('posts', '1', post); +const cachedPost = cache.find('posts', '1'); +``` + +#### Configuration + +- [`RefsCacheConfig`](/docs/api/@foscia/core/interfaces/RefsCacheConfig) + +#### Defined in + +- [`packages/core/src/cache/makeRefsCache.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/cache/makeRefsCache.ts) + +### `makeRegistry` + +[`makeRegistry`](/docs/api/@foscia/core/functions/makeRegistry) provides a +[`ModelsRegistry`](/docs/api/@foscia/core/interfaces/ModelsRegistry) implementation. + +Currently, it uses [`makeMapRegistry`](#makemapregistry). This factory is +agnostic of this implementation, so it may change in the future if a better +implementation exists. If you want to lock the used implementation, prefer +using [`makeMapRegistry`](#makemapregistry) directly. + +#### Example + +```typescript +import { makeRegistry } from '@foscia/core'; + +const { registry } = makeRegistry([User, Post, Comment]); + +const PostModel = registry.modelFor('posts'); +``` + +#### Configuration + +Since this factory is agnostic of implementation, no configuration is available. + +#### Defined in + +- [`packages/core/src/registry/makeRegistry.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/registry/makeRegistry.ts) + +### `makeMapRegistry` + +[`makeRegistry`](/docs/api/@foscia/core/functions/makeRegistry) provides a +[`ModelsRegistry`](/docs/api/@foscia/core/interfaces/ModelsRegistry) implementation +which stores a map of models keyed by their type. + +Type normalization function can be configured +when registering and resolving models. This can be useful when your models types +are different from the record types returned inside a JSON:API response. + +#### Usage + +```typescript +import { makeMapRegistry } from '@foscia/core'; +import Post from './models/post'; + +const { registry } = makeRegistry({ + models: [User, Post, Comment], +}); + +const PostModel = registry.modelFor('posts'); +``` + +#### Configuration + +- [`MapRegistryConfig`](/docs/api/@foscia/core/interfaces/MapRegistryConfig) + +#### Defined in + +- [`packages/core/src/registry/makeMapRegistry.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/registry/makeMapRegistry.ts) diff --git a/website/docs/digging-deeper/implementations/http.md b/website/docs/digging-deeper/implementations/http.md new file mode 100644 index 00000000..402acaf5 --- /dev/null +++ b/website/docs/digging-deeper/implementations/http.md @@ -0,0 +1,62 @@ +--- +sidebar_position: 30 +description: + Specificities of the HTTP implementation and available configuration. +--- + +# HTTP + +## Introduction + +HTTP implementation provides +[`makeHttpAdapter`](/docs/api/@foscia/http/functions/makeHttpAdapter) and +multiple other features which help using Foscia as an HTTP client. It is also +the foundation of [JSON:API](/docs/digging-deeper/implementations/jsonapi) and +[REST](/docs/digging-deeper/implementations/rest) implementations. + +## Implementations + +### `makeHttpAdapter` + + +[`makeHttpAdapter`](/docs/api/@foscia/http/functions/makeHttpAdapter) provides a +[`Adapter`](/docs/api/@foscia/core/interfaces/Adapter) implementation which +will execute context through HTTP requests using the +[`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). + +The adapter builds +[`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) objects +using the context, returns +[`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects +as data and handles multiple use cases such as: + +- Dynamic endpoint based on targeted models, IDs, etc. +- String or object query parameters using a customizable serializer. +- Various request body typologies. +- Requests cancellation through + [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). +- Errors handling through custom errors classes. +- Requests/responses/errors transformation. +- Defaults to JSON data for request/response bodies with appropriate headers. + +#### Usage + +```typescript +import { makeHttpAdapter } from '@foscia/http'; + +const { adapter } = makeHttpAdapter({ + /* ...configuration */ +}); + +const response = await adapter.execute({ + /* ...context */ +}); +``` + +#### Configuration {#makehttpadapter-configuration} + +- [`HttpAdapterConfig`](/docs/api/@foscia/http/interfaces/HttpAdapterConfig) + +#### Defined in + +- [`packages/http/src/makeHttpAdapter.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/http/src/makeHttpAdapter.ts) diff --git a/website/docs/digging-deeper/implementations/jsonapi.md b/website/docs/digging-deeper/implementations/jsonapi.md new file mode 100644 index 00000000..75c5ae6a --- /dev/null +++ b/website/docs/digging-deeper/implementations/jsonapi.md @@ -0,0 +1,224 @@ +--- +sidebar_position: 100 +description: + Specificities of the JSON:API implementation and available configuration. +--- + +# JSON:API + +## Introduction + +[JSON:API](https://jsonapi.org) implementation provides multiple dependencies +implementations to support read/write interactions with +[JSON:API specification](https://jsonapi.org) based data source. + +## Implementations + +### `makeJsonApiAdapter` + +[`makeJsonApiAdapter`](/docs/api/@foscia/jsonapi/functions/makeJsonApiAdapter) +provides a [`Adapter`](/docs/api/@foscia/core/interfaces/Adapter) +implementation which will execute context through HTTP requests using the +[`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). + +`makeJsonApiAdapter` uses +[`makeRestAdapter`](/docs/digging-deeper/implementations/rest#makerestadapter). + +#### Usage + +```typescript +import { makeJsonApiAdapter } from '@foscia/jsonapi'; + +const { adapter } = makeJsonApiAdapter({ + /* ...configuration */ +}); + +const response = await adapter.execute({ + /* ...context */ +}); +``` + +#### Configuration + +- [`JsonApiAdapterConfig`](/docs/api/@foscia/jsonapi/interfaces/JsonApiAdapterConfig) + +#### Defined in + +- [`packages/jsonapi/src/makeJsonApiAdapter.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/jsonapi/src/makeJsonApiAdapter.ts) + +### `makeJsonApiDeserializer` + +[`makeJsonApiDeserializer`](/docs/api/@foscia/jsonapi/functions/makeJsonApiDeserializer) +provides a [`Deserializer`](/docs/api/@foscia/core/interfaces/Deserializer) +implementation which will extract model instances from JSON:API documents. + +`makeJsonApiDeserializer` uses +[`makeDeserializer`](/docs/digging-deeper/implementations/serialization#makedeserializer). + +
+ + + +Deserialized JSON:API document example + + + +Here is an example of a JSON:API document which `makeJsonApiDeserializer` can +deserialize to model instances. + +```json +{ + "data": [ + { + "type": "posts", + "id": "1", + "attributes": { + "title": "Foo", + "body": "Foo Body", + "publishedAt": "2023-10-24T10:00:00.000Z" + }, + "relationships": { + "comments": { + "data": [ + { + "type": "comments", + "id": "1" + }, + { + "type": "comments", + "id": "2" + } + ] + } + } + }, + { + "type": "posts", + "id": "2", + "attributes": { + "title": "Bar", + "body": "Bar Body", + "publishedAt": null + }, + "relationships": { + "comments": { + "data": [] + } + } + } + ], + "included": [ + { + "type": "comments", + "id": "1", + "attributes": { + "body": "Foo Comment" + } + }, + { + "type": "comments", + "id": "2", + "attributes": { + "body": "Bar Comment" + } + } + ] +} +``` + +
+ +#### Usage + +```typescript +import { makeJsonApiDeserializer } from '@foscia/jsonapi'; + +const { deserializer } = makeJsonApiDeserializer({ + /* ...configuration */ +}); + +const { instances } = await deserializer.deserialize(data, { + /* ...context */ +}); +``` + +#### Configuration + +- [`JsonApiDeserializerConfig`](/docs/api/@foscia/jsonapi/interfaces/JsonApiDeserializerConfig) + +#### Defined in + +- [`packages/jsonapi/src/makeJsonApiDeserializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/jsonapi/src/makeJsonApiDeserializer.ts) + +### `makeJsonApiSerializer` + +[`makeJsonApiSerializer`](/docs/api/@foscia/jsonapi/functions/makeJsonApiSerializer) +provides a [`Serializer`](/docs/api/@foscia/core/interfaces/Serializer) +implementation which will create JSON:API documents from model instance +and relations. + +`makeJsonApiSerializer` uses +[`makeSerializer`](/docs/digging-deeper/implementations/serialization#makeserializer). + +
+ + + +Serialized JSON:API document example + + + +Here is an example of a JSON:API document which `makeJsonApiSerializer` can +create from a model instance. + +```json +{ + "data": { + "type": "posts", + "id": "1", + "attributes": { + "title": "Foo", + "body": "Foo Body", + "publishedAt": "2023-10-24T10:00:00.000Z" + }, + "relationships": { + "comments": { + "data": [ + { + "type": "comments", + "id": "1" + }, + { + "type": "comments", + "id": "2" + } + ] + } + } + } +} +``` + +
+ +#### Usage + +```typescript +import { makeJsonApiSerializer } from '@foscia/jsonapi'; + +const { serializer } = makeJsonApiSerializer({ + /* ...configuration */ +}); + +const data = await serializer.serializeInstance(instance, { + /* ...context */ +}); +``` + +#### Configuration + +- [`JsonApiSerializerConfig`](/docs/api/@foscia/jsonapi/interfaces/JsonApiSerializerConfig) + +#### Defined in + +- [`packages/jsonapi/src/makeJsonApiSerializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/jsonapi/src/makeJsonApiSerializer.ts) diff --git a/website/docs/digging-deeper/implementations/presentation.md b/website/docs/digging-deeper/implementations/presentation.md new file mode 100644 index 00000000..f82cc0da --- /dev/null +++ b/website/docs/digging-deeper/implementations/presentation.md @@ -0,0 +1,76 @@ +--- +sidebar_position: 1 +description: Quick introduction on available implementations for Foscia. +--- + +# Presentation + +## Introduction + +Foscia actions might require one or many dependencies to work. Dependencies are +implementations of interfaces which are used in multiple parts of the actions +process. + +There are 5 kinds of dependency: + +- [`Adapter`](/docs/api/@foscia/core/interfaces/Adapter) + create the exchange between your actions' built context and your data + source. As an example, it will _translate_ the context to an HTTP request when + using JSON:API or REST implementations. +- [`Deserializer`](/docs/api/@foscia/core/interfaces/Deserializer) + will deserialize records to instances. It might use the cache and + registry internally. +- [`Serializer`](/docs/api/@foscia/core/interfaces/Serializer) + will serialize instances' snapshots to the data source format. +- [`InstancesCache`](/docs/api/@foscia/core/interfaces/InstancesCache) + will store already fetched models instances. It will avoid multiple + instances of the same record coexisting and allows you to retrieve already + fetched record without making further requests to your data source. +- [`ModelsRegistry`](/docs/api/@foscia/core/interfaces/ModelsRegistry) + is a map of types and associated model. It is used by deserializer to + identify which models should map to which types. + +## Implementations + +### Core + +`@foscia/core` provides implementations for `InstancesCache` and `ModelsRegistry`. Those +implementations may be used for any Foscia usage (JSON:API, REST, etc.). + +- [Cache through `makeCache`](/docs/digging-deeper/implementations/core#makecache) +- [Cache through `makeRefsCache`](/docs/digging-deeper/implementations/core#makerefscache) +- [Registry through `makeRegistry`](/docs/digging-deeper/implementations/core#makeregistry) +- [Registry through `makeMapRegistry`](/docs/digging-deeper/implementations/core#makemapregistry) + +### HTTP + +`@foscia/http` provides implementation of `Adapter` to interact with HTTP data +sources. + +- [Adapter through `makeHttpAdapter`](/docs/digging-deeper/implementations/http#makehttpadapter) + +### JSON:API + +`@foscia/jsonapi` provides implementations of `Adapter`, `Serializer` and +`Deserializer` to interact with JSON:API data sources. + +- [Adapter through `makeJsonApiAdapter`](/docs/digging-deeper/implementations/jsonapi#makejsonapiadapter) +- [Deserializer through `makeJsonApiDeserializer`](/docs/digging-deeper/implementations/jsonapi#makejsonapideserializer) +- [Serializer through `makeJsonApiSerializer`](/docs/digging-deeper/implementations/jsonapi#makejsonapiserializer) + +### REST + +`@foscia/rest` provides implementations of `Adapter`, `Serializer` and +`Deserializer` to interact with JSON REST HTTP data sources. + +- [Adapter through `makeRestAdapter`](/docs/digging-deeper/implementations/rest#makerestadapter) +- [Deserializer through `makeRestDeserializer`](/docs/digging-deeper/implementations/rest#makerestdeserializer) +- [Serializer through `makeRestSerializer`](/docs/digging-deeper/implementations/rest#makerestserializer) + +### Serialization + +`@foscia/serialization` provides partial implementations of `Serializer` and +`Deserializer` to transform model instances to/from record generic records. + +- [Deserializer through `makeDeserializer`](/docs/digging-deeper/implementations/serialization#makedeserializer) +- [Serializer through `makeSerializer`](/docs/digging-deeper/implementations/serialization#makeserializer) diff --git a/website/docs/digging-deeper/implementations/rest.md b/website/docs/digging-deeper/implementations/rest.md new file mode 100644 index 00000000..19a0b4d7 --- /dev/null +++ b/website/docs/digging-deeper/implementations/rest.md @@ -0,0 +1,174 @@ +--- +sidebar_position: 110 +description: + Specificities of the REST implementation and available configuration. +--- + +# REST + +## Introduction + +REST implementation provides multiple dependencies implementations to support +read/write interactions with JSON REST data sources. + +## Implementations + +### `makeRestAdapter` + +[`makeRestAdapter`](/docs/api/@foscia/rest/functions/makeRestAdapter) +provides a [`Adapter`](/docs/api/@foscia/core/interfaces/Adapter) +implementation which will execute context through HTTP requests using the +[`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). + +`makeRestAdapter` use +[`makeHttpAdapter`](/docs/digging-deeper/implementations/http#makehttpadapter). + +#### Usage + +```typescript +import { makeRestAdapter } from '@foscia/rest'; + +const { adapter } = makeRestAdapter({ + /* ...configuration */ +}); + +const response = await adapter.execute({ + /* ...context */ +}); +``` + +#### Configuration {#makerestadapter-configuration} + +- [`RestAdapterConfig`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig) + +#### Defined in + +- [`packages/rest/src/makeRestAdapter.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/makeRestAdapter.ts) + +### `makeRestDeserializer` + +[`makeRestDeserializer`](/docs/api/@foscia/rest/functions/makeRestDeserializer) +provides a [`Deserializer`](/docs/api/@foscia/core/interfaces/Deserializer) +implementation which will extract model instances from object documents. + +`makeRestDeserializer` uses +[`makeDeserializer`](/docs/digging-deeper/implementations/serialization#makedeserializer). + +
+ + + +Deserialized REST document example + + + +Here is an example of a REST document which `makeRestDeserializer` can deserialize +to model instances. + +```json +[ + { + "id": "1", + "title": "Foo", + "body": "Foo Body", + "publishedAt": "2023-10-24T10:00:00.000Z", + "comments": [ + { + "id": "1", + "body": "Foo Comment" + }, + { + "id": "2", + "body": "Bar Comment" + } + ] + }, + { + "type": "posts", + "id": "2", + "title": "Bar", + "body": "Bar Body", + "publishedAt": null, + "comments": [] + } +] +``` + +
+ +#### Usage + +```typescript +import { makeRestDeserializer } from '@foscia/rest'; + +const { deserializer } = makeRestDeserializer({ + /* ...configuration */ +}); + +const { instances } = await deserializer.deserialize(data, { + /* ...context */ +}); +``` + +#### Configuration + +- [`RestDeserializerConfig`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig) + +#### Defined in + +- [`packages/rest/src/makeRestDeserializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/makeRestDeserializer.ts) + +### `makeRestSerializer` + +[`makeRestSerializer`](/docs/api/@foscia/rest/functions/makeRestSerializer) +provides a [`Serializer`](/docs/api/@foscia/core/interfaces/Serializer) +implementation which will create REST object documents from +model instance and relations. + +`makeRestSerializer` uses +[`makeSerializer`](/docs/digging-deeper/implementations/serialization#makeserializer). + +
+ + + +Serialized REST document example + + + +Here is an example of a REST document which `makeRestSerializer` can +create from a model instance. + +```json +{ + "id": "1", + "title": "Foo", + "body": "Foo Body", + "publishedAt": "2023-10-24T10:00:00.000Z", + "comments": ["1", "2"] +} +``` + +
+ +#### Usage + +```typescript +import { makeRestSerializer } from '@foscia/rest'; + +const { serializer } = makeRestSerializer({ + /* ...configuration */ +}); + +const data = await serializer.serializeInstance(instance, { + /* ...context */ +}); +``` + +#### Configuration + +- [`RestSerializerConfig`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig) + +#### Defined in + +[`packages/rest/src/makeRestSerializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/makeRestSerializer.ts) diff --git a/website/docs/digging-deeper/implementations/serialization.md b/website/docs/digging-deeper/implementations/serialization.md new file mode 100644 index 00000000..d48d9d34 --- /dev/null +++ b/website/docs/digging-deeper/implementations/serialization.md @@ -0,0 +1,104 @@ +--- +sidebar_position: 20 +description: + Specificities of the "serialization" implementation and available + configuration. +--- + +# Serialization + +## Introduction + +Serialization implementation provides partial generic implementations +for serializer and deserializer dependencies. + +## Implementations + +### `makeDeserializer` + +[`makeDeserializer`](/docs/api/@foscia/serialization/functions/makeDeserializer) +provides a [`Deserializer`](/docs/api/@foscia/core/interfaces/Deserializer) +implementation which will produce instances from a generic record object. + +It handles multiple features, such as: + +- Deduplicate records by identifier to avoid deserializing the same record + multiple times. +- Interact with the cache (if configured) to keep only one instance alive for + one record. +- Resolve model to deserialize record to automatically from context, relations + and configuration. +- Use model's properties aliases and value transformers. +- Run the models' `retrieved` hook for each deserialized instance. + +#### Usage + +```typescript +import { makeDeserializerRecordFactory, makeDeserializer } from '@foscia/serialization'; + +const deserializer = makeDeserializer({ + extractData: (data) => ({ + records: data as Arrayable> | null, + }), + createRecord: makeDeserializerRecordFactory( + (record) => record, + (record, { key }) => record[key], + (record, { key }) => record[key], + ), +}); + +const { instances } = await deserializer.deserialize(data, { + /* ...context */ +}); +``` + +#### Configuration {#makedeserializer-configuration} + +- [`RecordDeserializerConfig`](/docs/api/@foscia/serialization/interfaces/RecordDeserializerConfig) + +#### Defined in + +- [`packages/serialization/src/makeDeserializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/serialization/src/makeDeserializer.ts) + +### `makeSerializer` + +[`makeSerializer`](/docs/api/@foscia/serialization/functions/makeSerializer) +provides a [`Serializer`](/docs/api/@foscia/core/interfaces/Serializer) +implementation which will produce a generic record value from a model instance. + +It handles multiple features, such as: + +- Serialize instances' snapshots into generic record value +- Serialize relation instances' snapshots into generic record values +- Serialize generic record values into adapter's data format +- Only serialize changed instance's values +- Use model's properties aliases and value transformers. +- Serialize nested relation instances with circular references support + +#### Usage + +```typescript +import { makeSerializer, makeSerializerRecordFactory } from '@foscia/serialization'; + +const serializer = makeSerializer({ + createData: (records) => records, + createRecord: makeSerializerRecordFactory( + (snapshot) => ({ id: snapshot.$values.id } as Record), + (record, { key, value }) => { + record[key] = value; + }, + ), +}); + +const data = await serializer.serializeInstance(instance, { + /* ...context */ +}); +``` + +#### Configuration {#makeserializer-configuration} + +- [`RecordSerializerConfig`](/docs/api/@foscia/serialization/interfaces/RecordSerializerConfig) + +#### Defined in + +- [`packages/serialization/src/makeSerializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/serialization/src/makeSerializer.ts) diff --git a/website/docs/digging-deeper/models/_category_.json b/website/docs/digging-deeper/models/_category_.json index bba97b04..1785067c 100644 --- a/website/docs/digging-deeper/models/_category_.json +++ b/website/docs/digging-deeper/models/_category_.json @@ -2,6 +2,7 @@ "label": "Models", "position": 100, "link": { - "type": "generated-index" + "type": "generated-index", + "description": "Advanced usage of models, such as composition, changes tracking, etc." } } diff --git a/website/docs/digging-deeper/models/models-changes-tracking.md b/website/docs/digging-deeper/models/models-changes-tracking.md index a83ffded..5d52fe28 100644 --- a/website/docs/digging-deeper/models/models-changes-tracking.md +++ b/website/docs/digging-deeper/models/models-changes-tracking.md @@ -22,11 +22,23 @@ properties will be synced in an "original" snapshot. This original snapshot allows you to check if some properties have changed since last synchronization. +:::info + +Instance snapshot is a locked state of your record attributes and relations. +By default, related records are only stored inside a limited snapshot. +If you want to deeply store related records snapshots, you can disable +[`limitedSnapshots`](/docs/digging-deeper/models/models-configuration#limitedsnapshots) +on your models. + +::: + ## Taking a snapshot -You can take a snapshot of an instance at any time using `takeSnapshot`. This is +You can take a snapshot of an instance at any time using +[`takeSnapshot`](/docs/api/@foscia/core/functions/takeSnapshot). This is done automatically every time you send/fetch an instance to/form your data -source, and the created snapshot is saved into the `$original` properties of +source, and the created snapshot is saved into the +[`$original`](/docs/api/@foscia/core/interfaces/ModelInstance#original) properties of your instance. ```typescript @@ -37,28 +49,31 @@ const myPostSnapshot = takeSnapshot(myPost); ## Checking for changes -To check for changes between two snapshots, you can use `compareSnapshots`. To +To check for changes between two snapshots, you can use +[`isSameSnapshot`](/docs/api/@foscia/core/functions/isSameSnapshot). To check for changes between an instance and its original snapshot, you can use -`changed` (this will automatically take a new snapshot and compare against it). +[`changed`](/docs/api/@foscia/core/functions/changed) +(this will automatically take a new snapshot and compare against it). ```typescript -import { changed, compareSnapshots, takeSnapshot } from '@foscia/core'; +import { changed, isSameSnapshot, takeSnapshot } from '@foscia/core'; // True if any properties changed or instance does exists now. changed(myPost); // False in the same case as above. -compareSnapshots(takeSnapshot(myPost), myPost.$original); +isSameSnapshot(takeSnapshot(myPost), myPost.$original); // True only if title has changed. changed(myPost, ['title']); // False in the same case as above. -compareSnapshots(takeSnapshot(myPost), myPost.$original, ['title']); +isSameSnapshot(takeSnapshot(myPost), myPost.$original, ['title']); ``` ## Syncing changes -You can mark your instance as synced any time using `markSynced`. Just like -other helper functions, you can affect only specific properties. +You can mark your instance as synced any time using +[`markSynced`](/docs/api/@foscia/core/functions/markSynced). +Just like other helper functions, you can affect only specific properties. ```typescript import { markSynced } from '@foscia/core'; @@ -71,9 +86,10 @@ markSynced(myPost, ['title']); ## Restoring changes -You can restore a snapshot on your model as synced any time using `restore` and -`restoreSnapshot`. Just like other helper functions, you can affect only -specific properties. +You can restore a snapshot on your model as synced any time using +[`restore`](/docs/api/@foscia/core/functions/restore) and +[`restoreSnapshot`](/docs/api/@foscia/core/functions/restoreSnapshot). +Just like other helper functions, you can affect only specific properties. ```typescript import { restore, restoreSnapshot } from '@foscia/core'; diff --git a/website/docs/digging-deeper/models/models-composition.mdx b/website/docs/digging-deeper/models/models-composition.mdx index ee100f44..6733dc04 100644 --- a/website/docs/digging-deeper/models/models-composition.mdx +++ b/website/docs/digging-deeper/models/models-composition.mdx @@ -28,18 +28,14 @@ composition. ### Using CLI -You can generate a new composable using [`@foscia/cli`](/docs/digging-deeper/cli). +You can generate a new composable using [`@foscia/cli`](/docs/digging-deeper/usages/cli). ### Defining a composable The first step is to create a composable with the features you want to share. -This is done through `makeComposable` and uses the same syntax as `makeModel`. - -The created composable will get its custom properties (e.g. `published` below) -rewritten to protect their descriptor. This allows using spread syntax when -using composable in other models' definition. +This is done through [`makeComposable`](/docs/api/@foscia/core/functions/makeComposable). ```typescript title="composables/publishable.ts" import { attr, makeComposable, toDateTime } from '@foscia/core'; @@ -78,7 +74,7 @@ export default class Post extends makeModel('posts').extend({ publishable }) {} ### Using hooks {#composable-using-hooks} -Composables share the [models' hooks system](/docs/core-concepts/models#hooks). +Composables share the [models' hooks system](/docs/digging-deeper/models/models-hooks). Each hooks you define on a composable will be added to models which use the composable. This is useful when defining common behaviors, such as a UUID keyed model with automatic generation before first saving. @@ -102,8 +98,9 @@ export default uuidID; Be aware that you cant unregister hooks from a composable, because hooks are shallow cloned when extending the composable. -Hook registration function (such as `onCreating`) returned callback will -only unregister hooks on composable, not on extending models. +Hook registration function (such as +[`onCreating`](/docs/api/@foscia/core/functions/onCreating)) returned callback +will only unregister hooks on composable, not on extending models. ::: @@ -134,8 +131,14 @@ export default timestamps; ### Typechecking composables You can easily typecheck for models or instances using some of your composables -by defining type aliases. You can also use `isModelUsing` or `isInstanceUsing` -functions to check for composition existing on a model or instance: +by defining type aliases. You can also use +[`isModelUsing`](/docs/api/@foscia/core/functions/isModelUsing) or +[`isInstanceUsing`](/docs/api/@foscia/core/functions/isInstanceUsing) +functions to check for composition existing on a model or instance. + +You can also use [`ModelInstanceUsing`](/docs/api/@foscia/core/type-aliases/ModelInstanceUsing) +and [`ModelUsing`](/docs/api/@foscia/core/type-aliases/ModelUsing) to get type +aliases. ```typescript title="composables/publishable.ts" import { attr, makeComposable, toDateTime, isInstanceUsing, isModelUsing, ModelInstanceUsing, ModelUsing } from '@foscia/core'; @@ -162,9 +165,10 @@ isModelUsing(SomeModel, publishable); // `SomeModel` extends `publishable`. :::warning -`isInstanceUsing` and `isModelUsing` will only check that the model definition -contain *at some time* the composable, but won't guaranty that the composable -properties are not overwritten afterward. +[`isInstanceUsing`](/docs/api/@foscia/core/functions/isInstanceUsing) +and [`isModelUsing`](/docs/api/@foscia/core/functions/isModelUsing) will +only check that the model definition contain *at some time* the composable, +but won't guaranty that the composable properties are not overwritten afterward. ::: @@ -172,14 +176,17 @@ properties are not overwritten afterward. ### Using CLI -You can generate a new factory using [`@foscia/cli`](/docs/digging-deeper/cli). +You can generate a new factory using [`@foscia/cli`](/docs/digging-deeper/usages/cli). ### Defining a factory When you need to share features across **all** of your models, you should use a -custom model factory. It will replace the Foscia's `makeModel` function. +custom model factory using +[`makeModelFactory`](/docs/api/@foscia/core/functions/makeModelFactory). +It will replace the Foscia's +[`makeModel`](/docs/api/@foscia/core/functions/makeModel) function. ```typescript title="makeModel.ts" import { attr, makeModelFactory, toDateTime } from '@foscia/core'; @@ -196,19 +203,20 @@ export default makeModelFactory({ ``` Once your factory is ready, you can use it in replacement of the default -`makeModel` provided by Foscia. +[`makeModel`](/docs/api/@foscia/core/functions/makeModel) provided by Foscia. ```typescript import makeModel from './makeModel'; export default class Post extends makeModel('posts', { /* definition */ -}) {} +}) { +} ``` ### Using hooks {#factory-using-hooks} -Models factories share the [models' hooks system](/docs/core-concepts/models#hooks). +Models factories share the [models' hooks system](/docs/digging-deeper/models/models-hooks). Each hooks you define on a factory will be added to models which are defined using the factory. This is useful when defining common behaviors, such as a UUID keyed model with automatic generation before first saving. @@ -232,7 +240,8 @@ export default makeModel; Be aware that you cant unregister hooks from a factory, because hooks are shallow cloned when creating a model. -Hook registration function (such as `onCreating`) returned callback will -only unregister hooks on factory, not on created models. +Hook registration function (such as +[`onCreating`](/docs/api/@foscia/core/functions/onCreating)) returned callback +will only unregister hooks on factory, not on created models. ::: diff --git a/website/docs/digging-deeper/models/models-configuration.md b/website/docs/digging-deeper/models/models-configuration.md index f2b9d50b..bbef89bc 100644 --- a/website/docs/digging-deeper/models/models-configuration.md +++ b/website/docs/digging-deeper/models/models-configuration.md @@ -15,11 +15,12 @@ toc_max_heading_level: 4 ## How to configure a model -You may configure your model when creating them through your factory `makeModel` +You may configure your model when creating them through your factory +[`makeModel`](/docs/api/@foscia/core/functions/makeModel) or when defining a custom factory such as described in the [model composition guide](/docs/digging-deeper/models/models-composition#factory). -When using inside a model creation (`makeModel`), the configuration will be +When using inside a model creation, the configuration will be dedicated to this model. Configuration is the first argument and definition is the second one: @@ -34,8 +35,9 @@ makeModel({ }); ``` -When using inside a model factory creation (`makeModelFactory`), the -configuration will be shared between all models created through this factory. +When using inside a model factory creation +(using [`makeModelFactory`](/docs/api/@foscia/core/functions/makeModelFactory)), +the configuration will be shared between all models created through this factory. Configuration is the first argument and definition is the second one: ```typescript title="makeModel.ts" @@ -61,8 +63,12 @@ purpose. #### `type` -When using the model factory `makeModel`, you have probably seen that the first -argument of the function is a string. This is the **type** of the current model. +##### Description + +When using the model factory +[`makeModel`](/docs/api/@foscia/core/functions/makeModel), +you have probably seen that the first argument of the function is a string. +This is the **type** of the current model. It may be used for different purpose depending on the context: @@ -75,6 +81,8 @@ To define it, you should follow your data source convention. As an example, in a JSON:API the resource types are defined in plural kebab case, such as `blog-posts` or `comments`. +##### Example + You may define the type as the only configuration of the model or as a configuration property (if you want to define other properties): @@ -86,149 +94,190 @@ makeModel('posts'); makeModel({ type: 'posts' }); ``` -#### `path` +#### `guessAlias` -**Default**: The model `type`. +##### Description -The `path` is used to query the model. It defaults to the model's type. +**Default**: no transformation. + +**Recommandation**: use this configuration option inside a +[custom model factory](/docs/digging-deeper/models/models-composition#factory). + +`guessAlias` transform a model's property `key` to guess its `alias`. -In an HTTP API, it is used as the endpoint. In a SQL database, it would be the -table. +##### Example + +Here is an example of a path guesser using hypothetical `toKebabCase` function. +If your JSON:API record properties are using kebab cased keys but your models +are camel cased: ```typescript title="post.ts" -import { makeModel } from '@foscia/core'; +import { makeModel, isManyRelationDef } from '@foscia/core'; makeModel({ - type: 'posts', - path: 'blog-posts', + type: 'BlogPosts', + guessAlias: (key: string) => toKebabCase(key), }); ``` -#### `guessPath` +#### `guessRelationType` -**Default**: no transformation. +##### Description + +**Default**: pluralize key for "to one" relation. **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). -`guessPath` transform a model's `type` to guess its `path`. +To avoid defining types on all your relations even when necessary (for example +in some cases with REST implementation), you may configure a type guesser on +your models. -Here is an example of a path guesser using hypothetical `toKebabCase` function. -If your JSON:API record types are using camel cased types but your endpoint are -kebab cased: +##### Example + +Here is an example of a type guesser using hypothetical `toKebabCase` and +`pluralize` functions. For example, if a `Comment` model has a `blogPost` +relation, this would guess the type to `blog-posts`: ```typescript title="post.ts" -import { makeModel, isManyRelationDef } from '@foscia/core'; +import { makeModel, ModelRelation } from '@foscia/core'; makeModel({ - type: 'blogPosts', - guessPath: (type: string) => toKebabCase(type), + type: 'posts', + guessRelationType: (def: ModelRelation) => pluralize(def.key), }); ``` -#### `guessIdPath` +#### `guessRelationInverse` -**Default**: no transformation. +##### Description + +**Default**: singularize parent model type. **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). -`guessIdPath` transform a model's `id` to guess its `path` when querying. -This can be useful when you want to query records by given their endpoint as -their ID. +To avoid manually defining inverse, you can configure a custom inverse +resolution function which will guess the inverse of a relation. + +##### Example + +Here is an example of an inverse guesser using hypothetical `toCamelCase` and +`singularize` functions. For example, if a `Post` model has a `comments` +relation, this would guess the inverse to `post`: ```typescript title="post.ts" -import { makeModel, isManyRelationDef } from '@foscia/core'; +import { makeModel, ModelRelation } from '@foscia/core'; -// If `/api/posts/1` is given when querying Post, only `1` will be used -// as ID in requested endpoint. makeModel({ type: 'posts', - guessIdPath: (id) => String(id).split('/').pop()!, + guessRelationInverse: (def: ModelRelation) => toCamelCase(singularize(def.parent.$type)), }); ``` -#### `guessAlias` +#### `limitedSnapshots` -**Default**: no transformation. +##### Description + +**Default**: `true`. + +Enable storing related records of a snapshot as +[`ModelLimitedSnapshot`](/docs/api/@foscia/core/interfaces/ModelLimitedSnapshot), +instead of [`ModelSnapshot`](/docs/api/@foscia/core/interfaces/ModelSnapshot), +to improve memory footprint and performance. + +##### Example + +```typescript title="post.ts" +import { makeModel } from '@foscia/core'; + +makeModel({ + limitedSnapshots: false, +}); +``` + +#### `strict` + +##### Description + +**Default**: `undefined`. **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). -`guessAlias` transform a model's property `key` to guess its `alias`. +Globally enable all strict policies on model: -Here is an example of a path guesser using hypothetical `toKebabCase` function. -If your JSON:API record properties are using kebab cased keys but your models -are camel cased: +- [`strictProperties`](#strictproperties) +- [`strictReadOnly`](#strictreadonly) + +:::info + +If a specific strict policies is enabled/disabled, it supersedes the global +strict settings. + +::: + +##### Example ```typescript title="post.ts" -import { makeModel, isManyRelationDef } from '@foscia/core'; +import { makeModel } from '@foscia/core'; makeModel({ - type: 'BlogPosts', - guessAlias: (key: string) => toKebabCase(key), + strict: true, }); ``` -#### `guessRelationType` +#### `strictProperties` -**Default**: pluralize key for "to one" relation. +##### Description + +**Default**: `false`. **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). -To avoid defining types on all your relations even when necessary (for example -in some cases with REST implementation), you may configure a type guesser on -your models. +When enabled, getting a model's instance property value will throw an error if +the value was not retrieved from the store or if the relation is not loaded. -Here is an example of a type guesser using hypothetical `toKebabCase` and -`pluralize` functions. For example, if a `Comment` model has a `blogPost` -relation, this would guess the type to `blog-posts`; +##### Example ```typescript title="post.ts" -import { makeModel, isPluralRelationDef, ModelRelation } from '@foscia/core'; +import { makeModel } from '@foscia/core'; makeModel({ - type: 'posts', - guessRelationType: (def: ModelRelation) => ( - isPluralRelationDef(def) ? def.key : pluralize(def.key) - ), + strictReadOnly: true, }); ``` -#### `guessRelationPath` +#### `strictReadOnly` -**Default**: no transformation. +##### Description + +**Default**: `true`. **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). -`guessRelationPath` transform a model's relation to its "path". This is only -needed in specific implementation context (such as JSON:API when querying -related instances of a base model instance). +When enabled, setting a model's instance readonly property value will throw +an error. -Here is an example of a type guesser using hypothetical `toKebabCase` function. -If your JSON:API record properties are using kebab cased keys but your models -are camel cased: +##### Example ```typescript title="post.ts" -import { - makeModel, - isManyRelationDef, - ModelClass, - ModelRelation, -} from '@foscia/core'; +import { makeModel } from '@foscia/core'; makeModel({ - type: 'posts', - guessRelationPath: (def: ModelRelation) => toKebabCase(def.key), + strictReadOnly: true, }); ``` -#### `compareValue` and `cloneValue` +#### `compareValues` and `cloneValue` -**Default**: compare will check for strict equality and clone will return the -base value. +##### Description + +**Default**: defaults implementations are available at +[`compareModelValues`](/docs/api/@foscia/core/functions/compareModelValues) and +[`cloneModelValue`](/docs/api/@foscia/core/functions/cloneModelValue). **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). @@ -236,88 +285,166 @@ base value. You may have noticed that Foscia provide some model history features. Those allow you to know which parts of a model instance changed since its retrieval from the data source or interact with those changes, through -[some utilities functions](/docs/reference/models-utilities): `changed`, -`reset`, and `syncOriginal`. +[some utilities functions](/docs/api/@foscia/core/#utilities): +[`changed`](/docs/api/@foscia/core/functions/changed), +[`restore`](/docs/api/@foscia/core/functions/restore), etc. + +Those two configuration options allow you to customize how values are +compared and copied when taking a snapshot of an instance or comparing +two snapshots. -Currently, Foscia won't clone any value when syncing the instance values (on -save, etc.) and will do a strict equal comparison to known if the value changed. +##### Example The following model configuration is equivalent to the default behavior of Foscia: ```typescript title="post.ts" -import { makeModel } from '@foscia/core'; +import { makeModel, compareModelValues, cloneModelValue } from '@foscia/core'; makeModel({ type: 'posts', - compareValue: (newValue, prevValue) => nextValue === prevValue, - cloneValue: (value) => value, + compareValue: compareModelValues, + cloneValue: cloneModelValue, }); ``` -You may change those two functions to really clone values when syncing the -instance state. Keep in mind that: +:::tip + +You may change those two functions to implement more flexible cloner and +comparator, for example supporting objects, map, etc. Keep in mind that: - Values might be any value your instance could contain, including complex object and even other model instance -- Cloned values might be restored through `reset` utility -- Making a real clone of a value without updating the comparator will break the +- Cloned values might be restored through + [`restore`](/docs/api/@foscia/core/functions/restore) utility +- Making a real clone of a value without updating the comparator might break the history because of its default behavior -#### `strict` +::: -**Default**: `undefined`. +### HTTP -**Recommandation**: use this configuration option inside a -[custom model factory](/docs/digging-deeper/models/models-composition#factory). +The following configuration options are specific to HTTP models (when +interacting with JSON:API, JSON REST, etc.). -Globally enable all strict policies on model: +#### `baseURL` -- [`strictProperties`](#strictproperties) -- [`strictReadOnly`](#strictreadonly) +##### Description -:::info +You may define a `baseURL` configuration option on your models. It will replace +the default base URL defined on the adapter. -If a specific strict policies is enabled/disabled, it supersedes the global -strict settings. +##### Example -::: +```typescript title="post.ts" +import { makeModel } from '@foscia/core'; -#### `strictProperties` +makeModel({ + type: 'posts', + baseURL: 'https://example.com/api/v2', +}); +``` -**Default**: `false`. +#### `path` + +##### Description + +**Default**: The model `type`. + +The `path` is used to query the model. It defaults to the model's type. +In an HTTP API, it is used as the endpoint. + +##### Example + +```typescript title="post.ts" +import { makeModel } from '@foscia/core'; + +makeModel({ + type: 'posts', + path: 'blog-posts', +}); +``` + +#### `guessPath` + +##### Description + +**Default**: no transformation. **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). -When enabled, getting a model's instance property value will throw an error if -the value was not retrieved from the store or if the relation is not loaded. +`guessPath` transform a model's `type` to guess its `path`. -#### `strictReadOnly` +##### Example -**Default**: `true`. +Here is an example of a path guesser using hypothetical `toKebabCase` function. +If your JSON:API record types are using camel cased types but your endpoint are +kebab cased: + +```typescript title="post.ts" +import { makeModel, isManyRelationDef } from '@foscia/core'; + +makeModel({ + type: 'blogPosts', + guessPath: (type: string) => toKebabCase(type), +}); +``` + +#### `guessIdPath` + +##### Description + +**Default**: no transformation. **Recommandation**: use this configuration option inside a [custom model factory](/docs/digging-deeper/models/models-composition#factory). -When enabled, setting a model's instance readonly property value will throw -an error. +`guessIdPath` transform a model's `id` to guess its `path` when querying. +This can be useful when you want to query records by given their endpoint as +their ID. -### HTTP +##### Example -The following configuration options are specific to HTTP models (when -interacting with JSON:API, JSON REST, etc.). +```typescript title="post.ts" +import { makeModel, isManyRelationDef } from '@foscia/core'; -#### `baseURL` +// If `/api/posts/1` is given when querying Post, only `1` will be used +// as ID in requested endpoint. +makeModel({ + type: 'posts', + guessIdPath: (id) => String(id).split('/').pop()!, +}); +``` -You may define a `baseURL` configuration option on your models. It will replace -the default base URL define on the adapter. +#### `guessRelationPath` + +##### Description + +**Default**: no transformation. + +**Recommandation**: use this configuration option inside a +[custom model factory](/docs/digging-deeper/models/models-composition#factory). + +`guessRelationPath` transform a model's relation to its "path". This is only +needed in specific implementation context (such as JSON:API when querying +related instances of a base model instance). + +##### Example + +Here is an example of a type guesser using hypothetical `toKebabCase` function. +If your JSON:API record properties are using kebab cased keys but your models +are camel cased: ```typescript title="post.ts" -import { makeModel } from '@foscia/core'; +import { + makeModel, + isManyRelationDef, + ModelRelation, +} from '@foscia/core'; makeModel({ type: 'posts', - baseURL: 'https://example.com/api/v2', + guessRelationPath: (def: ModelRelation) => toKebabCase(def.key), }); ``` diff --git a/website/docs/digging-deeper/models/models-hooks.md b/website/docs/digging-deeper/models/models-hooks.md new file mode 100644 index 00000000..cf9c00eb --- /dev/null +++ b/website/docs/digging-deeper/models/models-hooks.md @@ -0,0 +1,219 @@ +--- +sidebar_position: 30 +description: Using hooks with models. +--- + +# Using hooks + +:::tip What you'll learn + +- Registering and unregistering hooks on models and instances + +::: + +## Usage + +You can hook multiple events on model's instances using hook registration +functions, such as [`onCreating`](/docs/api/@foscia/core/functions/onCreating). + +To hook on an event, use the dedicated hook registration function. Each +hook registration function will return a callback to unregister the hook. + +```typescript +import { onCreating } from '@foscia/core'; + +// After this, the hook will run on each User instance saving. +const unregisterThisHook = onCreating(User, async (user) => { + // TODO Do something (a)sync with user instance before saving. +}); + +// After this, this hook will never run again. +unregisterThisHook(); +``` + +You can also use [`unregisterHook`](/docs/api/@foscia/core/functions/unregisterHook) +to remove a registered hook from a model. + +```typescript +import { onCreating, unregisterHook } from '@foscia/core'; + +const myCreatingHook = async (user: User) => { + // TODO Do something (a)sync with user instance before saving. +}; + +// After this, the hook will run on each User instance saving. +onCreating(User, myCreatingHook); + +// After this, this hook will never run again. +unregisterHook(User, 'creating', myCreatingHook); +``` + +You can temporally disable hook execution for a given model by using the +[`withoutHooks`](/docs/api/@foscia/core/functions/withoutHooks) function. +[`withoutHooks`](/docs/api/@foscia/core/functions/withoutHooks) can receive +a sync or async callback: if an async callback is passed (e.g. returning a +`Promise`), it will also return a `Promise`. + +```typescript +import { withoutHooks } from '@foscia/core'; + +const asyncResultOfYourCallback = await withoutHooks(User, async () => { +// TODO Do something async and return it. +}); +``` + +:::warning + +**Foscia may also register hooks internally** when using some features, such +as relations inverse, etc. Be careful when running a callback +without model's hooks, as those hooks will also be disabled. + +::: + +## Instances hooks + +:::info + +Most instances hooks callbacks can be asynchronous and are executed in a +sequential fashion (one by one, not parallelized). Only `init` is a sync +hook callback. + +::: + +You can hook on multiple events on instances: + +- [`onInit`](/docs/api/@foscia/core/functions/onInit): + instance was constructed by calling `new` on model class. +- [`onRetrieved`](/docs/api/@foscia/core/functions/onRetrieved): + instance was deserialized from a backend response. +- [`onCreating`](/docs/api/@foscia/core/functions/onCreating): + action to create instance will run soon. +- [`onCreated`](/docs/api/@foscia/core/functions/onCreated): + action to create instance was ran successfully. +- [`onUpdating`](/docs/api/@foscia/core/functions/onUpdating): + action to update instance will run soon. +- [`onUpdated`](/docs/api/@foscia/core/functions/onUpdated): + action to update instance was ran successfully. +- [`onSaving`](/docs/api/@foscia/core/functions/onSaving): + action to save (create or update) instance will run soon (always + ran after `onCreating` and `onUpdating`). +- [`onSaved`](/docs/api/@foscia/core/functions/onSaved): + action to save (create or update) instance was ran successfully + (always ran after `onCreated` and `onUpdated`). +- [`onDestroying`](/docs/api/@foscia/core/functions/onDestroying): + action to destroy instance will run soon. +- [`onDestroyed`](/docs/api/@foscia/core/functions/onDestroyed): + action to destroy instance was ran successfully. + +Each of these hooks callback will receive an instance as parameter: + +```typescript +import { onCreating } from '@foscia/core'; + +onCreating(User, async (user) => { +}); +``` + +## Models hooks + +:::info + +Models hooks callbacks are synchronous and are executed in +a sequential fashion (one by one, not parallelized). + +::: + +Only `boot` event can be hooked on a model class, using +[`onBoot`](/docs/api/@foscia/core/functions/onBoot). +It is like [`onInit`](/docs/api/@foscia/core/functions/onInit), +but will be called only once per model and will receive the model class. + +```typescript +import { onBoot } from '@foscia/core'; + +onBoot(User, async (UserModel) => { +}); +``` + +## Properties hooks + +:::info + +Instances properties hooks callbacks are synchronous and are executed in +a sequential fashion (one by one, not parallelized). + +::: + +You can hook on multiple events on instances' properties: + +- [`onPropertyReading`](/docs/api/@foscia/core/functions/onPropertyReading): + an instance property getter is called (ran before getting value). +- [`onPropertyRead`](/docs/api/@foscia/core/functions/onPropertyRead): + an instance property getter is called (ran after getting value). +- [`onPropertyWriting`](/docs/api/@foscia/core/functions/onPropertyWriting): + an instance property setter is called (ran before setting value). +- [`onPropertyWrite`](/docs/api/@foscia/core/functions/onPropertyWrite): + an instance property setter is called (ran after setting value). + +Reading hooks will receive the instance and property key, current value and definition: + +```typescript +import { onPropertyReading, onPropertyRead } from '@foscia/core'; + +// Hook on specific property reading. +onPropertyReading(User, 'email', ({ instance, key, value, def }) => { +}); +onPropertyRead(User, 'email', ({ instance, key, value, def }) => { +}); + +// Hook on any property reading. +onPropertyReading(User, ({ instance, key, value, def }) => { +}); +onPropertyRead(User, ({ instance, key, value, def }) => { +}); +``` + +Writing hooks will receive the instance and property key, previous value, next value and definition: + +```typescript +import { onPropertyWriting, onPropertyWrite } from '@foscia/core'; + +// Hook on specific property reading. +onPropertyWriting(User, 'email', ({ instance, key, prev, next, def }) => { +}); +onPropertyWrite(User, 'email', ({ instance, key, prev, next, def }) => { +}); + +// Hook on any property reading. +onPropertyWriting(User, ({ instance, key, prev, next, def }) => { +}); +onPropertyWrite(User, ({ instance, key, prev, next, def }) => { +}); +``` + +To unregister a property hook callback using +[`unregisterHook`](/docs/api/@foscia/core/functions/unregisterHook), +you should pass the event name with or without the property's key, +depending on if it is a specific property hook callback or not: + +```typescript +import { unregisterHook } from '@foscia/core'; + +// Unregister specific property hook. +unregisterHook(User, 'property:reading:email', registeredCallback); +unregisterHook(User, 'property:read:email', registeredCallback); +unregisterHook(User, 'property:writing:email', registeredCallback); +unregisterHook(User, 'property:write:email', registeredCallback); + +// Unregister non-specific properties hook. +unregisterHook(User, 'property:reading', registeredCallback); +unregisterHook(User, 'property:read', registeredCallback); +unregisterHook(User, 'property:writing', registeredCallback); +unregisterHook(User, 'property:write', registeredCallback); +``` + +## Using hooks with composition + +All models, instances and properties hooks can be used on +[composables](/docs/digging-deeper/models/models-composition#composable-using-hooks) +and [models factories](/docs/digging-deeper/models/models-composition#factory-using-hooks). diff --git a/website/docs/digging-deeper/models/models-reduce-revive.mdx b/website/docs/digging-deeper/models/models-reduce-revive.mdx index e04490b6..450562c8 100644 --- a/website/docs/digging-deeper/models/models-reduce-revive.mdx +++ b/website/docs/digging-deeper/models/models-reduce-revive.mdx @@ -28,14 +28,16 @@ Nuxt SSR features. ## Using CLI -You can generate reducer and reviver using [`@foscia/cli`](/docs/digging-deeper/cli). +You can generate reducer and reviver using [`@foscia/cli`](/docs/digging-deeper/usages/cli). ## Reducing and reviving Foscia provides built-in tools to reduce and revive models' instances with two -functions: `makeModelsReducer` and `makeModelsReviver`. +functions: +[`makeModelsReducer`](/docs/api/@foscia/core/functions/makeModelsReducer) and +[`makeModelsReviver`](/docs/api/@foscia/core/functions/makeModelsReviver). Those tools support instance's state, values and relations (circular or not) reducing and reviving out of the box. @@ -46,7 +48,7 @@ import Post from './models/post'; import Comment from './models/comment'; const { reduce } = makeModelsReducer(); -const { revive } = makeModelsReviver([Post, Comment]); +const { revive } = makeModelsReviver({ models: [Post, Comment] }); const myPost = new Post(); @@ -56,29 +58,55 @@ const myRevivedPost = revive(JSON.parse(json)); :::info -Notice that: - -- `makeModelsReviver` must receive an array of revivable models. -- Reducing models will not "serialize" object values, such as Date. - You can use a tool like [**`devalue`**](https://github.com/Rich-Harris/devalue) - to leverage this. +Notice that [`makeModelsReviver`](/docs/api/@foscia/core/functions/makeModelsReviver) +must receive an array of revivable models. Otherwise, it won't be able to +resolve models to revive records in. ::: +## Handling advanced data types + +If you want to use the reducer and reviver to serialize advanced data types, +(such as date, etc.), you can use a tool like +[**`devalue`**](https://github.com/Rich-Harris/devalue), because +`JSON.stringify` does not support dates, `Map`, etc. + +Those kind of tool can be used to (de)serialize reduced models directly, or +can be configured inside the reducer and reviver using +[`reduce`](/docs/api/@foscia/core/type-aliases/ModelsReducerConfig#reduce) and +[`revive`](/docs/api/@foscia/core/type-aliases/ModelsReviverConfig#revive). + +```typescript +import { makeModelsReducer, makeModelsReviver } from '@foscia/core'; +import * as devalue from 'devalue'; + +const { reduce } = makeModelsReducer({ + reduce: (value) => devalue.stringify(value), +}); +const { revive } = makeModelsReviver({ + revive: (value) => devalue.parse(value), +}); +``` + ## Custom behaviors You can easily define custom reducing and reviving behaviors by defining -`$reduce` and `$revive` instance methods. Those custom behaviors can -support default state reducing/reviving or not, and can be strongly typed. +[`$reduce`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#reduce) and +[`$revive`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#revive) +instance methods. Those custom behaviors can support default state +reducing/reviving or not, and can be strongly typed. ### Keeping instance state To provide a custom reducing/reviving behavior while keeping instance state automatic reducing/reviving, you can use the provided `data` factory function -passed to `$reduce`. It will reduce internal data used by Foscia to correctly -revive instance state. +passed to +[`$reduce`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#reduce). +It will reduce internal data used by Foscia to correctly revive instance state. -During `$revive`, `data` will contain your reduced data provided by `$reduce`. +During [`$revive`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#revive), +`data` will contain your reduced data provided by +[`$reduce`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#reduce). ```typescript import { attr, makeModel, ModelReduceTools } from '@foscia/core'; @@ -109,10 +137,13 @@ export default class Post extends makeModel('posts', { ### Destroying instance state You can omit instance state reducing by not calling `data` function -inside your `$reduce` method. This can be useful when you need something +inside your [`$reduce`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#reduce) +method. This can be useful when you need something very specific, but be aware that no instance state will be restored -automatically (values, relations, `$exists` state, `$original` snapshot -or other instance properties). +automatically (values, relations, +[`$exists`](/docs/api/@foscia/core/interfaces/ModelInstance#exists) state, +[`$original`](/docs/api/@foscia/core/interfaces/ModelInstance#original) +snapshot or other instance properties). ```typescript import { attr, makeModel } from '@foscia/core'; @@ -139,8 +170,12 @@ export default class Post extends makeModel('posts', { ### Typechecking custom behaviors If you want to ensure you do not miss any implemented methods or typing, you can -use Foscia provided interface `ModelCanReduceRevive` and types to strict -type your implementations of custom `$reduce` and `$revive` methods. +use Foscia provided interface +[`ModelCanReduceRevive`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive) +and types to strict type your implementations of custom +[`$reduce`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#reduce) and +[`$revive`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#revive) +methods. ```typescript import { makeModel, ModelCanReduceRevive, ModelReduceTools, ModelReviveTools, ReducedModelInstanceCustomData } from '@foscia/core'; @@ -172,7 +207,10 @@ export default class Post ### Defining common custom behaviors If you want to share a common behavior between some or all of your models, -you can use [model composition](/docs/digging-deeper/models/models-composition) to define common `$reduce` and `$revive` +you can use [model composition](/docs/digging-deeper/models/models-composition) +to define common +[`$reduce`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#reduce) and +[`$revive`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive#revive) methods. #### With a composable diff --git a/website/docs/digging-deeper/models/models-relations.mdx b/website/docs/digging-deeper/models/models-relations.mdx index 805cad67..e1fdce63 100644 --- a/website/docs/digging-deeper/models/models-relations.mdx +++ b/website/docs/digging-deeper/models/models-relations.mdx @@ -19,11 +19,11 @@ import ShellCommand from '@site/src/components/ShellCommand'; ## Checking loading state If you want to check if an instance's relations (or deep relations) are loaded, -you can use the `loaded` function. +you can use the [`loaded`](/docs/api/@foscia/core/functions/loaded) function. -`loaded` recursively inspect relations. This means it will only return `true` if -**all** relations of the instance (and sub-instances for deep relations) are -loaded. +[`loaded`](/docs/api/@foscia/core/functions/loaded) recursively inspect +relations. This means it will only return `true` if **all** relations of +the instance (and sub-instances for deep relations) are loaded. ```typescript import { loaded } from '@foscia/core'; @@ -44,19 +44,35 @@ behaviors and options. ### Using CLI -You can generate a new loader using [`@foscia/cli`](/docs/digging-deeper/cli). +You can generate a new loader using [`@foscia/cli`](/docs/digging-deeper/usages/cli). ### 1. Instance refreshing +#### Description + This is the simplest way of loading relations if your data source implementation provides an inclusion of relations and a way to filter models based on IDs (e.g. -JSON:API). +JSON:API). It uses +[`makeRefreshIncludeLoader`](/docs/api/@foscia/core/functions/makeRefreshIncludeLoader). This loader will target the model index and include the requested relations. It supports nested relations keys if your data source supports them. +:::info + +This loader is recommended as it will only run one action for many models and +relations. But, be aware that you should implement an adapted `prepare` to +filter the fetched models (this will avoid overloading your data source with +useless records fetching). + +It should support polymorphism if your data provider does. + +::: + +#### Example + Here is an example when using a JSON:API backend with an `ids` filter available. ```typescript title="loaders/loadWithRefresh.ts" @@ -78,68 +94,35 @@ await loadWithRefresh(myPost, 'comments'); await loadWithRefresh(myPostsArray, ['comments', 'comments.author']); ``` -:::info - -This loader is recommended as it will only run one action for many models and -relations. But, be aware that you should implement an adapted `prepare` to -filter the fetched models (this will avoid overloading your data source with -useless records fetching). - -It should support polymorphism if your data provider does. - -::: - -#### Options +#### Configuration -##### `prepare: (action: Action, context: { instances: ModelInstance[]; relations: string[] }): Awaitable` +All available configuration options are described in the +[`RefreshIncludeLoaderOptions` API reference](/docs/api/@foscia/core/type-aliases/RefreshIncludeLoaderOptions). -A function to execute before running action allowing you to prepare the refresh -action (e.g. to avoid fetching a full list of models by filtering on instances' -IDs). - -##### `chunk: (instances: ModelInstance[]): ModelInstance[][]` - -A function to split the instances array to multiple arrays (e.g. to avoid -hitting pagination limit). - -
- - - -Array chunk function example - - - -```typescript -function chunk(items: T[], size: number) { - const chunks = [] as T[][]; - for (let i = 0; i < array.length; i += size) { - chunks.push(array.slice(i, i + chunkSize)); - } - - return chunks; -} -``` +### 2. Related model action -
+ -##### `exclude: (instance: ModelInstance, relations: ModelRelationDotKey): bool` +#### Description -A function to exclude some relation fetching (e.g. to avoid fetching already -loaded relations). Notice the following: +This method is best suited for multiple instance relation loading with +implementations providing IDs instead of included relations, such as some REST +implementations. It uses +[`makeQueryModelLoader`](/docs/api/@foscia/core/functions/makeQueryModelLoader). +If nested relations are passed (such as `comments.author`), +it will [`include`](/docs/api/@foscia/core/functions/include) the nested +relations during the root model action. -- If an instance is excluded for all relations, it will not be refreshed -- If a relation is excluded for all instances, it will not be included during - refresh +:::info -### 2. Related model action +This loader is recommended as it will only run one action per direct relation. +It supports polymorphic relations when +[**related models are correctly defined on relations**](/docs/core-concepts/models#recommandations) +and `extract` option correctly retrieve IDs. - +::: -This method is best suited for multiple instance relation loading with -implementations providing IDs instead of included relations, such as some REST -implementations. If nested relations are passed (such as `comments.author`), -it will `include` the nested relations during the root model action. +#### Example ```typescript title="loaders/loadWithModelQuery.ts" import { makeQueryModelLoader } from '@foscia/core'; @@ -159,17 +142,7 @@ await loadWithModelQuery(myPost, 'comments'); await loadWithModelQuery(myPostsArray, ['comments', 'comments.author']); ``` -:::info - -This loader is recommended as it will only run one action per direct relation. - -It supports polymorphic relations when -[**related models are correctly defined on relations**](/docs/core-concepts/models#recommandations) -and `extract` option correctly retrieve IDs. - -::: - -#### Options +#### Configuration ##### `extract: (instance: ModelInstance, relation: ModelRelationKey): Arrayable | null | undefined` @@ -278,10 +251,26 @@ loaded relations). ### 3. Relation action +#### Description + This method is best suited for one instance relation loading with implementations providing relation reading through a dedicated endpoint/query, -such as JSON:API. If nested relations are passed (such as `comments.author`), -it will `include` the nested relations during the root relation action. +such as JSON:API. It uses +[`makeQueryRelationLoader`](/docs/api/@foscia/core/functions/makeQueryRelationLoader). +If nested relations are passed (such as `comments.author`), +it will [`include`](/docs/api/@foscia/core/functions/include) the nested +relations during the root model action. + +:::warning + +Because this loader will run one action per instance and relation, it is only +recommended for one instance's relation loading, not many. + +It should support polymorphism if your data provider does. + +::: + +#### Example ```typescript title="loaders/loadWithRelationQuery.ts" import { makeQueryRelationLoader } from '@foscia/core'; @@ -298,16 +287,7 @@ import loadWithRelationQuery from './loaders/loadWithRelationQuery'; await loadWithRelationQuery(myPost, 'comments'); ``` -:::warning - -Because this loader will run one action per instance and relation, it is only -recommended for one instance's relation loading, not many. - -It should support polymorphism if your data provider does. - -::: - -#### Options +#### Configuration ##### `exclude: (instance: ModelInstance, relations: ModelRelationDotKey): bool` @@ -328,7 +308,7 @@ Every loader factory provides an `exclude` option which can remove some instances or relations before running actions. This is useful to avoid loading already loaded relations, and can be done -easily using the `loaded` function. +easily using the [`loaded`](/docs/api/@foscia/core/functions/loaded) function. ```typescript title="loaders/loadMissingWithRefresh.ts" import { makeRefreshIncludeLoader, loaded } from '@foscia/core'; diff --git a/website/docs/digging-deeper/models/models-transformers.mdx b/website/docs/digging-deeper/models/models-transformers.mdx index 38d172d7..f113c179 100644 --- a/website/docs/digging-deeper/models/models-transformers.mdx +++ b/website/docs/digging-deeper/models/models-transformers.mdx @@ -4,6 +4,7 @@ description: Creating custom functional or object transformers. --- import ShellCommand from '@site/src/components/ShellCommand'; +import FunctionInfo from '@site/src/components/FunctionInfo'; # Using transformers @@ -18,7 +19,9 @@ import ShellCommand from '@site/src/components/ShellCommand'; ### `toDateTime` -`toDateTime` converts values to `Date` object using +[`toDateTime`](/docs/api/@foscia/core/functions/toDateTime) converts values to +[`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) +object using [`Date.parse`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). It will serialize values to an [ISO date and time string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString). @@ -33,13 +36,16 @@ makeModel('posts', { :::info -`toDateTime` will log a warning if a value gets converted to an invalid date. +[`toDateTime`](/docs/api/@foscia/core/functions/toDateTime) will log a warning +if a value gets converted to an invalid date. ::: ### `toDate` -`toDate` converts values to `Date` object using a custom parser. It expects +[`toDate`](/docs/api/@foscia/core/functions/toDate) converts values to +[`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) +object using a custom parser. It expects date to be formatted as ISO date string (`YYYY-MM-DD`, e.g. `2023-12-25`). It will serialize values to an ISO string date with the same format. @@ -53,17 +59,20 @@ makeModel('users', { :::info -`toDate` will log a warning if a value gets converted to an invalid date. +[`toDate`](/docs/api/@foscia/core/functions/toDate) will log a warning +if a value gets converted to an invalid date. -`toDate` will also apply an "epoch shift" because serializing to a UTC date. +[`toDate`](/docs/api/@foscia/core/functions/toDate) will also +apply an "epoch shift" when serializing to a UTC date. This will avoid local date to be serialized to incorrect date due to UTC -serialization. +conversion. ::: ### `toNumber` -`toNumber` converts values to `Number` object using +[`toNumber`](/docs/api/@foscia/core/functions/toNumber) converts values to +`number` using [`Number()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number/Number). ```typescript @@ -76,13 +85,14 @@ makeModel('products', { :::info -`toNumber` will log a warning if a value gets converted to a `NaN` value. +[`toNumber`](/docs/api/@foscia/core/functions/toNumber) will log a warning if a value gets converted to a `NaN` value. ::: ### `toBoolean` -`toBoolean` converts values to `boolean` if the value match a truthy value. +[`toBoolean`](/docs/api/@foscia/core/functions/toBoolean) converts values to +`boolean` if the value match a truthy value. You can provide your own truthy values using `trueValues` option (defaults are `[true, 1, '1', 'true', 'yes']`). @@ -98,13 +108,14 @@ makeModel('users', { :::info -`toBoolean` won't convert `null` values. +[`toBoolean`](/docs/api/@foscia/core/functions/toBoolean) won't convert `null` values. ::: ### `toString` -`toString` converts values to `String` object using +[`toString`](/docs/api/@foscia/core/functions/toString) converts values to +`string` using [`String()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/String). ```typescript @@ -117,13 +128,14 @@ makeModel('posts', { :::info -`toBoolean` won't convert `null` values. +[`toString`](/docs/api/@foscia/core/functions/toString) won't convert `null` values. ::: ### `toArrayOf` -`toArrayOf` converts values of an array using the given transformer. This can be +[`toArrayOf`](/docs/api/@foscia/core/functions/toArrayOf) converts values of +an array using the given transformer. This can be useful when dealing with array of dates. ```typescript @@ -138,16 +150,17 @@ makeModel('posts', { ### `makeTransformer` (recommended) -You can generate a new transformer using [`@foscia/cli`](/docs/digging-deeper/cli). +You can generate a new transformer using [`@foscia/cli`](/docs/digging-deeper/usages/cli). -Foscia provides a `makeTransformer` utility function to create a new -transformer. This function creates a transformer using a `deserialize` function +Foscia provides a [`makeTransformer`](/docs/api/@foscia/core/functions/makeTransformer) +utility function to create a new transformer. +This function creates a transformer using a `deserialize` function and an optional `serialize` function (deserializer will be used as a default). -Transformers created with `makeTransformer` will transform `null` or `undefined` -value to `null`. +Transformers created with [`makeTransformer`](/docs/api/@foscia/core/functions/makeTransformer) +will transform `null` or `undefined` value to `null`. Here are two examples of transformers: @@ -171,7 +184,8 @@ export default () => makeTransformer( ); ``` -The return value of `makeTransformer` is a transformer object. +The return value of [`makeTransformer`](/docs/api/@foscia/core/functions/makeTransformer) +is a transformer object which can be used on model's attributes. ```typescript import { attr, makeModel } from '@foscia/core'; @@ -184,17 +198,22 @@ makeModel('posts', { }); ``` -### Custom transformers +### `makeCustomTransformer` + + If you want full control on your transformers, you can create transformer -objects manually. A transformer object should have two methods: +objects using [`makeCustomTransformer`](/docs/api/@foscia/core/functions/makeCustomTransformer). -- `serialize` which converts a _real_ value to its _raw_ counterpart -- `deserialize` which converts a _raw_ value to its _real_ counterpart +Custom transformers must handle `null` values because `null` may also be +transformed by serializers and deserializers (e.g. converting a `null` value to +an empty string). ```typescript -export default { - deserialize: (value: string | null) => { +import { makeCustomTransformer } from '@foscia/core'; + +export default makeCustomTransformer( + (value: string | null) => { if (value === null) { return null; } @@ -204,14 +223,6 @@ export default { return date; }, - serialize: (value: Date | null) => (value ? value.toISOString() : null), -}; + (value: Date | null) => (value ? value.toISOString() : null), +); ``` - -:::warning - -Custom transformers must handle `null` values because `null` may also be -transformed by serializers and deserializers (e.g. converting a `null` value to -an empty string). - -::: diff --git a/website/docs/digging-deeper/rest.md b/website/docs/digging-deeper/rest.md deleted file mode 100644 index 3e722819..00000000 --- a/website/docs/digging-deeper/rest.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -sidebar_label: Using REST -sidebar_position: 4000 -description: Using Foscia to interact with a JSON:API. ---- - -# REST - -:::tip What you'll learn - -- Using Foscia to interact with a REST API -- Common configuration recipes - -::: - -## Setup - -Please follow the [getting started guide](/docs/getting-started) to set up your -REST action factory. - -## Usage - -Currently, REST implementations does not support additional features compared to -generic Foscia features, because REST is not that much standardized about -filtering, sorting, etc. - -If your REST API supports eager loading relations, you should -[configure your REST adapter](/docs/reference/implementations/rest#makejsonrestadapter) -to serialize relationships inclusion in every request. - -If you need something specific, you can -[open a new issue on the repository](https://github.com/foscia-dev/foscia/issues/new/choose). - -## Configuration recipes - -Here are common configuration for `@foscia/rest` implementation. You can read -[the implementation and configuration guide](/docs/reference/implementations/rest) -for more details. - -You can also take a look at -[HTTP usage and common configuration recipes](/docs/digging-deeper/http), as -the REST adapter is based on HTTP adapter. - -### Customizing include query - -If your REST API supports eager loading relations, you can use `include` -enhancer to request relations loading. This will define a query parameter -such as `include=author,comments`. - -To define a custom query parameter, use the `includeParamKey` option. - -```typescript -import { makeJsonRestAdapter } from '@foscia/http'; - -const { adapter } = makeJsonRestAdapter({ - includeParamKey: 'with', -}); -``` - -### Changing content format - -If your REST API interacts with another data type than JSON, such as XML, you -can configure this behavior as a default on your adapter: - -```typescript -import { makeRestAdapterWith } from '@foscia/rest'; -import { objectToXML, objectFromXML } from 'some-xml-library'; - -makeRestAdapterWith({ - defaultBodyAs: (body) => objectToXML(body), - defaultResponseReader: (response) => objectFromXML(body), - defaultHeaders: { - Accept: 'application/xml', - 'Content-Type': 'application/xml', - }, -}); -``` - -### Deserializing nested data - -If your REST API document nest records inside the document (not at root, such as -inside a `data` property), you can add `extractData` option which will extract -records data using the given transformation function. - -```typescript -import { makeJsonRestDeserializer } from '@foscia/rest'; - -makeJsonRestSerializer({ - extractData: (data: { data: any }) => ({ records: data.data }), -}); -``` - -### Nesting serialized data - -If your REST API document expect record data to be nested (not at root, such as -inside a `data` property), you can add `createData` option which will wrap -records data using the given transformation function. - -```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; - -makeJsonRestSerializer({ - createData: (records) => ({ data: records }), -}); -``` - -### Changing endpoint case - -By default, JSON:API use kebab case for models and relations endpoints (e.g. -`favorite-posts` for a `favoritePosts` relation). If you want to use another -case for endpoints, you can use `modelPathTransformer` and -`relationPathTransformer` options. - -```typescript -import { camelCase } from 'lodash-es'; -import { makeJsonRestAdapter } from '@foscia/rest'; - -makeJsonRestAdapter({ - modelPathTransformer: (path) => camelCase(path), - relationPathTransformer: (path) => camelCase(path), -}); -``` - -### Changing serialization keys case - -By default, serialized and deserialized attributes and relations keep keys -specified in the model. If you are using camel cased keys (e.g. `firstName`) -but want to exchange kebab cased keys (e.g. `first-name`) with your API, -you can use `serializeKey` and `deserializeKey` options. - -```typescript -import { kebabCase } from 'lodash-es'; -import { makeJsonRestSerializer, makeJsonRestDeserializer } from '@foscia/rest'; - -makeJsonRestSerializer({ - serializeKey: ({ key }) => kebabCase(key), -}); - -makeJsonRestDeserializer({ - deserializeKey: ({ key }) => kebabCase(key), -}); -``` - -### Customizing relation serialized data - -By default, REST implementation will only serialize the related IDs as the -serialized relation's data. You can customize this behavior using -`serializeRelation` option. - -Here is an example which will serialize ID and type to support polymorphic -relations: - -```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; - -makeJsonRestSerializer({ - serializeRelation: (_, related) => ({ type: related.$model.$type, id: related.id }), -}); -``` - -Here is another example where we serialize the whole related record: - -```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; - -makeJsonRestSerializer({ - serializeRelation: ({ context, serializer }, related, parents) => serializer - .serializeInstance(related, context, parents) -}); -``` - -:::tip - -If you want to customize relations serialization behavior when writing -relations (e.g. using `attach`, `associate`, etc.), you can use -the `serializeRelated` option which have the same signature. - -::: - -### Parsing URL IDs - -Some API implementation may serialize records IDs as URL to the record endpoint -(such as `https://example.com/api/posts/1` for post `1`). You can customize -the deserializer to support ID and type extraction from URL ID using the -`pullIdentifier` option. - -```typescript -import { makeJsonRestDeserializer } from '@foscia/rest'; - -makeJsonRestDeserializer({ - pullIdentifier: (record) => { - // This will support IDs like `https://example.com/api/posts/1`, `/api/posts/1`, etc. - const [id, type] = String(record.id).split('/').reverse(); - - return { id, type }; - }, -}); -``` - -## Reference - -- [Implementation and configuration guide](/docs/reference/implementations/rest) diff --git a/website/docs/digging-deeper/testing.mdx b/website/docs/digging-deeper/testing.mdx deleted file mode 100644 index 27039260..00000000 --- a/website/docs/digging-deeper/testing.mdx +++ /dev/null @@ -1,207 +0,0 @@ ---- -sidebar_position: 20000 -description: Writing tests when using Foscia. ---- - -import ShellCommand from '@site/src/components/ShellCommand'; - -# Testing - -:::tip What you'll learn - -- Mocking action factory for unit tests -- Writing expectation about ran contexts - -::: - -## Integration tests - -Integration tests goal is to ensure separate parts of your software works -together. - -When using Foscia, this probably means you want to check that your UI correctly -interact with a backend or database using Foscia. - -**Therefore, Foscia does not provide any integration tests utilities, because -those tests should not mock or fake any Foscia behaviors.** - -## Unit tests - -Unit tests are focused on checking small and independent parts of your software. - -This probably means you want to check functions which are interacting with a -backend or database using Foscia API. - -The simplest way to write unit tests of parts of code using Foscia is to mock -your action factory and define expected results and contexts expectation. - -Foscia provide a simple set of functions and objects to help you writing unit -tests with any testing framework (Jest, Vitest, etc.). - -### Preparation - -Start by installing Foscia test helpers. - - - -Once test helpers are installed, you should make your action factory "mockable" -using `makeActionFactoryMockable`. This will transform the action factory -function so it can be mocked or unmocked using `mockAction` and `unmockAction`. - -```typescript title="action.ts" -import { makeActionFactory } from '@foscia/core'; -import { makeActionFactoryMockable } from '@foscia/test'; - -export default makeActionFactoryMockable( - makeActionFactory({ - // configuration... - }), -); -``` - -### Mocking action factory - -You can mock your action factory using `mockAction` function. It is important to -just pass your action factory (`action`) and not its return value (`action()`), -because the mock will install on the function itself. - -When mocking your action, Foscia is replacing the base factory function with a -proxy function which will built a proxy action instance. Each enhancer will -apply normally and make the context evolve, but when running the action it will -intercept the run to provide your mocked result. - -```typescript title="test/actionMock.ts" -import { ActionFactoryMock, mockAction, unmockAction } from '@foscia/test'; -import action from './action'; - -let actionMock: ActionFactoryMock; - -beforeEach(() => { - actionMock = mockAction(action); -}); - -afterEach(() => { - unmockAction(action); -}); - -export default actionMock; -``` - -### Unit tests example - -Here is a simple example of a function we will write tests for: - -```typescript title="src/registerUser.ts" -import { create, fill, oneOrCurrent } from '@foscia/core'; -import action from './action'; -import User from './models/user'; - -export default function registerUser( - email: string, - acceptedTerms: boolean, -): Promise { - if (!acceptedTerms) { - throw new Error('User did not accept terms and conditions of use.'); - } - - const user = fill(new User(), { - email, - acceptedTermsAt: new Date(), - }); - - return action().run(create(user), oneOrCurrent()); -} -``` - -#### Simple mock usage - -In this simple example, we will go through basics features of action mocking -(e.g. mocking next results). - -```typescript title="test/registerUser.test.ts" -import { fill } from '@foscia/core'; -import User from '../src/models/user'; -import registerUser from '../src/registerUser'; -import actionMock from './actionMock'; - -test('should create user', async () => { - const user = fill(new User(), { - email: 'john.doe@example.com', - acceptedTermsAt: new Date(), - }); - - actionMock.mockResult(user); - - const result = await registerUser('john.doe@example.com', true); - - expect(result).toStrictEqual(user); -}); - -test('should fail creating user with non accepted terms', async () => { - const result = await registerUser('john.doe@example.com', true); - - expect(() => - registerUser('john.doe@example.com', true), - ).rejects.toThrowError(); -}); -``` - -#### Complex mock usage - -Action mock provide enough flexibility for you to check complex cases. - -##### Mocking indefinitely or "n" times - -```typescript -// Mock result indefinitely. -actionMock.mockResult(value); -// Mock result for only for "n" next calls. -actionMock.mockResultOnce(value); -actionMock.mockResultTwice(value); -actionMock.mockResultTimes(3, value); -// Mock result factory function. -actionMock.mockResult(() => value); -``` - -:::warning - -Mocking results indefinitely will prevent your next mocked runs to be used. -Mocking for "n" times is therefore a safer way to mock results, since it will -also fail if there are no more runs expected. - -::: - -##### Mocking context conditionally - -```typescript -// Only mock when context is matching over given predicate. -actionMock.mockResult(value, (context) => context.model === Post); -``` - -##### Mocking context expectation - -```typescript -// Run expectation over context before returning value. -actionMock.mockResult({ - result: value, - expectation: (context) => { - expect(context.model).toStrictEqual(Post); - expect(context.action).toStrictEqual('create'); - }, -}); -``` - -##### Mocking full configuration - -```typescript -// Use differents mocking options. -actionMock.mockResult({ - result: value, - times: 3, - predicate: (context) => context.model === Post, - expectation: (context) => { - expect(context.model).toStrictEqual(Post); - expect(context.action).toStrictEqual('create'); - }, -}); -``` diff --git a/website/docs/digging-deeper/usages/_category_.json b/website/docs/digging-deeper/usages/_category_.json new file mode 100644 index 00000000..61671ae0 --- /dev/null +++ b/website/docs/digging-deeper/usages/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Usages", + "position": 300, + "link": { + "type": "generated-index", + "description": "Advanced usage and configuration guides for each available implementations." + } +} diff --git a/website/docs/digging-deeper/cli.mdx b/website/docs/digging-deeper/usages/cli.mdx similarity index 89% rename from website/docs/digging-deeper/cli.mdx rename to website/docs/digging-deeper/usages/cli.mdx index adb9e7ca..fd1f9341 100644 --- a/website/docs/digging-deeper/cli.mdx +++ b/website/docs/digging-deeper/usages/cli.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 10000 -title: Using Foscia CLI -description: Using Foscia CLI. +title: Foscia CLI +description: Foscia CLI installation, commands and usage. --- import FunctionInfo from '@site/src/components/FunctionInfo'; @@ -374,15 +374,16 @@ Example of a Foscia CLI configuration: Description of each configuration options: -| Key | Type | Description | -| ---------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------ | -| `path` | `string` | Directory to put your Foscia files in (action factory, models, etc.) | -| `alias` | `string`|`undefined` | Alias to use when importing models in files (instead of relative import path). | -| `packageManager` | `'npm'`|`'yarn'`|`'pnpm'`|`'bun'` | Package manager to use when installing Foscia dependencies | -| `usage` | `'jsonapi'`|`'jsonrest'`|`'http'`|`undefined` | Your usage of Foscia for this configuration. | -| `language` | `'ts'`|`'js'` | The language to use when generating files. | -| `modules` | `'esm'`|`'commonjs'` | The modules organization to use when generating files. | -| `tabSize` | `number` | The tab size to use when generating files (defaults to 2). | +| Key | Type | Description | +|------------------|--------------------------------------------------|------------------------------------------------------------------------------------| +| `path` | `string` | The directory to put your Foscia files in (action factory, models, etc.) | +| `alias` | `string`|`undefined` | The alias to use when importing models in files (instead of relative import path). | +| `packageManager` | `'npm'`|`'yarn'`|`'pnpm'`|`'bun'` | The package manager to use when installing Foscia dependencies. | +| `usage` | `'jsonapi'`|`'jsonrest'`|`'http'` | Your usage of Foscia for this configuration. | +| `framework` | `'nuxt'`|`undefined` | The framework you are using Foscia in. | +| `language` | `'ts'`|`'js'` | The language to use when generating files. | +| `modules` | `'esm'`|`'commonjs'` | The modules organization to use when generating files. | +| `tabSize` | `number` | The tab size to use when generating files (defaults to 2). | ### Multiple configurations diff --git a/website/docs/digging-deeper/http.mdx b/website/docs/digging-deeper/usages/http.mdx similarity index 58% rename from website/docs/digging-deeper/http.mdx rename to website/docs/digging-deeper/usages/http.mdx index c8a891c6..03bc0754 100644 --- a/website/docs/digging-deeper/http.mdx +++ b/website/docs/digging-deeper/usages/http.mdx @@ -1,5 +1,5 @@ --- -sidebar_label: Using HTTP client +sidebar_label: HTTP sidebar_position: 2000 description: Using Foscia as a simple HTTP client. --- @@ -32,7 +32,7 @@ action factory as an HTTP client. ### CLI (recommended) You can get started for Foscia as an HTTP client using the -[`@foscia/cli`](/docs/digging-deeper/cli). +[`@foscia/cli`](/docs/digging-deeper/usages/cli). @@ -54,11 +54,15 @@ export default makeActionFactory({ ### Basic usage Once your action factory is ready, sending HTTP request is pretty easy using -provided enhancers ([`makeGet`](/docs/reference/actions-enhancers#makeget), -[`makePost`](/docs/reference/actions-enhancers#makepost), etc.). Running a -request is done by [`raw`](/docs/reference/actions-runners#raw) runner, which -will retrieve a fetch `Response` object. You can also pass a callback to `raw` -to retrieve a value from the `Response` object. +provided enhancers ([`makeGet`](/docs/api/@foscia/http/functions/makeGet), +[`makePost`](/docs/api/@foscia/http/functions/makePost), etc.). Running a +request is done by [`raw`](/docs/api/@foscia/core/functions/raw) runner, which +will retrieve a fetch +[`Response`](https://developer.mozilla.org/docs/Web/API/Response) +object. You can also pass a callback to +[`raw`](/docs/api/@foscia/core/functions/raw) +to retrieve a value from the +[`Response`](https://developer.mozilla.org/docs/Web/API/Response) object. ```typescript import { raw } from '@foscia/core'; @@ -66,10 +70,10 @@ import { makeGet, makePost } from '@foscia/http'; import action from './action'; // GET https://example.com/ (and get Response object). -const response = await action().run(makeGet('/'), raw()); +const response = await action(makeGet('/'), raw()); // POST https://example.com/api/posts (and get JSON payload). -const data = await action().run( +const data = await action( makePost('/api/posts', { data: { title: 'Hello World!' }, }), @@ -79,9 +83,11 @@ const data = await action().run( ### Advanced usage -You can pass a lot of options to each request enhancers, from `headers` and -`params` options to any other -[Fetch `Request` options](https://developer.mozilla.org/docs/Web/API/Request/Request#options): +You can pass a lot of options to each request enhancers, from +[`headers`](/docs/api/@foscia/http/interfaces/HttpRequestConfig#headers) and +[`params`](/docs/api/@foscia/http/interfaces/HttpRequestConfig#params) options +to any other +[`Request` options](https://developer.mozilla.org/docs/Web/API/Request/Request#options): ```typescript import { raw } from '@foscia/core'; @@ -90,7 +96,7 @@ import action from './action'; const abortController = new AbortController(); -const data = await action().run( +const data = await action( makePost('/api/posts', { data: { title: 'Hello World!' }, }, { @@ -99,20 +105,20 @@ const data = await action().run( credentials: 'same-origin', abortSignal: abortController.signal, // cache, redirect, referrer, etc. - requestTransformers: [(request) => { + middlewares: [async (request, next) => { // Do something with `request`. - return request; - }], - responseTransformers: [(response) => { - // Do something with `response`. + try { + const response = await next(request); - return response; - }], - errorTransformers: [(error) => { - // Do something with `error`. + // Do something with `response`. + + return response; + } catch (error) { + // Do something with `error`. - return error; + throw error; + } }], }), raw((response) => response.json()), @@ -122,14 +128,14 @@ const data = await action().run( ### Custom `Request` object You can even pass a custom -[Fetch `Request` object](https://developer.mozilla.org/docs/Web/API/Request/Request): +[`Request` instance](https://developer.mozilla.org/docs/Web/API/Request/Request): ```typescript import { raw } from '@foscia/core'; import { configureRequest } from '@foscia/http'; import action from './action'; -const data = await action().run( +const data = await action( configureRequest({ request: new Request('https://example.com', { headers: {}, @@ -143,23 +149,24 @@ const data = await action().run( ## Configuration recipes Here are common configuration for `@foscia/http` implementation. You can read -[the implementation and configuration guide](/docs/reference/implementations/http) +[the implementation and configuration guide](/docs/digging-deeper/implementations/http) for more details. ### Defining default headers -To define a default request headers, use the `headers` option or implement -a request transformer (useful for unstable headers, such as `Accept-Language` -in an internationalized application). +To define a default request headers, use the +[`defaultHeaders`](/docs/api/@foscia/http/interfaces/HttpAdapterConfig#defaultheaders) +option or implement a middleware (useful for dynamic headers, +such as `Accept-Language` in an internationalized application). ```typescript import { makeHttpAdapter } from '@foscia/http'; const { adapter } = makeHttpAdapter({ - headers: { 'Accept-Language': 'fr-FR' }, - requestTransformers: [(request) => { + defaultHeaders: { 'Accept-Language': 'fr-FR' }, + middlewares: [(request, next) => { request.headers.set('Accept-Language', 'en-US'); - return request; + return next(request); }], }); ``` @@ -183,10 +190,11 @@ const { adapter } = makeHttpAdapter({ `@foscia/http` provides two params serializer: -- `paramsSerializer` (default for `makeHttpAdapter`) which serialize a params -object using `URLSearchParams`. -- `deepParamsSerializer` which deeply serialize a params object (handle deep -array or object, because `URLSearchParams` won't). +- A default one which serialize a params object using + [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams). +- [`deepParamsSerializer`](/docs/api/@foscia/http/functions/deepParamsSerializer) + which deeply serialize a params object (handle deep array or object, because + [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams) won't). To define a custom parameters serializer, use the `serializeParams` option. @@ -200,5 +208,5 @@ const { adapter } = makeHttpAdapter({ ## Reference -- [Dedicated enhancers API](/docs/reference/actions-enhancers#fosciahttp) -- [Implementation and configuration guide](/docs/reference/implementations/http) +- [Dedicated enhancers API reference](/docs/api/@foscia/http/#enhancers) +- [Implementation and configuration guide](/docs/digging-deeper/implementations/http) diff --git a/website/docs/digging-deeper/jsonapi.md b/website/docs/digging-deeper/usages/jsonapi.md similarity index 63% rename from website/docs/digging-deeper/jsonapi.md rename to website/docs/digging-deeper/usages/jsonapi.md index 4b241d9a..f0810688 100644 --- a/website/docs/digging-deeper/jsonapi.md +++ b/website/docs/digging-deeper/usages/jsonapi.md @@ -1,5 +1,5 @@ --- -sidebar_label: Using JSON:API +sidebar_label: JSON:API sidebar_position: 3000 description: Using Foscia to interact with a JSON:API. --- @@ -28,20 +28,20 @@ Internally, it will use the JSON:API relationships inclusion features. ```typescript import { query, all, include } from '@foscia/core'; -const posts = await action().run(query(Post), include('author'), all()); +const posts = await action(query(Post), include('author'), all()); ``` ### Filtering requests You can filter your request using the -[`filterBy`](/docs/reference/actions-enhancers#filterby) enhancer. This function +[`filterBy`](/docs/api/@foscia/jsonapi/functions/filterBy) enhancer. This function both supports key-value or object parameters. ```typescript import { query, all } from '@foscia/core'; import { filterBy } from '@foscia/jsonapi'; -const posts = await action().run( +const posts = await action( query(Post), // Key-value pair. filterBy('published', true), @@ -54,17 +54,18 @@ const posts = await action().run( ### Sorting results You can sort results using multiple enhancers: -[`sortBy`](/docs/reference/actions-enhancers#sortby), -[`sortByAsc`](/docs/reference/actions-enhancers#sortbyasc) and -[`sortByDesc`](/docs/reference/actions-enhancers#sortbydesc). `sortBy` supports -both object and arrays parameters and will apply ascending sorting by default. +[`sortBy`](/docs/api/@foscia/jsonapi/functions/sortBy), +[`sortByAsc`](/docs/api/@foscia/jsonapi/functions/sortByAsc) and +[`sortByDesc`](/docs/api/@foscia/jsonapi/functions/sortByDesc). +`sortBy` supports both object and arrays parameters and will apply +ascending sorting by default. `sortByAsc` and `sortByDesc` supports variadic keys parameter. ```typescript import { query, all } from '@foscia/core'; import { sortBy, sortByAsc } from '@foscia/jsonapi'; -const posts = await action().run( +const posts = await action( query(Post), // Ascending sorting. sortBy(['publishedAt', 'createdAt']), @@ -80,8 +81,8 @@ const posts = await action().run( ### Sparse fieldsets You can filter the record fields you will retrieve using -[`fields`](/docs/reference/actions-enhancers#fields) and -[`fieldsFor`](/docs/reference/actions-enhancers#fieldsfor) enhancers. Those are +[`fields`](/docs/api/@foscia/jsonapi/functions/fields) and +[`fieldsFor`](/docs/api/@foscia/jsonapi/functions/fieldsFor) enhancers. Those are strongly typed to your model's properties and will automatically query for properties' aliases if they are set. @@ -92,7 +93,7 @@ for the given model. import { query, all, include } from '@foscia/core'; import { sortBy, sortByDesc } from '@foscia/jsonapi'; -const posts = await action().run( +const posts = await action( query(Post), include('author'), fields('title', 'author'), @@ -104,11 +105,11 @@ const posts = await action().run( ### Paginating results Pagination is agnostic when using JSON:API. That's why Foscia propose a -[`paginate`](/docs/reference/actions-enhancers#paginate) enhancer in which you +[`paginate`](/docs/api/@foscia/jsonapi/functions/paginate) enhancer in which you can pass any object value. This provides support of all pagination style. You can combine this with the -[`usingDocument`](/docs/reference/actions-runners#usingdocument) utility +[`usingDocument`](/docs/api/@foscia/jsonapi/functions/usingDocument) utility function to retrieve the whole JSON:API document (for example when your server serialize pagination metadata in it). @@ -116,7 +117,7 @@ serialize pagination metadata in it). import { query, all } from '@foscia/core'; import { paginate, usingDocument } from '@foscia/jsonapi'; -const data = await action().run( +const data = await action( query(Post), // A standard pagination. paginate({ size: 10, number: 1 }), @@ -133,26 +134,52 @@ console.log(data.document); console.log(data.document.meta!.page.hasMore); ``` +### Non-standard requests + +Just like with the HTTP adapter, you can run custom HTTP requests when your +data source provides non-standard features. +This provides many possibilities, such as retrieving models instances from +a global search endpoint: + +```typescript +import { queryAs, all } from '@foscia/core'; +import { paginate, usingDocument } from '@foscia/jsonapi'; + +const results = await action( + // Notice the `queryAs` instead of `query`, this will + // GET `/api/v1/search` instead of `/api/v1/posts/search`. + queryAs([Post, Comment, User]), + makeGet('search', { search: 'Hello' }), + all(), +); + +// `results` is an array Post, Comment or User instances. +console.log(results); +``` + ## Configuration recipes Here are common configuration for `@foscia/jsonapi` implementation. You can read -[the implementation and configuration guide](/docs/reference/implementations/jsonapi) +[the implementation and configuration guide](/docs/digging-deeper/implementations/jsonapi) for more details. You can also take a look at -[HTTP usage and common configuration recipes](/docs/digging-deeper/http), as +[HTTP usage and common configuration recipes](/docs/digging-deeper/usages/http#configuration-recipes), as the JSON:API adapter is based on HTTP adapter. ### Changing endpoint case By default, JSON:API use kebab case for models and relations endpoints (e.g. `favorite-posts` for a `favoritePosts` relation). If you want to use another -case for endpoints, you can use `modelPathTransformer` and -`relationPathTransformer` options. +case for endpoints, you can use +[`modelPathTransformer`](/docs/api/@foscia/jsonapi/interfaces/JsonApiAdapterConfig#modelpathtransformer) +and +[`relationPathTransformer`](/docs/api/@foscia/jsonapi/interfaces/JsonApiAdapterConfig#relationpathtransformer) +options. ```typescript import { camelCase } from 'lodash-es'; -import { makeJsonApiAdapter } from '@foscia/rest'; +import { makeJsonApiAdapter } from '@foscia/jsonapi'; makeJsonApiAdapter({ modelPathTransformer: (path) => camelCase(path), @@ -165,11 +192,14 @@ makeJsonApiAdapter({ By default, serialized and deserialized attributes and relations keep keys specified in the model. If you are using camel cased keys (e.g. `firstName`) but want to exchange kebab cased keys (e.g. `first-name`) with your API, -you can use `serializeKey` and `deserializeKey` options. +you can use +[`serializeKey`](/docs/api/@foscia/jsonapi/interfaces/JsonApiSerializerConfig#serializekey) +and [`deserializeKey`](/docs/api/@foscia/jsonapi/interfaces/JsonApiDeserializerConfig#deserializekey) +options. ```typescript import { kebabCase } from 'lodash-es'; -import { makeJsonApiSerializer, makeJsonApiDeserializer } from '@foscia/rest'; +import { makeJsonApiSerializer, makeJsonApiDeserializer } from '@foscia/jsonapi'; makeJsonApiSerializer({ serializeKey: ({ key }) => kebabCase(key), @@ -185,10 +215,11 @@ makeJsonApiDeserializer({ Some API implementation may serialize records IDs as URL to the record endpoint (such as `https://example.com/api/posts/1` for post `1`). You can customize the deserializer to support ID and type extraction from URL ID using the -`pullIdentifier` option. +[`pullIdentifier`](/docs/api/@foscia/jsonapi/interfaces/JsonApiDeserializerConfig#pullidentifier) +option. ```typescript -import { makeJsonApiDeserializer } from '@foscia/rest'; +import { makeJsonApiDeserializer } from '@foscia/jsonapi'; makeJsonApiDeserializer({ pullIdentifier: (record) => { @@ -202,6 +233,6 @@ makeJsonApiDeserializer({ ## Reference -- [Dedicated enhancers API](/docs/reference/actions-enhancers#fosciajsonapi) -- [Dedicated runners API](/docs/reference/actions-runners#fosciajsonapi) -- [Implementation and configuration guide](/docs/reference/implementations/jsonapi) +- [Dedicated enhancers API reference](/docs/api/@foscia/jsonapi/#enhancers) +- [Dedicated runners API reference](/docs/api/@foscia/jsonapi/#runners) +- [Implementation and configuration guide](/docs/digging-deeper/implementations/jsonapi) diff --git a/website/docs/digging-deeper/usages/rest.md b/website/docs/digging-deeper/usages/rest.md new file mode 100644 index 00000000..c8c34dae --- /dev/null +++ b/website/docs/digging-deeper/usages/rest.md @@ -0,0 +1,308 @@ +--- +sidebar_label: REST +sidebar_position: 4000 +description: Using Foscia to interact with a JSON:API. +--- + +# REST + +:::tip What you'll learn + +- Using Foscia to interact with a REST API +- Common configuration recipes + +::: + +## Setup + +Please follow the [getting started guide](/docs/getting-started) to set up your +REST action factory. + +## Usage + +Currently, REST implementations does not support additional features compared to +generic Foscia features, because REST is not that much standardized about +filtering, sorting, etc. + +If your REST API supports eager loading relations, you should +[configure your REST adapter](/docs/digging-deeper/implementations/rest#makerestadapter) +to serialize relationships inclusion in every request. + +If you need something specific, you can +[open a new issue on the repository](https://github.com/foscia-dev/foscia/issues/new/choose). + +### Supporting polymorphism + +If you want your Foscia deserialization process to support polymorphism, +your server should return a `type` key which match the Foscia model's type +in addition to other attributes (ID, etc.). Without this, Foscia +cannot precisely determine which record match which model. + +```json +{ + "id": 1, + // highlight.addition + "type": "posts", + "title": "Hello World" +} +``` + +It can be useful when you use polymorphic relations or when your use +non-standard endpoints returning multiple models' instances. In addition, you +can [set up a models registry](/docs/digging-deeper/actions/models-registration) +to map types and models, and support circular models relations. + +### Non-standard requests + +Just like with the HTTP adapter, you can run custom HTTP requests when your +data source provides non-standard features. +This provides many possibilities, such as retrieving models instances from +a global search endpoint. In combination with + +```typescript +import { queryAs, all } from '@foscia/core'; + +const results = await action( + // Notice the `queryAs` instead of `query`, this will + // GET `/api/search` instead of `/api/posts/search`. + queryAs([Post, Comment, User]), + makeGet('search', { search: 'Hello' }), + all(), +); + +// `results` is an array Post, Comment or User instances. +console.log(results); +``` + +## Configuration recipes + +Here are common configuration for `@foscia/rest` implementation. You can read +[the implementation and configuration guide](/docs/digging-deeper/implementations/rest) +for more details. + +You can also take a look at +[HTTP usage and common configuration recipes](/docs/digging-deeper/usages/http#configuration-recipes), as +the REST adapter is based on HTTP adapter. + +### Customizing include query + +If your REST API supports eager loading relations, you can use +[`include`](/docs/api/@foscia/core/functions/include) to request relations +loading. This will define a query parameter such as `include=author,comments`. + +To define a custom query parameter, use the +[`includeParamKey`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig#includeparamkey) +option. + +```typescript +import { makeRestAdapter } from '@foscia/rest'; + +const { adapter } = makeRestAdapter({ + includeParamKey: 'with', +}); +``` + +### Changing content format + +If your REST API interacts with another data type than JSON, such as XML, you +can configure this behavior as a default on your adapter: + +```typescript +import { makeRestAdapter } from '@foscia/rest'; +import { objectToXML, objectFromXML } from 'some-xml-library'; + +makeRestAdapter({ + defaultBodyAs: (body) => objectToXML(body), + defaultResponseReader: (response) => objectFromXML(body), + defaultHeaders: { + Accept: 'application/xml', + 'Content-Type': 'application/xml', + }, +}); +``` + +### Deserializing nested data + +If your REST API document nest records inside the document (not at root, such as +inside a `data` property), you can add +[`extractData`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig#extractdata) option which will extract +records data using the given transformation function. + +```typescript +import { makeRestDeserializer } from '@foscia/rest'; + +makeRestDeserializer({ + extractData: (data: { data: any }) => ({ records: data.data }), +}); +``` + +When your server change the serialization key of results depending on the +requested model (such as in [dummyJSON free API](https://dummyjson.com/) +used by Foscia examples), you can also provide more customized behavior: + +```typescript +import { guessContextModel, consumeModel, consumeRelation } from '@foscia/core'; +import { makeRestDeserializer } from '@foscia/rest'; + +makeRestDeserializer({ + extractData: async (data: any, context) => { + // Detect targeted model with context. + const model = await guessContextModel({ + model: consumeModel(context, null), + relation: consumeRelation(context, null), + }); + + return { records: (model ? data[model.$type] : undefined) ?? data }; + }, +}); +``` + +In the example above, Foscia will try to identify the model targeted by the +current context. When a model is identified, it will search for the model's type +in the response body. Otherwise, it will use the whole responses body. + +This behavior can also be easily implemented when +[nesting serialized data](#nesting-serialized-data). + +### Nesting serialized data + +If your REST API document expect record data to be nested (not at root, such as +inside a `data` property), you can add +[`createData`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#createdata) +option which will wrap +records data using the given transformation function. + +```typescript +import { makeRestSerializer } from '@foscia/rest'; + +makeRestSerializer({ + createData: (records, context) => ({ data: records }), +}); +``` + +### Changing endpoint case + +By default, JSON:API use kebab case for models and relations endpoints (e.g. +`favorite-posts` for a `favoritePosts` relation). If you want to use another +case for endpoints, you can use +[`modelPathTransformer`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig#modelpathtransformer) +and +[`relationPathTransformer`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig#relationpathtransformer) +options. + +```typescript +import { camelCase } from 'lodash-es'; +import { makeRestAdapter } from '@foscia/rest'; + +makeRestAdapter({ + modelPathTransformer: (path) => camelCase(path), + relationPathTransformer: (path) => camelCase(path), +}); +``` + +### Changing serialization keys case + +By default, serialized and deserialized attributes and relations keep keys +specified in the model. If you are using camel cased keys (e.g. `firstName`) +but want to exchange kebab cased keys (e.g. `first-name`) with your API, +you can use +[`serializeKey`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#serializekey) +and [`deserializeKey`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig#deserializekey) +options. + +```typescript +import { kebabCase } from 'lodash-es'; +import { makeRestSerializer, makeRestDeserializer } from '@foscia/rest'; + +makeRestSerializer({ + serializeKey: ({ key }) => kebabCase(key), +}); + +makeRestDeserializer({ + deserializeKey: ({ key }) => kebabCase(key), +}); +``` + +### Customizing relation serialized data + +By default, REST implementation will only serialize the related IDs as the +serialized relation's data. You can customize this behavior using +[`serializeRelation`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#serializerelation) +option, which will provide the related instance snapshot. + +#### Supporting polymorphism + +Here is an example which will serialize ID and type to support polymorphic +relations: + +```typescript +import { makeRestSerializer } from '@foscia/rest'; + +makeRestSerializer({ + serializeRelation: (_, related) => ({ + type: related.$instance.$model.$type, + id: related.$values.id, + }), +}); +``` + +:::info + +Be aware that the serializer is using instance snapshots (not instances), this +is why we are accessing the `id` property inside a `related.$values` object +instead of directly on the `related` object. +This allows locking the values during an action execution process. + +::: + +#### Deeply serializing instances + +Here is another example where we serialize the whole related record. Since +related instances' snapshots are limited and only contain ID by default, +you must disable [`limitedSnapshots`](/docs/digging-deeper/models/models-configuration#limitedsnapshots) +to make it work. + +```typescript +import { makeRestSerializer } from '@foscia/rest'; + +makeRestSerializer({ + serializeRelation: ({ context, serializer }, related, parents) => serializer + .serializeInstance(related, context, parents) +}); +``` + +:::tip + +If you want to customize relations serialization behavior when writing +relations (e.g. using +[`attach`](/docs/api/@foscia/core/functions/attach), +[`associate`](/docs/api/@foscia/core/functions/associate), etc.), you can use the +[`serializeRelated`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#serializerelated) +option which have the same signature. + +::: + +### Parsing URL IDs + +Some API implementation may serialize records IDs as URL to the record endpoint +(such as `https://example.com/api/posts/1` for post `1`). You can customize +the deserializer to support ID and type extraction from URL ID using the +[`pullIdentifier`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig#pullidentifier) +option. + +```typescript +import { makeRestDeserializer } from '@foscia/rest'; + +makeRestDeserializer({ + pullIdentifier: (record) => { + // This will support IDs like `https://example.com/api/posts/1`, `/api/posts/1`, etc. + const [id, type] = String(record.id).split('/').reverse(); + + return { id, type }; + }, +}); +``` + +## Reference + +- [Implementation and configuration guide](/docs/digging-deeper/implementations/rest) diff --git a/website/docs/digging-deeper/usages/testing.mdx b/website/docs/digging-deeper/usages/testing.mdx new file mode 100644 index 00000000..df026b7e --- /dev/null +++ b/website/docs/digging-deeper/usages/testing.mdx @@ -0,0 +1,229 @@ +--- +sidebar_position: 20000 +description: Writing tests when using Foscia. +--- + +import ShellCommand from '@site/src/components/ShellCommand'; + +# Testing + +:::tip What you'll learn + +- Mocking action factory for unit tests +- Writing expectations about actions +- Conditionally running actions + +::: + +## Integration tests + +Integration tests goal is to ensure separate parts of your software works +together. + +When using Foscia, this probably means you want to check that your UI correctly +interact with a backend or database using Foscia. +**Therefore, Foscia does not provide any integration tests utilities, because +those tests should not mock or fake any Foscia behaviors.** + +## Unit tests + +:::warning + +**Unit testing Foscia is currently experimental and may change.** +This gives us the flexibility to improve the API. + +::: + +Unit tests are focused on checking small and independent parts of your software. + +This probably means you want to check functions which are interacting with a +backend or database using Foscia API. + +The simplest way to write unit tests of parts of code using Foscia is to mock +your action factory and define expected results and contexts expectation. + +Foscia provide a simple set of functions and objects to help you to write unit +tests with any testing framework (Jest, Vitest, etc.). +Because it is framework-agnostic, Foscia does not provide any +expectation functions. + +### Preparation + +Start by installing Foscia test helpers if not already. + + + +Once test helpers are installed, you should make your action factory "mockable" +using `makeActionFactoryMockable`. This will transform the action factory +function so it can be mocked or unmocked using `mockAction` and `unmockAction`. + +```typescript title="action.ts" +import { makeActionFactory } from '@foscia/core'; +import { makeActionFactoryMockable } from '@foscia/test'; + +export default makeActionFactoryMockable( + makeActionFactory({ + // ... + }), +); +``` + +### Mocking action factory + +You can mock your action factory using `mockAction` function. It is important to +just pass your action factory `action` and not the call to the factory +`action()`, because the mock will install on the function itself. + +When mocking your action, Foscia is replacing the base factory function with a +proxy function which will built a proxy action. Each enhancer will +apply normally and make the context evolve, but will be tracked. +Running the action won't execute any real runner, but will intercept the +context, check conditionals and expectations, and finally returns the mocked +result. + +```typescript title="test/actionMock.ts" +import { ActionFactoryMock, mockAction, unmockAction } from '@foscia/test'; +import action from './action'; + +let actionMock: ActionFactoryMock; + +beforeEach(() => { + actionMock = mockAction(action); +}); + +afterEach(() => { + unmockAction(action); +}); + +export default actionMock; +``` + +### Concret example + +Consider the following `registerUser` function we would like to unit test. + +```typescript +import { create, fill, oneOrCurrent } from '@foscia/core'; +import action from './action'; +import User from './models/user'; + +export default function registerUser( + email: string, + acceptedTerms: boolean, +): Promise { + if (!acceptedTerms) { + throw new Error('User did not accept terms and conditions.'); + } + + const user = fill(new User(), { + email, + acceptedTermsAt: new Date(), + }); + + return action(create(user), oneOrCurrent()); +} +``` + +We can write two tests for this function: + +1. One asserting an action run and return a user if he accepted the terms. +2. Another asserting no action run occurred and an error is throw if he refused the terms. + +```typescript +describe('registration', () => { + it('should create user', async () => { + // Expect one run and return value passed as `create` first argument. + actionMock.mock(({ calls }) => calls.args('create')[0]).once(); + + const user = await registerUser('john.doe@example.com', true); + + expect(user).toBeInstanceOf(User); + expect(user.email).toStrictEqual('john.doe@example.com'); + expect(user.acceptedTermsAt).toBeInstanceOf(Date); + }); + + it('should fail creating user with non accepted terms', async () => { + expect(() => registerUser('john.doe@example.com', true)) + .rejects.toThrow('User did not accept terms and conditions'); + }); +}); +``` + +### Advanced mock usage + +When calling `mock`, you create an action execution mock with +a custom return value, which you can customize using various options. +All options are chainable. + +#### Mock return value + +When using `mock`, you can specify a value to return on action run. If you +want to compute a value using a callback, just pass a function to it. +For this, your callback will receive an +[`ActionTestContext`](/docs/api/@foscia/test/type-aliases/ActionTestContext) object. + +```typescript +// Direct. +actionMock.mock(user); +// Factory. +actionMock.mock(() => new User()); +// Factory using test context. +actionMock.mock(({ calls }) => calls.args('create')[0]); +``` + +#### Mock duration + +You can setup a custom limit for your action mocking. This will avoid +mocking the return value of any action indefinitely (which is the default +behavior). It can also be used to prepare mock for consecutive action. + +```typescript +// Only mock for a specified number of execution. +actionMock.mock(value).once(); +actionMock.mock(value).twice(); +actionMock.mock(value).times(3); + +// Use it to mock consecutive actions. +actionMock.mock(valueForFirstAction).once(); +actionMock.mock(valueForSecondAction).once(); +``` + +#### Conditional mock + +You can also use `when` to only mock the action if it matches a given predicate. +For this, your callback will receive an +[`ActionTestContext`](/docs/api/@foscia/test/type-aliases/ActionTestContext) object. + +```typescript +// Only mock when having a `create` enhancer with a user instance. +actionMock.mock(value).when(({ calls }) => ( + calls.args('create')[0] instanceof User +)); +``` + +#### Expectations + +You can also use `expect` to run expectations using your favorite framework. +Expectations won't run if a predicate is not matching for your mock. +For this, your callback will receive an +[`ActionTestContext`](/docs/api/@foscia/test/type-aliases/ActionTestContext) object. + +```typescript +actionMock.mock(value).expect(({ calls }) => { + expect(calls.args('create')[0]).toBeInstanceOf(User); + expect(calls.args('create')[0].email).toStrictEqual('john.doe@example.com'); + expect(calls.has('oneOrCurrent')).toStrictEqual(true); +}); +``` + +#### Combining all options + +All mock options can be chained to create more specific mocks. + +```typescript +actionMock.mock(value) + .once() + .when(() => true) + .expect(() => { + }); +``` diff --git a/website/docs/examples/jsonapi-blog.mdx b/website/docs/examples/jsonapi-blog.mdx index fb480789..70b7bb6d 100644 --- a/website/docs/examples/jsonapi-blog.mdx +++ b/website/docs/examples/jsonapi-blog.mdx @@ -72,8 +72,13 @@ import { filterBy, sortByDesc, paginate, usingDocument } from '@foscia/jsonapi'; import action from './action'; import Post from './models/post'; -export default async function fetchAllPost(query = {}) { - return action().run( +type AllPostsQuery = { + search?: string; + page?: number; +}; + +export default async function fetchAllPosts(query: AllPostsQuery = {}) { + return action( query(Post), when(query.search, (a, s) => a.use(filterBy('search', s))), sortByDesc('createdAt'), @@ -82,7 +87,7 @@ export default async function fetchAllPost(query = {}) { ); } -const { instances, document } = await fetchAllPost({ search: 'Hello' }); +const { instances, document } = await fetchAllPosts({ search: 'Hello' }); ``` ### View one @@ -92,8 +97,12 @@ import { query, include, oneOrFail } from '@foscia/core'; import action from './action'; import Post from './models/post'; -export default async function fetchOnePost(id) { - return action().run(query(Post, id), include('tags'), oneOrFail()); +export default async function fetchOnePost(id: string) { + return action( + query(Post, id), + include('tags'), + oneOrFail(), + ); } const post = await fetchOnePost('123-abc'); @@ -102,15 +111,18 @@ const post = await fetchOnePost('123-abc'); ### Create or update one ```typescript -import { changed, fill, oneOrCurrent, reset, save, when } from '@foscia/core'; +import { changed, fill, oneOrCurrent, reset, save, when, ModelWritableValues } from '@foscia/core'; import action from './action'; import Post from './models/post'; -export default async function savePost(post, values = {}) { +export default async function savePost( + post: Post, + values: Partial> = {}, +) { fill(post, values); try { - await action().run( + await action( save(post), when(!post.$exists || changed(post), oneOrCurrent(), () => instance), ); @@ -136,14 +148,11 @@ await savePost(post, { ```typescript import { destroy, none } from '@foscia/core'; import action from './action'; -import Post from './models/post'; -export default async function deletePost(post) { - await action().run(destroy(post), none()); +export default async function deletePost(post: Post) { + await action(destroy(post), none()); } -const post = new Post(); - await deletePost(post); ``` @@ -161,19 +170,18 @@ import action from './action'; import Post from './models/post'; export default function bestPosts() { - return action().run( + return action( query(Post), - .makeGet('actions/best-posts'), + makeGet('actions/best-posts'), all(), ); } -export default function publishPost(post, query = {}) { - return action().run( +export default function publishPost(post: Post) { + return action( query(post), - when(query.include, (a, i) => a.use(include(i))), makePost('actions/publish', { - publishedAt: new Date(), + data: { publishedAt: new Date(), }, }), oneOrFail(), ); diff --git a/website/docs/getting-started.mdx b/website/docs/getting-started.mdx index 3d4edff9..27d721ce 100644 --- a/website/docs/getting-started.mdx +++ b/website/docs/getting-started.mdx @@ -13,7 +13,7 @@ import ShellCommand from '@site/src/components/ShellCommand'; :::tip What you'll learn - Creating your first model and using it through model instances -- Creating your action factory using blueprints +- Creating your action factory - Running basic actions on your models with your action factory ::: @@ -21,14 +21,14 @@ import ShellCommand from '@site/src/components/ShellCommand'; :::info Only here for a simple HTTP client? Check out -[**the simple HTTP client guide**](/docs/digging-deeper/http). +[**the simple HTTP client guide**](/docs/digging-deeper/usages/http). ::: ## Before getting started Before getting started, you must have installed Foscia packages you will need. -[`@foscia/cli`](/docs/digging-deeper/cli) is the recommended way to install and +[`@foscia/cli`](/docs/digging-deeper/usages/cli) is the recommended way to install and get started using Foscia. -To declare a model, you just need to use the `makeModel` function. This function -takes up to 2 arguments and returns an ES6 class: +To declare a model, you just need to use the +[`makeModel`](/docs/api/@foscia/core/functions/makeModel) function. +This function takes up to 2 arguments and returns a +[`Model`](/docs/api/@foscia/core/interfaces/Model) constructor: - The model `type`, which is used by other services to identify your model or - interact with a data source. +interact with a data source. - The model `definition`, which contains your attributes/relations definitions - and custom properties and methods. +and custom properties and methods. ```typescript title="models/post.ts" import { makeModel, attr, hasMany, toDateTime } from '@foscia/core'; @@ -110,7 +112,7 @@ export default class Post extends makeModel('posts', { ### Using models classes -Model classes can be used like any ES6 class. It can be instantiated, +Model classes can be used like any JavaScript class. It can be instantiated, manipulated, etc. Properties will be defined on each instance from the model definition. @@ -125,14 +127,15 @@ console.log(post.published); // true console.log(post.$exists); // false ``` - + Read the full guide on models ## First actions -### Action factory - #### Creating the action factory :::info @@ -148,20 +151,22 @@ using this action factory will be seen in the [next part of this guide](#running-simple-actions). Action factory will set up all the required context for interacting with data -source (adapter, (de)serializer, etc.). +source (adapter, (de)serializer, etc.). You can use +[`makeActionFactory`](/docs/api/@foscia/core/functions/makeActionFactory) to +create your action factory. It takes one argument: an initial `context` +as an object or an object factory. -To quickly get started, multiple Foscia packages provide blueprint factories +To quickly get started, multiple Foscia packages provide factories which provide a quick initialization of this action factory with sensible defaults. - -Depending on the usage you are targetting, here is what your action factory may -looks like: +Depending on the usage you are targeting, here is what your action factory may +look like: - ```typescript @@ -182,101 +187,48 @@ export default makeActionFactory({ }); ``` - - + + ```typescript import { makeActionFactory, makeCache } from '@foscia/core'; import { - makeJsonRestAdapter, - makeJsonRestDeserializer, - makeJsonRestSerializer, + makeRestAdapter, + makeRestDeserializer, + makeRestSerializer, } from '@foscia/rest'; export default makeActionFactory({ ...makeCache(), - ...makeJsonRestDeserializer(), - ...makeJsonRestSerializer(), - ...makeJsonRestAdapter({ + ...makeRestDeserializer(), + ...makeRestSerializer(), + ...makeRestAdapter({ baseURL: '/api', }), }); -``` - - - - -#### Builder pattern syntax - -If you want to use the builder pattern syntax (e.g. `action().query(Post).all()` -instead of `action().run(query(Post), all())`), you must provide the extensions -you want to use during your action factory creation. - -Extensions are the second argument passed to `makeActionFactory`. - - - - -```typescript -import { makeActionFactory } from '@foscia/core'; -import { jsonApiStarterExtensions } from '@foscia/jsonapi'; - -export default makeActionFactory( - { - // makeJsonApiAdapter(), ...etc. - }, - jsonApiStarterExtensions, -); -``` - - - - -```typescript -import { makeActionFactory } from '@foscia/core'; -import { jsonRestStarterExtensions } from '@foscia/rest'; - -export default makeActionFactory( - { - // makeJsonRestAdapter(), ...etc. - }, - jsonRestStarterExtensions, -); ``` -:::warning - -Be aware that using extensions and builder pattern syntax might increase -your bundle size, because functions are imported even if you are not -using it. - -::: - ### Running simple actions -To run an action, you can initialize a new action instance by calling your -factory. With this instance, you can provide **context enhancers** to -modify the action context, and a **context runner** to run the action. +To run an action, you can initialize a new +[`Action`](/docs/api/@foscia/core/interfaces/Action) object by calling your +factory. With this instance, you can provide *context enhancers* to +modify the action context, and a *context runner* to run the action. -Simplest way of running an action is to call `run`, providing enhancers -as argument and providing a runner as last argument. This is called -the variadic `run` calls. Documentation is generally using this call format. +Simplest way of running an action is to call the factory, providing *enhancers* +as arguments and providing a *runner* as the last argument. This is called +the variadic factory calls. +Documentation and guides are generally using this call format. -You can also decompose those calls through `use` and `run` individual calls, -which will better show how Foscia actions works. +You can also decompose those calls through `use` and `run` individual calls +(which are also supporting variadic arguments), which will better show +[how Foscia actions lifecycle work](/docs/core-concepts/actions#lifecycle). ```typescript import { all, query } from '@foscia/core'; @@ -284,7 +236,7 @@ import Post from './models/post'; import action from './action'; // Variadic run. -const posts = await action().run( +const posts = await action( query(Post), all(), ); @@ -299,23 +251,29 @@ action you will run. Context runners only exists to tell how you wish to run the action and retrieve the result (raw result, model instance, etc.). A great example of this is when finding a model using its ID. You'll not use a -"find" context runner. Instead, you will need to use a `query` context enhancer -and a `oneOrFail` context runner. This way, you are able to do a find query and -retrieve a raw result when needed. +"find" context runner. Instead, you will need to use a +[`query`](/docs/api/@foscia/core/functions/query) context enhancer +and a [`oneOrFail`](/docs/api/@foscia/core/functions/oneOrFail) context runner. +This way, you are able to do a find query and retrieve a raw result when needed. ```typescript import { query, oneOrFail } from '@foscia/core'; import Post from './models/post'; import action from './action'; -const post = await action().run( +const post = await action( query(Post, 'abc-123'), oneOrFail(), ); ``` -This works the same to send write operations through actions. In the following -example, we are retrieving a raw adapter response instead of model instances. +This works the same to send write operations through actions, such as with +[`create`](/docs/api/@foscia/core/functions/create). In the following +example, we are retrieving the created instance: +[`oneOrCurrent`](/docs/api/@foscia/core/functions/oneOrCurrent) is used to +retrieve a result from the response if it is available but will also support +empty responses by returning the context's instance (as an example, +for *HTTP 204* responses). ```typescript import { create, fill, oneOrCurrent } from '@foscia/core'; @@ -327,13 +285,35 @@ const post = fill(new Post(), { description: 'Your first post', }); -const createdPost = await action().run( +const createdPost = await action( create(post), oneOrCurrent(), ); ``` - +But, pay attention, this behavior also means that you **must** provide an +action runner, even if you do not expect a return result from the action +execution. This is a common task when deleting a record, because you +won't use the deletion result. For this, Foscia provides a +[`none`](/docs/api/@foscia/core/functions/none) runner which ignores +the data source response. + +```typescript +import { destroy, none } from '@foscia/core'; +import action from './action'; + +// This has no effect an may failed because `destroy` is not a runner! +// highlight.deletion +await action(destroy(post)); +// Do this instead: +// highlight.addition +await action(destroy(post), none()); +``` + + Read the full guide on actions @@ -341,25 +321,30 @@ const createdPost = await action().run( #### HTTP custom actions -Using JSON:API or REST blueprints, you can also use Foscia to make non-standard +Using JSON:API or REST, you can also use Foscia to make non-standard (non-CRUD) API calls. This way, you can standardize all API calls across your application, even when those are non JSON:API/REST related. +In the following example, we use +[`makePost`](/docs/api/@foscia/http/functions/makePost) to make a *POST* request +and [`raw`](/docs/api/@foscia/core/functions/raw) to retrieve the original +[`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. + ```typescript import { query, raw, oneOrFail } from '@foscia/core'; import { makePost } from '@foscia/http'; import Comment from './comment'; import action from './action'; -const comment = await action().run( +const comment = await action( query(Comment, '1'), oneOrFail(), ); // Make a POST call to "https://example.com/api/v1/comments/1/publish" -const response = await action().run( +const response = await action( query(comment), makePost('publish', { data: { publishedAt: new Date() }, diff --git a/website/docs/help/faq.md b/website/docs/help/faq.md index 68a5a1d8..36690a23 100644 --- a/website/docs/help/faq.md +++ b/website/docs/help/faq.md @@ -78,12 +78,13 @@ body. But, since we are not building the action factory for you, you must initialize this factory yourself with the things you need: an adapter, a serializer, etc. -Don't worry, the process is still pretty simple thanks to blueprints. +Don't worry, the process is still pretty simple thanks to preconfigured +dependencies. -In addition, you will need a supplementary step to be able to use -`action().query(Post).all()`, because those functions must be imported to be -used. This is possible through actions' extensions, which will plug functions to -your action instance and allow you to use a builder pattern call style. +In addition, you can use method chaining (e.g. `action().query(Post).all()`), +because those functions must be imported to be used. +This can make the API less readable as it cannot be inspected just from the IDE. +That's Foscia provides a clear and organized documentation. ## Why is my IDE slow when using Foscia? diff --git a/website/docs/installation.mdx b/website/docs/installation.mdx index fd78a5dc..40e57ed9 100644 --- a/website/docs/installation.mdx +++ b/website/docs/installation.mdx @@ -13,21 +13,24 @@ Foscia is composed of multiple packages for different purposes (see ## CLI (recommended) -Best way to install Foscia is to install the CLI, and let it install the -additional dependencies. +Best way to install Foscia is to install +[`@foscia/cli`](/docs/digging-deeper/usages/cli), +and let it install the additional dependencies. -Once `@foscia/cli` is installed, you can run the `init` command with the -directory where you want to store your Foscia related files (models, action, -etc.). +Once [`@foscia/cli`](/docs/digging-deeper/usages/cli) is installed, you can run +the [`init`](/docs/digging-deeper/usages/cli#init-path) command with the +directory where you want to store your Foscia related files +(models, action, etc.). ## Manual -If you don't want to install `@foscia/cli`, you can manually install the set of -package you will need depending on your usage. +If you don't want to install [`@foscia/cli`](/docs/digging-deeper/usages/cli), +you can manually install the set of package you will need depending on your +usage. diff --git a/website/docs/integrations/nuxt.mdx b/website/docs/integrations/nuxt.mdx index 377cd6ad..ed1c8720 100644 --- a/website/docs/integrations/nuxt.mdx +++ b/website/docs/integrations/nuxt.mdx @@ -31,7 +31,18 @@ configuration to support server-side rendering (SSR) for two reasons: Foscia instances are not serializable without a [payload plugin](https://nuxt.com/docs/api/composables/use-nuxt-app#payload). -### Setup +## Setup + +### CLI (recommended) + +If you got Foscia started using +[`foscia init`](/docs/digging-deeper/usages/cli#init-path), your framework +integration with Nuxt is probably already setup. If you need to get started, +you can use the following command and read the instruction bellow: + + + +### Manually #### `fetch` implementation in action factory @@ -45,18 +56,19 @@ project. Once `ofetch` is installed, you can configure it on your action factory -adapter. Here is an exemple with `makeJsonRestAdapter`, but the configuration -is the same with other HTTP based adapters. +adapter. Here is an exemple with +[`makeRestAdapter`](/docs/api/@foscia/rest/functions/makeRestAdapter), +but the configuration is the same with other HTTP based adapters. ```typescript import { makeActionFactory } from '@foscia/core'; -import { makeJsonRestAdapter } from '@foscia/rest'; +import { makeRestAdapter } from '@foscia/rest'; // highlight.addition import { ofetch } from 'ofetch'; export default makeActionFactory({ // ... - ...makeJsonRestAdapter({ + ...makeRestAdapter({ // highlight.addition fetch: ofetch.native, }), @@ -89,7 +101,7 @@ tasks. If you need to priorise it, check out the ::: -### Usage +## Usage You can now use Foscia in any server/client context using `useAsyncData`. @@ -101,7 +113,7 @@ import Post from '../data/models/post'; const { data: posts } = useAsyncData( 'posts', - () => action().run(query(Post), all()), + () => action(query(Post), all()), ); diff --git a/website/docs/reference/actions-enhancers.mdx b/website/docs/reference/actions-enhancers.mdx deleted file mode 100644 index 2a16fb26..00000000 --- a/website/docs/reference/actions-enhancers.mdx +++ /dev/null @@ -1,872 +0,0 @@ ---- -sidebar_position: 2 -description: Available actions enhancers. ---- - -import Chip from '@site/src/components/Chip'; -import FunctionInfo from '@site/src/components/FunctionInfo'; - -# Actions enhancers - -## Note - -Many actions enhancers are available. Each may: - -- version: requires a minimal version of Foscia packages -- only: be available in a specific use case - (JSON:API, REST, HTTP, etc.) -- provide: provide a given context to next - enhancers or runners -- require: require a given context from previous - enhancers or runners - -Examples of this guide will omit imports of your action factories or models to -provide shorter examples. - -## `@foscia/core` - -### `when` - -See -[Conditionals on actions core concepts](/docs/core-concepts/actions#conditionals). - -### `context` - -Merge the given context into the action's current context. **The context is not -deeply merged.** - -This is the most basic context enhancer. It is used by a lot of Foscia -enhancers. - -#### Example - -```typescript -import { context } from '@foscia/core'; - -action().use( - context({ - /* additional context */ - }), -); -``` - -#### Arguments - -- `{}` `contextToMerge` a context object to merge into the action's current - context - -### `query` - - - -Query the given model, instance or instance's relation. - -#### Example - -```typescript -import { query } from '@foscia/core'; - -// Query model. -action().use(query(Post)); -// Query model's record by ID. -action().use(query(Post, '123')); -// Query model's instance. -action().use(query(myPost)); -// Query model's instance's relation. -action().use(query(myPost, 'comments')); -``` - -#### Arguments - -##### Model signature - -- [`Model`](/docs/reference/api/@foscia/core/type-aliases/Model) `query` -the model to query -- [`ModelIdType | undefined`](/docs/reference/api/@foscia/core/type-aliases/ModelIdType) `id` -the record's ID to query - -##### Instance signature - -- [`ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) `instance` -the instance to query -- [`ModelRelationKey | undefined`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) -`relation` the optional instance's relation to query - -### `create` - - - -Prepare context for an instance creation (directly or through a parent -relationships). - -#### Example - -```typescript -import { create } from '@foscia/core'; - -// Create post. -const post = new Post(); -action().use(create(post)); - -// Create comment through existing post "comments" relation. -const comment = new Comment(); -action().use(create(post, 'comments', post)); -``` - -#### Arguments - -##### Instance signature - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance to create - -##### Through instance signature - - - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`throughInstance` an instance to create through -- [`K extends ModelRelationKey`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) -`throughRelation` a relation of `throughInstance` to create through -- [`RI extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` an instance to create - -### `update` - - - -Prepare context for an instance update. - -#### Example - -```typescript -import { update } from '@foscia/core'; - -action().use(update(post)); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to update - -### `save` - - - -Prepare context for an instance creation or update depending on its existence -state. Calls `update` if the instance exists, otherwise call `create`. - -#### Example - -```typescript -import { save } from '@foscia/core'; - -action().use(save(post)); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to save - -### `destroy` - - - -Prepare context for an instance deletion. - -#### Example - -```typescript -import { destroy } from '@foscia/core'; - -action().use(destroy(post)); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to delete - -### `associate` - - - -Prepare context for an instance's `hasOne` relation associate operation. - -#### Example - -```typescript -import { associate } from '@foscia/core'; - -action().use(associate(post, 'author', user)); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to update relation on -- [`ModelRelationKey`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) - `relation` a relation to update -- [`ModelInferPropValue`](/docs/reference/api/@foscia/core/type-aliases/ModelInferPropValue) - `value` an instance to associate - -### `dissociate` - - - -Prepare context for an instance's `hasOne` relation dissociate operation. - -#### Example - -```typescript -import { dissociate } from '@foscia/core'; - -action().use(dissociate(post, 'author')); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to update relation on -- [`ModelRelationKey`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) - `relation` a relation to update - -### `attach` - - - -Prepare context for an instance's `hasMany` relation attach operation. - -#### Example - -```typescript -import { attach } from '@foscia/core'; - -action().use(attach(post, 'tags', [tag1, tag2])); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to update relation on -- [`ModelRelationKey`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) - `relation` a relation to update -- [`ModelInferPropValue | ModelInferPropValue[number]`](/docs/reference/api/@foscia/core/type-aliases/ModelInferPropValue) - `value` one or many instance to attach - -### `detach` - - - -Prepare context for an instance's `hasMany` relation detach operation. - -#### Example - -```typescript -import { detach } from '@foscia/core'; - -action().use(detach(post, 'tags', [tag1, tag2])); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to update relation on -- [`ModelRelationKey`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) - `relation` a relation to update -- [`ModelInferPropValue | ModelInferPropValue[number]`](/docs/reference/api/@foscia/core/type-aliases/ModelInferPropValue) - `value` one or many instance to detach - -### `updateRelation` - - - -Prepare context for an instance's `hasOne` or `hasMany` relation update. - -#### Example - -```typescript -import { ActionName, updateRelation } from '@foscia/core'; - -action().use(updateRelation(post, 'author', user)); -action().use(updateRelation(post, 'tags', [tag1, tag2])); -action().use(updateRelation(post, 'tags', [tag1, tag2], ActionName.ATTACH_RELATION)); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to update relation on -- [`ModelRelationKey`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) - `relation` a relation to update -- [`ModelInferPropValue | ModelInferPropValue[number]`](/docs/reference/api/@foscia/core/type-aliases/ModelInferPropValue) - `value` one or many instance to set as the relation's value -- `ActionName.UPDATE_RELATION | ActionName.ATTACH_RELATION | ActionName.DETACH_RELATION` - `actionName` the action name to run (defaults to `ActionName.UPDATE_RELATION`) - -### `instanceData` - - - -Serialize the given instance as the context's data. - -#### Example - -```typescript -import { instanceData } from '@foscia/core'; - -action().use(instanceData(post)); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to serialize - -### `relationData` - - - -Serialize the given instance's relation as the context's data. - -#### Example - -```typescript -import { relationData } from '@foscia/core'; - -action().use(relationData(post, 'tags')); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) - `instance` an instance of model to serialize -- [`ModelRelationKey`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) - `relation` a relation to serialize - -### `include` - - - -Eager load the given relations for the current model definition. It accepts deep -relations through dot notation. The new relations will be merged with the -previous ones. - -#### Example - -```typescript -import { include } from '@foscia/core'; - -action().use(include('author')); -action().use(include('author', 'comments', 'comments.reactions')); -action().use(include(['author', 'comments', 'comments.reactions'])); -``` - -#### Arguments - -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationDotKey) - `...relations` a relation or a set of relation to eager load - -### `onRunning` - -Register a `running` hook callback on action. Callback may be async. - -#### Example - -```typescript -import { onRunning } from '@foscia/jsonapi'; - -action().use( - onRunning((event) => { - /* Do something */ - }), -); -``` - -#### Arguments - -- `(event: { context: {}; runner: Function }) => Awaitable` `callback` - callback to run on event - -### `onSuccess` - -Register a `success` hook callback on action. Callback may be async. - -#### Example - -```typescript -import { onSuccess } from '@foscia/jsonapi'; - -action().use( - onSuccess((event) => { - /* Do something */ - }), -); -``` - -#### Arguments - -- `(event: { context: {}; result: unknown }) => Awaitable` `callback` - callback to run on event - -### `onError` - -Register a `error` hook callback on action. Callback may be async. - -#### Example - -```typescript -import { onError } from '@foscia/jsonapi'; - -action().use( - onError((event) => { - /* Do something */ - }), -); -``` - -#### Arguments - -- `(event: { context: {}; error: unknown }) => Awaitable` `callback` - callback to run on event - -### `onFinally` - -Register a `finally` hook callback on action. Callback may be async. - -#### Example - -```typescript -import { onFinally } from '@foscia/jsonapi'; - -action().use( - onFinally((event) => { - /* Do something */ - }), -); -``` - -#### Arguments - -- `(event: { context: {} }) => Awaitable` `callback` callback to run on - event - -## `@foscia/http` - -### `makeGet` - - - -HTTP GET method shortcut for the [`makeRequest` function](#makerequest). - -#### Example - -```typescript -import { makeGet } from '@foscia/http'; - -action().use( - makeGet('https://example.com', { - /* config */ - }), -); -``` - -#### Arguments - -- `string` `pathOrBaseURL` a path or base URL for the request -- [`HttpRequestConfig | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `config` a request configuration object - -### `makePost` - - - -HTTP POST method shortcut for the [`makeRequest` function](#makerequest). - -#### Example - -```typescript -import { makePost } from '@foscia/http'; - -action().use( - makePost( - 'https://example.com', - { data: 'foobar' }, - { - /* config */ - }, - ), -); -``` - -#### Arguments - -- `string` `pathOrBaseURL` a path or base URL for the request -- [`HttpRequestConfig['body'] | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `body` a request body -- [`HttpRequestConfig | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `config` a request configuration object - -### `makePut` - - - -HTTP PUT method shortcut for the [`makeRequest` function](#makerequest). - -#### Example - -```typescript -import { makePut } from '@foscia/http'; - -action().use( - makePut( - 'https://example.com', - { data: 'foobar' }, - { - /* config */ - }, - ), -); -``` - -#### Arguments - -- `string` `pathOrBaseURL` a path or base URL for the request -- [`HttpRequestConfig['body'] | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `body` a request body -- [`HttpRequestConfig | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `config` a request configuration object - -### `makePatch` - - - -HTTP PATCH method shortcut for the [`makeRequest` function](#makerequest). - -#### Example - -```typescript -import { makePatch } from '@foscia/http'; - -action().use( - makePatch( - 'https://example.com', - { data: 'foobar' }, - { - /* config */ - }, - ), -); -``` - -#### Arguments - -- `string` `pathOrBaseURL` a path or base URL for the request -- [`HttpRequestConfig['body'] | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `body` a request body -- [`HttpRequestConfig | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `config` a request configuration object - -### `makeDelete` - - - -HTTP DELETE method shortcut for the [`makeRequest` function](#makerequest). - -#### Example - -```typescript -import { makeDelete } from '@foscia/http'; - -action().use( - makeDelete( - 'https://example.com', - { data: 'foobar' }, - { - /* config */ - }, - ), -); -``` - -#### Arguments - -- `string` `pathOrBaseURL` a path or base URL for the request -- [`HttpRequestConfig['body'] | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `body` a request body -- [`HttpRequestConfig | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `config` a request configuration object - -### `makeRequest` - - - -Prepare a generic HTTP request. If given path starts with scheme (HTTPS, etc.), -it will be used as the base URL of action, otherwise it will only be used as -path. - -#### Example - -```typescript -import { makeRequest } from '@foscia/http'; - -action().use( - makeRequest('https://example.com', { - /* config */ - }), -); -``` - -#### Arguments - -- `string` `pathOrBaseURL` a path or base URL for the request -- [`HttpRequestConfig | undefined`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `config` a request configuration object - -### `configureRequest` - - - -Configure an HTTP request used by the HTTP adapter. -Some configuration options will be merged when possible (object query params, -headers, transformers, etc.). - -This enhancer can be used to configure a full request object or preconfigure -some common options (e.g. headers and transformers). - -Passing a [Fetch request object](https://developer.mozilla.org/docs/Web/API/Request) -as `request` option will ignore any other configuration and request -object will be directly passed to the adapter. Transformers will still be -applied, but other automatic transformation or data passing (params, body, etc.) -won't be applied. - -#### Example - -```typescript -import {configureRequest} from '@foscia/http'; - -// Configure a request object. -action().use(configureRequest({ - baseURL: 'https://example.com/api', - path: 'posts/1', - body: { - title: 'Hello World!', - }, -})); -// Preconfigure common options. -action().use(configureRequest({ - headers: { - Authorization: 'Bearer ...', - }, -})); -// Configure with a custom fetch request object. -action().use(configureRequest({ - request: new Request('https://example.com/api/posts/1', { - /* fetch request init */ - }), -})); -``` - -#### Arguments - -- [`HttpRequestConfig`](/docs/reference/api/@foscia/http/type-aliases/HttpRequestConfig) - `config` a request configuration object - -### `param` - - - -Set the given query param on the request. The new params will be merged with the -previous ones. - -#### Example - -```typescript -import { param } from '@foscia/http'; - -action().use(param('foo', 'foo')); // Key and value. -action().use(param({ bar: 'bar' })); // Object. -``` - -#### Arguments - -- `string | Dictionary` `key` a key for the param or a params object -- `unknown | undefined` `value` a value for the param - -### `abortSignal` - - - -Configure an abort signal on the request to -[make it abortable](https://developer.chrome.com/blog/abortable-fetch/). - -#### Example - -```typescript -import { abortSignal } from '@foscia/http'; - -const abortController = new AbortController(); - -action().use(abortSignal(abortController)); -``` - -#### Arguments - -- `Optional` `controllerOrSignal` an abort - controller or signal instance to configure (or undefined/null to cancel a - previous configuration) - -## `@foscia/jsonapi` - -### `fields` - - - -[Select the given JSON:API fieldsets](https://jsonapi.org/format/#fetching-sparse-fieldsets) -for the current context's model. The new fieldsets will be merged with the -previous ones. - -#### Example - -```typescript -import { fields } from '@foscia/jsonapi'; - -action().use(fields('title')); -action().use(fields('title', 'description')); -action().use(fields(['title', 'description'])); -``` - -#### Arguments - -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelKey) - `...fieldset` a field or a set of field to select for the current context's - model - -### `fieldsFor` - - - -[Select the given JSON:API fieldsets](https://jsonapi.org/format/#fetching-sparse-fieldsets) -for the given model. The new fieldsets will be merged with the previous ones. - -#### Example - -```typescript -import { fieldsFor } from '@foscia/jsonapi'; - -action().use(fieldsFor(Post, 'title')); -action().use(fieldsFor(Post, 'title', 'description')); -action().use(fieldsFor(Post, ['title', 'description'])); -``` - -#### Arguments - -- [`M extends ModelClass`](/docs/reference/api/@foscia/core/type-aliases/ModelClass) - `model` the model to select the fieldsets for -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelKey) - `...fieldset` a field or a set of field to select for the given model - -### `filterBy` - - - -[Filter the JSON:API resource](https://jsonapi.org/format/#fetching-filtering) -by the given key and value. When key is an object, it will spread the object as -a filter values map. The new filter will be merged with the previous ones. - -#### Example - -```typescript -import { filterBy } from '@foscia/jsonapi'; - -action().use(filterBy('isPublished', true)); -``` - -#### Arguments - -- `string | Dictionary` `key` a key for the filter or a filter object -- `unknown | undefined` `value` a value for the filter - -### `sortBy` - - - -[Sort the JSON:API resource](https://jsonapi.org/format/#fetching-sorting) by -the given keys and directions. The new sort will be merged with the previous -ones. **Sorts priority are kept**. - -#### Example - -```typescript -import { sortBy } from '@foscia/jsonapi'; - -action().use(sortBy('createdAt')); -action().use(sortBy('createdAt', 'desc')); -action().use(sortBy(['name', 'createdAt'], ['asc', 'asc'])); -action().use(sortBy({ name: 'asc', createdAt: 'asc' })); -``` - -#### Arguments - -- `Arrayable | Dictionary` `key` the key(s) for the - sort -- `Arrayable<'asc' | 'desc'> = 'asc'` `direction` the direction(s) for the - sort - -### `sortByAsc` - - - -Shortcut for the [`sortBy` function](#sortby) with an `asc` direction. - -#### Example - -```typescript -import { sortByAsc } from '@foscia/jsonapi'; - -action().use(sortByAsc('createdAt')); -``` - -#### Arguments - -- `ArrayableVariadic` `...keys` the key(s) for the sort - -### `sortByDesc` - - - -Shortcut for the [`sortBy` function](#sortby) with a `desc` direction. - -#### Example - -```typescript -import { sortByDesc } from '@foscia/jsonapi'; - -action().use(sortByDesc('createdAt')); -``` - -#### Arguments - -- `ArrayableVariadic` `...keys` the key(s) for the sort - -### `paginate` - - - -[Paginate the JSON:API resource](https://jsonapi.org/format/#fetching-pagination) -by the given params. JSON:API specification on pagination is agnostic, so page -params may be anything used by your implementation. - -#### Example - -```typescript -import { paginate } from '@foscia/jsonapi'; - -action().use(paginate({ number: 1, size: 10 })); -``` - -#### Arguments - -- `unknown` `page` a pagination value which match your implementation diff --git a/website/docs/reference/actions-extensions.md b/website/docs/reference/actions-extensions.md deleted file mode 100644 index 1b842feb..00000000 --- a/website/docs/reference/actions-extensions.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -sidebar_position: 4 -description: Available actions extensions. ---- - -# Actions extensions - -Foscia provides multiple extension packs, which contains many enhancers or -runners to integrate into your action to get -[builder pattern style calls](/docs/core-concepts/actions#extensions). - -## `@foscia/core` - -### `coreExtensions` - -- [`when`](/docs/reference/actions-enhancers#when) -- [`query`](/docs/reference/actions-enhancers#query) -- [`include`](/docs/reference/actions-enhancers#include) -- [`context`](/docs/reference/actions-enhancers#context) - -### `crudExtensions` - -Combination of: - -- [`readExtensions`](#readextensions) -- [`writeExtensions`](#writeextensions) - -### `readExtensions` - -- [`all`](/docs/reference/actions-runners#all) -- [`one`](/docs/reference/actions-runners#one) -- [`oneOrFail`](/docs/reference/actions-runners#oneorfail) -- [`oneOrCurrent`](/docs/reference/actions-runners#oneorcurrent) -- [`oneOr`](/docs/reference/actions-runners#oneor) -- [`none`](/docs/reference/actions-runners#none) -- [`raw`](/docs/reference/actions-runners#raw) -- [`cached`](/docs/reference/actions-runners#cached) -- [`cachedOrFail`](/docs/reference/actions-runners#cachedorfail) -- [`cachedOr`](/docs/reference/actions-runners#cachedor) - -### `writeExtensions` - -- [`create`](/docs/reference/actions-enhancers#create) -- [`update`](/docs/reference/actions-enhancers#update) -- [`save`](/docs/reference/actions-enhancers#save) -- [`destroy`](/docs/reference/actions-enhancers#destroy) -- [`instanceData`](/docs/reference/actions-enhancers#instancedata) - -### `hooksExtensions` - -- [`onRunning`](/docs/reference/actions-enhancers#onrunning) -- [`onSuccess`](/docs/reference/actions-enhancers#onsuccess) -- [`onError`](/docs/reference/actions-enhancers#onerror) -- [`onFinally`](/docs/reference/actions-enhancers#onfinally) - -## `@foscia/http` - -### `httpExtensions` - -- [`makeGet`](/docs/reference/actions-enhancers#makeget) -- [`makePost`](/docs/reference/actions-enhancers#makepost) -- [`makePut`](/docs/reference/actions-enhancers#makeput) -- [`makePatch`](/docs/reference/actions-enhancers#makepatch) -- [`makeDelete`](/docs/reference/actions-enhancers#makedelete) -- [`makeRequest`](/docs/reference/actions-enhancers#makerequest) -- [`param`](/docs/reference/actions-enhancers#param) -- [`abortSignal`](/docs/reference/actions-enhancers#abortsignal) - -## `@foscia/jsonapi` - -### `jsonApiExtensions` - -- [`filterBy`](/docs/reference/actions-enhancers#filterby) -- [`fields`](/docs/reference/actions-enhancers#fields) -- [`fieldsFor`](/docs/reference/actions-enhancers#fieldsfor) -- [`sortBy`](/docs/reference/actions-enhancers#sortby) -- [`sortByDesc`](/docs/reference/actions-enhancers#sortbydesc) -- [`paginate`](/docs/reference/actions-enhancers#paginate) - -### `jsonApiStarterExtensions` - -Combination of: - -- [`coreExtensions`](#coreextensions) -- [`crudExtensions`](#crudextensions) -- [`hooksExtensions`](#hooksextensions) -- [`httpExtensions`](#httpextensions) -- [`jsonApiExtensions`](#jsonapiextensions) - -## `@foscia/rest` - -### `jsonRestExtensions` - -No enhancers/runners available for now in this extension. - -### `jsonRestStarterExtensions` - -Combination of: - -- [`coreExtensions`](#coreextensions) -- [`crudExtensions`](#crudextensions) -- [`hooksExtensions`](#hooksextensions) -- [`httpExtensions`](#httpextensions) -- [`jsonRestStarterExtensions`](#jsonreststarterextensions) diff --git a/website/docs/reference/actions-runners.mdx b/website/docs/reference/actions-runners.mdx deleted file mode 100644 index 3d28d189..00000000 --- a/website/docs/reference/actions-runners.mdx +++ /dev/null @@ -1,318 +0,0 @@ ---- -sidebar_position: 3 -description: Available actions runners. ---- - -import Chip from '@site/src/components/Chip'; -import FunctionInfo from '@site/src/components/FunctionInfo'; - -# Actions runners - -## Note - -Many actions runners are available. Each may: - -- version: requires a minimal version of Foscia packages -- only: be available in a specific use case - (JSON:API, REST, HTTP, etc.) -- provide: provide a given context to next - enhancers or runners -- require: require a given context from previous - enhancers or runners - -Examples of this guide will omit imports of your action factories or models to -provide shorter examples. - -## `@foscia/core` - -### `when` - -See -[Conditionals on actions core concepts](/docs/core-concepts/actions#conditionals). - -### `none` - - - -Run the action and ignore the content of the result. Adapter errors are not -caught and so may be thrown. - -#### Example - -```typescript -import { none } from '@foscia/core'; - -await action().run(none()); -``` - -#### Returns - -`Promise` - -### `raw` - - - -Run the action and retrieve the raw adapter's data. - -#### Example - -```typescript -import { raw } from '@foscia/core'; - -// When using HttpAdapter, raw result is a fetch Response object. -const response = await action().run(raw()); -// You may also transform the data by passing a function. -const data = await action().run(raw((r) => r.json())); -``` - -#### Arguments - -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `AD` is your adapter's data (e.g. a fetch Response object) -or a transformed data if `transform` callback was provided. - -### `all` - - - -Run the action and deserialize an array of model's instance. - -#### Example - -```typescript -import { all } from '@foscia/core'; - -const posts = await action().run(all()); -``` - -#### Arguments - -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model or a transformed -data if `transform` callback was provided. - -### `one` - - - -Run the action and deserialize one model's instance. Returns null when not -found. - -#### Example - -```typescript -import { one } from '@foscia/core'; - -const post = await action().run(one()); -``` - -#### Arguments - -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model or a -transformed data if `transform` callback was provided. - -### `oneOrFail` - - - -Run the action and deserialize one model's instance. Throws an -`ExpectedRunFailureError` when not found or empty result. - -#### Example - -```typescript -import { oneOrFail } from '@foscia/core'; - -const post = await action().run(oneOrFail()); -``` - -#### Arguments - -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model or a transformed -data if `transform` callback was provided. - -### `oneOrCurrent` - - - -Run the action and deserialize one model's instance. Returns current instance -when not found or empty result. - -#### Example - -```typescript -import { oneOrCurrent } from '@foscia/core'; - -const post = await action().run(oneOrCurrent()); -``` - -#### Arguments - -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model or a transformed -data if `transform` callback was provided. - -### `oneOr` - - - -Run the action and deserialize one model's instance. - -#### Example - -```typescript -import { oneOr } from '@foscia/core'; - -const post = await action().run(oneOr(() => null)); -``` - -#### Arguments - -- `Function` `nilRunner` the runner to use when one result is empty -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model, a transformed -data if `transform` callback was provided or the `nilRunner` result. - -### `cached` - - - -Retrieve an instance from the cache. If the instance is not in cache or if the -included relations are not loaded, returns null. - -#### Example - -```typescript -import { cached } from '@foscia/core'; - -const post = await action().run(cached()); -``` - -#### Arguments - -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model or a -transformed data if `transform` callback was provided. - -### `cachedOrFail` - - - -Retrieve an instance from the cache. If the instance is not in cache or if the -included relations are not loaded, throws an `ExpectedRunFailureError`. - -#### Example - -```typescript -import { cachedOrFail } from '@foscia/core'; - -const post = await action().run(cachedOrFail()); -``` - -#### Arguments - -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model or a transformed -data if `transform` callback was provided. - -### `cachedOr` - - - -Retrieve an instance from the cache. If the instance is not in cache or if the -included relations are not loaded, runs the given runner. - -#### Example - -```typescript -import { cachedOr } from '@foscia/core'; - -const post = await action().run(cachedOr(() => null)); -``` - -#### Arguments - -- `Function` `nilRunner` the runner to use when cached result is empty -- `Function | undefined` `transform` the callback to transform the data object - -#### Returns - -`Promise` where `I` is an instance of the targeted model, a transformed -data if `transform` callback was provided or the `nilRunner` result. - -### `catchIf` - - - -Run given runner and catch errors using catchCallback. If catchCallback is -omitted, will return null on error. If catchCallback returns a function, will -run it as an action's runner. Else, will ignore error and return null only if -callback for error is truthy. - -#### Example - -```typescript -import { catchIf, one } from '@foscia/core'; - -const postOrNull = await action().run( - catchIf(one(), (error) => error instanceof ErrorToCatch), -); -``` - -#### Arguments - -- `Function` `runner` the runner to run and catch errors from -- `Function | undefined` `catchCallback` the callback to use when an error is - caught - -#### Returns - -`Promise` where `T` is either the runner result, the catch runner result or -null. - -## `@foscia/jsonapi` - -### `usingDocument` - - - -Append the JSON:API document object to data object. Use it as the parameter of -`all` and `one` (and derivatives) runners. - -#### Example - -```typescript -import { all } from '@foscia/core'; -import { usingDocument } from '@foscia/jsonapi'; - -const data = await action().run(all(usingDocument)); -data.instances; // Model instances. -data.document; // JSON:API document with meta, etc. -``` diff --git a/website/docs/reference/implementations/_category_.json b/website/docs/reference/implementations/_category_.json deleted file mode 100644 index 2f8d0d53..00000000 --- a/website/docs/reference/implementations/_category_.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Implementations and configuration", - "position": 500, - "link": { - "type": "generated-index" - } -} diff --git a/website/docs/reference/implementations/core.md b/website/docs/reference/implementations/core.md deleted file mode 100644 index 23b8b52d..00000000 --- a/website/docs/reference/implementations/core.md +++ /dev/null @@ -1,138 +0,0 @@ ---- -sidebar_position: 10 -description: - Specificities of the core implementations and available configuration. ---- - -# Core - -## Introduction - -Core implementations are implementations of actions' dependencies which can be -used when using Foscia for any purpose. - -Since `Registry` and `Cache` can be agnostic of data source you are interacting -with, Foscia proposes core implementations of those dependencies. - -## Implementations - -### `makeMapRegistryWith` - -This implementation of the registry stores a map of resolved models keyed by -their type. Resolvable models can be registered synchronously or asynchronously, -with or without specifying an explicit type. - -When registering models asynchronously without specifying their type, the -registry will try to resolve each registered model to find the one matching the -searched type. - -A type normalization function can be configured to normalize the type when -registering or resolving models. This can be useful when your models types are -different from the record types returned inside a JSON:API response. - -:::tip - -`makeMapRegistryWith` is used as the default registry factory in `makeRegistry`. -You can use it to -[**register your models**](/docs/digging-deeper/actions/models-registration) -when necessary. - -::: - -#### Usage - -```typescript -import { makeMapRegistryWith, makeRegistry } from '@foscia/core'; -import Post from './models/post'; - -// Using blueprint (preconfigured with sensible defaults). -const { registry } = makeRegistry( - [Post, /* ...registered models */], - { /* ...configuration */ }, -); -// Using constructor (no default configuration provided). -const registry = makeMapRegistryWith({ - /* ...configuration */ -}); - -// Register post synchronously. -registry.register([Post]); -// Register post asynchronously with explicit type. -registry.register({ - posts: async () => (await import('./models/post')).default, -}); -registry.register([ - { - type: 'posts', - resolver: async () => (await import('./models/post')).default, - }, -]); -// Register post asynchronously without explicit type. -registry.register([async () => (await import('./models/post')).default]); -``` - -#### Configuration - -| Name | Type | Description | -| --------------- | --------------------------------------------------- | ---------------------------------------------------------- | -| `normalizeType` | ((type: string) => string) | null | Normalize the type before registering or resolving models. | - -#### Defined in - -- [`packages/core/src/blueprints/makeRegistry.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/blueprints/makeRegistry.ts) -- [`packages/core/src/registry/makeMapRegistryWith.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/registry/makeMapRegistryWith.ts) - -### `makeRefsCacheWith` - -This implementation of the cache stores reference to model instance created by a -`RefManager`. - -The `RefManager` is responsible to: - -- Create a ref object for a cached instance. -- Retrieve value for this ref object (may return undefined if the ref has - expired). - -Foscia proposes a simple implementation of a `RefManager`, named -`weakRefManager`, which will store model instance as -[`WeakRef`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakRef). -With this implementation, only instance that are still stored in your -application memory (not garbage collected) remains in cache. - -You can define another implementation of `RefManager`, for example based on an -expiration timeout. - -#### Usage - -```typescript -import { makeCache, makeRefsCacheWith, weakRefManager } from '@foscia/core'; -import Post from './models/post'; - -// Using blueprint (preconfigured with sensible defaults). -const { cache } = makeCache({ - /* ...configuration */ -}); -// Using constructor (no default configuration provided). -const cache = makeRefsCacheWith({ - manager: weakRefManager, -}); - -const post = new Post(); - -// Store post. -cache.put('posts', '1', post); -// Retrieve post. -cache.find('posts', '1'); -``` - -#### Configuration - -| Name | Type | Description | -|---------------|--------------------------------------------------------------------------|------------------------------------------------------------------| -| `manager` | [`RefManager`](/docs/reference/api/@foscia/core/type-aliases/RefManager) | Create refs to instances and retrieve/expire those refs' values. | -| `normalizeId` | ((id: ModelIdType) => ModelIdType) | null | Normalize the type before registering or resolving models. | - -#### Defined in - -- [`packages/core/src/blueprints/makeCache.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/blueprints/makeCache.ts) -- [`packages/core/src/cache/makeRefsCacheWith.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/core/src/cache/makeRefsCacheWith.ts) diff --git a/website/docs/reference/implementations/http.md b/website/docs/reference/implementations/http.md deleted file mode 100644 index d116dd2d..00000000 --- a/website/docs/reference/implementations/http.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -sidebar_position: 30 -description: - Specificities of the HTTP implementation and available configuration. ---- - -# HTTP - -## Introduction - -HTTP implementation provides `makeHttpAdapter`/`makeHttpAdapterWith` and -multiple other features which help using Foscia as an HTTP client. It is also -the foundation of [JSON:API](/docs/reference/implementations/jsonapi) and -[REST](/docs/reference/implementations/rest) implementations. - -## Implementations - -### `makeHttpAdapter` - -This implementation of the adapter will execute context through HTTP requests -using the -[`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). - -The adapter returns `fetch` `Response` objects as data. - -It will build `fetch` `Request` objects using the context, and handles multiple -use cases such as: - -- Dynamic endpoint based on targeted models, IDs, etc. -- String or object query parameters using a serializer. -- Various request body typologies. -- Requests cancellation through - [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). -- Errors handling through custom errors classes. -- Requests/responses/errors transformation. -- Defaults to JSON data for request/response bodies with appropriate headers. - -#### Usage - -```typescript -import { makeHttpAdapter, makeHttpAdapterWith, paramsSerializer } from '@foscia/http'; - -// Using blueprint (preconfigured with sensible defaults). -const { adapter } = makeHttpAdapter({ - /* ...configuration */ -}); -// Using constructor (no default configuration provided). -const adapter = makeHttpAdapterWith({ - serializeParams: paramsSerializer, - /* ...configuration */ -}); - -const response = await adapter.execute({ - /* ...context */ -}); -``` - -#### Configuration {#makehttpadapter-configuration} - -| Name | Type | Description | -|-------------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------| -| `fetch` | `fetch` | `fetch` implementation to use. Default to `globalThis.fetch`. | -| `baseURL` | string | null | Base URL to merge with path when building the request endpoint. Default to `/`. | -| `buildURL` | `(urlContext: HttpURLContext, context: {}) => string` | Customize the URL parts joining function. | -| `serializeParams` | (params: Dictionary) => string | undefined | Function to serialize a query param object. | -| `defaultHeaders` | `Dictionary` | Default headers to use in the request. | -| `defaultBodyAs` | ((body: unknown, headers: Dictionary\) => Awaitable\) | null | Default body transformation. Default to `JSON.stringify()`. If set to null, body won't be transformed. | -| `defaultResponseReader` | (response: Response) => any | undefined | Default reader for the response's data before passing to deserializer. Default to `response.json()`. | -| `appendParams` | `(context: {}) => Dictionary` | Define additional request query parameters based on context. | -| `appendHeaders` | `(context: {}) => Dictionary` | Define additional request headers based on context. | -| `requestTransformers` | `((request: Request) => Awaitable)[]` | Functions to transform a request object before sending. | -| `responseTransformers` | `((response: Response) => Awaitable)[]` | Functions to transform a response object after a successful request. | -| `errorTransformers` | `((error: unknown) => Awaitable)[]` | Functions to transform an error after a `fetch` error or an unsuccessful request. | - -#### Defined in - -- [`packages/http/src/blueprints/makeHttpAdapter.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/http/src/blueprints/makeHttpAdapter.ts) -- [`packages/http/src/makeHttpAdapterWith.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/http/src/makeHttpAdapterWith.ts) diff --git a/website/docs/reference/implementations/jsonapi.md b/website/docs/reference/implementations/jsonapi.md deleted file mode 100644 index d2cf3956..00000000 --- a/website/docs/reference/implementations/jsonapi.md +++ /dev/null @@ -1,237 +0,0 @@ ---- -sidebar_position: 100 -description: - Specificities of the JSON:API implementation and available configuration. ---- - -# JSON:API - -## Introduction - -JSON:API implementation provides multiple dependencies implementations to -support read/write interactions with [JSON:API](https://jsonapi.org) based data -source. - -## Implementations - -### `makeJsonApiAdapter` - -This implementation of the adapter will execute context through HTTP requests -using the -[`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). - -`makeJsonApiAdapter` uses -[`makeRestAdapterWith`](/docs/reference/implementations/rest#makejsonrestadapter). - -#### Usage - -```typescript -import { deepParamsSerializer } from '@foscia/http'; -import { makeJsonApiAdapter } from '@foscia/jsonapi'; - -// Using blueprint (preconfigured with sensible defaults). -const { adapter } = makeJsonApiAdapter({ - /* ...configuration */ -}); - -const response = await adapter.execute({ - /* ...context */ -}); -``` - -#### Configuration - -`JsonApiAdapter` extends its configuration object from: - -- [`makeHttpAdapter`](/docs/reference/implementations/http#makehttpadapter-configuration) -- [`makeRestAdapterWith`](/docs/reference/implementations/rest#makejsonrestadapter-configuration) - -#### Defined in - -- [`packages/jsonapi/src/blueprints/makeJsonApiAdapter.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/jsonapi/src/blueprints/makeJsonApiAdapter.ts) - -### `makeJsonApiDeserializer` - -This implementation of the deserializer extract model instances from JSON:API -documents. - -`makeJsonApiDeserializer` extends the -[`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith). - -
- - - -Deserialized JSON:API document example - - - -Here is an example of a JSON:API document which `makeJsonApiDeserializer` can -deserialize to model instances. - -```json -{ - "data": [ - { - "type": "posts", - "id": "1", - "attributes": { - "title": "Foo", - "body": "Foo Body", - "publishedAt": "2023-10-24T10:00:00.000Z" - }, - "relationships": { - "comments": { - "data": [ - { - "type": "comments", - "id": "1" - }, - { - "type": "comments", - "id": "2" - } - ] - } - } - }, - { - "type": "posts", - "id": "2", - "attributes": { - "title": "Bar", - "body": "Bar Body", - "publishedAt": null - }, - "relationships": { - "comments": { - "data": [] - } - } - } - ], - "included": [ - { - "type": "comments", - "id": "1", - "attributes": { - "body": "Foo Comment" - } - }, - { - "type": "comments", - "id": "2", - "attributes": { - "body": "Bar Comment" - } - } - ] -} -``` - -
- -#### Usage - -```typescript -import { makeJsonApiDeserializer } from '@foscia/jsonapi'; - -// Using blueprint (preconfigured with sensible defaults). -const { deserializer } = makeJsonApiDeserializer({ - /* ...configuration */ -}); - -const { instances } = await deserializer.deserialize(data, { - /* ...context */ -}); -``` - -#### Configuration - -`makeJsonApiDeserializer` extends its configuration object from: - -- [`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith-configuration) - -| Name | Type | Description | -|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| -| `pullIdentifier` | `(record: Record, context: {}) => Awaitable` | Extract identifier (type and ID) from record. | -| `pullAttribute` | `(record: Record, deserializerContext: DeserializerContext, extract: Extract) => Awaitable` | Extract raw attribute value from record. | -| `pullAttribute` | (record: Record, deserializerContext: DeserializerContext, extract: Extract) => Awaitable\ | null | undefined\> | Extract raw relation value from record. | - -#### Defined in - -- [`packages/jsonapi/src/blueprints/makeJsonApiDeserializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/jsonapi/src/blueprints/makeJsonApiDeserializer.ts) - -### `makeJsonApiSerializer` - -This implementation of the serializer creates a JSON:API documents from model -instance and relations. - -`makeJsonApiSerializer` extends the -[`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith). - -
- - - -Serialized JSON:API document example - - - -Here is an example of a JSON:API document which `makeJsonApiSerializer` can -create from a model instance. - -```json -{ - "data": { - "type": "posts", - "id": "1", - "attributes": { - "title": "Foo", - "body": "Foo Body", - "publishedAt": "2023-10-24T10:00:00.000Z" - }, - "relationships": { - "comments": { - "data": [ - { - "type": "comments", - "id": "1" - }, - { - "type": "comments", - "id": "2" - } - ] - } - } - } -} -``` - -
- -#### Usage - -```typescript -import { makeJsonApiSerializer } from '@foscia/jsonapi'; - -// Using blueprint (preconfigured with sensible defaults). -const { serializer } = makeJsonApiSerializer({ - /* ...configuration */ -}); - -const data = await serializer.serializeInstance(instance, { - /* ...context */ -}); -``` - -#### Configuration - -`makeJsonApiSerializer` extends its configuration object from: - -- [`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith-configuration) - -#### Defined in - -- [`packages/jsonapi/src/blueprints/makeJsonApiSerializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/jsonapi/src/blueprints/makeJsonApiSerializer.ts) diff --git a/website/docs/reference/implementations/presentation.md b/website/docs/reference/implementations/presentation.md deleted file mode 100644 index 96365da1..00000000 --- a/website/docs/reference/implementations/presentation.md +++ /dev/null @@ -1,260 +0,0 @@ ---- -sidebar_position: 1 -description: Quick introduction on available implementations for Foscia. ---- - -# Presentation - -## Introduction - -Foscia actions might require one or many dependencies to work. Dependencies are -implementations of interfaces which are used in multiple parts of the actions -process. - -There are 5 kinds of dependency: - -- [Adapter](#adapter) -- [Deserializer](#deserializer) -- [Serializer](#serializer) -- [Cache](#cache) -- [Registry](#registry) - -## Interfaces - -### Adapter - -Adapter create the exchange between your actions' built context and your data -source. As an example, it will _translate_ the context to an HTTP request when -using JSON:API or REST implementations. - -```typescript -/** - * Adapter response data wrapper object. - * - * @typeParam RawData Adapter's original response its implementation - * (e.g. a Response object for HTTP adapter using fetch). - * @typeParam Data Content of the adapter's original response. - */ -export interface AdapterResponseI { - /** - * The raw original data (e.g. a Response object for HttpAdapter). - */ - readonly raw: RawData; - - /** - * Read the original response data. - * This will be used to deserialize instances from data. - * This method may not support to be called multiple times, - * prefer calling it only once and reusing returned value. - */ - read(): Promise; -} - -/** - * Adapter interacting with the data source. - * - * @typeParam RawData Adapter's original response its implementation - * (e.g. a Response object for HTTP adapter using fetch). - * @typeParam Data Content of the adapter's original response. - */ -export interface AdapterI { - /** - * Execute a given context to retrieve a raw data response. - * Context data will already be serialized using serializer if available. - * - * @param context - */ - execute(context: {}): Awaitable>; -} -``` - -### Deserializer - -Deserializer will deserialize records to instances. It might use the cache and -registry internally. - -```typescript -/** - * Base deserialized data which must contain at least an instances set. - */ -export interface DeserializedData { - instances: I[]; -} - -/** - * Deserializer converting adapter data to a deserialized set of instances. - * - * @typeParam Data Content of the adapter's original response. - * @typeParam Deserialized Object containing deserialized instances and other - * relevant deserialized data (e.g. the document for a JSON:API response). - */ -export interface DeserializerI { - /** - * Deserialize adapter data to a deserialized set of instances. - * - * @param data - * @param context - */ - deserialize(data: Data, context: {}): Awaitable; -} -``` - -### Serializer - -Serializer will serialize instances to the data source format. - -```typescript -/** - * Serializer converting model instances to adapter data source format. - * - * @typeParam Record Serialized value for an instance. - * @typeParam Related Serialized value for a related instance. - * @typeParam Data Serialized value for one/many/none instances. - */ -export interface SerializerI { - /** - * Serialize a given instance value. - * - * @param value - * @param context - */ - serializeInstance(value: ModelInstance, context: {}): Awaitable; - - /** - * Serialize a given instance's relation value. - * - * @param instance - * @param def - * @param value - * @param context - */ - serializeRelation( - instance: ModelInstance, - def: ModelRelation, - value: Arrayable | null, - context: {}, - ): Awaitable | null>; - - /** - * Serialize a set of already serialized records. - * This can be used to "wrap" records. - * - * @param records - * @param context - */ - serialize(records: Arrayable | null, context: {}): Awaitable; -} -``` - -### Cache - -Cache will store already fetched models instances. It will avoid multiple -instances of the same record coexisting and allows you to retrieve already -fetched record without making further requests to your data source. - -```typescript -/** - * Cache containing already synced models instances. - */ -export interface CacheI { - /** - * Retrieve a model instance from cache. - * - * @param type - * @param id - */ - find(type: string, id: ModelIdType): Promise; - - /** - * Put a model instance inside cache. - * - * @param type - * @param id - * @param instance - */ - put(type: string, id: ModelIdType, instance: ModelInstance): Promise; - - /** - * Forget a model's instance. - * - * @param type - * @param id - */ - forget(type: string, id: ModelIdType): Promise; - - /** - * Forget all model's instances. - * - * @param type - */ - forgetAll(type: string): Promise; - - /** - * Forget all models' instances. - */ - clear(): Promise; -} -``` - -### Registry - -Registry is a map of types and associated model. It is used by deserializer to -identify which models should map to which types. - -```typescript -/** - * Registry containing available models for actions. - */ -export interface RegistryI { - /** - * Resolve a registered model by its type. - * Type may be normalized for easier resolve. - * - * @param rawType - */ - modelFor(rawType: string): Promise; -} -``` - -## Implementations - -### Core - -`@foscia/core` provides implementations for `CacheI` and `RegistryI`. Those -implementations may be used for any Foscia usage (JSON:API, REST, etc.). - -- [Registry through `makeMapRegistryWith`](/docs/reference/implementations/core#makemapregistrywith) -- [Cache through `makeRefsCacheWith`](/docs/reference/implementations/core#makerefscachewith) - -### HTTP - -`@foscia/http` provides implementation of `AdapterI` to interact with HTTP data -sources. - -- [Adapter through `makeHttpAdapter`](/docs/reference/implementations/http#makehttpadapter) - -### JSON:API - -`@foscia/jsonapi` provides implementations of `AdapterI`, `SerializerI` and -`DeserializerI` to interact with JSON:API data sources. - -- [Adapter through `makeJsonApiAdapter`](/docs/reference/implementations/jsonapi#makejsonapiadapter) -- [Serializer through `makeJsonApiSerializer`](/docs/reference/implementations/jsonapi#makejsonapiserializer) -- [Deserializer through `makeJsonApiDeserializer`](/docs/reference/implementations/jsonapi#makejsonapideserializer) - -### REST - -`@foscia/rest` provides implementations of `AdapterI`, `SerializerI` and -`DeserializerI` to interact with JSON REST HTTP data sources. - -- [Adapter through `makeJsonRestAdapter`](/docs/reference/implementations/rest#makejsonrestadapter) -- [Serializer through `makeJsonRestSerializer`](/docs/reference/implementations/rest#makejsonrestserializer) -- [Deserializer through `makeJsonRestDeserializer`](/docs/reference/implementations/rest#makejsonrestdeserializer) - -### Serialization - -`@foscia/serialization` provides partial implementations of `SerializerI` and -`DeserializerI` to transform model instances to/from record generic values. - -- [Serializer through `makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith) -- [Deserializer through `makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith) diff --git a/website/docs/reference/implementations/rest.md b/website/docs/reference/implementations/rest.md deleted file mode 100644 index 5dfa61e2..00000000 --- a/website/docs/reference/implementations/rest.md +++ /dev/null @@ -1,200 +0,0 @@ ---- -sidebar_position: 110 -description: - Specificities of the REST implementation and available configuration. ---- - -# REST - -## Introduction - -REST implementation provides multiple dependencies implementations to support -read/write interactions with JSON REST data sources. - -## Implementations - -### `makeJsonRestAdapter` - -This implementation of the adapter will execute context through HTTP requests -using the -[`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). - -`makeJsonRestAdapter` and `makeRestAdapterWith` use -[`makeHttpAdapterWith`](/docs/reference/implementations/http#makehttpadapter). - -#### Usage - -```typescript -import { paramsSerializer } from '@foscia/http'; -import { makeRestAdapterWith, makeJsonRestAdapter } from '@foscia/rest'; - -// Using blueprint (preconfigured with sensible defaults). -const { adapter } = makeJsonRestAdapter({ - /* ...configuration */ -}); -// Using constructor (no default configuration provided). -const { adapter } = makeRestAdapterWith({ - serializeParams: paramsSerializer, - /* ...configuration */ -}); - -const response = await adapter.execute({ - /* ...context */ -}); -``` - -#### Configuration {#makejsonrestadapter-configuration} - -`makeJsonRestAdapter` and `makeRestAdapterWith` extend its configuration object from: - -- [`makeHttpAdapter`](/docs/reference/implementations/http#makehttpadapter-configuration) - -| Name | Type | Description | -|-------------------| ------------------------------- |-------------------------------------------------------------------------------------------------------------------------| -| `includeParamKey` | string | null | Define the query parameter to append when relationships inclusion is requested through `include`. Default to `include`. | - -#### Defined in - -- [`packages/rest/src/blueprints/makeJsonRestAdapter.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeJsonRestAdapter.ts) -- [`packages/rest/src/makeRestAdapterWith.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/makeRestAdapterWith.ts) - -### `makeJsonRestDeserializer` - -This implementation of the deserializer extract model instances from object -documents. - -`makeJsonRestDeserializer` extends the -[`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith). - -
- - - -Deserialized REST document example - - - -Here is an example of a REST document which `makeJsonRestDeserializer` can deserialize -to model instances. - -```json -[ - { - "id": "1", - "title": "Foo", - "body": "Foo Body", - "publishedAt": "2023-10-24T10:00:00.000Z", - "comments": [ - { - "id": "1", - "body": "Foo Comment" - }, - { - "id": "2", - "body": "Bar Comment" - } - ] - }, - { - "type": "posts", - "id": "2", - "title": "Bar", - "body": "Bar Body", - "publishedAt": null, - "comments": [] - } -] -``` - -
- -#### Usage - -```typescript -import { makeJsonRestDeserializer } from '@foscia/rest'; - -// Using blueprint (preconfigured with sensible defaults). -const { deserializer } = makeJsonRestDeserializer({ - /* ...configuration */ -}); - -const { instances } = await deserializer.deserialize(data, { - /* ...context */ -}); -``` - -#### Configuration - -`makeJsonRestDeserializer` extends its configuration object from: - -- [`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith-configuration) - -| Name | Type | Description | -|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| -| `pullIdentifier` | `(record: Record, context: {}) => Awaitable` | Extract identifier (type and ID) from record. | -| `pullAttribute` | `(record: Record, deserializerContext: DeserializerContext, extract: Extract) => Awaitable` | Extract raw attribute value from record. | -| `pullAttribute` | (record: Record, deserializerContext: DeserializerContext, extract: Extract) => Awaitable\ | null | undefined\> | Extract raw relation value from record. | - -#### Defined in - -- [`packages/rest/src/blueprints/makeJsonRestDeserializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeJsonRestDeserializer.ts) - -### `makeJsonRestSerializer` - -This implementation of the serializer creates a REST documents from model -instance and relations. - -`makeJsonRestSerializer` extends the -[`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith). - -
- - - -Serialized REST document example - - - -Here is an example of a REST document which `makeJsonRestSerializer` can -create from a model instance. - -```json -{ - "id": "1", - "title": "Foo", - "body": "Foo Body", - "publishedAt": "2023-10-24T10:00:00.000Z", - "comments": ["1", "2"] -} -``` - -
- -#### Usage - -```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; - -// Using blueprint (preconfigured with sensible defaults). -const { serializer } = makeJsonRestSerializer({ - /* ...configuration */ -}); - -const data = await serializer.serializeInstance(instance, { - /* ...context */ -}); -``` - -#### Configuration - -`makeJsonRestSerializer` extends its configuration object from: - -- [`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith-configuration) - -| Name | Type | Description | -|-----------------|-----------|------------------------------------------------------| -| `serializeType` | `boolean` | Append the instance `type` to the serialized object. | - -#### Defined in - -[`packages/rest/src/blueprints/makeJsonRestSerializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeJsonRestSerializer.ts) diff --git a/website/docs/reference/implementations/serialization.md b/website/docs/reference/implementations/serialization.md deleted file mode 100644 index 5149c987..00000000 --- a/website/docs/reference/implementations/serialization.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -sidebar_position: 20 -description: - Specificities of the "serialization" implementation and available - configuration. ---- - -# Serialization - -## Introduction - -Serialization implementation provides partial implementations for serializer and -deserializer dependencies. - -## Implementations - -### `makeDeserializerWith` - -This **partial** implementation of the deserializer will produce instances -from a generic record object. - -It handles multiple features, such as: - -- Deduplicate records by identifier to avoid deserializing the same record - multiple times. -- Interact with the cache (if configured) to keep only one instance alive for - one record. -- Resolve model to deserialize record to automatically from context, relations - and configuration. -- Use model's properties aliases and value transformers. -- Run the models' `retrieved` hook for each deserialized instance. - -#### Usage - -```typescript -import { makeDeserializerRecordFactory, makeDeserializerWith } from '@foscia/serialization'; - -const deserializer = makeDeserializerWith({ - extractData: (data) => ({ - records: data as Arrayable> | null, - }), - createRecord: makeDeserializerRecordFactory( - (record) => record, - (record, { key }) => record[key], - (record, { key }) => record[key], - ), -}); - -const { instances } = await deserializer.deserialize(data, { - /* ...context */ -}); -``` - -#### Configuration {#makedeserializerwith-configuration} - -| Name | Type | Description | -|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| -| `extractData` | `(data: Data, context: {}) => Awaitable` | Extract records contained in data and other useful data for deserialization. | -| `createRecord` | `DeserializerRecordFactory` | Create a deserializer record object which provides identity and values pulling. | -| `createData` | `(instances: ModelInstance[], extract: Extract, context: {}) => Awaitable` | Create deserialized instances wrapper object which might contain other data. Default to no additional data provided. | -| `shouldDeserialize` | `(deserializerContext: DeserializerContext) => Awaitable` | Check if an instance attribute or relation should be deserialized or not. Default to value not `undefined`. | -| `deserializeKey` | `(deserializerContext: DeserializerContext) => Awaitable` | Deserialize an instance attribute or relation key. Default to key aliasing and normalization. | -| `deserializeAttribute` | `(deserializerContext: DeserializerContext) => Awaitable` | Deserialize an instance attribute value. Default to use of attribute transformer. | -| `deserializeRelated` | `(deserializerContext: DeserializerContext, related: DeserializerRecord, instancesMap: DeserializerInstancesMap) => Awaitable` | Deserialize an instance relation's related instance(s). Default to instance deserialization through deserializer. | - -#### Defined in - -- [`packages/serialization/src/makeDeserializerWith.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/serialization/src/makeDeserializerWith.ts) - -### `makeSerializerWith` - -This **partial** implementation of the serializer will produce a generic record -value from a model instance. - -It handles multiple features, such as: - -- Serialize instance into generic record value -- Serialize relation instances into generic record values -- Serialize generic record values into adapter's data format -- Only serialize changed instance's values -- Use model's properties aliases and value transformers. -- Serialize nested relation instances with circular references support - -#### Usage - -```typescript -import { makeSerializerWith, makeSerializerRecordFactory } from '@foscia/serialization'; - -const serializer = makeSerializerWith({ - createData: (records) => records, - createRecord: makeSerializerRecordFactory( - (instance) => ({ id: instance.id } as Record), - (record, { key, value }) => { - record[key] = value; - }, - ), -}); - -const data = await serializer.serializeInstance(instance, { - /* ...context */ -}); -``` - -#### Configuration {#makeserializerwith-configuration} - -| Name | Type | Description | -|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `createRecord` | `SerializerRecordFactory` | Create a serializer record object which can be hydrated and retrieved. | -| `createData` | (records: Arrayable\ | null, context: {}) => Awaitable\ | Create adapter data value from a set of serialized record. Default to no transformation. | -| `shouldSerialize` | `(serializerContext: SerializerContext) => Awaitable` | Check if an instance attribute or relation should be serialized or not. Default to value not `undefined` and changed. | -| `serializeKey` | `(serializerContext: SerializerContext) => Awaitable` | Serialize an instance attribute or relation key. Default to key aliasing and normalization. | -| `serializeAttribute` | `(serializerContext: SerializerContext) => Awaitable` | Serialize an instance attribute value. Default to use of attribute transformer. | -| `serializeRelation` | `(serializerContext: SerializerContext, related: ModelInstance, parents: SerializerParents) => Awaitable` | Serialize an instance relation's related instance(s). Default to the instance ID. | -| `serializeRelated` | (serializerContext: SerializerContext\, related: ModelInstance, parents: SerializerParents) => Awaitable\ | null\> | Serialize an instance relation's related instance(s) (outside of a parent serialization context). Default to the instance ID. | -| `isCircularRelation` | `(serializerContext: SerializerContext, parents: SerializerParents) => Awaitable` | Detect if the given context is a circular relation. Default to `true` if model's relation is in the parents chain. | -| `circularRelationBehavior` | `(serializerContext: SerializerContext, parents: SerializerParents) => Awaitable` | Tell how circular relation should be handled by returning the behavior to use: `throw` to throw an error, `skip` to avoid serializing a relation referencing an already serialized instance or `keep` to avoid serializing already serialized relations. Default to `skip`. | - -#### Defined in - -- [`packages/serialization/src/makeSerializerWith.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/serialization/src/makeSerializerWith.ts) diff --git a/website/docs/reference/models-utilities.mdx b/website/docs/reference/models-utilities.mdx deleted file mode 100644 index 4dd5f52d..00000000 --- a/website/docs/reference/models-utilities.mdx +++ /dev/null @@ -1,377 +0,0 @@ ---- -sidebar_position: 1 -description: Available models utilities. ---- - -import FunctionInfo from '@site/src/components/FunctionInfo'; - -# Models utilities - -## Common - -### `fill` - -Fill the model instance's values with the given values. - -#### Example - -```typescript -import { fill } from '@foscia/core'; - -const post = fill(new Post(), { title: 'Hello', description: 'World' }); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` -- [`Partial>`](/docs/reference/api/@foscia/core/type-aliases/ModelWritableValues) -`values` - -#### Returns - -`I`, the affected instance. - -### `forceFill` - - - -Fill the model instance's values with the given values, even read-only values. -`forceFill` will temporary disable -[`strictReadOnly`](/docs/digging-deeper/models/models-configuration#strictproperties) -policy on the model. - -#### Example - -```typescript -import { forceFill } from '@foscia/core'; - -const post = forceFill(new Post(), { author: user }); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` -- [`Partial>`](/docs/reference/api/@foscia/core/type-aliases/ModelValues) -`values` - -#### Returns - -`I`, the affected instance. - -### `isSame` - -Check if two values are the same model instance. Ensure the equality by checking -if the two instances: - -- Are Foscia model instances -- Have the same type -- Have the same and non-NIL ID - -**This function does not deeply compare instances' values.** - -#### Example - -```typescript -import { isSame } from '@foscia/core'; - -const areSameInstances = isSame(foo, bar); -if (areSameInstances) { - /* do something */ -} -``` - -#### Arguments - -- `unknown` `value` -- `unknown` `otherValue` - -#### Returns - -`boolean` - -### `filled` - - - -Check if instance contains any values, even defined as null. It excludes ID -and LID from checked values. -This can be useful to check if any data has been loaded on an instance from -the store. If no attributes/relations are declared on model, it will always -return true. - -#### Example - -```typescript -import { filled } from '@foscia/core'; - -const isFilled = filled(post); -if (isFilled) { - /* probably not fetched from data source */ -} -``` - -#### Arguments - -- [`ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` - -#### Returns - -`boolean` - -### `loaded` - -Check if the given relations are loaded on the instance or its related -instances. Can check for sub relations using dot relation keys: will check each -related instances regardless of the number of concerned instances. If no -relations are provided, wont perform any check. - -#### Example - -```typescript -import { loaded } from '@foscia/core'; - -const isFullyLoaded = loaded(post, ['comments', 'comments.author']); -if (!isFullyLoaded) { - /* do something */ -} -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationDotKey) -`...relations` - -#### Returns - -`boolean` - -### `changed` - -Check if the given keys have been changed since last instance sync. If no keys -are provided, will check whole original snapshot (including IDs). - -#### Example - -```typescript -import { changed } from '@foscia/core'; - -const wasChanged = changed(post, ['title', 'description']); -if (wasChanged) { - /* do something */ -} -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` -- [`ArrayableVariadic`](/docs/reference/api/@foscia/core/type-aliases/ModelKey) -`...only` - -#### Returns - -`boolean` if keys (or instance) changed since last sync. - -### `markSynced` - -Mark the model instance's values as synced. If no keys are provided, will -replace whole original snapshot (including IDs). - -#### Example - -```typescript -import { markSynced } from '@foscia/core'; - -markSynced(post, ['title', 'description']); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelKey) -`...only` - -#### Returns - -`I`, the affected instance. - -### `restore` - -Restore the model instance's original values. If no keys are provided, will -restore whole original snapshot (including IDs). - -#### Example - -```typescript -import { restore } from '@foscia/core'; - -restore(post, ['title', 'description']); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelKey) -`...only` - -#### Returns - -`I`, the affected instance. - -### `takeSnapshot` - -Take a snapshot of the model's state (IDs, values, etc.). - -#### Example - -```typescript -import { takeSnapshot } from '@foscia/core'; - -const postSnapshot = takeSnapshot(post); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` - -#### Returns - -[`ModelSnapshot`](/docs/reference/api/@foscia/core/type-aliases/ModelSnapshot), -the snapshot. - -### `restoreSnapshot` - -Restore a snapshot of the model's state (IDs, values, etc.). If no keys are -provided, will restore whole original snapshot (including IDs). - -#### Example - -```typescript -import { restoreSnapshot } from '@foscia/core'; - -restoreSnapshot(post, postSnapshot, ['title', 'description']); -``` - -#### Arguments - -- [`I extends ModelInstance`](/docs/reference/api/@foscia/core/type-aliases/ModelInstance) -`instance` -- [`ModelSnapshot`](/docs/reference/api/@foscia/core/type-aliases/ModelSnapshot) -`snapshot` -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelKey) -`...only` - -#### Returns - -`I`, the affected instance. - -### `compareSnapshots` - -Check if the given keys are different between two snapshots. If no keys are -provided, will check whole snapshots (including IDs). - -#### Example - -```typescript -import { compareSnapshots } from '@foscia/core'; - -compareSnapshots(nextSnapshot, prevSnapshot, ['title', 'description']); -``` - -#### Arguments - -- [`ModelSnapshot`](/docs/reference/api/@foscia/core/type-aliases/ModelSnapshot) -`nextSnapshot` -- [`ModelSnapshot`](/docs/reference/api/@foscia/core/type-aliases/ModelSnapshot) -`prevSnapshot` -- [`ArrayableVariadic>`](/docs/reference/api/@foscia/core/type-aliases/ModelKey) -`...only` - -#### Returns - -`boolean` if keys (or all values) are different on given snapshots. - -## Factories - -### `makeModel` - -Create a model class. - -#### Example - -```typescript -import { makeModel } from '@foscia/core'; - -const PostModel = makeModel('posts', { - /* definition */ -}); -``` - -#### Arguments - -- [`ModelConfig | string`](/docs/reference/api/@foscia/core/type-aliases/ModelConfig) -`config` -- `ModelDefinition` `rawDefinition` - -#### Returns - -[`ModelClass`](/docs/reference/api/@foscia/core/type-aliases/ModelClass) - -### `makeModelFactory` - -Create a model class factory. - -#### Example - -```typescript -import { makeModelFactory } from '@foscia/core'; - -const makeModel = makeModelFactory( - { - /* ...common configuration */ - }, - { - /* ...common definition */ - }, -); -``` - -#### Arguments - -- `ModelDefinition | undefined` `baseRawDefinition` -- [`ModelConfig | undefined`](/docs/reference/api/@foscia/core/type-aliases/ModelConfig) -`baseConfig` (omitting `type`) - -#### Returns - -[`makeModel`](#makemodel), a customized model factory function. - -### `makeComposable` - -Create a composable definition to integrate in models. - -#### Example - -```typescript -import { makeComposable } from '@foscia/core'; - -const publishable = makeComposable({ - /* definition */ -}); -``` - -#### Arguments - -- `ModelDefinition` `rawDefinition` - -#### Returns - -[`ModelParsedDefinition`](/docs/reference/api/@foscia/core/type-aliases/ModelParsedDefinition) diff --git a/website/docs/upgrade/dependencies.mdx b/website/docs/upgrade/dependencies.mdx index c1c8e8da..44d76bde 100644 --- a/website/docs/upgrade/dependencies.mdx +++ b/website/docs/upgrade/dependencies.mdx @@ -5,22 +5,11 @@ sidebar_position: 0 import ShellCommand from '@site/src/components/ShellCommand'; -# Dependencies +# Upgrading packages You can upgrade all Foscia packages to latest or next version using a single command. -:::warning - -**All major release of Foscia may contain breaking changes!** -You should check the [**migration guides**](/docs/upgrade/migration) -and update relevant parts of your code according to those guides. - -Until Foscia `v1.0.0` is released, each minor release (e.g. `v0.1.x` to -`v0.2.x`) contains breaking changes. - -::: - ## Latest `latest` tagged versions contain the stable major release of Foscia packages. diff --git a/website/docs/upgrade/migration.md b/website/docs/upgrade/migration.md index 5585836c..631ae8bd 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -4,7 +4,348 @@ toc_max_heading_level: 2 sidebar_position: 5 --- -# Migration +# Migration guides + +## 0.13.x from 0.12.x + +### High impacts changes + +- [Main dependencies types have been renamed](#main-dependencies-types-have-been-renamed) +- [Builder pattern calls and actions extensions are removed](#builder-pattern-calls-and-actions-extensions-are-removed) +- [Dependencies factories functions signature changed](#dependencies-factories-functions-signature-changed) + +### Medium impacts changes + +- [Action hooks events now provide action instead of context](#action-hooks-events-now-provide-action-instead-of-context) +- [HTTP transformers replaced with middlewares](#http-transformers-replaced-with-middlewares) +- [Properties definition are now defined using factories](#properties-definition-are-now-defined-using-factories) +- [Internal APIs are now tagged and may have changed](#internal-apis-are-now-tagged-and-may-have-changed) +- [Relation `.config()` chained modifier is removed](#relation-config-chained-modifier-is-removed) + +### Low impacts changes + +- [Custom transformers must use `makeCustomTransformer`](#custom-transformers-must-use-makecustomtransformer) +- [`$model` property of snapshots is replaced by `$instance`](#model-property-of-snapshots-is-replaced-by-instance) + +### Main dependencies types have been renamed + +**Likelihood Of Impact: High** + +To unify Foscia types definition, main dependencies types names have changed. +If you are using those, you must use the new names: + +- `RegistryI` to `ModelsRegistry` +- `CacheI` to `InstancesCache` +- `AdapterResponseI` to `AdapterResponse` +- `AdapterI` to `Adapter` +- `DeserializerI` to `Deserializer` +- `SerializerI` to `Serializer` + +### Builder pattern calls and actions extensions are removed + +**Likelihood Of Impact: High** + +Since its first release, Foscia provided the "builder pattern calls" through +actions' extensions. This feature allowed users to call enhancers and runners +directly on the action, without using `use` or `run`. + +In this release, we are removing the extensions and this way of running +enhancers and runners. This change has two main reasons: + +- TypeScript does not support higher kinded types (HKT), and extensions + typing must be maintained aside from the function definition. This + make maintaining enhancers and runners functions hard and error-prone. +- Extensions will increase the build size without that much benefit and are + against Foscia functional programming approach. + +You can replace those builder pattern calls by the functional programming +approach. Here is an example: + +```typescript +// highlight.addition +import { query, all } from '@foscia/core'; + +const posts = await action() + // highlight.deletion + .query(Post).all(); +// highlight.addition + .run(query(Post), all()); +``` + +If you are using some special types, such as `Action`, `ContextEnhancer` or +`ContextRunner`, you should remove the extension generic type. + +```typescript +import { Action, ContextEnhancer, ConsumeModel } from '@foscia/core'; +// highlight.deletion +type CustomAction = Action; +// highlight.addition +type CustomAction = Action; +// highlight.deletion +type CustomEnhancer = ContextEnhancer<{}, any, ConsumeModel>; +// highlight.addition +type CustomEnhancer = ContextEnhancer<{}, ConsumeModel>; +``` + +> When TypeScript will provide higher kinded types, this feature will +> probably be restored. + +### Dependencies factories functions signature changed + +**Likelihood Of Impact: High** + +`makeCache` and `makeRegistry` are now returning agnostic objects +(`InstancesCache` and `ModelsRegistry`) instead of real implementations. + +`weakRefManager` is now a factory function named `makeWeakRefManager`. + +`makeMapRegistryWith` have been renamed to `makeMapRegistry` and some +functions signatures have been simplified with fewer features (no more +async model resolving and register method). + +`makeHttpAdapter` now use a default query params serializer (the simple one). +As a consequence, `paramsSerializer` export have been removed. + +In addition, multiple factories functions have been renamed: + +- `makeRefsCacheWith` to `makeRefsCache` +- `makeHttpAdapterWith` and `makeHttpAdapter` merged to `makeHttpAdapter` +- `makeSerializerWith` to `makeSerializer` +- `makeDeserializerWith` to `makeDeserializer` +- `makeJsonRestAdapter` and `makeRestAdapterWith` merged to `makeRestAdapter` +- `makeJsonRestSerializer` to `makeRestSerializer` +- `makeJsonRestDeserializer` to `makeRestDeserializer` + +### Action hooks events now provide action instead of context + +**Likelihood Of Impact: Medium** + +Action hooks events now provide an action property instead of a context +property, because this might lead to outdated context values. + +If your action hooks are using the context, you can still access it using +`useContext` on the action provided in the event: + +```typescript +import { onRunning } from '@foscia/core'; + +action.use(onRunning(async (event) => { +// highlight.deletion + console.log(event.context); +// highlight.addition + console.log(await event.action.useContext()); +})); +``` + +### HTTP transformers replaced with middlewares + +**Likelihood Of Impact: Medium** + +To provide a simpler API and improve maintainability, HTTP adapter's and +request's transformers have been replaced by middlewares. +Instead of `requestTransformers`, `responseTransformers` and `errorTransformers`, +you should now use `middlewares`. + +```typescript +import { onRunning } from '@foscia/core'; + +makeHttpAdapter({ +// highlight.deletion + requestTransformers: [(request) => { +// highlight.deletion + // Transform request... +// highlight.deletion + return request; +// highlight.deletion + }], +// highlight.deletion + responseTransformers: [(response) => { +// highlight.deletion + // Transform response... +// highlight.deletion + return response; +// highlight.deletion + }], +// highlight.deletion + errorTransformers: [(error) => { +// highlight.deletion + // Transform error... +// highlight.deletion + return error; +// highlight.deletion + }], +// highlight.addition + middlewares: [async (request, next) => { +// highlight.addition + // Transform request... +// highlight.addition + try { +// highlight.addition + const response = await next(request); +// highlight.addition + // Transform response... +// highlight.addition + return response; +// highlight.addition + } catch (error) { +// highlight.addition + // Transform error... +// highlight.addition + throw error; +// highlight.addition + } +// highlight.addition + }], +}); +``` + +### Properties definition are now defined using factories + +**Likelihood Of Impact: Medium** + +Previous properties definition objects (such as `attr()`, etc.) have been +replaced by properties definition factories. +Instead of returning a direct object, those functions now return a factory +to create the final property definition. This will provide more polyvalent +model's properties in the future (such as memoized properties, etc.). As a +consequence, many internal types of Foscia changed. + +In most usages of Foscia this will not have any impact, but if you are using +an internal API on properties definition, you may have to change your code. + +### Internal APIs are now tagged and may have changed + +**Likelihood Of Impact: Medium** + +Since its release, lots of Foscia APIs were missing their documentation, even +important notes like `@internal` or `@experimental` features. +All packages have been revised and all types, functions or objects +are now correctly documented. Some internal or experimental types or functions +may have been renamed or removed. + +If you are using an internal APIs, you should avoid using them or +[open an issue to request the API to be publicly maintained](https://github.com/foscia-dev/foscia/issues/new/choose). + +### Relation `.config()` chained modifier is removed + +**Likelihood Of Impact: Medium** + +Thanks to the new relation factories signature, you can now define your +relations without calling `.config()` modifier, which has been removed. +You must now use the new call signature. + +```typescript +export default class Post extends makeModel('posts', { +// highlight.deletion + comments: hasMany().config('comments'), +// highlight.addition + comments: hasMany('comments'), +// highlight.deletion + author: hasOne(() => User).config({ path: 'author' }), +// highlight.addition + author: hasOne(() => User, { path: 'author' }), +}) {} +``` + +### Custom transformers must use `makeCustomTransformer` + +**Likelihood Of Impact: Low** + +To improve attributes and relations factories' parameters typologies, +transformers are now special Foscia objects, like models, instances, etc. +This has no impact to transformer created using `makeTransformer`, but you +must now use a factory when defining totally custom transformers. +This is made possible with `makeCustomTransformer`: + +```typescript +// highlight.addition +import { makeCustomTransformer } from '@foscia/core'; + +// highlight.deletion +export default { +// highlight.deletion + deserialize: (value: string | null) => { +// highlight.addition +export default makeCustomTransformer( +// highlight.addition + (value: string | null) => { + if (value === null) { + return null; + } + + const date = new Date(); + date.setTime(Date.parse(value)); + + return date; + }, +// highlight.addition + (value: Date | null) => (value ? value.toISOString() : null), +// highlight.addition +); +// highlight.deletion + serialize: (value: Date | null) => (value ? value.toISOString() : null), +// highlight.deletion +}; +``` + +### `makeRefsCache` manager is replaced by references factories + +To simplify the code for reference holding inside the cache, the `manager` +config option of `makeRefsCache` have been replaced by a `makeRef` option. +In addition, `makeWeakRefManager` is replaced by `makeWeakRefFactory`. + +```typescript +// highlight.deletion +import { makeWeakRefManager } from '@foscia/core'; +// highlight.addition +import { makeWeakRefFactory } from '@foscia/core'; + +makeRefsCache({ +// highlight.deletion + manager: makeWeakRefManager(), +// highlight.addition + makeRef: makeWeakRefFactory(), +}); +``` + +### Serializer functions signature changed to use snapshots + +**Likelihood Of Impact: Low** + +`makeSerializer` and all associated functions or types are now serializing +instances' snapshots instead of instances. This provides a more consistent +attributes and relations serialization and operation in time-critic systems. + +If you are directly using the serializer, you should use the new call signature +and provide snapshots instead of instances. If you are using a custom +serializer, you must change your implementation to match the signature +requirements. + +```typescript +// highlight.deletion +serializer.serializeInstance(instance, context) +// highlight.addition +serializer.serializeToRecords(takeSnapshot(instance), context) +``` + +### `$model` property of snapshots is replaced by `$instance` + +**Likelihood Of Impact: Low** + +To better represent where a snapshot is coming from, the `$model` property +have been replaced by an `$instance` property. If you are using this property +on a snapshot, you can just access the `$model` of the `$instance`. + +```typescript +import { takeSnapshot } from '@foscia/core'; + +const snapshot = takeSnapshot(post); + +// highlight.deletion +console.log(snapshot.$model); +// highlight.addition +console.log(snapshot.$instance.$model); +``` ## 0.12.x from 0.11.x @@ -70,7 +411,7 @@ import { makeActionFactory, query, all } from '@foscia/core'; import { jsonApiStarterExtensions } from '@foscia/jsonapi'; export default makeActionFactory({ - // makeJsonRestAdapter(), ...etc. + // makeRestAdapter(), ...etc. }, { // highlight.deletion ...jsonApiStarterExtensions, @@ -100,7 +441,8 @@ export default makeActionFactory({ `v0.7.0` deprecated types and functions have been removed: -- [`forModel`, `forInstance`, `forRelation`, `forId` and `find` are removed](#formodel-forinstance-forrelation-forid-and-find-are-deprecated) +- [`forModel`, `forInstance`, `forRelation`, `forId` and + `find` are removed](#formodel-forinstance-forrelation-forid-and-find-are-deprecated) - [`makeForRelationLoader` is removed](#makeforrelationloader-is-deprecated) - [`runHook` is removed](#runhook-is-deprecated) - [`ModelHookCallback` type is removed](#modelhookcallback-type-is-deprecated) @@ -128,7 +470,8 @@ You must ensure you are using Node 18+. ### High impacts changes - [`makeComposable` return value change](#makecomposable-return-value-change) -- [`forModel`, `forInstance`, `forRelation`, `forId` and `find` are deprecated](#formodel-forinstance-forrelation-forid-and-find-are-deprecated) +- [`forModel`, `forInstance`, `forRelation`, `forId` and + `find` are deprecated](#formodel-forinstance-forrelation-forid-and-find-are-deprecated) ### Medium impacts changes @@ -160,7 +503,8 @@ export default class Post extends makeModel('posts', { ...publishable, // highlight.addition publishable, -}) {} +}) { +} ``` ### `forModel`, `forInstance`, `forRelation`, `forId` and `find` are deprecated @@ -169,7 +513,7 @@ export default class Post extends makeModel('posts', { `forModel`, `forInstance`, `forRelation`, `forId` and `find` enhancers have been deprecated and will be removed in a next major release, you should -use [`query` enhancer](/docs/reference/actions-enhancers#query) instead: +use [`query` enhancer](/docs/api/@foscia/core/functions/query) instead: ```typescript // `forModel` replacement. diff --git a/website/docs/upgrade/support-policy.md b/website/docs/upgrade/support-policy.md new file mode 100644 index 00000000..7ebb5229 --- /dev/null +++ b/website/docs/upgrade/support-policy.md @@ -0,0 +1,32 @@ +--- +description: Support policy for Foscia packages. +sidebar_position: 20 +--- + +# Support policy + +:::info + +Currently, only the latest major version of Foscia is actively maintained. + +::: + +**All major release of Foscia may contain breaking changes!** + +In addition, some APIs may be documented with the following tags: + +- `@internal` describes an internal API you should not be using. +- `@experimental` describes a public API you can use but which may not follow + semantic versioning rules. + +You should check the [**migration guides**](/docs/upgrade/migration) when +updating Foscia packages to a new major release and update relevant parts +of your code according to those guides. + +:::info + +Until Foscia `v1.0.0` is released, each minor release (e.g. `v0.1.x` to +`v0.2.x`) will contain breaking changes, and is considered a major release +regarding our support policy. + +::: diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 39b21358..2d498947 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -42,7 +42,7 @@ const config = { sidebarPath: require.resolve('./sidebars.js'), editUrl: 'https://github.com/foscia-dev/foscia/tree/main/website/', showLastUpdateTime: true, - exclude: ['reference/api/index.md'], + exclude: ['api/index.md', 'api/packages.md'], }, blog: false, theme: { @@ -52,16 +52,38 @@ const config = { ], ], plugins: [ - ['docusaurus-plugin-typedoc', { - id: 'api', - name: 'API reference', - out: 'docs/reference/api', - entryPointStrategy: 'packages', - entryPoints: packages - .filter((pkg) => pkg.name !== 'cli') - .map((pkg) => `../packages/${pkg.name}`), - tsconfig: path.resolve(__dirname, '../tsconfig.json'), - }], + [ + 'docusaurus-plugin-typedoc', + /** @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions} */ + ({ + id: 'api', + name: 'API reference', + out: 'docs/api', + entryPointStrategy: 'packages', + entryPoints: packages + .filter((pkg) => pkg.name !== 'cli') + .map((pkg) => `../packages/${pkg.name}`), + tsconfig: path.resolve(__dirname, '../tsconfig.json'), + plugin: [ + 'typedoc-plugin-mdn-links', + path.resolve(__dirname, 'typedoc-plugin.mjs'), + ], + blockTagsPreserveOrder: [ + '@deprecated', + '@since', + '@provideContext', + '@requireContext', + '@example', + ], + categoryOrder: [ + 'Enhancers', + 'Runners', + 'Utilities', + 'Factories', + 'Hooks', + ], + }), + ], ], themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ @@ -79,7 +101,7 @@ const config = { autoCollapseCategories: true, }, }, - announcementBar: process.env.VERSION ? { + announcementBar: !process.env.VERSION ? { // Dev/next version announcement. id: `${process.env.VERSION}-announcement`, content: 'Warning: you are browsing an upcoming version of Foscia.
Get back to stable docs', @@ -88,8 +110,8 @@ const config = { isCloseable: false, } : { // Production announcement. - id: '0.9.0-announcement', - content: 'v0.9.0 released with new @foscia/cli features! Give your feedback', + id: '0.13.0-announcement', + content: 'v0.13.0 relations inverses and more! Give your feedback', backgroundColor: 'var(--ifm-background-surface-color)', textColor: 'inherit', isCloseable: false, @@ -124,7 +146,7 @@ const config = { { position: 'left', label: 'API', - to: '/docs/category/reference', + to: '/docs/category/api', }, { position: 'right', @@ -163,6 +185,14 @@ const config = { label: 'Digging deeper', to: '/docs/category/digging-deeper', }, + { + label: 'Integrations', + to: '/docs/category/integrations', + }, + { + label: 'API', + to: '/docs/category/api', + }, ], }, { @@ -174,7 +204,7 @@ const config = { }, { label: 'Examples', - href: 'https://github.com/foscia-dev/foscia-examples', + href: '/docs/category/examples', }, { label: 'GitHub issues', diff --git a/website/package.json b/website/package.json index 06d7352c..23c0821f 100644 --- a/website/package.json +++ b/website/package.json @@ -16,27 +16,28 @@ "preinstall": "npx only-allow pnpm" }, "dependencies": { - "@docusaurus/core": "^3.4.0", - "@docusaurus/preset-classic": "^3.4.0", - "@docusaurus/theme-common": "^3.4.0", - "@fontsource-variable/onest": "^5.0.4", - "@fontsource/fira-mono": "^5.0.13", - "@mdx-js/react": "^3.0.1", + "@docusaurus/core": "^3.7.0", + "@docusaurus/preset-classic": "^3.7.0", + "@docusaurus/theme-common": "^3.7.0", + "@fontsource-variable/onest": "^5.1.1", + "@fontsource/fira-mono": "^5.1.1", + "@mdx-js/react": "^3.1.0", "clsx": "^2.1.1", - "docusaurus-plugin-typedoc": "^1.0.1", - "dotenv": "^16.4.5", - "prism-react-renderer": "^2.3.1", + "docusaurus-plugin-typedoc": "^1.2.1", + "dotenv": "^16.4.7", + "prism-react-renderer": "^2.4.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "typedoc": "^0.25.13", - "typedoc-plugin-markdown": "^4.0.3", - "typescript": "^5.4.5" + "typedoc": "^0.27.6", + "typedoc-plugin-markdown": "^4.4.1", + "typedoc-plugin-mdn-links": "^4.0.8", + "typescript": "^5.7.3" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^3.4.0", + "@docusaurus/module-type-aliases": "^3.7.0", "docusaurus-mdx-checker": "^3.0.0", - "prettier": "^3.3.1", - "rimraf": "^5.0.7" + "prettier": "^3.4.2", + "rimraf": "^5.0.10" }, "browserslist": { "production": [ diff --git a/website/sidebars.js b/website/sidebars.js index 99dffd9c..c75ce950 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -10,7 +10,7 @@ */ // @ts-check -import typedocSidebar from './docs/reference/api/typedoc-sidebar.cjs'; +import typedocSidebar from './docs/api/typedoc-sidebar.cjs'; /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { @@ -118,49 +118,13 @@ const sidebars = { }, { type: 'category', - label: 'API Reference', - items: [ - { - type: 'doc', - id: 'reference/models-utilities', - }, - { - type: 'doc', - id: 'reference/actions-enhancers', - }, - { - type: 'doc', - id: 'reference/actions-runners', - }, - { - type: 'doc', - id: 'reference/actions-extensions', - }, - { - type: 'category', - label: 'Implementations', - items: [{ - type: 'autogenerated', - dirName: 'reference/implementations', - }], - link: { - type: 'generated-index', - title: 'Implementations and configuration', - slug: '/category/implementations', - }, - }, - { - type: 'category', - label: 'API', - description: 'GitHub repository containing examples of projects using Foscia.', - items: typedocSidebar, - }, - ], + label: 'API', + items: typedocSidebar, link: { type: 'generated-index', - title: 'API Reference', - description: 'Functions documentation and API reference.', - slug: '/category/reference', + title: 'API', + description: 'API reference of each packages.', + slug: '/category/api', }, }, { diff --git a/website/src/components/Chip/index.js b/website/src/components/Chip/index.js index dea28557..beb3b9ab 100644 --- a/website/src/components/Chip/index.js +++ b/website/src/components/Chip/index.js @@ -1,10 +1,9 @@ import clsx from 'clsx'; import React from 'react'; -import styles from './styles.module.css'; export default function Chip({ children, color }) { return ( - + {children} ); diff --git a/website/src/components/Chip/styles.module.css b/website/src/components/Chip/styles.module.css deleted file mode 100644 index 8dc4cf37..00000000 --- a/website/src/components/Chip/styles.module.css +++ /dev/null @@ -1,51 +0,0 @@ -.chip { - display: inline-block; - position: relative; - padding: 2px 12px; - border-radius: var(--ifm-global-radius); - font-size: 0.875rem; - font-weight: bold; - margin-right: 4px; - margin-bottom: 4px; -} - -.chip::before { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: currentColor; - opacity: 0.12; - border-radius: inherit; - pointer-events: none; -} - -.chip.chip--primary { - color: var(--ifm-color-primary); -} - -.chip.chip--success { - color: var(--ifm-color-success-darkest); -} - -.chip.chip--info { - color: var(--ifm-color-info-darkest); -} - -.chip.chip--danger { - color: var(--ifm-color-danger-darkest); -} - -[data-theme="dark"] .chip.chip--success { - color: var(--ifm-color-success-lightest); -} - -[data-theme="dark"] .chip.chip--info { - color: var(--ifm-color-info-lightest); -} - -[data-theme="dark"] .chip.chip--danger { - color: var(--ifm-color-danger-lightest); -} diff --git a/website/src/components/HomeFeatures/index.js b/website/src/components/HomeFeatures/index.js index ccf89a3d..f7e20915 100644 --- a/website/src/components/HomeFeatures/index.js +++ b/website/src/components/HomeFeatures/index.js @@ -29,7 +29,7 @@ function HomeActionsPresentation() { ; const example = ` -const posts = await action().run( +const posts = await action( query(Post), include('author'), all(), @@ -39,7 +39,7 @@ const post = fill(new Post(), { title: 'Hello World!', }); -await action().run(create(post), one()); +await action(create(post), one()); `.trim(); return ; const description = <> - + @foscia/cli diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 914270f6..72fab614 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -6,460 +6,532 @@ /* You can override the default Infima variables here. */ :root, .theme--light { - --nav-background-color: 255, 255, 255; - - --ifm-font-family-monospace: "Fira Mono", monospace; - --ifm-font-family-title: "Onest Variable", sans-serif; - --ifm-heading-font-family: "Onest Variable", sans-serif; - --ifm-font-family-base: "Onest Variable", sans-serif; - --ifm-heading-font-weight: 900; - --ifm-tabs-padding-vertical: 0.5rem; - --ifm-global-radius: 24px; - --ifm-card-border-radius: 24px; - --ifm-pre-border-radius: 8px; - --ifm-code-border-radius: 8px; - --ifm-alert-border-left-width: 0px; - --ifm-navbar-height: 76px; - --ifm-navbar-item-padding-vertical: 0.25rem; - --ifm-navbar-item-padding-horizontal: 0.5rem; - - --ifm-background-color: #ffffff; - --ifm-background-surface-color: #fef7fa; - --ifm-hero-background-color: transparent; - --ifm-footer-background-color: #fef7fa; - --ifm-code-background: #fef7fa; - --ifm-details-background: #fef7fa; - --ifm-details-code-background: #fdf3f7; - --ifm-hover-overlay: #fef7fa; - --ifm-color-accent: #ffc013; - --ifm-color-primary: #ac1b55; - --ifm-color-primary-dark: #9b184c; - --ifm-color-primary-darker: #921748; - --ifm-color-primary-darkest: #78133b; - --ifm-color-primary-light: #bd1e5d; - --ifm-color-primary-lighter: #c61f62; - --ifm-color-primary-lightest: #dd266f; - --ifm-table-stripe-background: rgba(0, 0, 0, 0.03); - --ifm-breadcrumb-item-background-active: var(--ifm-code-background); - --ifm-menu-color: var(--ifm-color-emphasis-800); + --nav-background-color: 255, 255, 255; + + --ifm-font-family-monospace: "Fira Mono", monospace; + --ifm-font-family-title: "Onest Variable", sans-serif; + --ifm-heading-font-family: "Onest Variable", sans-serif; + --ifm-font-family-base: "Onest Variable", sans-serif; + --ifm-heading-font-weight: 900; + --ifm-tabs-padding-vertical: 0.5rem; + --ifm-global-radius: 24px; + --ifm-card-border-radius: 24px; + --ifm-pre-border-radius: 8px; + --ifm-code-border-radius: 8px; + --ifm-alert-border-left-width: 0px; + --ifm-navbar-height: 76px; + --ifm-navbar-item-padding-vertical: 0.25rem; + --ifm-navbar-item-padding-horizontal: 0.5rem; + + --ifm-background-color: #ffffff; + --ifm-background-surface-color: #fef7fa; + --ifm-hero-background-color: transparent; + --ifm-footer-background-color: #fef7fa; + --ifm-code-background: #fef7fa; + --ifm-details-background: #fef7fa; + --ifm-details-code-background: #fdf3f7; + --ifm-hover-overlay: #fef7fa; + --ifm-color-accent: #ffc013; + --ifm-color-primary: #ac1b55; + --ifm-color-primary-dark: #9b184c; + --ifm-color-primary-darker: #921748; + --ifm-color-primary-darkest: #78133b; + --ifm-color-primary-light: #bd1e5d; + --ifm-color-primary-lighter: #c61f62; + --ifm-color-primary-lightest: #dd266f; + --ifm-table-stripe-background: rgba(0, 0, 0, 0.03); + --ifm-breadcrumb-item-background-active: var(--ifm-code-background); + --ifm-menu-color: var(--ifm-color-emphasis-800); } /* For readability concerns, you should choose a lighter palette in dark mode. */ html[data-theme="dark"], .theme--dark { - --nav-background-color: 19, 12, 15; - --ifm-background-color: #130c0f; - --ifm-background-surface-color: #0f0a0c; - --ifm-hero-background-color: transparent; - --ifm-footer-background-color: #0f0a0c; - --ifm-code-background: #1c1216; - --ifm-details-background: #1c1216; - --ifm-details-code-background: #1f1418; - --ifm-hover-overlay: #1c1216; - --ifm-color-accent: #ff6bc1; - --ifm-color-primary: #ffa44c; - --ifm-color-primary-dark: #ff932b; - --ifm-color-primary-darker: #ff8b1a; - --ifm-color-primary-darkest: #e87200; - --ifm-color-primary-light: #ffb56d; - --ifm-color-primary-lighter: #ffbd7e; - --ifm-color-primary-lightest: #ffd6af; - --ifm-table-stripe-background: rgba(255, 255, 255, 0.02); + --nav-background-color: 19, 12, 15; + --ifm-background-color: #130c0f; + --ifm-background-surface-color: #0f0a0c; + --ifm-hero-background-color: transparent; + --ifm-footer-background-color: #0f0a0c; + --ifm-code-background: #1c1216; + --ifm-details-background: #1c1216; + --ifm-details-code-background: #1f1418; + --ifm-hover-overlay: #1c1216; + --ifm-color-accent: #ff6bc1; + --ifm-color-primary: #ffa44c; + --ifm-color-primary-dark: #ff932b; + --ifm-color-primary-darker: #ff8b1a; + --ifm-color-primary-darkest: #e87200; + --ifm-color-primary-light: #ffb56d; + --ifm-color-primary-lighter: #ffbd7e; + --ifm-color-primary-lightest: #ffd6af; + --ifm-table-stripe-background: rgba(255, 255, 255, 0.02); } .DocSearch { - font-family: inherit; - --docsearch-modal-background: var(--ifm-background-surface-color); - --docsearch-footer-background: var(--ifm-background-color); - --docsearch-searchbox-background: var(--ifm-code-background); - --docsearch-key-gradient: linear-gradient( - -26.5deg, - var(--ifm-color-emphasis-200) 0%, - var(--ifm-color-emphasis-100) 100% - ); + font-family: inherit; + --docsearch-modal-background: var(--ifm-background-surface-color); + --docsearch-footer-background: var(--ifm-background-color); + --docsearch-searchbox-background: var(--ifm-code-background); + --docsearch-key-gradient: linear-gradient( + -26.5deg, + var(--ifm-color-emphasis-200) 0%, + var(--ifm-color-emphasis-100) 100% + ); } .header-version-link { - background: var(--ifm-code-background); - font-family: var(--ifm-font-family-monospace); - font-weight: bold; + background: var(--ifm-code-background); + font-family: var(--ifm-font-family-monospace); + font-weight: bold; } .header-github-link { - padding: 0; - margin: 0 8px; + padding: 0; + margin: 0 8px; } .header-github-link:hover { - opacity: 0.6; + opacity: 0.6; } .header-github-link:before { - background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; - content: ""; - display: flex; - height: 24px; - width: 24px; + background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; + content: ""; + display: flex; + height: 24px; + width: 24px; } html[data-theme="dark"] .header-github-link:before { - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; } .logo-gradient__start, .svg-gradient-primary__start { - stop-color: var(--ifm-color-primary); + stop-color: var(--ifm-color-primary); } .logo-gradient__end, .svg-gradient-primary__end { - stop-color: var(--ifm-color-accent); + stop-color: var(--ifm-color-accent); } .text--large { - font-size: 1.15rem; + font-size: 1.15rem; } @media screen and (max-width: 996px) { - .text--large { - font-size: 1.05rem; - } + .text--large { + font-size: 1.05rem; + } } .theme-doc-markdown.markdown > table { - display: block; + display: block; } .theme-doc-markdown.markdown > table code { - word-break: normal; + word-break: normal; } code { - display: inline-block; - word-break: break-all; - padding: 0 .375rem; - font-weight: 600; + display: inline-block; + word-break: break-all; + padding: 0 .375rem; + font-weight: 600; } .anchor code, a code { - text-decoration: none !important; - border-style: dashed; + text-decoration: none !important; + border-style: dashed; } .anchor del code, a del code { - text-decoration: line-through !important; - border-style: dashed; + text-decoration: line-through !important; + border-style: dashed; } .anchor:hover code, .anchor:focus code, a:hover code, a:focus code { - border-color: var(--ifm-tabs-color-active-border, var(--ifm-color-primary)); + border-color: var(--ifm-tabs-color-active-border, var(--ifm-color-primary)); } .code-block-addition-line { - --code-block-line-symbol: "+"; - --code-block-line-color: var(--ifm-color-success-dark); - --code-block-line-bg-color: var(--ifm-color-success-contrast-background); + --code-block-line-symbol: "+"; + --code-block-line-color: var(--ifm-color-success-dark); + --code-block-line-bg-color: var(--ifm-color-success-contrast-background); } .code-block-deletion-line { - --code-block-line-symbol: "-"; - --code-block-line-color: var(--ifm-color-danger-dark); - --code-block-line-bg-color: var(--ifm-color-danger-contrast-background); + --code-block-line-symbol: "-"; + --code-block-line-color: var(--ifm-color-danger-dark); + --code-block-line-bg-color: var(--ifm-color-danger-contrast-background); } .code-block-addition-line, .code-block-deletion-line { - position: relative; - display: block; - margin: 0 calc(-1 * var(--ifm-pre-padding)); - padding: 0 var(--ifm-pre-padding); + position: relative; + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); } .code-block-addition-line:before, .code-block-deletion-line:before { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: var(--code-block-line-color); - opacity: 0.12; - pointer-events: none; + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: var(--code-block-line-color); + opacity: 0.12; + pointer-events: none; } .code-block-addition-line:after, .code-block-deletion-line:after { - content: var(--code-block-line-symbol); - position: absolute; - font-size: inherit; - font-weight: bold; - top: 0; - left: 2px; - color: var(--code-block-line-color); - pointer-events: none; + content: var(--code-block-line-symbol); + position: absolute; + font-size: inherit; + font-weight: bold; + top: 0; + left: 2px; + color: var(--code-block-line-color); + pointer-events: none; } img { - border-radius: 8px; + border-radius: 8px; } img.img--cli { - max-width: 450px; + max-width: 450px; } .text--gradient { - background: -webkit-linear-gradient(315deg, var(--ifm-color-primary) 25%, var(--ifm-color-accent)); - background-size: 300%; - background-position: center; - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; + background: -webkit-linear-gradient(315deg, var(--ifm-color-primary) 25%, var(--ifm-color-accent)); + background-size: 300%; + background-position: center; + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; } .border--gradient { - --border-primary-background: var(--ifm-background-color); - position: relative; - fill: var(--ifm-font-color-base); - color: var(--ifm-font-color-base); - border: solid 2px transparent; - background-image: linear-gradient(var(--border-primary-background), var(--border-primary-background)), radial-gradient(circle at top left, var(--ifm-color-primary), var(--ifm-color-accent)); - background-origin: border-box; - background-position: center; - background-size: 300%; - background-clip: padding-box, border-box; - z-index: 1; + --border-primary-background: var(--ifm-background-color); + position: relative; + fill: var(--ifm-font-color-base); + color: var(--ifm-font-color-base); + border: solid 2px transparent; + background-image: linear-gradient(var(--border-primary-background), var(--border-primary-background)), radial-gradient(circle at top left, var(--ifm-color-primary), var(--ifm-color-accent)); + background-origin: border-box; + background-position: center; + background-size: 300%; + background-clip: padding-box, border-box; + z-index: 1; } .border--gradient.border--large { - border-width: 4px; + border-width: 4px; } .border--gradient:hover { - fill: var(--ifm-font-color-base) !important; - color: var(--ifm-font-color-base) !important; + fill: var(--ifm-font-color-base) !important; + color: var(--ifm-font-color-base) !important; } .blur--gradient { - position: relative; + position: relative; } .blur--gradient:before, .blur--gradient:after { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: -1; + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -1; } .blur--gradient:before { - background: -webkit-linear-gradient(315deg, var(--ifm-color-primary) 25%, var(--ifm-color-accent)); - filter: blur(16px); - opacity: 0; - transition: all 250ms ease-in-out; + background: -webkit-linear-gradient(315deg, var(--ifm-color-primary) 25%, var(--ifm-color-accent)); + filter: blur(16px); + opacity: 0; + transition: all 250ms ease-in-out; } .blur--gradient:after { - background: var(--ifm-background-color); - border-radius: inherit; + background: var(--ifm-background-color); + border-radius: inherit; } .blur--gradient:hover:before, .blur--gradient:focus:before { - opacity: 0.5; + opacity: 0.5; } div[class^="language-"] { - --prism-background-color: var(--ifm-code-background) !important; + --prism-background-color: var(--ifm-code-background) !important; } div[class^="language-"] pre { - background-color: transparent !important; + background-color: transparent !important; } table { - background-color: var(--ifm-code-background); - box-shadow: var(--ifm-global-shadow-lw); - border-radius: var(--ifm-pre-border-radius); - border-spacing: 0; - border-collapse: separate; - display: table; - width: 100%; + --ifm-table-background: var(--ifm-code-background); + border-spacing: 0; + border-collapse: separate; +} + +table > thead, table > tbody { + background-color: var(--ifm-code-background); +} + +table > thead > tr > th { + background-color: var(--ifm-table-stripe-background); } table > thead > tr > th, table > tbody > tr > td { - border: none; + border: none; } table > thead > tr > th:first-child { - border-top-left-radius: var(--ifm-pre-border-radius); + border-top-left-radius: var(--ifm-pre-border-radius); } table > thead > tr > th:last-child { - border-top-right-radius: var(--ifm-pre-border-radius); + border-top-right-radius: var(--ifm-pre-border-radius); } table > tbody > tr:last-child > td:first-child { - border-bottom-left-radius: var(--ifm-pre-border-radius); + border-bottom-left-radius: var(--ifm-pre-border-radius); } table > tbody > tr:last-child > td:last-child { - border-bottom-right-radius: var(--ifm-pre-border-radius); + border-bottom-right-radius: var(--ifm-pre-border-radius); } table > thead > tr > th:not(:last-child), table > tbody > tr > td:not(:last-child) { - border-right: 1px solid var(--ifm-color-emphasis-300); + border-right: 1px solid var(--ifm-color-emphasis-300); } .card { - border: none !important; + border: none !important; } details { - --ifm-alert-border-radius: var(--ifm-code-border-radius) !important; - --ifm-alert-background-color: var(--ifm-details-background) !important; - --ifm-alert-background-color-highlight: var(--ifm-details-background) !important; - --ifm-alert-foreground-color: var(--ifm-font-color-base) !important; - --ifm-alert-border-color: var(--ifm-color-primary) !important; - border: none !important; + --ifm-alert-border-radius: var(--ifm-code-border-radius) !important; + --ifm-alert-background-color: var(--ifm-details-background) !important; + --ifm-alert-background-color-highlight: var(--ifm-details-background) !important; + --ifm-alert-foreground-color: var(--ifm-font-color-base) !important; + --ifm-alert-border-color: var(--ifm-color-primary) !important; + border: none !important; } details > summary { - color: var(--ifm-color-primary); + color: var(--ifm-color-primary); } details > div > div { - margin-top: 0 !important; - border: 0 !important; + margin-top: 0 !important; + border: 0 !important; } details > div > div > div[class^="language-"] { - background-color: var(--ifm-details-code-background) !important; + background-color: var(--ifm-details-code-background) !important; } details > div > div > :last-child { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .tabs-container { - background-color: var(--ifm-code-background); - box-shadow: var(--ifm-global-shadow-lw); - border-radius: var(--ifm-pre-border-radius); + background-color: var(--ifm-code-background); + box-shadow: var(--ifm-global-shadow-lw); + border-radius: var(--ifm-pre-border-radius); } .tabs-container > .margin-top--md { - margin-top: 0 !important; + margin-top: 0 !important; } .tabs-container > .tabs { - border-bottom: 1px solid var(--ifm-color-emphasis-300); + border-bottom: 1px solid var(--ifm-color-emphasis-300); } .tabs-container [role="tabpanel"] { - --ifm-global-shadow-lw: none; + --ifm-global-shadow-lw: none; } .tabs-container [role="tabpanel"] > p { - padding: 0 var(--ifm-tabs-padding-horizontal); + padding: 0 var(--ifm-tabs-padding-horizontal); } .tabs-container [role="tabpanel"] > ul { - padding: 0 var(--ifm-tabs-padding-horizontal) 0 calc(var(--ifm-tabs-padding-horizontal) + var(--ifm-list-left-padding)); + padding: 0 var(--ifm-tabs-padding-horizontal) 0 calc(var(--ifm-tabs-padding-horizontal) + var(--ifm-list-left-padding)); } .tabs-container [role="tabpanel"] > p:first-child { - margin-top: var(--ifm-paragraph-margin-bottom); + margin-top: var(--ifm-paragraph-margin-bottom); } .tabs-container [role="tabpanel"] > p:last-child, .tabs-container [role="tabpanel"] > ul:last-child { - padding-bottom: var(--ifm-paragraph-margin-bottom); + padding-bottom: var(--ifm-paragraph-margin-bottom); } .navbar { - background: transparent; + background: transparent; } .navbar .navbar__logo { - height: 32px; + height: 32px; } .navbar:before { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - background-color: transparent; + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + background-color: rgba(var(--nav-background-color), 0.6); } .navbar .navbar__inner { - z-index: 1; + z-index: 1; } .navbar .navbar-sidebar { - z-index: 2; + z-index: 2; } .navbar .navbar__item { - font-size: 0.875rem; - letter-spacing: 0.01rem; + font-size: 0.875rem; + letter-spacing: 0.01rem; } div[class^="announcementBar"] { - margin: 0 16px; - border: none !important; - border-radius: 0 0 24px 24px; + margin: 0 16px; + border: none !important; + border-radius: 0 0 24px 24px; } div[class^="announcementBar"] > div { - padding: 4px 8px; + padding: 4px 8px; } aside, .table-of-contents__left-border { - border: none !important; + border: none !important; } div[class^="tableOfContents_"] { - background-color: var(--ifm-code-background) !important; - border-radius: 8px !important; - box-shadow: var(--ifm-global-shadow-lw) !important; + background-color: var(--ifm-code-background) !important; + border-radius: 8px !important; + box-shadow: var(--ifm-global-shadow-lw) !important; } .menu__list-item-collapsible, .menu__link, .menu__caret { - border-radius: 24px !important; + border-radius: 24px !important; } .menu__caret:before, .menu__link--sublist-caret:after { - background-size: 20px 20px; + background-size: 20px 20px; } .theme-doc-sidebar-item-link, .theme-doc-sidebar-item-category { - font-size: 0.8rem; + font-size: 0.8rem; } .theme-doc-sidebar-item-link.theme-doc-sidebar-item-link-level-1 > .menu__link, .theme-doc-sidebar-item-category.theme-doc-sidebar-item-category-level-1 > div > .menu__link { - font-size: 0.875rem; - font-weight: bold; + font-size: 0.875rem; + font-weight: bold; } .button > p { - margin: 0; + margin: 0; } .pagination-nav__link { - background: var(--ifm-code-background); - border: none; - box-shadow: var(--ifm-global-shadow-lw); + background: var(--ifm-code-background); + border: none; + box-shadow: var(--ifm-global-shadow-lw); } .footer { - background-color: transparent; - padding: 16px 32px 0 32px; + background-color: transparent; + padding: 16px 32px 0 32px; } .footer > .container { - background-color: var(--ifm-footer-background-color); - padding: 32px; - border-radius: 32px 32px 0 0; + background-color: var(--ifm-footer-background-color); + padding: 32px; + border-radius: 32px 32px 0 0; +} + +.chip { + display: inline-flex; + align-items: center; + position: relative; + padding: 2px 12px; + border-radius: var(--ifm-global-radius); + font-size: 0.875rem; + font-weight: bold; + margin-right: 4px; + margin-bottom: 4px; + min-height: 24px; +} + +.chip svg { + height: 18px; + vertical-align: middle; + margin-right: 8px; +} + +.chip svg path { + fill: currentColor !important; +} + +.chip a { + text-decoration: underline !important; +} + +.chip::before { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: currentColor; + opacity: 0.12; + border-radius: inherit; + pointer-events: none; +} + +.chip.chip--primary { + color: var(--ifm-color-primary); +} + +.chip.chip--success { + color: var(--ifm-color-success-darkest); +} + +.chip.chip--info { + color: var(--ifm-color-info-darkest); +} + +.chip.chip--danger { + color: var(--ifm-color-danger-darkest); +} + +[data-theme="dark"] .chip.chip--success { + color: var(--ifm-color-success-lightest); +} + +[data-theme="dark"] .chip.chip--info { + color: var(--ifm-color-info-lightest); +} + +[data-theme="dark"] .chip.chip--danger { + color: var(--ifm-color-danger-lightest); } diff --git a/website/src/icons/flask.svg b/website/src/icons/flask.svg new file mode 100644 index 00000000..145d9704 --- /dev/null +++ b/website/src/icons/flask.svg @@ -0,0 +1 @@ +flask diff --git a/website/src/icons/lock.svg b/website/src/icons/lock.svg new file mode 100644 index 00000000..5c0eb3f6 --- /dev/null +++ b/website/src/icons/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/typedoc-plugin.mjs b/website/typedoc-plugin.mjs new file mode 100644 index 00000000..d05f1758 --- /dev/null +++ b/website/typedoc-plugin.mjs @@ -0,0 +1,79 @@ +// @ts-check + +import fs from 'node:fs'; +import path from 'node:path'; +import { MarkdownPageEvent } from 'typedoc-plugin-markdown'; +import { useRootDirname } from '../scripts/utils.js'; + +const rootDirname = useRootDirname(); +const lockSvg = fs.readFileSync(path.resolve(rootDirname, './website/src/icons/lock.svg')); +const flaskSvg = fs.readFileSync(path.resolve(rootDirname, './website/src/icons/flask.svg')); + +/** + * @param {import('typedoc-plugin-markdown').MarkdownApplication} app + */ +export function load(app) { + app.renderer.on(MarkdownPageEvent.END, (page) => { + if (page.contents) { + const specialBlockTags = [ + [ + /\n###? Deprecated\n\n([^\n]+)\n/, + (matches) => `deprecated: ${matches[1]}`, + ], + [ + /\n###? Since\n\n([^\n]+)\n/, + (matches) => `version: v${matches[1]}+`, + ], + [ + /\n###? Require Context\n\n([^\n]+)\n/, + (matches) => `require: ${matches[1]}`, + ], + [ + /\n###? Provide Context\n\n([^\n]+)\n/, + (matches) => `provide: ${matches[1]}`, + ], + ]; + + page.contents = page.contents.split('\n> ').map((content, index) => { + const specialBlockTagsChips = specialBlockTags + .map(([regexp, chipFactory]) => { + const matches = regexp.exec(content); + if (matches) { + content = content.replace(matches[0], ''); + + return chipFactory(matches); + } + + return null; + }) + .filter((chip) => !!chip); + + const joinWith = index === 0 ? '' : '\n> '; + + return specialBlockTagsChips.length + ? `${specialBlockTagsChips.join('\n')}\n${joinWith}${content}` + : `${joinWith}${content}`; + }).join(''); + + const specialMarkTags = [ + [ + /\s\*\*`Experimental`\*\*\s/, + () => `${flaskSvg}experimental`, + ], + [ + /\s\*\*`Internal`\*\*\s/, + () => `${lockSvg}internal`, + ], + ]; + + specialMarkTags.forEach(([regexp, chipFactory]) => { + let matches; + while ((matches = regexp.exec(page.contents)) !== null) { + page.contents = page.contents.replace(matches[0], `\n${chipFactory()}\n`); + } + }); + + page.contents = page.contents.replace(/\[\\`([^`]+)\\`]\(([^)]+)\)/g, '[`$1`]($2)'); + } + }); +}