From b4b2043baac63b6fbe381eb3d43591bc7b0b7f4c Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 5 Sep 2024 20:31:09 +0200 Subject: [PATCH 01/32] feat(core): add `queryAs` enhancer --- .../context/consumers/consumeQueryAs.ts | 8 ++ .../src/actions/context/enhancers/queryAs.ts | 34 +++++++++ .../context/guessers/guessContextModel.ts | 5 ++ .../src/actions/extensions/coreExtensions.ts | 2 + packages/core/src/actions/index.ts | 4 + packages/core/src/actions/types.ts | 24 +++--- .../tests/mocks/composables/imageable.mock.ts | 6 ++ .../core/tests/mocks/models/comment.mock.ts | 2 + packages/core/tests/mocks/models/post.mock.ts | 2 + .../core/tests/typecheck/actions.test-d.ts | 46 +++++++++++- .../jsonapi/tests/integration/crud.test.ts | 72 +++++++++++++++++- packages/rest/tests/integration/crud.test.ts | 60 ++++++++++++++- .../serialization/src/makeDeserializerWith.ts | 2 + website/docs/digging-deeper/jsonapi.md | 23 ++++++ website/docs/digging-deeper/rest.md | 74 ++++++++++++++++++- website/docs/reference/actions-enhancers.mdx | 26 +++++++ website/docs/reference/actions-extensions.md | 1 + 17 files changed, 378 insertions(+), 13 deletions(-) create mode 100644 packages/core/src/actions/context/consumers/consumeQueryAs.ts create mode 100644 packages/core/src/actions/context/enhancers/queryAs.ts create mode 100644 packages/core/tests/mocks/composables/imageable.mock.ts 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..69f2b83a --- /dev/null +++ b/packages/core/src/actions/context/consumers/consumeQueryAs.ts @@ -0,0 +1,8 @@ +import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; +import { ConsumeQueryAs } from '@foscia/core/actions/types'; +import { Model } from '@foscia/core/model/types'; + +export default ( + context: C & Partial>, + defaultValue?: D, +) => consumeContext(context, 'queryAs', ['queryAs'], defaultValue); 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..fc0029be --- /dev/null +++ b/packages/core/src/actions/context/enhancers/queryAs.ts @@ -0,0 +1,34 @@ +import context from '@foscia/core/actions/context/enhancers/context'; +import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +import { Action, ConsumeQueryAs, WithParsedExtension } from '@foscia/core/actions/types'; +import { Model } from '@foscia/core/model/types'; +import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; + +/** + * Define models targeted by the query. This will keep the context state when + * executing the query, but will take priority when 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. + * + * @param models + * + * @category Enhancers + */ +const queryAs = ( + ...models: ArrayableVariadic +) => context({ queryAs: wrapVariadic(...models) }); + +export default /* @__PURE__ */ appendExtension( + 'queryAs', + queryAs, + 'use', +) as WithParsedExtension( + this: Action, + ...models: ArrayableVariadic + ): Action, E>; +}>; diff --git a/packages/core/src/actions/context/guessers/guessContextModel.ts b/packages/core/src/actions/context/guessers/guessContextModel.ts index c5934816..11aa41d3 100644 --- a/packages/core/src/actions/context/guessers/guessContextModel.ts +++ b/packages/core/src/actions/context/guessers/guessContextModel.ts @@ -4,6 +4,7 @@ import { RegistryI } from '@foscia/core/types'; import { isNil, Optional, wrap } from '@foscia/shared'; type GuessContextModelContext = { + queryAs?: Optional; model?: Optional; relation?: Optional; registry?: Optional; @@ -35,6 +36,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); diff --git a/packages/core/src/actions/extensions/coreExtensions.ts b/packages/core/src/actions/extensions/coreExtensions.ts index f75bc178..97187ebf 100644 --- a/packages/core/src/actions/extensions/coreExtensions.ts +++ b/packages/core/src/actions/extensions/coreExtensions.ts @@ -1,11 +1,13 @@ 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 queryAs from '@foscia/core/actions/context/enhancers/queryAs'; import catchIf from '@foscia/core/actions/context/runners/catchIf'; import when from '@foscia/core/actions/when'; export default () => ({ ...query.extension(), + ...queryAs.extension(), ...include.extension(), ...context.extension(), ...when.extension(), diff --git a/packages/core/src/actions/index.ts b/packages/core/src/actions/index.ts index ea557771..57e37e53 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -1,6 +1,7 @@ import ActionName from '@foscia/core/actions/actionName'; import consumeAction from '@foscia/core/actions/context/consumers/consumeAction'; import consumeAdapter from '@foscia/core/actions/context/consumers/consumeAdapter'; +import consumeQueryAs from '@foscia/core/actions/context/consumers/consumeQueryAs'; import consumeCache from '@foscia/core/actions/context/consumers/consumeCache'; import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; import consumeData from '@foscia/core/actions/context/consumers/consumeData'; @@ -30,6 +31,7 @@ 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 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 cached from '@foscia/core/actions/context/runners/cached'; @@ -83,6 +85,7 @@ export { catchIf, context, query, + queryAs, include, instanceData, relationData, @@ -100,6 +103,7 @@ export { consumeInclude, consumeInstance, consumeModel, + consumeQueryAs, consumeRegistry, consumeRelation, consumeSerializer, diff --git a/packages/core/src/actions/types.ts b/packages/core/src/actions/types.ts index 199c762f..67748270 100644 --- a/packages/core/src/actions/types.ts +++ b/packages/core/src/actions/types.ts @@ -79,17 +79,19 @@ export type ContextRunner = ( ) => R; 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; + C extends { queryAs: Constructor[] } ? I extends ModelInstance ? I : never + : 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; 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; + C extends { queryAs: Constructor[] } ? I extends ModelInstance ? I : never + : 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; export type ConsumeAction = { action: ActionName | string; @@ -99,6 +101,10 @@ export type ConsumeData = { data: unknown; }; +export type ConsumeQueryAs = { + queryAs: M[]; +}; + export type ConsumeModel = { model: M; }; 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/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..2cb6d05c 100644 --- a/packages/core/tests/mocks/models/post.mock.ts +++ b/packages/core/tests/mocks/models/post.mock.ts @@ -1,7 +1,9 @@ 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(), diff --git a/packages/core/tests/typecheck/actions.test-d.ts b/packages/core/tests/typecheck/actions.test-d.ts index 3c2b2cd7..80491445 100644 --- a/packages/core/tests/typecheck/actions.test-d.ts +++ b/packages/core/tests/typecheck/actions.test-d.ts @@ -14,7 +14,7 @@ import { one, oneOrCurrent, oneOrFail, - query, + query, queryAs, raw, SerializerI, update, @@ -29,6 +29,7 @@ test('Actions are type safe', async () => { const action = () => { const ActionClass = makeActionClass().extend({ ...query.extension(), + ...queryAs.extension(), ...include.extension(), ...all.extension(), ...oneOrFail.extension(), @@ -65,11 +66,15 @@ test('Actions are type safe', async () => { 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(manualPostsUsingRunVariadic).toMatchTypeOf(); const postUsingFunc = await action() .use(query(new PostMock())) @@ -88,6 +93,45 @@ test('Actions are type safe', async () => { expectTypeOf(postUsingVariadic).toMatchTypeOf(); expectTypeOf(postUsingRunVariadic).toMatchTypeOf(); + const asPostsUsingFunc = await action() + .use(queryAs(PostMock)) + .use(include('comments.postedBy')) + .run(all()); + const asPostsUsingBuild = await action() + .queryAs(PostMock) + .include('comments.postedBy') + .all(); + const asPostsOrCommentsUsingFunc = await action() + .use(queryAs(PostMock, CommentMock)) + .use(include('images')) + .run(all()); + const asPostsOrCommentsUsingBuild = await action() + .queryAs(PostMock, CommentMock) + .include('images') + .all(); + const asPostsOrCommentsArrayUsingFunc = await action() + .use(queryAs([PostMock, CommentMock])) + .use(include('images')) + .run(all()); + const asPostsOrCommentsArrayUsingBuild = await action() + .queryAs([PostMock, CommentMock]) + .include('images') + .all(); + + expectTypeOf(asPostsUsingFunc).toMatchTypeOf(); + expectTypeOf(asPostsUsingBuild).toMatchTypeOf(); + expectTypeOf(asPostsOrCommentsUsingFunc).toMatchTypeOf<(PostMock | CommentMock)[]>(); + expectTypeOf(asPostsOrCommentsUsingBuild).toMatchTypeOf<(PostMock | CommentMock)[]>(); + expectTypeOf(asPostsOrCommentsArrayUsingFunc).toMatchTypeOf<(PostMock | CommentMock)[]>(); + expectTypeOf(asPostsOrCommentsArrayUsingBuild).toMatchTypeOf<(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')) diff --git a/packages/jsonapi/tests/integration/crud.test.ts b/packages/jsonapi/tests/integration/crud.test.ts index 631ad585..e225b595 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,7 @@ describe('integration: JSON:API', () => { expect(post.comments).toBeUndefined(); }); - it('should run action: custom', async () => { + it('should run action: contextualized custom', async () => { const fetchMock = createFetchMock(); fetchMock.mockImplementationOnce(createFetchResponse().json({ data: [ @@ -651,6 +652,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/rest/tests/integration/crud.test.ts b/packages/rest/tests/integration/crud.test.ts index fe43f128..ae192a8f 100644 --- a/packages/rest/tests/integration/crud.test.ts +++ b/packages/rest/tests/integration/crud.test.ts @@ -11,10 +11,11 @@ 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'; @@ -289,6 +290,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 = makeJsonRestActionMock(); + + 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 = makeJsonRestActionMock(); + + 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([ diff --git a/packages/serialization/src/makeDeserializerWith.ts b/packages/serialization/src/makeDeserializerWith.ts index 70530191..d2ba123e 100644 --- a/packages/serialization/src/makeDeserializerWith.ts +++ b/packages/serialization/src/makeDeserializerWith.ts @@ -3,6 +3,7 @@ import { consumeCache, consumeInstance, consumeModel, + consumeQueryAs, consumeRegistry, consumeRelation, DeserializedData, @@ -71,6 +72,7 @@ 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({ + queryAs: consumeQueryAs(context, null), model: (record.parent?.instance.$model ?? consumeModel(context, null)) as Model, relation: record.parent?.def ?? consumeRelation(context, null), registry, diff --git a/website/docs/digging-deeper/jsonapi.md b/website/docs/digging-deeper/jsonapi.md index 4b241d9a..786d039f 100644 --- a/website/docs/digging-deeper/jsonapi.md +++ b/website/docs/digging-deeper/jsonapi.md @@ -133,6 +133,29 @@ 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().run( + // 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 diff --git a/website/docs/digging-deeper/rest.md b/website/docs/digging-deeper/rest.md index 3e722819..3e2a5534 100644 --- a/website/docs/digging-deeper/rest.md +++ b/website/docs/digging-deeper/rest.md @@ -31,6 +31,50 @@ 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'; +import { paginate, usingDocument } from '@foscia/jsonapi'; + +const results = await action().run( + // 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 @@ -90,6 +134,34 @@ makeJsonRestSerializer({ }); ``` +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 { makeJsonRestDeserializer } from '@foscia/rest'; + +makeJsonRestSerializer({ + 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 @@ -100,7 +172,7 @@ records data using the given transformation function. import { makeJsonRestSerializer } from '@foscia/rest'; makeJsonRestSerializer({ - createData: (records) => ({ data: records }), + createData: (records, context) => ({ data: records }), }); ``` diff --git a/website/docs/reference/actions-enhancers.mdx b/website/docs/reference/actions-enhancers.mdx index 2a16fb26..d27bc800 100644 --- a/website/docs/reference/actions-enhancers.mdx +++ b/website/docs/reference/actions-enhancers.mdx @@ -92,6 +92,32 @@ the instance to query - [`ModelRelationKey | undefined`](/docs/reference/api/@foscia/core/type-aliases/ModelRelationKey) `relation` the optional instance's relation to query +### `queryAs` + + + +Define models targeted by the query. This will keep the context state when +executing the query, but will take priority when 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`). + +#### Example + +```typescript +import { queryAs } from '@foscia/core'; + +action().use(queryAs(Post)); +action().use(queryAs(Post, Comment)); +action().use(queryAs([Post, Comment])); +``` + +#### Arguments + +- [`ArrayableVariadic`](/docs/reference/api/@foscia/core/type-aliases/Model) `...models` +the model the query is targeting + ### `create` diff --git a/website/docs/reference/actions-extensions.md b/website/docs/reference/actions-extensions.md index 1b842feb..faa54e9a 100644 --- a/website/docs/reference/actions-extensions.md +++ b/website/docs/reference/actions-extensions.md @@ -15,6 +15,7 @@ runners to integrate into your action to get - [`when`](/docs/reference/actions-enhancers#when) - [`query`](/docs/reference/actions-enhancers#query) +- [`queryAs`](/docs/reference/actions-enhancers#queryas) - [`include`](/docs/reference/actions-enhancers#include) - [`context`](/docs/reference/actions-enhancers#context) From cc67b0285dee5a8b95eed39971f3d2e70c85493f Mon Sep 17 00:00:00 2001 From: paul Date: Wed, 27 Nov 2024 00:23:39 +0100 Subject: [PATCH 02/32] feat: rework to factories functions and improve docs --- .../templates/concerns/renderImportsList.ts | 2 +- .../cli/src/templates/make/renderEnhancer.ts | 13 +- .../cli/src/templates/make/renderRunner.ts | 12 +- packages/cli/tests/unit/templates.test.ts | 83 + packages/cli/typedoc.json | 8 - packages/core/src/actions/actionName.ts | 5 + .../core/src/actions/actionVariadicRun.ts | 37 +- .../core/src/actions/actionVariadicUse.ts | 37 +- .../src/actions/checks/isContextFunction.ts | 20 + .../actions/checks/isWhenContextFunction.ts | 14 + .../src/actions/context/enhancers/context.ts | 34 +- .../context/enhancers/crud/associate.ts | 94 +- .../actions/context/enhancers/crud/attach.ts | 83 +- .../actions/context/enhancers/crud/create.ts | 139 +- .../actions/context/enhancers/crud/destroy.ts | 43 +- .../actions/context/enhancers/crud/detach.ts | 83 +- .../context/enhancers/crud/dissociate.ts | 76 +- .../context/enhancers/crud/instanceData.ts | 20 +- .../context/enhancers/crud/relationData.ts | 34 +- .../actions/context/enhancers/crud/save.ts | 63 +- .../actions/context/enhancers/crud/update.ts | 55 +- .../context/enhancers/crud/updateRelation.ts | 91 +- .../enhancers/crud/utils/syncRelationValue.ts | 15 - .../context/enhancers/hooks/onError.ts | 28 +- .../context/enhancers/hooks/onFinally.ts | 28 +- .../context/enhancers/hooks/onRunning.ts | 28 +- .../context/enhancers/hooks/onSuccess.ts | 28 +- .../src/actions/context/enhancers/include.ts | 40 +- .../src/actions/context/enhancers/query.ts | 150 +- .../src/actions/context/enhancers/queryAs.ts | 47 +- .../core/src/actions/context/runners/all.ts | 40 +- .../src/actions/context/runners/cached.ts | 43 +- .../src/actions/context/runners/cachedOr.ts | 47 +- .../actions/context/runners/cachedOrFail.ts | 47 +- .../src/actions/context/runners/catchIf.ts | 42 +- .../core/src/actions/context/runners/none.ts | 28 +- .../core/src/actions/context/runners/one.ts | 43 +- .../core/src/actions/context/runners/oneOr.ts | 47 +- .../actions/context/runners/oneOrCurrent.ts | 47 +- .../src/actions/context/runners/oneOrFail.ts | 45 +- .../core/src/actions/context/runners/raw.ts | 27 +- .../context/utils/serializeRelation.ts | 28 +- .../src/actions/extensions/appendExtension.ts | 55 - .../src/actions/extensions/coreExtensions.ts | 15 - .../src/actions/extensions/crudExtensions.ts | 7 - .../src/actions/extensions/hooksExtensions.ts | 11 - .../src/actions/extensions/readExtensions.ts | 23 - .../src/actions/extensions/writeExtensions.ts | 25 - packages/core/src/actions/index.ts | 24 +- packages/core/src/actions/makeActionClass.ts | 105 - .../core/src/actions/makeActionFactory.ts | 116 +- .../src/actions/makeContextFunctionFactory.ts | 28 + packages/core/src/actions/makeEnhancer.ts | 20 + packages/core/src/actions/makeRunner.ts | 20 + packages/core/src/actions/types.ts | 295 +- packages/core/src/actions/when.ts | 215 +- packages/core/src/blueprints/makeCache.ts | 13 - packages/core/src/blueprints/makeRegistry.ts | 15 - packages/core/src/cache/makeCache.ts | 15 + packages/core/src/cache/makeRefsCache.ts | 57 + packages/core/src/cache/makeRefsCacheWith.ts | 49 - .../core/src/cache/makeTimeoutRefManager.ts | 47 + ...eakRefManager.ts => makeWeakRefManager.ts} | 11 +- packages/core/src/cache/types.ts | 54 +- packages/core/src/errors/adapterError.ts | 8 + packages/core/src/errors/deserializerError.ts | 2 + .../src/errors/expectedRunFailureError.ts | 2 + .../core/src/errors/flags/isNotFoundError.ts | 6 + packages/core/src/errors/fosciaError.ts | 2 + .../core/src/errors/invalidContextError.ts | 2 + packages/core/src/errors/serializerError.ts | 2 + packages/core/src/hooks/registerHook.ts | 10 + packages/core/src/hooks/runHooks.ts | 2 + packages/core/src/hooks/runHooksSync.ts | 2 + packages/core/src/hooks/types.ts | 35 + packages/core/src/hooks/unregisterHook.ts | 9 + packages/core/src/hooks/withoutHooks.ts | 8 + packages/core/src/index.ts | 67 +- .../core/src/model/checks/isAttributeDef.ts | 6 +- packages/core/src/model/checks/isIdDef.ts | 6 +- packages/core/src/model/checks/isInstance.ts | 7 + .../core/src/model/checks/isInstanceUsing.ts | 17 + packages/core/src/model/checks/isModel.ts | 11 +- .../core/src/model/checks/isModelUsing.ts | 8 + .../core/src/model/checks/isPendingPropDef.ts | 7 - packages/core/src/model/checks/isPropDef.ts | 2 +- .../core/src/model/checks/isPropFactory.ts | 7 + .../core/src/model/checks/isRelationDef.ts | 6 +- .../core/src/model/checks/isValuePropDef.ts | 10 + .../src/model/checks/isValuePropDefOfType.ts | 10 + packages/core/src/model/fill.ts | 17 +- packages/core/src/model/filled.ts | 20 +- packages/core/src/model/forceFill.ts | 22 +- packages/core/src/model/hooks/onBoot.ts | 7 +- packages/core/src/model/hooks/onCreated.ts | 7 +- packages/core/src/model/hooks/onCreating.ts | 7 +- packages/core/src/model/hooks/onDestroyed.ts | 7 +- packages/core/src/model/hooks/onDestroying.ts | 7 +- packages/core/src/model/hooks/onInit.ts | 7 +- packages/core/src/model/hooks/onRetrieved.ts | 7 +- packages/core/src/model/hooks/onSaved.ts | 7 +- packages/core/src/model/hooks/onSaving.ts | 7 +- packages/core/src/model/hooks/onUpdated.ts | 7 +- packages/core/src/model/hooks/onUpdating.ts | 7 +- .../model/hooks/properties/onPropertyRead.ts | 7 +- .../hooks/properties/onPropertyReading.ts | 7 +- .../model/hooks/properties/onPropertyWrite.ts | 7 +- .../hooks/properties/onPropertyWriting.ts | 7 +- packages/core/src/model/isSame.ts | 19 + packages/core/src/model/makeComposable.ts | 13 +- packages/core/src/model/makeDefinition.ts | 21 +- packages/core/src/model/makeModel.ts | 15 + packages/core/src/model/makeModelClass.ts | 144 +- packages/core/src/model/makeModelFactory.ts | 22 +- .../core/src/model/props/builders/attr.ts | 25 +- .../core/src/model/props/builders/hasMany.ts | 28 +- .../core/src/model/props/builders/hasOne.ts | 28 +- packages/core/src/model/props/builders/id.ts | 29 +- .../props/builders/makeBuilderPropFactory.ts | 26 + .../model/props/builders/makePendingProp.ts | 32 - .../model/props/builders/makePropFactory.ts | 13 + .../props/builders/makeValuePropFactory.ts | 79 + .../core/src/model/props/builders/relation.ts | 35 +- .../core/src/model/props/builders/types.ts | 174 +- .../src/model/props/mappers/mapAttributes.ts | 2 +- .../core/src/model/props/mappers/mapIds.ts | 2 +- .../core/src/model/props/mappers/mapProps.ts | 20 +- .../src/model/props/mappers/mapRelations.ts | 2 +- packages/core/src/model/props/shouldSync.ts | 4 +- packages/core/src/model/relations/loaded.ts | 19 + .../model/relations/makeQueryModelLoader.ts | 142 +- .../makeQueryModelLoaderExtractor.ts | 9 + .../relations/makeQueryRelationLoader.ts | 64 +- .../relations/makeRefreshIncludeLoader.ts | 100 +- .../utilities/groupRelationsByModels.ts | 4 +- .../src/model/revivers/makeModelsReducer.ts | 10 +- .../src/model/revivers/makeModelsReviver.ts | 8 + packages/core/src/model/revivers/types.ts | 54 +- packages/core/src/model/snapshots/changed.ts | 17 + .../src/model/snapshots/cloneModelValue.ts | 4 +- .../src/model/snapshots/compareModelValue.ts | 4 +- .../src/model/snapshots/compareSnapshots.ts | 22 +- .../core/src/model/snapshots/markSynced.ts | 15 + packages/core/src/model/snapshots/restore.ts | 15 + .../src/model/snapshots/restoreSnapshot.ts | 27 +- .../core/src/model/snapshots/takeSnapshot.ts | 14 + packages/core/src/model/types.ts | 594 ++- .../normalization/normalizeDotRelations.ts | 13 +- .../core/src/normalization/normalizeKey.ts | 14 +- packages/core/src/registry/makeMapRegistry.ts | 22 + .../core/src/registry/makeMapRegistryWith.ts | 83 - packages/core/src/registry/makeRegistry.ts | 16 + packages/core/src/registry/types.ts | 34 +- packages/core/src/symbols.ts | 100 +- .../core/src/transformers/makeTransformer.ts | 8 + packages/core/src/transformers/toArrayOf.ts | 7 + packages/core/src/transformers/toBoolean.ts | 7 + packages/core/src/transformers/toDate.ts | 7 +- packages/core/src/transformers/toDateTime.ts | 7 +- packages/core/src/transformers/toNumber.ts | 5 + packages/core/src/transformers/toString.ts | 5 + packages/core/src/transformers/types.ts | 13 + packages/core/src/types.ts | 84 +- .../core/tests/mocks/makeFakeAction.mock.ts | 7 - .../typecheck/action-factories.test-d.ts | 5 +- .../tests/typecheck/action-generics.test-d.ts | 50 +- .../core/tests/typecheck/actions.test-d.ts | 182 +- .../typecheck/models-composition.test-d.ts | 56 +- .../core/tests/typecheck/models.test-d.ts | 36 +- .../actions/context/enhancers/context.test.ts | 5 +- .../unit/actions/makeActionClass.test.ts | 118 - .../unit/actions/makeActionFactory.test.ts | 220 + ...acheWith.test.ts => makeRefsCache.test.ts} | 8 +- .../unit/cache/makeTimeoutRefManager.test.ts | 32 + ...ger.test.ts => makeWeakRefManager.test.ts} | 9 +- .../normalizeDotRelations.test.ts | 5 +- .../unit/registry/makeMapRegistry.test.ts | 28 + .../unit/registry/makeMapRegistryWith.test.ts | 50 - packages/core/tests/utils/evaluateContext.ts | 7 +- .../context/consumers/consumeRequestConfig.ts | 6 + .../consumers/consumeRequestObjectParams.ts | 5 + .../actions/context/enhancers/abortSignal.ts | 33 +- .../context/enhancers/configureRequest.ts | 63 +- .../actions/context/enhancers/makeDelete.ts | 29 +- .../src/actions/context/enhancers/makeGet.ts | 31 +- .../actions/context/enhancers/makePatch.ts | 32 +- .../src/actions/context/enhancers/makePost.ts | 32 +- .../src/actions/context/enhancers/makePut.ts | 32 +- .../actions/context/enhancers/makeRequest.ts | 35 +- .../src/actions/context/enhancers/param.ts | 35 +- .../http/src/blueprints/makeHttpAdapter.ts | 11 - packages/http/src/errors/httpAbortedError.ts | 8 +- packages/http/src/errors/httpAdapterError.ts | 5 + packages/http/src/errors/httpConflictError.ts | 5 + .../http/src/errors/httpForbiddenError.ts | 5 + .../http/src/errors/httpInterruptedError.ts | 11 +- .../src/errors/httpInvalidRequestError.ts | 5 + packages/http/src/errors/httpNotFoundError.ts | 5 + packages/http/src/errors/httpResponseError.ts | 5 + packages/http/src/errors/httpServerError.ts | 5 + .../src/errors/httpTooManyRequestsError.ts | 5 + .../http/src/errors/httpUnauthorizedError.ts | 5 + packages/http/src/httpExtensions.ts | 19 - packages/http/src/index.ts | 8 +- ...eHttpAdapterWith.ts => makeHttpAdapter.ts} | 19 +- packages/http/src/makeHttpAdapterResponse.ts | 8 + packages/http/src/types.ts | 171 +- packages/http/src/utilities/clearEndpoint.ts | 13 +- .../src/utilities/deepParamsSerializer.ts | 18 + .../http/src/utilities/paramsSerializer.ts | 3 - .../unit/utilities/clearEndpoint.test.ts | 23 + .../utilities/deepParamsSerializer.test.ts | 22 + .../src/actions/context/enhancers/fields.ts | 35 +- .../actions/context/enhancers/fieldsFor.ts | 38 +- .../src/actions/context/enhancers/filterBy.ts | 33 +- .../src/actions/context/enhancers/paginate.ts | 27 +- .../src/actions/context/enhancers/sortBy.ts | 92 +- .../actions/context/enhancers/sortByAsc.ts | 32 +- .../actions/context/enhancers/sortByDesc.ts | 32 +- .../actions/context/runners/usingDocument.ts | 18 +- .../src/blueprints/makeJsonApiAdapter.ts | 33 - .../src/blueprints/makeJsonApiDeserializer.ts | 45 - .../src/blueprints/makeJsonApiSerializer.ts | 50 - packages/jsonapi/src/index.ts | 10 +- packages/jsonapi/src/jsonApiExtensions.ts | 17 - .../jsonapi/src/jsonApiStarterExtensions.ts | 11 - packages/jsonapi/src/makeJsonApiAdapter.ts | 36 + .../jsonapi/src/makeJsonApiDeserializer.ts | 52 + packages/jsonapi/src/makeJsonApiSerializer.ts | 57 + packages/jsonapi/src/types.ts | 60 + .../src/blueprints/makeJsonRestAdapter.ts | 16 - .../blueprints/makeJsonRestDeserializer.ts | 29 - .../src/blueprints/makeJsonRestSerializer.ts | 28 - packages/rest/src/index.ts | 18 +- packages/rest/src/jsonRestExtensions.ts | 1 - .../rest/src/jsonRestStarterExtensions.ts | 11 - packages/rest/src/makeIncludeParam.ts | 8 + packages/rest/src/makeRestAdapter.ts | 25 + packages/rest/src/makeRestAdapterWith.ts | 11 - packages/rest/src/makeRestDeserializer.ts | 37 + packages/rest/src/makeRestSerializer.ts | 36 + packages/rest/src/types.ts | 60 +- packages/rest/tests/integration/crud.test.ts | 22 +- .../tests/integration/endpoint-ids.test.ts | 12 +- ...tAction.mock.ts => makeRestAction.mock.ts} | 14 +- ...r.test.ts => makeRestDeserializer.test.ts} | 22 +- ...zer.test.ts => makeRestSerializer.test.ts} | 13 +- .../errors/serializerCircularRelationError.ts | 2 + packages/serialization/src/index.ts | 8 +- ...eserializerWith.ts => makeDeserializer.ts} | 15 +- .../src/makeDeserializerRecordFactory.ts | 9 + ...akeSerializerWith.ts => makeSerializer.ts} | 13 +- .../src/makeSerializerRecordFactory.ts | 8 + packages/serialization/src/types.ts | 226 +- ...zerWith.test.ts => makeSerializer.test.ts} | 6 +- packages/shared/README.md | 3 +- packages/shared/src/arrays/mapArrayable.ts | 8 + packages/shared/src/arrays/mapWithKeys.ts | 18 +- .../shared/src/arrays/sequentialTransform.ts | 16 +- packages/shared/src/arrays/uniqueValues.ts | 7 + packages/shared/src/arrays/wrap.ts | 7 + packages/shared/src/arrays/wrapVariadic.ts | 7 + packages/shared/src/checks/isFosciaType.ts | 15 +- packages/shared/src/checks/isNil.ts | 7 + packages/shared/src/checks/isNone.ts | 7 + packages/shared/src/configs/mergeConfig.ts | 2 + .../shared/src/dates/removeTimezoneOffset.ts | 2 + .../shared/src/descriptors/eachDescriptors.ts | 8 + .../src/descriptors/isDescriptorHolder.ts | 7 + .../src/descriptors/makeDescriptorHolder.ts | 12 + packages/shared/src/descriptors/types.ts | 2 + packages/shared/src/env.ts | 11 + packages/shared/src/functions/value.ts | 7 + packages/shared/src/identifiers/uniqueId.ts | 8 + packages/shared/src/identifiers/unsafeId.ts | 5 + packages/shared/src/index.ts | 5 +- .../shared/src/maps/makeIdentifiersMap.ts | 5 + packages/shared/src/strings/optionalJoin.ts | 8 + packages/shared/src/strings/pluralize.ts | 7 + packages/shared/src/strings/toKebabCase.ts | 7 + packages/shared/src/types.ts | 89 +- packages/test/src/fosciaTestError.ts | 12 + packages/test/src/index.ts | 4 +- packages/test/src/makeActionFactoryMock.ts | 221 +- .../test/src/makeActionFactoryMockable.ts | 13 +- packages/test/src/makeActionMock.ts | 59 + .../test/src/makeActionMockedHistoryItem.ts | 6 - packages/test/src/makeActionMockedRun.ts | 38 - packages/test/src/makeActionTestContext.ts | 85 + packages/test/src/mockAction.ts | 7 +- packages/test/src/types.ts | 323 +- packages/test/src/unexpectedActionError.ts | 22 + packages/test/src/unexpectedMockedRunError.ts | 17 - packages/test/src/unmockAction.ts | 7 +- packages/test/tests/unit/mocking.test.ts | 103 +- pnpm-lock.yaml | 3636 ++++++++++------- scripts/generate-files.js | 26 +- scripts/lint.js | 16 +- typedoc.json | 43 +- website/.gitignore | 2 - website/.prettierignore | 1 - website/docs/.gitignore | 1 + website/docs/core-concepts/actions.mdx | 221 +- website/docs/core-concepts/models.mdx | 206 +- website/docs/core-concepts/project.md | 6 +- .../digging-deeper/actions/_category_.json | 3 +- .../actions/custom-action-enhancers.mdx | 137 +- .../actions/custom-action-runners.mdx | 99 +- .../actions/models-registration.mdx | 19 +- .../implementations/_category_.json | 8 + .../digging-deeper/implementations/core.md | 160 + .../implementations/http.md | 4 +- .../implementations/jsonapi.md | 14 +- .../implementations/presentation.md | 76 + .../implementations/rest.md | 54 +- .../implementations/serialization.md | 0 .../digging-deeper/models/_category_.json | 3 +- .../models/models-changes-tracking.md | 24 +- .../models/models-composition.mdx | 47 +- .../models/models-configuration.md | 279 +- .../models/models-reduce-revive.mdx | 47 +- .../models/models-relations.mdx | 146 +- .../models/models-transformers.mdx | 49 +- website/docs/digging-deeper/testing.mdx | 207 - .../digging-deeper/usages/_category_.json | 8 + .../docs/digging-deeper/{ => usages}/cli.mdx | 4 +- .../docs/digging-deeper/{ => usages}/http.mdx | 52 +- .../digging-deeper/{ => usages}/jsonapi.md | 46 +- .../docs/digging-deeper/{ => usages}/rest.md | 90 +- .../docs/digging-deeper/usages/testing.mdx | 229 ++ website/docs/getting-started.mdx | 183 +- website/docs/help/faq.md | 8 +- website/docs/installation.mdx | 17 +- website/docs/integrations/nuxt.mdx | 9 +- website/docs/reference/actions-enhancers.mdx | 898 ---- website/docs/reference/actions-extensions.md | 105 - website/docs/reference/actions-runners.mdx | 318 -- .../reference/implementations/_category_.json | 7 - .../docs/reference/implementations/core.md | 138 - .../reference/implementations/presentation.md | 260 -- website/docs/reference/models-utilities.mdx | 377 -- website/docs/upgrade/dependencies.mdx | 13 +- website/docs/upgrade/migration.md | 130 +- website/docs/upgrade/support-policy.md | 28 + website/docusaurus.config.js | 55 +- website/package.json | 27 +- website/sidebars.js | 48 +- website/src/components/Chip/index.js | 3 +- website/src/components/Chip/styles.module.css | 51 - website/src/css/custom.css | 532 +-- website/src/icons/flask.svg | 1 + website/src/icons/lock.svg | 1 + website/typedoc-plugin.mjs | 77 + 353 files changed, 10324 insertions(+), 8792 deletions(-) create mode 100644 packages/cli/tests/unit/templates.test.ts delete mode 100644 packages/cli/typedoc.json create mode 100644 packages/core/src/actions/checks/isContextFunction.ts create mode 100644 packages/core/src/actions/checks/isWhenContextFunction.ts delete mode 100644 packages/core/src/actions/context/enhancers/crud/utils/syncRelationValue.ts delete mode 100644 packages/core/src/actions/extensions/appendExtension.ts delete mode 100644 packages/core/src/actions/extensions/coreExtensions.ts delete mode 100644 packages/core/src/actions/extensions/crudExtensions.ts delete mode 100644 packages/core/src/actions/extensions/hooksExtensions.ts delete mode 100644 packages/core/src/actions/extensions/readExtensions.ts delete mode 100644 packages/core/src/actions/extensions/writeExtensions.ts delete mode 100644 packages/core/src/actions/makeActionClass.ts create mode 100644 packages/core/src/actions/makeContextFunctionFactory.ts create mode 100644 packages/core/src/actions/makeEnhancer.ts create mode 100644 packages/core/src/actions/makeRunner.ts delete mode 100644 packages/core/src/blueprints/makeCache.ts delete mode 100644 packages/core/src/blueprints/makeRegistry.ts create mode 100644 packages/core/src/cache/makeCache.ts create mode 100644 packages/core/src/cache/makeRefsCache.ts delete mode 100644 packages/core/src/cache/makeRefsCacheWith.ts create mode 100644 packages/core/src/cache/makeTimeoutRefManager.ts rename packages/core/src/cache/{weakRefManager.ts => makeWeakRefManager.ts} (50%) delete mode 100644 packages/core/src/model/checks/isPendingPropDef.ts create mode 100644 packages/core/src/model/checks/isPropFactory.ts create mode 100644 packages/core/src/model/checks/isValuePropDef.ts create mode 100644 packages/core/src/model/checks/isValuePropDefOfType.ts create mode 100644 packages/core/src/model/props/builders/makeBuilderPropFactory.ts delete mode 100644 packages/core/src/model/props/builders/makePendingProp.ts create mode 100644 packages/core/src/model/props/builders/makePropFactory.ts create mode 100644 packages/core/src/model/props/builders/makeValuePropFactory.ts create mode 100644 packages/core/src/registry/makeMapRegistry.ts delete mode 100644 packages/core/src/registry/makeMapRegistryWith.ts create mode 100644 packages/core/src/registry/makeRegistry.ts delete mode 100644 packages/core/tests/mocks/makeFakeAction.mock.ts delete mode 100644 packages/core/tests/unit/actions/makeActionClass.test.ts create mode 100644 packages/core/tests/unit/actions/makeActionFactory.test.ts rename packages/core/tests/unit/cache/{makeRefsCacheWith.test.ts => makeRefsCache.test.ts} (91%) create mode 100644 packages/core/tests/unit/cache/makeTimeoutRefManager.test.ts rename packages/core/tests/unit/cache/{weakRefManager.test.ts => makeWeakRefManager.test.ts} (50%) create mode 100644 packages/core/tests/unit/registry/makeMapRegistry.test.ts delete mode 100644 packages/core/tests/unit/registry/makeMapRegistryWith.test.ts delete mode 100644 packages/http/src/blueprints/makeHttpAdapter.ts delete mode 100644 packages/http/src/httpExtensions.ts rename packages/http/src/{makeHttpAdapterWith.ts => makeHttpAdapter.ts} (93%) delete mode 100644 packages/http/src/utilities/paramsSerializer.ts create mode 100644 packages/http/tests/unit/utilities/clearEndpoint.test.ts create mode 100644 packages/http/tests/unit/utilities/deepParamsSerializer.test.ts delete mode 100644 packages/jsonapi/src/blueprints/makeJsonApiAdapter.ts delete mode 100644 packages/jsonapi/src/blueprints/makeJsonApiDeserializer.ts delete mode 100644 packages/jsonapi/src/blueprints/makeJsonApiSerializer.ts delete mode 100644 packages/jsonapi/src/jsonApiExtensions.ts delete mode 100644 packages/jsonapi/src/jsonApiStarterExtensions.ts create mode 100644 packages/jsonapi/src/makeJsonApiAdapter.ts create mode 100644 packages/jsonapi/src/makeJsonApiDeserializer.ts create mode 100644 packages/jsonapi/src/makeJsonApiSerializer.ts delete mode 100644 packages/rest/src/blueprints/makeJsonRestAdapter.ts delete mode 100644 packages/rest/src/blueprints/makeJsonRestDeserializer.ts delete mode 100644 packages/rest/src/blueprints/makeJsonRestSerializer.ts delete mode 100644 packages/rest/src/jsonRestExtensions.ts delete mode 100644 packages/rest/src/jsonRestStarterExtensions.ts create mode 100644 packages/rest/src/makeRestAdapter.ts delete mode 100644 packages/rest/src/makeRestAdapterWith.ts create mode 100644 packages/rest/src/makeRestDeserializer.ts create mode 100644 packages/rest/src/makeRestSerializer.ts rename packages/rest/tests/mocks/{makeJsonRestAction.mock.ts => makeRestAction.mock.ts} (59%) rename packages/rest/tests/unit/{makeJsonRestDeserializer.test.ts => makeRestDeserializer.test.ts} (93%) rename packages/rest/tests/unit/{makeJsonRestSerializer.test.ts => makeRestSerializer.test.ts} (96%) rename packages/serialization/src/{makeDeserializerWith.ts => makeDeserializer.ts} (97%) rename packages/serialization/src/{makeSerializerWith.ts => makeSerializer.ts} (95%) rename packages/serialization/tests/unit/{makeSerializerWith.test.ts => makeSerializer.test.ts} (87%) create mode 100644 packages/test/src/fosciaTestError.ts create mode 100644 packages/test/src/makeActionMock.ts delete mode 100644 packages/test/src/makeActionMockedHistoryItem.ts delete mode 100644 packages/test/src/makeActionMockedRun.ts create mode 100644 packages/test/src/makeActionTestContext.ts create mode 100644 packages/test/src/unexpectedActionError.ts delete mode 100644 packages/test/src/unexpectedMockedRunError.ts create mode 100644 website/docs/.gitignore create mode 100644 website/docs/digging-deeper/implementations/_category_.json create mode 100644 website/docs/digging-deeper/implementations/core.md rename website/docs/{reference => digging-deeper}/implementations/http.md (97%) rename website/docs/{reference => digging-deeper}/implementations/jsonapi.md (88%) create mode 100644 website/docs/digging-deeper/implementations/presentation.md rename website/docs/{reference => digging-deeper}/implementations/rest.md (69%) rename website/docs/{reference => digging-deeper}/implementations/serialization.md (100%) delete mode 100644 website/docs/digging-deeper/testing.mdx create mode 100644 website/docs/digging-deeper/usages/_category_.json rename website/docs/digging-deeper/{ => usages}/cli.mdx (99%) rename website/docs/digging-deeper/{ => usages}/http.mdx (65%) rename website/docs/digging-deeper/{ => usages}/jsonapi.md (77%) rename website/docs/digging-deeper/{ => usages}/rest.md (70%) create mode 100644 website/docs/digging-deeper/usages/testing.mdx delete mode 100644 website/docs/reference/actions-enhancers.mdx delete mode 100644 website/docs/reference/actions-extensions.md delete mode 100644 website/docs/reference/actions-runners.mdx delete mode 100644 website/docs/reference/implementations/_category_.json delete mode 100644 website/docs/reference/implementations/core.md delete mode 100644 website/docs/reference/implementations/presentation.md delete mode 100644 website/docs/reference/models-utilities.mdx create mode 100644 website/docs/upgrade/support-policy.md delete mode 100644 website/src/components/Chip/styles.module.css create mode 100644 website/src/icons/flask.svg create mode 100644 website/src/icons/lock.svg create mode 100644 website/typedoc-plugin.mjs 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/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/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 index 43543f8d..50408842 100644 --- a/packages/core/src/actions/actionVariadicRun.ts +++ b/packages/core/src/actions/actionVariadicRun.ts @@ -3,20 +3,25 @@ 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>; +/** + * Variadic action run methods type. + * + * @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>; }; diff --git a/packages/core/src/actions/actionVariadicUse.ts b/packages/core/src/actions/actionVariadicUse.ts index 80471ebc..622332e0 100644 --- a/packages/core/src/actions/actionVariadicUse.ts +++ b/packages/core/src/actions/actionVariadicUse.ts @@ -3,20 +3,25 @@ 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; +/** + * Variadic action use methods type. + * + * @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; }; 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/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..a81a424b 100644 --- a/packages/core/src/actions/context/enhancers/crud/associate.ts +++ b/packages/core/src/actions/context/enhancers/crud/associate.ts @@ -1,66 +1,54 @@ -import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; -import syncRelationValue from '@foscia/core/actions/context/enhancers/crud/utils/syncRelationValue'; +import updateRelation, { + UpdateRelationValue, +} from '@foscia/core/actions/context/enhancers/crud/updateRelation'; 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, + InferModelSchemaProp, ModelInstance, ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, + ModelWritableValues, } 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 string, + R extends InferModelSchemaProp, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue, -) => (action: Action, E>) => action.use( + instance: I, + relation: K & ModelRelationKey, + value: UpdateRelationValue, +) => (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 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..00248752 100644 --- a/packages/core/src/actions/context/enhancers/crud/attach.ts +++ b/packages/core/src/actions/context/enhancers/crud/attach.ts @@ -1,64 +1,49 @@ 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 updateRelation, { + UpdateRelationValue, +} from '@foscia/core/actions/context/enhancers/crud/updateRelation'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - Action, - ConsumeId, - ConsumeModel, - ConsumeRelation, - ConsumeSerializer, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { - Model, - ModelClassInstance, - ModelInferPropValue, + InferModelSchemaProp, ModelInstance, + ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, } from '@foscia/core/model/types'; -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 string, + R extends InferModelSchemaProp, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], -) => updateRelation( + instance: I, + relation: K & ModelRelationKey, + value: UpdateRelationValue, +) => updateRelation( instance, relation, value, 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..fd4de74b 100644 --- a/packages/core/src/actions/context/enhancers/crud/create.ts +++ b/packages/core/src/actions/context/enhancers/crud/create.ts @@ -4,7 +4,7 @@ import instanceData from '@foscia/core/actions/context/enhancers/crud/instanceDa 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 makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, ConsumeId, @@ -13,73 +13,37 @@ 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>) => 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)), @@ -89,41 +53,54 @@ const create: { 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< +)) 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..8f3f0a94 100644 --- a/packages/core/src/actions/context/enhancers/crud/destroy.ts +++ b/packages/core/src/actions/context/enhancers/crud/destroy.ts @@ -3,17 +3,11 @@ 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 { - Action, - ConsumeId, - ConsumeInstance, - ConsumeModel, - WithParsedExtension, -} from '@foscia/core/actions/types'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action } 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 { ModelInstance } from '@foscia/core/model/types'; /** * Prepare context for an instance deletion. @@ -21,14 +15,20 @@ import { Model, ModelClassInstance, ModelInstance } from '@foscia/core/model/typ * @param instance * * @category Enhancers + * @provideContext model, instance, id + * + * @example + * ```typescript + * import { destroy, none } from '@foscia/core'; + * + * await action().run(destroy(post), none()); + * ``` */ -const destroy = < +export default /* @__PURE__ */ makeEnhancer('destroy', < C extends {}, - E extends {}, - D extends {}, - I extends ModelInstance, ->(instance: ModelClassInstance & I) => (action: Action) => action.use( - query(instance), + I extends ModelInstance, +>(instance: I) => (action: Action) => action.use( + query(instance), context({ action: ActionName.DESTROY, // Rewrite ID to ensure destroy targets the record termination point @@ -42,15 +42,4 @@ const destroy = < 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>; -}>; +)); diff --git a/packages/core/src/actions/context/enhancers/crud/detach.ts b/packages/core/src/actions/context/enhancers/crud/detach.ts index cea0b810..9416d0e5 100644 --- a/packages/core/src/actions/context/enhancers/crud/detach.ts +++ b/packages/core/src/actions/context/enhancers/crud/detach.ts @@ -1,64 +1,49 @@ 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 updateRelation, { + UpdateRelationValue, +} from '@foscia/core/actions/context/enhancers/crud/updateRelation'; +import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - Action, - ConsumeId, - ConsumeModel, - ConsumeRelation, - ConsumeSerializer, - WithParsedExtension, -} from '@foscia/core/actions/types'; -import { - Model, - ModelClassInstance, - ModelInferPropValue, + InferModelSchemaProp, ModelInstance, + ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, } from '@foscia/core/model/types'; -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 string, + R extends InferModelSchemaProp, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], -) => updateRelation( + instance: I, + relation: K & ModelRelationKey, + value: UpdateRelationValue, +) => updateRelation( instance, relation, value, 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..731294ce 100644 --- a/packages/core/src/actions/context/enhancers/crud/dissociate.ts +++ b/packages/core/src/actions/context/enhancers/crud/dissociate.ts @@ -1,55 +1,43 @@ import associate from '@foscia/core/actions/context/enhancers/crud/associate'; -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, + InferModelSchemaProp, ModelInstance, + ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, } 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 string, + R extends InferModelSchemaProp, 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 & ModelRelationKey, +) => 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..f20c7949 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 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; -}>; +}))); diff --git a/packages/core/src/actions/context/enhancers/crud/relationData.ts b/packages/core/src/actions/context/enhancers/crud/relationData.ts index dfd81bff..1ede4582 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 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( + action, + 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..04407ceb 100644 --- a/packages/core/src/actions/context/enhancers/crud/update.ts +++ b/packages/core/src/actions/context/enhancers/crud/update.ts @@ -4,18 +4,11 @@ import instanceData from '@foscia/core/actions/context/enhancers/crud/instanceDa 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 makeEnhancer from '@foscia/core/actions/makeEnhancer'; +import { Action, ConsumeSerializer } 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 { ModelInstance } from '@foscia/core/model/types'; /** * Prepare context for an instance update. @@ -23,21 +16,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: Action>, ) => action.use( - query, E, D, I>(instance), + query(instance), context({ action: ActionName.UPDATE, // Rewrite ID to ensure update targets the record termination point @@ -52,23 +52,4 @@ const update = < 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>; -}>; +)); diff --git a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts index f0efc960..1586fc60 100644 --- a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts +++ b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts @@ -2,80 +2,79 @@ 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 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, + InferModelValuePropType, ModelInstance, + ModelRelation, ModelRelationKey, - ModelSchema, - ModelSchemaRelations, } from '@foscia/core/model/types'; import { wrap } from '@foscia/shared'; -type UpdateRelationActionName = +export type UpdateRelationActionName = | ActionName.UPDATE_RELATION | ActionName.ATTACH_RELATION | ActionName.DETACH_RELATION; -const updateRelation = < +export type UpdateRelationValue = R extends ModelRelation + ? NonNullable extends any[] ? T | NonNullable[number] : T : never; + +/** + * 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 string, + R extends InferModelSchemaProp, Record, Related, Data, >( - instance: ModelClassInstance & I, - relation: ModelRelationKey & K, - value: ModelInferPropValue | NonNullable>[number], + instance: I, + relation: K & ModelRelationKey, + value: UpdateRelationValue, actionName: UpdateRelationActionName = ActionName.UPDATE_RELATION, -) => async (action: Action, E>) => { - const wrappedValue = isSingularRelationDef(instance.$model.$schema[relation] as RD[K]) +) => async (action: Action>) => { + const wrappedValue = isSingularRelationDef(instance.$model.$schema[relation] as R) ? value : wrap(value); return action.use( query(instance, relation), context({ action: actionName, - data: await serializeRelation(action, instance, relation, wrappedValue), + data: await serializeRelation( + action, + instance, + relation, + wrappedValue as InferModelValuePropType, + ), }), - ) 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>; -}>; + ) 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..48b2e1eb 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,18 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * + * @example + * ```typescript + * import { onError } from '@foscia/core'; + * + * action().use(onError((event) => { + * console.log(event.context, event.error); + * })); + * ``` */ -const onError = ( +export default /* @__PURE__ */ makeEnhancer('onError', ( callback: (event: { context: C; 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..5787e366 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,18 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * + * @example + * ```typescript + * import { onFinally } from '@foscia/core'; + * + * action().use(onFinally((event) => { + * console.log(event.context); + * })); + * ``` */ -const onFinally = ( +export default /* @__PURE__ */ makeEnhancer('onFinally', ( callback: (event: { context: C; }) => 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..07dec5e0 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,18 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * + * @example + * ```typescript + * import { onRunning } from '@foscia/core'; + * + * action().use(onRunning((event) => { + * console.log(event.context, event.runner); + * })); + * ``` */ -const onRunning = ( +export default /* @__PURE__ */ makeEnhancer('onRunning', ( callback: (event: { context: C; 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..7807d6be 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,18 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * + * @example + * ```typescript + * import { onSuccess } from '@foscia/core'; + * + * action().use(onSuccess((event) => { + * console.log(event.context, event.result); + * })); + * ``` */ -const onSuccess = ( +export default /* @__PURE__ */ makeEnhancer('onSuccess', ( callback: (event: { context: C; 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/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 index fc0029be..914a0ee6 100644 --- a/packages/core/src/actions/context/enhancers/queryAs.ts +++ b/packages/core/src/actions/context/enhancers/queryAs.ts @@ -1,34 +1,39 @@ import context from '@foscia/core/actions/context/enhancers/context'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; -import { Action, ConsumeQueryAs, WithParsedExtension } from '@foscia/core/actions/types'; +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. This will keep the context state when - * executing the query, but will take priority when deserializing models, + * 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. - * - * @param models - * - * @category Enhancers */ -const queryAs = ( +export default /* @__PURE__ */ makeEnhancer('queryAs', ( ...models: ArrayableVariadic -) => context({ queryAs: wrapVariadic(...models) }); - -export default /* @__PURE__ */ appendExtension( - 'queryAs', - queryAs, - 'use', -) as WithParsedExtension( - this: Action, - ...models: ArrayableVariadic - ): Action, E>; -}>; +) => async ( + action: Action, +) => action.use(context({ queryAs: wrapVariadic(...models) }))); diff --git a/packages/core/src/actions/context/runners/all.ts b/packages/core/src/actions/context/runners/all.ts index 25bc617a..3546a671 100644 --- a/packages/core/src/actions/context/runners/all.ts +++ b/packages/core/src/actions/context/runners/all.ts @@ -3,13 +3,12 @@ import deserializeInstances, { } from '@foscia/core/actions/context/utils/deserializeInstances'; import executeContextThroughAdapter from '@foscia/core/actions/context/utils/executeContextThroughAdapter'; -import appendExtension from '@foscia/core/actions/extensions/appendExtension'; +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'; @@ -31,10 +30,18 @@ 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, @@ -59,25 +66,4 @@ const all = < ? 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..2efc762a 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,12 +9,12 @@ 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'; export type CachedData = { @@ -24,25 +24,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); @@ -66,23 +72,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..40a9232a 100644 --- a/packages/core/src/actions/context/runners/catchIf.ts +++ b/packages/core/src/actions/context/runners/catchIf.ts @@ -1,14 +1,14 @@ -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 = ( +export type CatchCallback = ( error: unknown, -) => Awaitable> | boolean>; +) => Awaitable> | boolean>; /** * Run given runner and catch errors using catchCallback. - * If catchCallback is omitted, will return null on error. + * If catchCallback is omitted, it will return null on any 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. * @@ -16,11 +16,21 @@ export type CatchCallback = ( * @param catchCallback * * @category Runners + * + * @example + * ```typescript + * import { catchIf, one, query } from '@foscia/core'; + * + * const postOrNull = await action().run( + * query(Post, '123'), + * catchIf(one(), (error) => error instanceof ErrorToCatch), + * ); + * ``` */ -const catchIf = ( - runner: ContextRunner>, - catchCallback?: CatchCallback, -) => async (action: Action): Promise => { +export default makeRunner('catchIf', ( + runner: ContextRunner>, + catchCallback?: CatchCallback, +) => async (action: Action): Promise => { try { return await action.run(runner); } catch (error) { @@ -35,16 +45,4 @@ const catchIf = ( return null as any; } -}; - -export default /* @__PURE__ */ appendExtension( - 'catchIf', - catchIf, - 'run', -) as WithParsedExtension( - this: Action, - runner: ContextRunner>, - catchCallback?: CatchCallback, - ): Promise | Awaited>; -}>; +}); 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..651dfe39 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 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; -}>; +) => 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..e2bee1a0 100644 --- a/packages/core/src/actions/context/runners/oneOr.ts +++ b/packages/core/src/actions/context/runners/oneOr.ts @@ -1,13 +1,12 @@ 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 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 { ModelInstance } from '@foscia/core/model/types'; @@ -29,11 +28,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 +47,11 @@ const oneOr = < Next = I, >( // eslint-disable-next-line max-len - nilRunner: ContextRunner & ConsumeDeserializer, Deserialized>, E, 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) => { @@ -66,27 +72,4 @@ const oneOr = < } 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..f170849c 100644 --- a/packages/core/src/actions/context/runners/oneOrCurrent.ts +++ b/packages/core/src/actions/context/runners/oneOrCurrent.ts @@ -1,15 +1,8 @@ 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'; +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,10 +12,18 @@ 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, @@ -30,27 +31,7 @@ const oneOrCurrent = < Next = CI, >( transform?: (data: OneData, I>) => Awaitable, -) => oneOr, any, I, RawData, Data, Deserialized, CI, Next>( +) => 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..63a2427f 100644 --- a/packages/core/src/actions/context/runners/oneOrFail.ts +++ b/packages/core/src/actions/context/runners/oneOrFail.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 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 +11,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(() => { +) => 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..d367ae47 100644 --- a/packages/core/src/actions/context/runners/raw.ts +++ b/packages/core/src/actions/context/runners/raw.ts @@ -1,15 +1,23 @@ 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 makeRunner from '@foscia/core/actions/makeRunner'; +import { Action, ConsumeAdapter } from '@foscia/core/actions/types'; import { Awaitable } 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( @@ -17,15 +25,4 @@ const raw = ( ); 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; -}>; +}); diff --git a/packages/core/src/actions/context/utils/serializeRelation.ts b/packages/core/src/actions/context/utils/serializeRelation.ts index 5c3a54c6..b2038880 100644 --- a/packages/core/src/actions/context/utils/serializeRelation.ts +++ b/packages/core/src/actions/context/utils/serializeRelation.ts @@ -1,18 +1,32 @@ 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 { + InferModelSchemaProp, + InferModelValuePropType, + ModelInstance, + ModelRelation, + ModelRelationKey, +} from '@foscia/core/model/types'; import { Arrayable } from '@foscia/shared'; -export default ( +export default < + C extends {}, + I extends ModelInstance, + K extends string, + R extends InferModelSchemaProp, + Record, + Related, + Data, +>( action: Action>, - instance: ModelInstance, - relation: string, - value: Arrayable | null, + instance: I, + relation: K & ModelRelationKey, + value: InferModelValuePropType, ) => serializeWith(action, async (serializer, context) => serializer.serialize( await serializer.serializeRelation( instance, - instance.$model.$schema[relation], - value, + instance.$model.$schema[relation] as R, + value as Arrayable | null, 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 97187ebf..00000000 --- a/packages/core/src/actions/extensions/coreExtensions.ts +++ /dev/null @@ -1,15 +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 queryAs from '@foscia/core/actions/context/enhancers/queryAs'; -import catchIf from '@foscia/core/actions/context/runners/catchIf'; -import when from '@foscia/core/actions/when'; - -export default () => ({ - ...query.extension(), - ...queryAs.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 57e37e53..97b37f6d 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -1,7 +1,7 @@ import ActionName from '@foscia/core/actions/actionName'; +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 consumeQueryAs from '@foscia/core/actions/context/consumers/consumeQueryAs'; import consumeCache from '@foscia/core/actions/context/consumers/consumeCache'; import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; import consumeData from '@foscia/core/actions/context/consumers/consumeData'; @@ -10,6 +10,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'; @@ -44,14 +45,10 @@ import oneOr, { OneData } 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 isContextFunction from '@foscia/core/actions/checks/isContextFunction'; 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 { @@ -108,13 +105,10 @@ export { 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..e53fa03a 100644 --- a/packages/core/src/actions/makeActionFactory.ts +++ b/packages/core/src/actions/makeActionFactory.ts @@ -1,12 +1,110 @@ -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, 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, sequentialTransform, value } from '@foscia/shared'; -export default ( - initialContext?: Context, - extensions?: Extensions, -) => { - const ActionClass = makeActionClass(extensions); +/** + * Create an action factory. + * + * @param initialContext + * + * @category Factories + */ +export default ( + initialContext?: Context | (() => Context), +) => () => { + 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: 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 context = await this.useContext(); + + await runHooks(this, 'running', { context, 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 withoutHooks(this, async () => this.track(runner)); + + 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', { context, result }); + + return result; + } catch (error) { + await runHooks(this, 'error', { context, error }); + + throw error; + } finally { + await runHooks(this, 'finally', { context }); + } + }, + 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 action; }; diff --git a/packages/core/src/actions/makeContextFunctionFactory.ts b/packages/core/src/actions/makeContextFunctionFactory.ts new file mode 100644 index 00000000..315903fa --- /dev/null +++ b/packages/core/src/actions/makeContextFunctionFactory.ts @@ -0,0 +1,28 @@ +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); + +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..6de7396d --- /dev/null +++ b/packages/core/src/actions/makeEnhancer.ts @@ -0,0 +1,20 @@ +import makeContextFunctionFactory from '@foscia/core/actions/makeContextFunctionFactory'; +import { ContextEnhancer } from '@foscia/core/actions/types'; +import { SYMBOL_ACTION_CONTEXT_ENHANCER } from '@foscia/core/symbols'; + +type ContextEnhancerFactory = + | ((...args: any[]) => ContextEnhancer) + | ((...args: never[]) => ContextEnhancer); + +/** + * Make a context enhancer factory with incorporated metadata (name, arguments). + * + * @param name + * @param factory + * + * @category Factories + */ +export default (( + 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..02cd9279 --- /dev/null +++ b/packages/core/src/actions/makeRunner.ts @@ -0,0 +1,20 @@ +import makeContextFunctionFactory from '@foscia/core/actions/makeContextFunctionFactory'; +import { ContextRunner } from '@foscia/core/actions/types'; +import { SYMBOL_ACTION_CONTEXT_RUNNER } from '@foscia/core/symbols'; + +type ContextRunnerFactory = + | ((...args: any[]) => ContextRunner) + | ((...args: never[]) => ContextRunner); + +/** + * Make a context runner factory with incorporated metadata (name, arguments). + * + * @param name + * @param factory + * + * @category Factories + */ +export default ( + 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 67748270..6d2c60a7 100644 --- a/packages/core/src/actions/types.ts +++ b/packages/core/src/actions/types.ts @@ -2,13 +2,13 @@ import ActionName from '@foscia/core/actions/actionName'; import { ActionVariadicRun } from '@foscia/core/actions/actionVariadicRun'; import { ActionVariadicUse } from '@foscia/core/actions/actionVariadicUse'; 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, @@ -17,132 +17,325 @@ import { RegistryI, SerializerI, } from '@foscia/core/types'; -import { Awaitable, Constructor, DescriptorHolder } from '@foscia/shared'; +import { Awaitable, Constructor, FosciaObject } from '@foscia/shared'; export * from '@foscia/core/actions/actionVariadicUse'; +export * from '@foscia/core/actions/actionVariadicRun'; +/** + * Action hooks definition. + * + * @internal + */ export type ActionHooksDefinition = { - running: HookCallback<{ context: {}; runner: Function; }>; + enhancing: HookCallback<{ enhancer: ContextEnhancer; depth: number; }>; + running: HookCallback<{ context: {}; runner: ContextRunner; }>; success: HookCallback<{ context: {}; result: unknown; }>; error: HookCallback<{ context: {}; error: unknown; }>; finally: HookCallback<{ context: {}; }>; }; -export type Action = +/** + * Action. + * + * @typeParam Context The current context of the action. + */ +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; - -export type ActionClass = { - extend( - newExtensions?: NewExtension & ThisType>, - ): ActionClass; -} & Constructor>; - -export type ActionFactory = ( + & ActionVariadicUse + & ActionVariadicRun + & Hookable; + +/** + * Action factory. + * + * @internal + */ +export type ActionFactory = ( ...args: Args -) => Action; +) => Action; -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; + +/** + * Enhancer context function. + * + * @internal + */ +export type ContextEnhancerFunction = { + (): ContextEnhancer; + readonly meta: { readonly factory: ContextFunctionFactory; readonly args: any[]; }; +} & FosciaObject; + +/** + * 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; -export type InferConsumedInstance = +/** + * 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: RawModelRelation> } ? I extends ModelInstance ? I : never - : C extends { relation: RawModelRelation } ? 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; -export type InferConsumedModelOrInstance = +/** + * 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: RawModelRelation> } ? I extends ModelInstance ? I : never - : C extends { relation: RawModelRelation } ? I extends ModelInstance ? I : never - : C extends { model: infer M } ? M - : InferConsumedInstance; + : 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[]; }; -export type ResolvableContextDependency = T | (() => Awaitable); +/** + * Context dependency which can be lazy-loaded. + * + * @internal + */ +type ResolvableContextDependency = T | (() => Awaitable); +/** + * Define the cache implementation to use. + * + * @internal + */ export type ConsumeCache = { cache: ResolvableContextDependency; }; +/** + * Define the registry implementation to use. + * + * @internal + */ export type ConsumeRegistry = { registry: ResolvableContextDependency; }; +/** + * Define the adapter implementation to use. + * + * @internal + */ export type ConsumeAdapter = { adapter: ResolvableContextDependency>; }; +/** + * Define the deserializer implementation to use. + * + * @internal + */ export type ConsumeDeserializer = { deserializer: ResolvableContextDependency>; }; +/** + * Define the serializer implementation to use. + * + * @internal + */ export type ConsumeSerializer = { serializer: ResolvableContextDependency>; }; 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..b124b277 --- /dev/null +++ b/packages/core/src/cache/makeCache.ts @@ -0,0 +1,15 @@ +import makeRefsCache from '@foscia/core/cache/makeRefsCache'; +import makeWeakRefManager from '@foscia/core/cache/makeWeakRefManager'; +import { CacheI } from '@foscia/core/types'; +import { toKebabCase } from '@foscia/shared'; + +/** + * Make a default {@link CacheI | `CacheI`} implementation. + * + * @category Factories + */ +export default (): { cache: CacheI; } => makeRefsCache({ + manager: makeWeakRefManager(), + normalizeType: toKebabCase, + 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..79547670 --- /dev/null +++ b/packages/core/src/cache/makeRefsCache.ts @@ -0,0 +1,57 @@ +import { RefsCache, RefsCacheConfig } from '@foscia/core/cache/types'; +import { ModelIdType, ModelInstance } from '@foscia/core/model/types'; +import { makeIdentifiersMap } from '@foscia/shared'; + +/** + * Make a cache interacting with instance references + * through configured {@link RefsManager | `RefsManager`}. + * + * @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), + ); + + const forgetAll = async (type: string) => instances.forgetAll(normalizeType(type)); + + const clear = async () => instances.clear(); + + const find = async (type: string, id: ModelIdType) => { + const ref = instances.find(normalizeType(type), normalizeId(id)); + if (ref) { + const instance = await config.manager.value(ref); + if (instance) { + return instance; + } + + await forget(normalizeType(type), normalizeId(id)); + } + + return null; + }; + + const put = async (type: string, id: ModelIdType, instance: ModelInstance) => instances.put( + normalizeType(type), + normalizeId(id), + await config.manager.ref(instance), + ); + + return { + cache: { + forget, + forgetAll, + clear, + find, + put, + } 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/makeTimeoutRefManager.ts b/packages/core/src/cache/makeTimeoutRefManager.ts new file mode 100644 index 00000000..045eb21a --- /dev/null +++ b/packages/core/src/cache/makeTimeoutRefManager.ts @@ -0,0 +1,47 @@ +import { RefManager, TimeoutRef, TimeoutRefConfig } from '@foscia/core/cache/types'; +import { ModelInstance } from '@foscia/core/model/types'; + +/** + * Create a timeout ref object. + * + * @param instance + * @param config + */ +const makeTimeoutRef = (instance: T, config: TimeoutRefConfig): TimeoutRef => { + let removeTimeout: ReturnType | undefined; + const scheduleRemove = () => { + clearTimeout(removeTimeout); + removeTimeout = setTimeout(() => { + removeTimeout = undefined; + }, config.timeout); + }; + + scheduleRemove(); + + return { + deref: () => { + if (removeTimeout === undefined) { + return undefined; + } + + scheduleRemove(); + + return instance; + }, + }; +}; + +/** + * Make a {@link RefManager | `RefManager`} using + * {@link !setTimeout | `setTimeout`} to retain instance refs. + * + * @param config + * + * @category Factories + * @since 0.13.0 + * @experimental + */ +export default (config: TimeoutRefConfig) => ({ + ref: (instance: ModelInstance) => makeTimeoutRef(instance, config), + value: (ref: TimeoutRef) => ref.deref(), +} as RefManager>); diff --git a/packages/core/src/cache/weakRefManager.ts b/packages/core/src/cache/makeWeakRefManager.ts similarity index 50% rename from packages/core/src/cache/weakRefManager.ts rename to packages/core/src/cache/makeWeakRefManager.ts index ac24961e..bae4d254 100644 --- a/packages/core/src/cache/weakRefManager.ts +++ b/packages/core/src/cache/makeWeakRefManager.ts @@ -1,7 +1,14 @@ import { RefManager } from '@foscia/core/cache/types'; import { ModelInstance } from '@foscia/core/model/types'; -export default { +/** + * Make a {@link RefManager | `RefManager`} using + * {@link !WeakRef | `WeakRef`} to retain instance refs. + * + * @category Factories + * @since 0.13.0 + */ +export default () => ({ ref: (instance: ModelInstance) => new WeakRef(instance), value: (ref: WeakRef) => ref.deref(), -} as RefManager>; +} as RefManager>); diff --git a/packages/core/src/cache/types.ts b/packages/core/src/cache/types.ts index 957b0c4e..493069f8 100644 --- a/packages/core/src/cache/types.ts +++ b/packages/core/src/cache/types.ts @@ -2,15 +2,65 @@ import { ModelIdType, ModelInstance } from '@foscia/core/model/types'; import { CacheI } from '@foscia/core/types'; import { Awaitable, Optional, Transformer } from '@foscia/shared'; +/** + * Config for the timeout ref manager. + * + * @internal + */ +export type TimeoutRefConfig = { + timeout: number; +}; + +/** + * Timeout ref object. + * + * @internal + */ +export type TimeoutRef = { deref: () => T | undefined; }; + +/** + * Reference manager to retain cached instances in cache. + * + * @internal + */ export type RefManager = { + /** + * Create a ref to an instance. + * + * @param instance + */ ref(instance: ModelInstance): Awaitable; + /** + * Retrieve an instance from a ref. If ref expired, it can return `undefined`. + * + * @param ref + */ value(ref: R): Awaitable; }; +/** + * Config for refs cache implementation. + * + * @internal + */ export type RefsCacheConfig = { + /** + * Manager to use to create and resolve reference to instances. + */ manager: RefManager; + /** + * 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. + * + * @internal + */ +export type RefsCache = CacheI; 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 index 65122fb0..ccc184dd 100644 --- a/packages/core/src/errors/flags/isNotFoundError.ts +++ b/packages/core/src/errors/flags/isNotFoundError.ts @@ -1,5 +1,11 @@ import { NotFoundErrorI } from '@foscia/core/errors/flags/types'; +/** + * Check if an error should be interpreted by Foscia as a "Record not found" + * error. + * + * @param error + */ export default (error: unknown): error is NotFoundErrorI => !!error && typeof error === 'object' && '$FOSCIA_ERROR_NOT_FOUND' in error 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/hooks/registerHook.ts b/packages/core/src/hooks/registerHook.ts index 741c93a9..5ab7eb43 100644 --- a/packages/core/src/hooks/registerHook.ts +++ b/packages/core/src/hooks/registerHook.ts @@ -2,6 +2,16 @@ import { Hookable, HooksDefinition } from '@foscia/core/hooks/types'; import unregisterHook from '@foscia/core/hooks/unregisterHook'; +/** + * Register a hook on a hookable object. + * Return value is a function which unregister the hook. + * + * @param hookable + * @param key + * @param callback + * + * @category Hooks + */ export default ( hookable: Hookable, key: K, diff --git a/packages/core/src/hooks/runHooks.ts b/packages/core/src/hooks/runHooks.ts index 2be2ea39..84097c62 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 + * + * @category Hooks */ export default ( hookable: Hookable, diff --git a/packages/core/src/hooks/runHooksSync.ts b/packages/core/src/hooks/runHooksSync.ts index 311d5565..25fa66c3 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 + * + * @category Hooks */ 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..f115baf9 100644 --- a/packages/core/src/hooks/withoutHooks.ts +++ b/packages/core/src/hooks/withoutHooks.ts @@ -1,6 +1,14 @@ /* 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, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 42dee1d4..7406dd2e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,8 +1,8 @@ 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 makeCache from '@foscia/core/cache/makeCache'; +import makeRefsCache from '@foscia/core/cache/makeRefsCache'; +import makeTimeoutRefManager from '@foscia/core/cache/makeTimeoutRefManager'; +import makeWeakRefManager from '@foscia/core/cache/makeWeakRefManager'; import AdapterError from '@foscia/core/errors/adapterError'; import DeserializerError from '@foscia/core/errors/deserializerError'; import ExpectedRunFailureError from '@foscia/core/errors/expectedRunFailureError'; @@ -20,12 +20,12 @@ 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 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,7 +42,6 @@ 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'; @@ -53,15 +52,20 @@ 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 guessRelationType from '@foscia/core/model/relations/utilities/guessRelationType'; import makeModelsReducer from '@foscia/core/model/revivers/makeModelsReducer'; import makeModelsReviver from '@foscia/core/model/revivers/makeModelsReviver'; @@ -73,14 +77,21 @@ import restoreSnapshot from '@foscia/core/model/snapshots/restoreSnapshot'; import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; 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_RELATION_HAS_MANY, SYMBOL_MODEL_RELATION_HAS_ONE, } from '@foscia/core/symbols'; @@ -113,10 +124,11 @@ export { ExpectedRunFailureError, isNotFoundError, makeRegistry, - makeMapRegistryWith, + makeMapRegistry, makeCache, - makeRefsCacheWith, - weakRefManager, + makeRefsCache, + makeWeakRefManager, + makeTimeoutRefManager, attr, hasMany, hasOne, @@ -133,8 +145,11 @@ export { makeModel, makeModelFactory, makeQueryModelLoader, + QueryModelLoaderOptions, makeQueryModelLoaderExtractor, + QueryRelationLoaderOptions, makeQueryRelationLoader, + RefreshIncludeLoaderOptions, makeRefreshIncludeLoader, toArrayOf, toBoolean, @@ -175,11 +190,9 @@ export { isInstance, isModelUsing, isInstanceUsing, - isPendingPropDef, mapIds, mapAttributes, mapRelations, - mapProps, shouldSync, guessRelationType, normalizeDotRelations, @@ -188,12 +201,18 @@ export { makeModelsReducer, makeModelsReviver, logger, - SYMBOL_MODEL_PROP_PENDING, - SYMBOL_MODEL_PROP_ID, - SYMBOL_MODEL_PROP_ATTRIBUTE, - SYMBOL_MODEL_PROP_RELATION, + SYMBOL_MODEL_PROP_FACTORY, + 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_ACTION_CONTEXT_WHEN, + SYMBOL_ACTION_CONTEXT_ENHANCER, + SYMBOL_ACTION_CONTEXT_RUNNER, + SYMBOL_ACTION_CONTEXT_FUNCTION_FACTORY, }; diff --git a/packages/core/src/model/checks/isAttributeDef.ts b/packages/core/src/model/checks/isAttributeDef.ts index 21590cb8..02da6ef6 100644 --- a/packages/core/src/model/checks/isAttributeDef.ts +++ b/packages/core/src/model/checks/isAttributeDef.ts @@ -1,7 +1,7 @@ +import isValuePropDefOfType from '@foscia/core/model/checks/isValuePropDefOfType'; 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'; export default ( value: unknown, -): value is ModelAttribute => isFosciaType(value, SYMBOL_MODEL_PROP_ATTRIBUTE); +): value is ModelAttribute => isValuePropDefOfType(value, SYMBOL_MODEL_PROP_KIND_ATTRIBUTE); diff --git a/packages/core/src/model/checks/isIdDef.ts b/packages/core/src/model/checks/isIdDef.ts index 1dae5f89..1ddd57c5 100644 --- a/packages/core/src/model/checks/isIdDef.ts +++ b/packages/core/src/model/checks/isIdDef.ts @@ -1,7 +1,7 @@ +import isValuePropDefOfType from '@foscia/core/model/checks/isValuePropDefOfType'; 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'; export default ( value: unknown, -): value is ModelId => isFosciaType(value, SYMBOL_MODEL_PROP_ID); +): value is ModelId => isValuePropDefOfType(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..7dad92ea 100644 --- a/packages/core/src/model/checks/isInstanceUsing.ts +++ b/packages/core/src/model/checks/isInstanceUsing.ts @@ -2,6 +2,23 @@ import isInstance from '@foscia/core/model/checks/isInstance'; import isModelUsing from '@foscia/core/model/checks/isModelUsing'; import { ModelComposable, ModelInstanceUsing } 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(myRecord, publishable)) { + * myRecord.publishedAt = new Date(); + * } + * ``` + */ export default ( value: unknown, composable: C, 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..73214634 100644 --- a/packages/core/src/model/checks/isModelUsing.ts +++ b/packages/core/src/model/checks/isModelUsing.ts @@ -1,6 +1,14 @@ import isModel from '@foscia/core/model/checks/isModel'; import { ModelComposable, ModelUsing } from '@foscia/core/model/types'; +/** + * Check if value is a model using given composable. + * + * @param value + * @param composable + * + * @category Utilities + */ export default ( value: unknown, composable: C, 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/isPropDef.ts b/packages/core/src/model/checks/isPropDef.ts index 999b4c4a..ef1af1fe 100644 --- a/packages/core/src/model/checks/isPropDef.ts +++ b/packages/core/src/model/checks/isPropDef.ts @@ -5,6 +5,6 @@ import { ModelAttribute, ModelId, ModelRelation } from '@foscia/core/model/types export default ( def: unknown, -): def is ModelId | ModelAttribute | ModelRelation => isIdDef(def) +): def is ModelId | ModelAttribute | ModelRelation => isIdDef(def) || isAttributeDef(def) || isRelationDef(def); diff --git a/packages/core/src/model/checks/isPropFactory.ts b/packages/core/src/model/checks/isPropFactory.ts new file mode 100644 index 00000000..71b604d2 --- /dev/null +++ b/packages/core/src/model/checks/isPropFactory.ts @@ -0,0 +1,7 @@ +import { ModelPropFactory } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP_FACTORY } from '@foscia/core/symbols'; +import { isFosciaType } from '@foscia/shared'; + +export default ( + value: unknown, +): value is ModelPropFactory => isFosciaType(value, SYMBOL_MODEL_PROP_FACTORY); diff --git a/packages/core/src/model/checks/isRelationDef.ts b/packages/core/src/model/checks/isRelationDef.ts index 736d0e33..09ec7387 100644 --- a/packages/core/src/model/checks/isRelationDef.ts +++ b/packages/core/src/model/checks/isRelationDef.ts @@ -1,7 +1,7 @@ +import isValuePropDefOfType from '@foscia/core/model/checks/isValuePropDefOfType'; 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'; export default ( value: unknown, -): value is ModelRelation => isFosciaType(value, SYMBOL_MODEL_PROP_RELATION); +): value is ModelRelation => isValuePropDefOfType(value, SYMBOL_MODEL_PROP_KIND_RELATION); diff --git a/packages/core/src/model/checks/isValuePropDef.ts b/packages/core/src/model/checks/isValuePropDef.ts new file mode 100644 index 00000000..999b4c4a --- /dev/null +++ b/packages/core/src/model/checks/isValuePropDef.ts @@ -0,0 +1,10 @@ +import isAttributeDef from '@foscia/core/model/checks/isAttributeDef'; +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'; + +export default ( + def: unknown, +): def is ModelId | ModelAttribute | ModelRelation => isIdDef(def) + || isAttributeDef(def) + || isRelationDef(def); diff --git a/packages/core/src/model/checks/isValuePropDefOfType.ts b/packages/core/src/model/checks/isValuePropDefOfType.ts new file mode 100644 index 00000000..37da9c9b --- /dev/null +++ b/packages/core/src/model/checks/isValuePropDefOfType.ts @@ -0,0 +1,10 @@ +import { ModelAttribute, ModelId, ModelRelation } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP } from '@foscia/core/symbols'; +import { isFosciaType } from '@foscia/shared'; + +export default

| ModelAttribute | ModelRelation>( + def: unknown, + propType: P['$VALUE_PROP_TYPE'], +): def is P => isFosciaType(def, SYMBOL_MODEL_PROP) + && '$VALUE_PROP_TYPE' in def + && def.$VALUE_PROP_TYPE === propType; diff --git a/packages/core/src/model/fill.ts b/packages/core/src/model/fill.ts index 207370fc..86569e6c 100644 --- a/packages/core/src/model/fill.ts +++ b/packages/core/src/model/fill.ts @@ -1,11 +1,26 @@ -/* eslint-disable no-param-reassign */ import { ModelInstance, ModelKey, ModelWritableValues } from '@foscia/core/model/types'; +/** + * 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>, ) => { Object.entries(values).forEach(([key, value]) => { + // eslint-disable-next-line no-param-reassign instance[key as ModelKey] = value as any; }); 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..bde190c4 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); } finally { - instance.$model.$config.strictReadonly = strictReadonly; + instance.$model.$config.strictReadOnly = strictReadOnly; } }; 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/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 index fac0ef3c..4fbd44bc 100644 --- a/packages/core/src/model/makeComposable.ts +++ b/packages/core/src/model/makeComposable.ts @@ -11,11 +11,22 @@ import { SYMBOL_MODEL_COMPOSABLE } from '@foscia/core/symbols'; * Create a composable definition which will be used by a model factory. * * @param rawDefinition + * + * @category Factories + * + * @example + * ```typescript + * import { makeComposable } from '@foscia/core'; + * + * export default makeComposable({ + * // Definition... + * }); + * ``` */ export default ( rawDefinition?: D & ThisType>>>, ) => ({ $FOSCIA_TYPE: SYMBOL_MODEL_COMPOSABLE, - $definition: makeDefinition(rawDefinition), + def: makeDefinition(rawDefinition), $hooks: {}, } as ModelComposable>); diff --git a/packages/core/src/model/makeDefinition.ts b/packages/core/src/model/makeDefinition.ts index baead404..a890f45e 100644 --- a/packages/core/src/model/makeDefinition.ts +++ b/packages/core/src/model/makeDefinition.ts @@ -1,22 +1,11 @@ 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 isPropFactory from '@foscia/core/model/checks/isPropFactory'; 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 }; - } +const parseDescriptor = (descriptor: PropertyDescriptor) => { + if (descriptor.value && (isComposable(descriptor.value) || isPropFactory(descriptor.value))) { + return descriptor.value; } return makeDescriptorHolder(descriptor); @@ -26,7 +15,7 @@ export default (definition?: D) => { const parsedDefinition: Dictionary = {}; eachDescriptors(definition ?? {}, (key, descriptor) => { - parsedDefinition[key] = parseDescriptor(key, descriptor); + parsedDefinition[key] = parseDescriptor(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..fb3561d2 100644 --- a/packages/core/src/model/makeModelClass.ts +++ b/packages/core/src/model/makeModelClass.ts @@ -2,11 +2,9 @@ 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 isPropFactory from '@foscia/core/model/checks/isPropFactory'; import makeDefinition from '@foscia/core/model/makeDefinition'; import id from '@foscia/core/model/props/builders/id'; import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; @@ -16,9 +14,12 @@ import { ModelConfig, ModelHooksDefinition, ModelInstance, + ModelPropFactory, } from '@foscia/core/model/types'; import { SYMBOL_MODEL_CLASS, SYMBOL_MODEL_INSTANCE } from '@foscia/core/symbols'; -import { eachDescriptors, mergeConfig, value } from '@foscia/shared'; +import { eachDescriptors, mapWithKeys, mergeConfig } from '@foscia/shared'; + +const { defineProperty } = Object; const createModelClass = ( type: string, @@ -33,70 +34,15 @@ const createModelClass = ( const ModelClass = PrevModelClass ? class extends PrevModelClass { } : 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.`, - ); - } + 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: takeSnapshot(this) }); - forceFill(this, { [def.key]: value(def.default) }); - } - }); + Object.values(this.$model.$schema).forEach((def) => def.bind?.(this)); if (!this.$model.$booted) { this.$model.$booted = true; @@ -106,13 +52,43 @@ const createModelClass = ( 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 }); + const propFactories = new Map(); + + defineProperty(ModelClass, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_CLASS }); + defineProperty(ModelClass, '$type', { value: type }); + defineProperty(ModelClass, '$config', { value: { ...config } }); + defineProperty(ModelClass, '$composables', { value: [] }); + defineProperty(ModelClass, '$hooks', { writable: true, value: {} }); + defineProperty(ModelClass, '$booted', { writable: true, value: false }); + + defineProperty(ModelClass, '$schema', { + configurable: true, + get() { + const $schema = mapWithKeys([...propFactories.entries()], ([key, factory]) => { + if (key === 'type') { + throw new FosciaError( + '`type` is forbidden as a definition key because it may be used with some implementations.', + ); + } + + const def = factory.make(this, key); + + if ((key === 'id' || key === 'lid') && !isIdDef(def)) { + throw new FosciaError( + `\`id\` and \`lid\` must be defined with \`id()\` factory (found \`${key}\`).`, + ); + } + + def.boot?.(this); + + return { [key]: def }; + }); + + defineProperty(this, '$schema', { get: () => $schema }); + + return $schema; + }, + }); ModelClass.configure = function configureModel(newConfig?: ModelConfig, override = true) { return createModelClass( @@ -137,28 +113,16 @@ const createModelClass = ( 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); + applyDefinition(descriptor.value.def); ModelClass.$hooks = mergeHooks(ModelClass.$hooks!, descriptor.value.$hooks!); - } else if (isPropDef(descriptor.value)) { - ModelClass.$schema[key] = descriptor.value; + } else if (isPropFactory(descriptor.value)) { + propFactories.set(key, descriptor.value); } else { - Object.defineProperty(ModelClass.prototype, key, descriptor); + defineProperty(ModelClass.prototype, key, descriptor); } }); diff --git a/packages/core/src/model/makeModelFactory.ts b/packages/core/src/model/makeModelFactory.ts index a1499031..a392de72 100644 --- a/packages/core/src/model/makeModelFactory.ts +++ b/packages/core/src/model/makeModelFactory.ts @@ -1,11 +1,31 @@ import makeModelClass from '@foscia/core/model/makeModelClass'; import { - ModelConfig, ModelFactory, + ModelConfig, + ModelFactory, ModelFlattenDefinition, ModelInstance, ModelParsedDefinition, } from '@foscia/core/model/types'; +/** + * 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 diff --git a/packages/core/src/model/props/builders/attr.ts b/packages/core/src/model/props/builders/attr.ts index 4f2bb576..febaaa35 100644 --- a/packages/core/src/model/props/builders/attr.ts +++ b/packages/core/src/model/props/builders/attr.ts @@ -1,15 +1,22 @@ -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 { ModelAttributeFactory } 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'; +/** + * Create an attribute property factory. + * + * @param config + * + * @category Factories + */ export default ( config?: ObjectTransformer | T | (() => T), -) => makePendingProp({ - ...PROP_MODIFIERS, - transform: (transformer: ObjectTransformer) => ({ transformer }), -})({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_ATTRIBUTE, +) => makeValuePropFactory({ + $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_ATTRIBUTE, default: typeof config !== 'object' ? config : undefined, transformer: typeof config === 'object' ? config : undefined, -}) as unknown as PendingModelAttribute; +} as ModelAttribute, { + transform: (transformer: ObjectTransformer) => ({ transformer }), +}) as ModelAttributeFactory; diff --git a/packages/core/src/model/props/builders/hasMany.ts b/packages/core/src/model/props/builders/hasMany.ts index d22e04ce..266a76a1 100644 --- a/packages/core/src/model/props/builders/hasMany.ts +++ b/packages/core/src/model/props/builders/hasMany.ts @@ -1,22 +1,34 @@ 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 { SYMBOL_MODEL_RELATION_HAS_MANY } from '@foscia/core/symbols'; import { Awaitable } from '@foscia/shared'; const hasMany: { - ( + /** + * Create a has many relation property factory. + * + * @param config + * + * @category Factories + */( config?: string | string[] | ModelRelationConfig, - ): PendingModelRelation; - ( + ): ModelRelationFactory; + /** + * Create a has many relation property factory. + * + * @param resolver + * + * @category Factories + */( resolver: () => Awaitable, - ): PendingModelRelation[], false>; + ): ModelRelationFactory[], false>; } = ( - config?: PendingModelRelationConfig, + config?: ModelRelationFactoryConfig, ) => relation(SYMBOL_MODEL_RELATION_HAS_MANY, config) as any; export default hasMany; diff --git a/packages/core/src/model/props/builders/hasOne.ts b/packages/core/src/model/props/builders/hasOne.ts index 122a3c9b..061e49ef 100644 --- a/packages/core/src/model/props/builders/hasOne.ts +++ b/packages/core/src/model/props/builders/hasOne.ts @@ -1,22 +1,34 @@ 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 { SYMBOL_MODEL_RELATION_HAS_ONE } from '@foscia/core/symbols'; import { Awaitable } from '@foscia/shared'; const hasOne: { - ( + /** + * Create a has one relation property factory. + * + * @param config + * + * @category Factories + */( config?: string | string[] | ModelRelationConfig, - ): PendingModelRelation; - ( + ): ModelRelationFactory; + /** + * Create a has one relation property factory. + * + * @param resolver + * + * @category Factories + */( resolver: () => Awaitable, - ): PendingModelRelation, false>; + ): ModelRelationFactory, false>; } = ( - config?: PendingModelRelationConfig, + config?: ModelRelationFactoryConfig, ) => relation(SYMBOL_MODEL_RELATION_HAS_ONE, config) as any; export default hasOne; diff --git a/packages/core/src/model/props/builders/id.ts b/packages/core/src/model/props/builders/id.ts index 9cc0dd7f..e1540cff 100644 --- a/packages/core/src/model/props/builders/id.ts +++ b/packages/core/src/model/props/builders/id.ts @@ -1,15 +1,22 @@ -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 { ModelIdFactory } 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'; +/** + * Create an ID property factory. + * + * @param config + * + * @category Factories + */ export default ( config?: ObjectTransformer | T | (() => T), -) => makePendingProp({ - ...PROP_MODIFIERS, - transform: (transformer: ObjectTransformer) => ({ transformer }), -})({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_ID, - transformer: config, -}) as unknown as PendingModelId; +) => makeValuePropFactory({ + $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_ID, + default: typeof config !== 'object' ? config : undefined, + transformer: typeof config === 'object' ? config : undefined, +} as ModelId, { + transform: (transformer: ObjectTransformer) => ({ transformer }), +}) as ModelIdFactory; diff --git a/packages/core/src/model/props/builders/makeBuilderPropFactory.ts b/packages/core/src/model/props/builders/makeBuilderPropFactory.ts new file mode 100644 index 00000000..3bcbca84 --- /dev/null +++ b/packages/core/src/model/props/builders/makeBuilderPropFactory.ts @@ -0,0 +1,26 @@ +import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; +import makePropFactory from '@foscia/core/model/props/builders/makePropFactory'; +import { ModelProp, ModelPropFactory } from '@foscia/core/model/types'; +import { Dictionary, mapWithKeys } from '@foscia/shared'; + +type BuilderPropFactory< + P extends ModelProp, + M extends Dictionary<(...args: any[]) => Partial

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

; + +const makeBuilderPropFactory = < + P extends ModelProp, + M extends Dictionary<(...args: any[]) => Partial

>, +>(prop: ModelPropFactoryDefinition

, modifiers: M): BuilderPropFactory => ({ + ...mapWithKeys(modifiers, (modifier, key) => ({ + [key]: (...args: Parameters) => makeBuilderPropFactory({ + ...prop, + ...modifier(...args), + }, modifiers), + })), + ...makePropFactory(prop), +} as BuilderPropFactory); + +export default makeBuilderPropFactory; 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/makePropFactory.ts b/packages/core/src/model/props/builders/makePropFactory.ts new file mode 100644 index 00000000..2d489e0b --- /dev/null +++ b/packages/core/src/model/props/builders/makePropFactory.ts @@ -0,0 +1,13 @@ +import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; +import { ModelProp, ModelPropFactory } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_PROP, SYMBOL_MODEL_PROP_FACTORY } from '@foscia/core/symbols'; + +export default

(prop: ModelPropFactoryDefinition

) => ({ + $FOSCIA_TYPE: SYMBOL_MODEL_PROP_FACTORY, + make: (parent, key) => ({ + ...(prop ?? {}), + $FOSCIA_TYPE: SYMBOL_MODEL_PROP, + parent, + key, + }), +} as ModelPropFactory

); 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..52a67595 --- /dev/null +++ b/packages/core/src/model/props/builders/makeValuePropFactory.ts @@ -0,0 +1,79 @@ +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 makeBuilderPropFactory from '@foscia/core/model/props/builders/makeBuilderPropFactory'; +import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; +import { ModelPropSync, ModelValueProp } from '@foscia/core/model/types'; +import { Dictionary, value } from '@foscia/shared'; + +const VALUE_PROP_MODIFIERS = { + default: (defaultValue: unknown | (() => unknown)) => ({ default: defaultValue }), + alias: (alias: string) => ({ alias }), + readOnly: (readOnly?: boolean) => ({ readOnly }), + sync: (sync: boolean | ModelPropSync) => ({ sync }), + nullable: () => ({}), +}; + +export default < + P extends ModelValueProp, + M extends Dictionary<(...args: any[]) => Partial

>, +>(prop: ModelPropFactoryDefinition

, modifiers: M) => makeBuilderPropFactory({ + ...prop, + bind(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 values must be defined using a factory function.`, + ); + } + + forceFill(instance, { [this.key]: value(this.default) }); + } + }, +}, { ...VALUE_PROP_MODIFIERS, ...modifiers }); diff --git a/packages/core/src/model/props/builders/relation.ts b/packages/core/src/model/props/builders/relation.ts index 1ac7990e..a5978806 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -1,24 +1,16 @@ -import makePendingProp, { PROP_MODIFIERS } from '@foscia/core/model/props/builders/makePendingProp'; +import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; import { - PendingModelRelation, - PendingModelRelationConfig, + 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'; -/** - * Make a pending relation definition. - * - * @param relationType - * @param config - * - * @internal - */ export default ( relationType: ModelRelationType, - config?: PendingModelRelationConfig, + config?: ModelRelationFactoryConfig, ) => { - const resolveConfig = (configValue: PendingModelRelationConfig) => { + const resolveConfig = (configValue: ModelRelationFactoryConfig) => { if (typeof configValue === 'string' || Array.isArray(configValue)) { return { type: configValue }; } @@ -30,14 +22,11 @@ export default ( return { ...configValue }; }; - const makePendingRelation = makePendingProp({ - ...PROP_MODIFIERS, - config: resolveConfig, - }); - - return makePendingRelation({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_RELATION, + return makeValuePropFactory({ + $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_RELATION, $RELATION_TYPE: relationType, ...resolveConfig(config ?? {}), - }) as unknown as PendingModelRelation; + } as ModelRelation, { + config: resolveConfig, + }) as ModelRelationFactory; }; diff --git a/packages/core/src/model/props/builders/types.ts b/packages/core/src/model/props/builders/types.ts index 6d5b91d5..62c27763 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -1,56 +1,166 @@ import { Model, + ModelAttribute, + ModelId, ModelIdType, + ModelProp, + ModelPropFactory, ModelPropSync, + ModelRelation, ModelRelationConfig, - PendingModelProp, - RawModelAttribute, - RawModelId, - RawModelRelation, } from '@foscia/core/model/types'; import { ObjectTransformer } from '@foscia/core/transformers/types'; -import { Awaitable, Constructor, Dictionary } from '@foscia/shared'; +import { Awaitable, Constructor } from '@foscia/shared'; -export type PendingDefinitionModifiers = Dictionary<(...args: any[]) => PendingDefinition>; +/** + * Default prop factory definition object type. + * + * @internal + */ +export type ModelPropFactoryDefinition

= + Partial

& ThisType

; -export type PendingDefinition = PendingModelProp & PendingDefinitionModifiers; - -export type PendingModelId = { +/** + * Model ID factory object. + * + * @internal + */ +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; +} & ModelPropFactory>; -export type PendingModelAttribute = { +/** + * Model attribute factory object. + * + * @internal + */ +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>; + ) => 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; +} & ModelPropFactory>; -export type PendingModelRelationInstance = +/** + * Infer related instance types from relationship models. + * + * @internal + */ +export type InferModelRelationFactoryInstance = M extends Constructor[] ? I : M extends Constructor ? I : never; -export type PendingModelRelationConfig = +/** + * Model relationship factory configuration object. + * + * @internal + */ +export type ModelRelationFactoryConfig = | 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>; +/** + * Model relationship factory object. + * + * @internal + */ +export type ModelRelationFactory = { + /** + * Define the related types or the configuration object. + * + * @param config + */ + config: (config: string | string[] | ModelRelationConfig) => 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; +} & ModelPropFactory>; diff --git a/packages/core/src/model/props/mappers/mapAttributes.ts b/packages/core/src/model/props/mappers/mapAttributes.ts index c5498353..8cb4152f 100644 --- a/packages/core/src/model/props/mappers/mapAttributes.ts +++ b/packages/core/src/model/props/mappers/mapAttributes.ts @@ -8,5 +8,5 @@ export default ( ) => mapProps( instance, callback as any, - (def) => isAttributeDef(def), + isAttributeDef, ) as R[]; diff --git a/packages/core/src/model/props/mappers/mapIds.ts b/packages/core/src/model/props/mappers/mapIds.ts index d8b3b9c0..09644b38 100644 --- a/packages/core/src/model/props/mappers/mapIds.ts +++ b/packages/core/src/model/props/mappers/mapIds.ts @@ -8,5 +8,5 @@ export default ( ) => mapProps( instance, callback as any, - (def) => isIdDef(def), + isIdDef, ) as R[]; diff --git a/packages/core/src/model/props/mappers/mapProps.ts b/packages/core/src/model/props/mappers/mapProps.ts index 2d4b1cbd..a937ab24 100644 --- a/packages/core/src/model/props/mappers/mapProps.ts +++ b/packages/core/src/model/props/mappers/mapProps.ts @@ -1,22 +1,12 @@ -import { - ModelAttribute, - ModelId, - ModelInstance, - ModelKey, - ModelRelation, -} from '@foscia/core/model/types'; +import { ModelInstance, ModelProp } from '@foscia/core/model/types'; -export default ( +export default ( instance: I, - callback: ( - def: ModelId> | ModelAttribute> | ModelRelation>, - ) => R, - predicate?: ( - def: ModelId> | ModelAttribute> | ModelRelation>, - ) => boolean, + callback: (def: P) => R, + predicate?: (def: ModelProp) => def is P, ) => Object.values(instance.$model.$schema).reduce((stack, def) => { if (!predicate || predicate(def)) { - stack.push(callback(def)); + stack.push(callback(def as P)); } return stack; diff --git a/packages/core/src/model/props/mappers/mapRelations.ts b/packages/core/src/model/props/mappers/mapRelations.ts index 33f84dac..44cd0e10 100644 --- a/packages/core/src/model/props/mappers/mapRelations.ts +++ b/packages/core/src/model/props/mappers/mapRelations.ts @@ -8,5 +8,5 @@ export default ( ) => mapProps( instance, callback as any, - (def) => isRelationDef(def), + isRelationDef, ) as R[]; diff --git a/packages/core/src/model/props/shouldSync.ts b/packages/core/src/model/props/shouldSync.ts index 4acc7b90..5f709dff 100644 --- a/packages/core/src/model/props/shouldSync.ts +++ b/packages/core/src/model/props/shouldSync.ts @@ -1,6 +1,6 @@ -import { ModelPropSync, RawModelProp } from '@foscia/core/model/types'; +import { ModelPropSync, ModelValueProp } from '@foscia/core/model/types'; -export default (def: RawModelProp, actions: ModelPropSync[]) => ( +export default (def: ModelValueProp, 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..e04359fe 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> diff --git a/packages/core/src/model/relations/makeQueryModelLoader.ts b/packages/core/src/model/relations/makeQueryModelLoader.ts index 474c231b..c0193784 100644 --- a/packages/core/src/model/relations/makeQueryModelLoader.ts +++ b/packages/core/src/model/relations/makeQueryModelLoader.ts @@ -36,22 +36,121 @@ import { 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; + /** + * 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,10 +159,9 @@ 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[]>, ) => { @@ -105,11 +203,10 @@ const fetchRelatedMap = async < Data, Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, - E extends {}, I extends ModelInstance, >( - action: ActionFactory<[], C, E>, - options: QueryModelLoaderOptions, + action: ActionFactory<[], C>, + options: QueryModelLoaderOptions, relations: Map[]; nested: string[] }>, ids: Map, Arrayable | null>>, ) => { @@ -136,7 +233,7 @@ const fetchRelatedMap = async < 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, }); @@ -193,15 +290,34 @@ const remapRelated = ( }); }); +/** + * 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<[], C>, + 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..4c3f34eb 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, diff --git a/packages/core/src/model/relations/makeQueryRelationLoader.ts b/packages/core/src/model/relations/makeQueryRelationLoader.ts index 73b953a5..df2afe3b 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<[], C>, 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..39944fe3 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<[], C>, + 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<[], C>, + options: RefreshIncludeLoaderOptions = {}, ) => async ( instances: Arrayable, ...relations: ArrayableVariadic> diff --git a/packages/core/src/model/relations/utilities/groupRelationsByModels.ts b/packages/core/src/model/relations/utilities/groupRelationsByModels.ts index 6cfdca66..aec36956 100644 --- a/packages/core/src/model/relations/utilities/groupRelationsByModels.ts +++ b/packages/core/src/model/relations/utilities/groupRelationsByModels.ts @@ -1,6 +1,6 @@ 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'; export default async ( model: M, @@ -9,7 +9,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/revivers/makeModelsReducer.ts b/packages/core/src/model/revivers/makeModelsReducer.ts index 18ea67e2..e5305355 100644 --- a/packages/core/src/model/revivers/makeModelsReducer.ts +++ b/packages/core/src/model/revivers/makeModelsReducer.ts @@ -7,9 +7,15 @@ import { ReducedModelInstanceData, ReducedModelSnapshot, } from '@foscia/core/model/revivers/types'; -import { ModelClass, ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; +import { Model, ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; import { Dictionary, mapWithKeys, uniqueId, unsafeId } from '@foscia/shared'; +/** + * Create a models reducer. + * + * @category Factories + * @since 0.8.6 + */ export default () => { let reduceInstance: ( instance: ModelInstance, @@ -18,7 +24,7 @@ export default () => { const generateRef = (refs: string[]): string => uniqueId(unsafeId, refs); - const reduceModel = (model: ModelClass) => ({ + const reduceModel = (model: Model) => ({ $FOSCIA_TYPE: 'model', $type: model.$type, } as ReducedModel); diff --git a/packages/core/src/model/revivers/makeModelsReviver.ts b/packages/core/src/model/revivers/makeModelsReviver.ts index 877fed3e..b786f521 100644 --- a/packages/core/src/model/revivers/makeModelsReviver.ts +++ b/packages/core/src/model/revivers/makeModelsReviver.ts @@ -9,6 +9,14 @@ import { import { Model, ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; import { Dictionary, mapWithKeys } from '@foscia/shared'; +/** + * Create a models reviver. + * + * @param options + * + * @category Factories + * @since 0.8.6 + */ export default (options: { models: Model[]; }) => { let reviveInstance: ( instance: ReducedModelInstance | ReducedModelCircularRef, diff --git a/packages/core/src/model/revivers/types.ts b/packages/core/src/model/revivers/types.ts index 9473efbd..25611e82 100644 --- a/packages/core/src/model/revivers/types.ts +++ b/packages/core/src/model/revivers/types.ts @@ -3,6 +3,8 @@ import { Dictionary } from '@foscia/shared'; /** * Reduced (serialized) model class. + * + * @internal */ export type ReducedModel = { $FOSCIA_TYPE: 'model'; @@ -11,6 +13,8 @@ export type ReducedModel = { /** * Reduced (serialized) model snapshot. + * + * @internal */ export type ReducedModelSnapshot = { $FOSCIA_TYPE: 'snapshot'; @@ -23,6 +27,8 @@ export type ReducedModelSnapshot = { /** * Reduced (serialized) model instance data. + * + * @internal */ export type ReducedModelInstanceData = { $exists: boolean; @@ -32,15 +38,10 @@ export type ReducedModelInstanceData = { $original: ReducedModelSnapshot; }; -/** - * Reduced (serialized) model instance custom data. - */ -export type ReducedModelInstanceCustomData = { - $data?: ReducedModelInstanceData; -}; - /** * Reduced (serialized) model instance. + * + * @internal */ export type ReducedModelInstance = { $FOSCIA_TYPE: 'instance'; @@ -50,8 +51,21 @@ export type ReducedModelInstance = { $custom?: ReducedModelInstanceCustomData; }; +/** + * Reduced (serialized) model instance custom data. + */ +export type ReducedModelInstanceCustomData = { + /** + * Contains the instance data reduced using `tools.data(instance)`. + * This provides Foscia data override. + */ + $data?: ReducedModelInstanceData; +}; + /** * Reduced (serialized) model instance reference. + * + * @internal */ export type ReducedModelCircularRef = { $FOSCIA_TYPE: 'circular'; @@ -62,7 +76,17 @@ export type ReducedModelCircularRef = { * 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 +94,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 +106,17 @@ 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; }; diff --git a/packages/core/src/model/snapshots/changed.ts b/packages/core/src/model/snapshots/changed.ts index 85436ebf..74baef6f 100644 --- a/packages/core/src/model/snapshots/changed.ts +++ b/packages/core/src/model/snapshots/changed.ts @@ -3,6 +3,23 @@ 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> diff --git a/packages/core/src/model/snapshots/cloneModelValue.ts b/packages/core/src/model/snapshots/cloneModelValue.ts index 033363ff..4b1df993 100644 --- a/packages/core/src/model/snapshots/cloneModelValue.ts +++ b/packages/core/src/model/snapshots/cloneModelValue.ts @@ -1,6 +1,6 @@ -import { ModelClass } from '@foscia/core/model/types'; +import { Model } from '@foscia/core/model/types'; -export default (model: ModelClass, value: T) => ( +export default (model: Model, 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 index 056ab94d..e2d6fbec 100644 --- a/packages/core/src/model/snapshots/compareModelValue.ts +++ b/packages/core/src/model/snapshots/compareModelValue.ts @@ -1,7 +1,7 @@ -import { ModelClass } from '@foscia/core/model/types'; +import { Model } from '@foscia/core/model/types'; export default ( - model: ModelClass, + model: Model, nextValue: unknown, prevValue: unknown, ) => ( diff --git a/packages/core/src/model/snapshots/compareSnapshots.ts b/packages/core/src/model/snapshots/compareSnapshots.ts index 54bac925..005f702e 100644 --- a/packages/core/src/model/snapshots/compareSnapshots.ts +++ b/packages/core/src/model/snapshots/compareSnapshots.ts @@ -1,8 +1,26 @@ import compareModelValue from '@foscia/core/model/snapshots/compareModelValue'; -import { ModelClass, ModelKey, ModelSnapshot } from '@foscia/core/model/types'; +import { Model, ModelKey, ModelSnapshot } from '@foscia/core/model/types'; import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; -export default ( +/** + * Compare two snapshots. + * + * @param nextSnapshot + * @param prevSnapshot + * @param only + * + * @category Utilities + * + * @example + * ```typescript + * import { compareSnapshot } from '@foscia/core'; + * + * const titleChanged = compareSnapshot(newSnapshot, oldSnapshot, ['title']); + * if (titleChanged) { + * } + * ``` + */ +export default ( nextSnapshot: ModelSnapshot, prevSnapshot: ModelSnapshot, ...only: ArrayableVariadic> diff --git a/packages/core/src/model/snapshots/markSynced.ts b/packages/core/src/model/snapshots/markSynced.ts index 22264a19..9ce66019 100644 --- a/packages/core/src/model/snapshots/markSynced.ts +++ b/packages/core/src/model/snapshots/markSynced.ts @@ -3,6 +3,21 @@ import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; import { ModelInstance, ModelKey } from '@foscia/core/model/types'; import { ArrayableVariadic, 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> diff --git a/packages/core/src/model/snapshots/restore.ts b/packages/core/src/model/snapshots/restore.ts index c71e5919..970281f1 100644 --- a/packages/core/src/model/snapshots/restore.ts +++ b/packages/core/src/model/snapshots/restore.ts @@ -2,6 +2,21 @@ 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> diff --git a/packages/core/src/model/snapshots/restoreSnapshot.ts b/packages/core/src/model/snapshots/restoreSnapshot.ts index d4e45b8f..9ae5e03e 100644 --- a/packages/core/src/model/snapshots/restoreSnapshot.ts +++ b/packages/core/src/model/snapshots/restoreSnapshot.ts @@ -1,19 +1,34 @@ /* eslint-disable no-param-reassign */ +import isValuePropDef from '@foscia/core/model/checks/isValuePropDef'; 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, ModelSnapshot, + ModelValueProp, ModelValues, } from '@foscia/core/model/types'; import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; +/** + * 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, @@ -27,9 +42,7 @@ export default ( instance.$loaded = snapshot.$loaded; } - const restoreForDef = >( - def: ModelId | ModelAttribute | ModelRelation, - ) => { + const restoreForDef = (def: ModelValueProp>) => { if (keys.length && keys.indexOf(def.key) === -1) { return; } @@ -43,7 +56,7 @@ export default ( } }; - mapProps(instance, restoreForDef); + mapProps(instance, restoreForDef, isValuePropDef); markSynced(instance, ...only); return instance; diff --git a/packages/core/src/model/snapshots/takeSnapshot.ts b/packages/core/src/model/snapshots/takeSnapshot.ts index 34aa02ba..6350dc08 100644 --- a/packages/core/src/model/snapshots/takeSnapshot.ts +++ b/packages/core/src/model/snapshots/takeSnapshot.ts @@ -2,6 +2,20 @@ import cloneModelValue from '@foscia/core/model/snapshots/cloneModelValue'; import { ModelInstance, ModelSnapshot, ModelValues } from '@foscia/core/model/types'; import { mapWithKeys } from '@foscia/shared'; +/** + * 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, ): ModelSnapshot => ({ diff --git a/packages/core/src/model/types.ts b/packages/core/src/model/types.ts index 1b0d8321..aced1a84 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -3,10 +3,11 @@ 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_FACTORY, + 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, } from '@foscia/core/symbols'; @@ -26,28 +27,121 @@ import { /** * 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; - path?: Optional; - guessPath?: Transformer; - guessIdPath?: Transformer; - guessRelationPath?: Transformer; + /** + * 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 alias from a property's name. + * Defaults is to keep the property's name. + */ guessAlias?: Transformer; + /** + * Compare two properties values. + * Defaults to strict equality (`next === prev`). + * This is used when comparing snapshot values. + * + * @param nextValue + * @param prevValue + */ compareValue?: (nextValue: unknown, prevValue: unknown) => boolean; + /** + * Clone a property value. + * Defaults to no clone at all. + * This is used when creating a snapshot. + * + * @param value + */ cloneValue?: (value: T) => T; - [K: string]: any; + + // 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; }; /** - * Model composable definition which can be included on any models. + * Model composable which can be included on any model definition. + * + * @typeParam D Definition of the composable. + * + * @internal */ export type ModelComposable = & { - $definition: D; + /** + * Definition of the composable. + * + * @internal + */ + readonly def: D; } & FosciaObject & Hookable; @@ -58,84 +152,136 @@ export type ModelComposable = export type ModelIdType = string | number; /** - * Sync precise configuration for a property (will only do defined action). - */ -export type ModelPropSync = 'pull' | 'push'; - -/** - * Normalized part of a property's definition (ID, attribute or relation). + * Factory for a model property. + * + * @typeParam P Model property created by the factory. + * + * @internal */ -export type ModelPropNormalized = { - key: K; -}; +export type ModelPropFactory

= ModelProp> = + & { + /** + * Create the model property for a model and key. + */ + readonly make: (parent: Model, key: string) => P; + } + & FosciaObject; /** - * Pending definition for a model's property (ID, attribute or relation). + * Model property appended to a model schema. + * + * @internal */ -export type PendingModelProp> = - { - definition: D; +export type ModelProp = + & { + /** + * Boot the property before it is appended to a model schema. + * At this step, the model schema is not complete and should not be accessed. + */ + readonly boot?: (model: Model) => void; + /** + * Bind the property to the given instance. + * Usually, this will use `Object.defineProperty` to configure + * the property behavior. + */ + readonly bind?: (instance: ModelInstance) => void; + /** + * The model the property is attached to. + */ + readonly parent: Model; + /** + * The key the property uses in the model schema and instances. + */ + readonly key: K; + /** + * The type the property's value is of. + * This is a generic type annotation only property and should not be accessed. + * + * @private + */ + readonly __type__: T; + /** + * Tells if the property is read-only. + * This is a generic type annotation only property and should not be accessed. + * + * @private + */ + readonly __readOnly__: R; } - & FosciaObject; + & FosciaObject; /** - * Raw definition for a model's property (ID, attribute or relation). + * 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 RawModelProp = { - /** - * Default value for the property. - */ - default?: T | (() => T) | undefined; - /** - * 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; -}; +export type ModelPropSync = 'pull' | 'push'; /** - * Raw Definition for a model's ID. + * Model property which holds a value that can be exchanged with the data source. + * + * @internal */ -export type RawModelId = - { - transformer?: ObjectTransformer | undefined; +export type ModelValueProp< + K = string, + T = unknown, + R extends boolean = boolean, +> = + & { + /** + * Tells if the property is read-only. + */ + readonly readOnly?: R; + /** + * Default value for the property. + */ + default?: T | (() => T) | undefined; + /** + * Alias of the property (might be used when (de)serializing). + */ + alias?: string | undefined; + /** + * Tells if the property should be synced with the data store. + */ + sync?: boolean | ModelPropSync; } - & RawModelProp - & FosciaObject; + & ModelProp; /** - * Definition for a model's ID. + * Model ID property definition. */ export type ModelId = - & ModelPropNormalized - & RawModelId; - -/** - * Raw definition for a model's attribute. - */ -export type RawModelAttribute = { - 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 definition. */ export type ModelAttribute = - & ModelPropNormalized - & RawModelAttribute; + { + /** + * 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 @@ -143,61 +289,71 @@ export type ModelRelationType = /** * Configuration of a model's relation. + * + * @internal */ export type ModelRelationConfig = { + /** + * The related type(s) to help Foscia resolving related models. + */ type?: string | string[]; - [K: string]: any; + + // Specific HTTP config. + + /** + * The path to use when requesting relation's endpoint. + * + * @remarks + * This is specific to HTTP implementations (REST, JSON:API). + */ + path?: string; }; /** - * Raw definition for a model's relation. + * Model sync relation property definition. */ -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; model?: () => Awaitable; } & ModelRelationConfig - & RawModelProp - & FosciaObject; - -/** - * Definition for a model's attribute. - */ -export type ModelRelation = - & ModelPropNormalized - & RawModelRelation; + & ModelValueProp; /** - * 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. + * Infer a model's property type from the property definition. + * + * @internal */ -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; +export type InferModelValuePropType

= P extends ModelValueProp ? T : never; /** * The parsed model definition with non composables/attributes/relations * properties' descriptors wrapped in holders. + * + * @internal */ export type ModelParsedDefinition = { - [K in keyof D]: D[K] extends ModelComposable - ? D[K] : D[K] extends PendingModelProp> - ? ModelParsedDefinitionProp : ModelParsedDefinitionProp; + [K in keyof D]: D[K] extends ModelComposable | ModelPropFactory | DescriptorHolder + ? D[K] : DescriptorHolder }; /** * The flatten and parsed model definition with composables properties * flattened to the definition root. + * + * @internal */ export type ModelFlattenDefinition = & UnionToIntersection<{} | { @@ -207,58 +363,51 @@ export type ModelFlattenDefinition = & OmitNever<{ [K in keyof D]: D[K] extends ModelComposable ? never : D[K] }>; /** - The flatten and parsed model definition from a composable object type. + * The flatten and parsed model definition from a composable object type. + * + * @internal */ -export type ModelComposableDefinition> = ModelFlattenDefinition<{ - composable: C; +export type ModelDefinitionFromComposable> = ModelFlattenDefinition<{ + __composable__: C; }>; /** - * Extract model's IDs, attributes and relations from the whole definition. + * Extract model's properties from definition factories. + * + * @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; -}; - -/** - Extract model's relations from the whole definition. - */ -export type ModelSchemaRelations = { - [K in keyof D]: D[K] extends ModelRelation - ? D[K] : never; + readonly [K in keyof D]: D[K] extends ModelPropFactory + ? P & { key: K; } : never; }; -/** - * Model instance generic hook callback function. - * - * @deprecated Use `HookCallback` instead. - */ -export type ModelInstanceHookCallback = HookCallback; - /** * Model instance read property generic hook callback function. + * + * @internal */ export type ModelInstancePropertyReadHookCallback = SyncHookCallback<{ - instance: ModelInstance; - def: ModelId | ModelAttribute | ModelRelation; - value: unknown; + readonly instance: ModelInstance; + readonly def: ModelValueProp; + readonly value: unknown; }>; /** * Model instance write property generic hook callback function. + * + * @internal */ export type ModelInstancePropertyWriteHookCallback = SyncHookCallback<{ - instance: ModelInstance; - def: ModelId | ModelAttribute | ModelRelation; - prev: unknown; - next: unknown; + readonly instance: ModelInstance; + readonly def: ModelValueProp; + readonly prev: unknown; + readonly next: unknown; }>; /** * Model's hooks definition. + * + * @internal */ export type ModelHooksDefinition = & { @@ -284,39 +433,70 @@ export type ModelHooksDefinition = & Record<`property:write:${string}`, ModelInstancePropertyWriteHookCallback>; /** - * Extendable model class holding the configuration and schema. + * Model class. + * + * @typeParam D Definition of the model. + * @typeParam I Instance of the model. */ -export type ModelClass = - { +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. + * It is parsed once on first property get (usually on first constructor call) + * and will be cached afterward. It should not be updated afterward. + */ readonly $schema: ModelSchema; + /** + * Composables used by the model. + * + * @internal + */ readonly $composables: ModelComposable[]; + /** + * Tells if the model was already booted (constructed at least once). + * + * @internal + */ $booted: boolean; } - & 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; + & Constructor + & Hookable + & FosciaObject; /** * Model class using a given composable. */ export type ModelUsing = - Model, ModelInstanceUsing>; + Model, ModelInstanceUsing>; /** * Model class which can be configured or extended. + * + * @internal */ export type ExtendableModel = any> = & { + /** + * Create a new model class with an extended configuration. + * + * @param config + * @param override + */ configure(config: ModelConfig, override?: boolean): ExtendableModel>; + /** + * Create a new model class with an extended definition. + * + * @param rawDefinition + */ extend( // eslint-disable-next-line max-len rawDefinition?: ND & ThisType>>>, @@ -327,6 +507,8 @@ export type ExtendableModel = any /** * Model class factory. + * + * @internal */ export type ModelFactory< D extends {} = {}, @@ -337,16 +519,10 @@ export type ModelFactory< // eslint-disable-next-line max-len ) => ExtendableModel>, ModelInstance>>>); -/** - * Model instance for a dedicated model class. - * This type is used to keep instance generic typing across actions enhancements. - */ -export type ModelClassInstance = { - readonly $model: ModelClass; -}; - /** * Model defaults IDs typing when not defined by definition. + * + * @internal */ export type ModelIdsDefaults = & (D extends { id: any } ? {} : { id: ModelIdType | null }) @@ -354,35 +530,41 @@ export type ModelIdsDefaults = /** * Model properties map (IDs/attributes/relations/custom props). + * + * @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]; + [K in keyof D]: D[K] extends ModelPropFactory> + ? T : D[K] extends DescriptorHolder + ? T : D[K]; }; /** * Model properties key (only writable IDs/attributes/relations). + * + * @internal */ export type ModelDefinitionWritableKey = { [K in keyof D]: D[K] extends DescriptorHolder ? never - : D[K] extends RawModelProp ? never : K; + : D[K] extends ModelPropFactory> ? K : never; }[keyof D] | keyof ModelIdsDefaults; /** * Model properties key (only readonly IDs/attributes/relations). + * + * @internal */ export type ModelDefinitionReadOnlyKey = { [K in keyof D]: D[K] extends DescriptorHolder ? never - : D[K] extends RawModelProp ? K : never; + : D[K] extends ModelPropFactory> ? K : never; }[keyof D]; /** * Model descriptors key (only custom properties). + * + * @internal */ export type ModelDefinitionDescriptorKey = { [K in keyof D]: D[K] extends DescriptorHolder ? K : never; @@ -390,18 +572,24 @@ export type ModelDefinitionDescriptorKey = { /** * Model properties map (only writable IDs/attributes/relations). + * + * @internal */ export type ModelDefinitionWritableValues = Pick, ModelDefinitionWritableKey>; /** * Model properties map (only readonly IDs/attributes/relations). + * + * @internal */ export type ModelDefinitionReadOnlyValues = Readonly, ModelDefinitionReadOnlyKey>>; /** * Model properties map (IDs/attributes/relations). + * + * @internal */ export type ModelDefinitionValues = & ModelDefinitionWritableValues @@ -409,20 +597,43 @@ export type ModelDefinitionValues = /** * Model descriptors map (only custom properties). + * + * @internal */ export type ModelDefinitionDescriptors = Pick, ModelDefinitionDescriptorKey>; /** * Model instance holding state and values. + * + * @typeParam D Definition of the model instance. */ export type ModelInstance = { - readonly $model: ModelClass; + /** + * 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; + /** + * Tells which relation is considered to be loaded. + */ $loaded: Dictionary; + /** + * Internal values of the instance's properties. + */ $values: Partial>; + /** + * Raw value of the data source record. + */ $raw: any; + /** + * Latest synced snapshot. + */ $original: ModelSnapshot; } & ModelDefinitionValues @@ -433,59 +644,70 @@ export type ModelInstance = * Model instance using a given composable. */ export type ModelInstanceUsing = - ModelInstance>; + ModelInstance>; /** * Model class or instance snapshot. */ export type ModelSnapshot = { - $model: ModelClass; - $exists: boolean; - $raw: any; - $loaded: Dictionary; - $values: Partial>; + readonly $model: Model; + readonly $exists: boolean; + readonly $raw: any; + readonly $loaded: Dictionary; + readonly $values: Partial>; }; /** - * Model class or instance. + * Infer the definition from a model class or model instance. + * + * @internal */ -export type ModelClassOrInstance = ModelClass | ModelClassInstance; +export type InferModelDefinition = M extends Model + ? D : M extends ModelInstance ? D + : M extends {} + ? M + : never; /** - * Infer the definition from a model class or model instance. + * Infer the schema from a model class or model instance. + * + * @internal */ -export type ModelInferDefinition = M extends ModelClassOrInstance - ? D - : M extends {} - ? M - : never; +export type InferModelSchema = ModelSchema>; /** - * Infer the schema from a model class or model instance. + * Infer a schema property from a model class or model instance + * and ensure it extends the given `P` generic type. + * + * @internal */ -export type ModelInferSchema = ModelSchema>; +export type InferModelSchemaProp = + K extends keyof ModelSchema> + ? ModelSchema>[K] extends P + ? ModelSchema>[K] + : never : never; /** * Model class or instance values map (only IDs/attributes/relations). */ -export type ModelValues = ModelDefinitionValues>; +export type ModelValues = ModelDefinitionValues>; /** * Model class or instance values map (only writable IDs/attributes/relations). */ -export type ModelWritableValues = ModelDefinitionWritableValues>; +export type ModelWritableValues = ModelDefinitionWritableValues>; /** * Model class or instance values map (only readonly IDs/attributes/relations). */ -export type ModelReadOnlyValues = ModelDefinitionReadOnlyValues>; +export type ModelReadOnlyValues = ModelDefinitionReadOnlyValues>; /** * Model class or instance IDs/attributes/relations key. */ export type ModelKey = & string - & keyof ModelInferSchema + & keyof InferModelSchema & keyof ModelValues; /** @@ -495,13 +717,15 @@ export type ModelKey = * const keys: ModelRelationKey[] = ['comments', 'tags']; */ export type ModelRelationKey = - keyof ModelInferSchema extends infer K - ? K extends string & keyof ModelInferSchema & keyof ModelValues - ? ModelInferSchema[K] extends never + keyof InferModelSchema extends infer K + ? K extends ModelKey + ? InferModelSchema[K] extends never ? never - : ModelInferSchema[K] extends ModelRelation + : InferModelSchema[K] extends ModelRelation ? K - : never : never : never; + : InferModelSchema[K] extends ModelProp + ? 0 extends (1 & T) ? K + : never : never : never : never; /** * Model class or instance relations key (supports nested relation using dot separator). @@ -512,12 +736,14 @@ 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 + : keyof InferModelSchema extends infer K + ? K extends ModelKey + ? InferModelSchema[K] extends never ? never - : ModelInferSchema[K] extends ModelRelation + : InferModelSchema[K] extends ModelRelation ? T extends any[] ? K | `${K}.${ModelRelationDotKey}` : K | `${K}.${ModelRelationDotKey}` - : never : never : never; + : InferModelSchema[K] extends ModelProp + ? 0 extends (1 & T) ? K : never + : never : never : never; diff --git a/packages/core/src/normalization/normalizeDotRelations.ts b/packages/core/src/normalization/normalizeDotRelations.ts index 901727f5..9cab3db9 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, ModelRelationDotKey } from '@foscia/core/model/types'; import normalizeKey from '@foscia/core/normalization/normalizeKey'; import { RegistryI } from '@foscia/core/types'; import { isNone, Optional } from '@foscia/shared'; +/** + * Normalize a set of dot relation keys. + * + * @param model + * @param relations + * @param registry + * + * @internal + */ const normalizeDotRelations = ( model: Model, 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 as keyof typeof model['$schema']]; 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.`, diff --git a/packages/core/src/normalization/normalizeKey.ts b/packages/core/src/normalization/normalizeKey.ts index 8730d24a..02182d30 100644 --- a/packages/core/src/normalization/normalizeKey.ts +++ b/packages/core/src/normalization/normalizeKey.ts @@ -1,10 +1,16 @@ -import { ModelClass, ModelKey } from '@foscia/core/model/types'; +import { Model, ModelKey, ModelValueProp } from '@foscia/core/model/types'; +/** + * Normalize a property key using its alias or a guessed alias if available. + * + * @param model + * @param key + */ export default ( - model: ModelClass, - key: ModelKey>, + model: Model, + key: ModelKey>, ) => { - const def = model.$schema[key]; + const def = model.$schema[key] as unknown as ModelValueProp; return def.alias ?? ( model.$config.guessAlias ? 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..6cce071d --- /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, + ])); + + const modelFor = async (type: string) => modelsMap.get(normalizeType(type)) ?? null; + + return { + registry: { modelFor } 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..5b162396 --- /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 { RegistryI } from '@foscia/core/types'; +import { toKebabCase } from '@foscia/shared'; + +/** + * Make a default {@link RegistryI | `RegistryI`} implementation. + * + * @param models + * + * @category Factories + */ +export default (models: Model[]): { registry: RegistryI; } => makeMapRegistry({ + models, + normalizeType: toKebabCase, +}); diff --git a/packages/core/src/registry/types.ts b/packages/core/src/registry/types.ts index 722e49a0..2950ec52 100644 --- a/packages/core/src/registry/types.ts +++ b/packages/core/src/registry/types.ts @@ -1,28 +1,20 @@ 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 { Optional, Transformer } from '@foscia/shared'; +/** + * Config for registry map implementation. + * + * @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. + * + * @internal + */ +export type MapRegistry = RegistryI; diff --git a/packages/core/src/symbols.ts b/packages/core/src/symbols.ts index 76ebb3f3..5aec4416 100644 --- a/packages/core/src/symbols.ts +++ b/packages/core/src/symbols.ts @@ -1,9 +1,97 @@ -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. + * + * @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 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/makeTransformer.ts b/packages/core/src/transformers/makeTransformer.ts index 029e33bb..1e3e500c 100644 --- a/packages/core/src/transformers/makeTransformer.ts +++ b/packages/core/src/transformers/makeTransformer.ts @@ -1,6 +1,14 @@ import { ObjectTransformer } from '@foscia/core/transformers/types'; import { Awaitable, isNil, Optional } from '@foscia/shared'; +/** + * Create a transformer. + * + * @param deserializeFn + * @param serializeFn + * + * @category Factories + */ export default ( deserializeFn: (value: DS) => Awaitable, serializeFn?: (value: T) => Awaitable, 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..ea7cfeeb 100644 --- a/packages/core/src/transformers/toBoolean.ts +++ b/packages/core/src/transformers/toBoolean.ts @@ -6,6 +6,13 @@ type ToBooleanOptions = { const DEFAULT_TRUE_VALUES = [true, 1, '1', 'true', 'yes']; +/** + * Create a boolean transformer. + * + * @param options + * + * @category Factories + */ export default (options?: ToBooleanOptions) => makeTransformer( (value: unknown) => (options?.trueValues ?? DEFAULT_TRUE_VALUES).indexOf(value) !== -1, ); diff --git a/packages/core/src/transformers/toDate.ts b/packages/core/src/transformers/toDate.ts index 0ea3bee7..c567379d 100644 --- a/packages/core/src/transformers/toDate.ts +++ b/packages/core/src/transformers/toDate.ts @@ -1,7 +1,12 @@ import makeDateTransformer from '@foscia/core/transformers/makeDateTransformer'; import { removeTimezoneOffset } 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('-') : []; 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..f053fb7a 100644 --- a/packages/core/src/transformers/toNumber.ts +++ b/packages/core/src/transformers/toNumber.ts @@ -1,6 +1,11 @@ import logger from '@foscia/core/logger/logger'; import makeTransformer from '@foscia/core/transformers/makeTransformer'; +/** + * Create a number transformer. + * + * @category Factories + */ export default () => makeTransformer((value: unknown) => { const number = Number(value); if (Number.isNaN(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..31801b00 100644 --- a/packages/core/src/transformers/types.ts +++ b/packages/core/src/transformers/types.ts @@ -1,14 +1,27 @@ import { Awaitable, Optional, Transformer } from '@foscia/shared'; +/** + * Bi-directional object transformer. + */ export type ObjectTransformer = { deserialize: Transformer>; serialize: Transformer>; }; +/** + * Object transformer factory options. + * + * @internal + */ export type ObjectTransformerFactoryOptions = { nullable?: N; }; +/** + * Object transformer factory result from options. + * + * @internal + */ export type ObjectTransformerFactoryResult = ( options?: ObjectTransformerFactoryOptions, ) => N extends true diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 0852071c..510dd5ac 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -2,22 +2,31 @@ import type { Model, ModelIdType, ModelInstance, ModelRelation } from '@foscia/c 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 DeserializerI | `DeserializerI`}, + * to deserialize raw data source records in the correct models. */ -export interface RegistryI { +export type RegistryI = { /** * 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 DeserializerI | `DeserializerI`}, + * to prevent multiple instance of the same record coexisting or to store an instance + * as retrieved. */ -export interface CacheI { +export type CacheI = { /** * Retrieve a model instance from cache. * @@ -25,7 +34,6 @@ export interface CacheI { * @param id */ find(type: string, id: ModelIdType): Promise; - /** * Put a model instance inside cache. * @@ -34,7 +42,6 @@ export interface CacheI { * @param instance */ put(type: string, id: ModelIdType, instance: ModelInstance): Promise; - /** * Forget a model's instance. * @@ -42,52 +49,48 @@ 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 AdapterResponseI = { /** - * 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 DeserializerI | `DeserializerI`}. * 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. */ -export interface AdapterI { +export type AdapterI = { /** * Execute a given context to retrieve a raw data response. * Context data will already be serialized using serializer if available. @@ -95,7 +98,7 @@ export interface AdapterI { * @param context */ execute(context: {}): Awaitable>; -} +}; /** * Serializer converting model instances to adapter data source format. @@ -103,8 +106,9 @@ export interface AdapterI { * @typeParam Record Serialized value for an instance. * @typeParam Related Serialized value for a related instance. * @typeParam Data Serialized value for one/many/none instances. + * Usually, it is a wrapper type for `Record` or `Related` records. */ -export interface SerializerI { +export type SerializerI = { /** * Serialize a given instance value. * @@ -112,7 +116,6 @@ export interface SerializerI { * @param context */ serializeInstance(value: ModelInstance, context: {}): Awaitable; - /** * Serialize a given instance's relation value. * @@ -127,38 +130,39 @@ export interface SerializerI { value: Arrayable | null, context: {}, ): Awaitable | null>; - /** * Serialize a set of already serialized records. - * This can be used to "wrap" records. + * This should be used to "wrap" records. * * @param records * @param context */ serialize(records: Arrayable | null, context: {}): Awaitable; -} +}; /** - * Base deserialized data which must contain at least an instances set. + * Base deserialized data which must contain at least an instances array. */ -export interface DeserializedData { +export type DeserializedData = { + /** + * Deserialized instances. + */ instances: I[]; -} +}; /** - * Deserializer converting adapter data to a deserialized set of instances. + * Deserializer converting adapter response read data to a deserialized array of instances. * - * @typeParam Data Content of the adapter's original response, containing - * records or relations data. + * @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). */ -export interface DeserializerI { +export type DeserializerI = { /** - * Deserialize adapter data to a deserialized set of instances. + * Deserialize adapter data to a deserialized array of instances. * * @param data * @param context */ deserialize(data: Data, context: {}): Awaitable; -} +}; 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/typecheck/action-factories.test-d.ts b/packages/core/tests/typecheck/action-factories.test-d.ts index 620ee864..4ae4f5c2 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 { CacheI, makeActionFactory, makeCache, makeRegistry, RegistryI } 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(); }); diff --git a/packages/core/tests/typecheck/action-generics.test-d.ts b/packages/core/tests/typecheck/action-generics.test-d.ts index e4a780e8..c13908f7 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, ConsumeInstance, ConsumeModel, - context, DeserializerI, include, - makeActionClass, + makeActionFactory, Model, ModelIdType, ModelInstance, @@ -18,28 +15,17 @@ import { query, save, SerializerI, - when, } 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 AdapterI, + cache: null as unknown as CacheI, + deserializer: null as unknown as DeserializerI, + serializer: null as unknown as SerializerI, + }); 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 80491445..2f4cb02b 100644 --- a/packages/core/tests/typecheck/actions.test-d.ts +++ b/packages/core/tests/typecheck/actions.test-d.ts @@ -2,19 +2,21 @@ import { AdapterI, all, associate, + attach, + cached, cachedOr, CacheI, - context, create, DeserializerI, destroy, dissociate, include, - makeActionClass, + makeActionFactory, one, oneOrCurrent, oneOrFail, - query, queryAs, + query, + queryAs, raw, SerializerI, update, @@ -26,38 +28,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(), - ...queryAs.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 AdapterI, + cache: null as unknown as CacheI, + serializer: null as unknown as SerializerI, + deserializer: null as unknown as DeserializerI, + }); 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()); @@ -70,60 +51,40 @@ test('Actions are type safe', async () => { all(), ) as PostMock[]; - expectTypeOf(postsUsingFunc).toMatchTypeOf(); - expectTypeOf(postsUsingBuild).toMatchTypeOf(); - expectTypeOf(postsUsingVariadic).toMatchTypeOf(); - expectTypeOf(postsUsingRunVariadic).toMatchTypeOf(); - expectTypeOf(manualPostsUsingRunVariadic).toMatchTypeOf(); + expectTypeOf(postsUsingFunc).toEqualTypeOf(); + expectTypeOf(postsUsingVariadic).toEqualTypeOf(); + expectTypeOf(postsUsingRunVariadic).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 asPostsUsingBuild = await action() - .queryAs(PostMock) - .include('comments.postedBy') - .all(); const asPostsOrCommentsUsingFunc = await action() .use(queryAs(PostMock, CommentMock)) .use(include('images')) .run(all()); - const asPostsOrCommentsUsingBuild = await action() - .queryAs(PostMock, CommentMock) - .include('images') - .all(); const asPostsOrCommentsArrayUsingFunc = await action() .use(queryAs([PostMock, CommentMock])) .use(include('images')) .run(all()); - const asPostsOrCommentsArrayUsingBuild = await action() - .queryAs([PostMock, CommentMock]) - .include('images') - .all(); - expectTypeOf(asPostsUsingFunc).toMatchTypeOf(); - expectTypeOf(asPostsUsingBuild).toMatchTypeOf(); - expectTypeOf(asPostsOrCommentsUsingFunc).toMatchTypeOf<(PostMock | CommentMock)[]>(); - expectTypeOf(asPostsOrCommentsUsingBuild).toMatchTypeOf<(PostMock | CommentMock)[]>(); - expectTypeOf(asPostsOrCommentsArrayUsingFunc).toMatchTypeOf<(PostMock | CommentMock)[]>(); - expectTypeOf(asPostsOrCommentsArrayUsingBuild).toMatchTypeOf<(PostMock | CommentMock)[]>(); + 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')); @@ -136,10 +97,6 @@ test('Actions are type safe', async () => { .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'), @@ -148,74 +105,75 @@ 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().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 @@ -238,6 +196,16 @@ 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 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()])); // @ts-expect-error title is not a post relation await action().query(PostMock).include('title'); @@ -267,16 +235,12 @@ test('Actions are type safe', async () => { 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(); }); diff --git a/packages/core/tests/typecheck/models-composition.test-d.ts b/packages/core/tests/typecheck/models-composition.test-d.ts index 20edd6a2..297c695a 100644 --- a/packages/core/tests/typecheck/models-composition.test-d.ts +++ b/packages/core/tests/typecheck/models-composition.test-d.ts @@ -27,14 +27,14 @@ 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(); + 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 +43,16 @@ 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(); + 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 +67,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, diff --git a/packages/core/tests/typecheck/models.test-d.ts b/packages/core/tests/typecheck/models.test-d.ts index ae36414b..8c5affe1 100644 --- a/packages/core/tests/typecheck/models.test-d.ts +++ b/packages/core/tests/typecheck/models.test-d.ts @@ -9,13 +9,13 @@ import UserMock from '../mocks/models/user.mock'; test('Models are type safe', () => { 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.published).toEqualTypeOf(); post.title = 'Hello World'; // @ts-expect-error publishedAt is readonly @@ -31,11 +31,11 @@ test('Models are type safe', () => { 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 @@ -55,11 +55,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 +69,7 @@ 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(); }); 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..7836227d --- /dev/null +++ b/packages/core/tests/unit/actions/makeActionFactory.test.ts @@ -0,0 +1,220 @@ +import { + Action, + ActionCall, + cachedOr, + context, + ContextFunctionType, + isContextFunction, + logger, + makeActionFactory, + makeCache, + makeModel, + onError, + onFinally, + onRunning, + onSuccess, + 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'; + + await expect( + makeActionFactory()() + .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( + makeActionFactory()() + .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 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 91% rename from packages/core/tests/unit/cache/makeRefsCacheWith.test.ts rename to packages/core/tests/unit/cache/makeRefsCache.test.ts index a8c20d39..281f40ee 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 { makeRefsCache, makeWeakRefManager } from '@foscia/core'; import { describe, expect, it, vi } 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({ manager: makeWeakRefManager() }); expect(await cache.find('posts', '1')).toBeNull(); expect(await cache.find('posts', '2')).toBeNull(); @@ -64,7 +64,7 @@ describe.concurrent('unit: makeRefsCacheWith', () => { value: vi.fn(), }; - const cache = makeRefsCacheWith({ manager: refManager }); + const { cache } = makeRefsCache({ manager: refManager }); expect(await cache.find('posts', '1')).toBeNull(); expect(refManager.value).not.toHaveBeenCalled(); diff --git a/packages/core/tests/unit/cache/makeTimeoutRefManager.test.ts b/packages/core/tests/unit/cache/makeTimeoutRefManager.test.ts new file mode 100644 index 00000000..90e6924d --- /dev/null +++ b/packages/core/tests/unit/cache/makeTimeoutRefManager.test.ts @@ -0,0 +1,32 @@ +import { makeTimeoutRefManager } from '@foscia/core'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import PostMock from '../../mocks/models/post.mock'; + +describe.concurrent('unit: makeTimeoutRefManager', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should use a timeout ref instance', async () => { + const post = new PostMock(); + + const manager = makeTimeoutRefManager({ timeout: 1000 }); + const ref = await manager.ref(post); + + expect(manager.value(ref)).toBe(post); + + vi.advanceTimersByTime(500); + expect(manager.value(ref)).toBe(post); + + // Value reset timers. + vi.advanceTimersByTime(750); + expect(manager.value(ref)).toBe(post); + + vi.advanceTimersByTime(1100); + expect(manager.value(ref)).toBe(undefined); + }); +}); diff --git a/packages/core/tests/unit/cache/weakRefManager.test.ts b/packages/core/tests/unit/cache/makeWeakRefManager.test.ts similarity index 50% rename from packages/core/tests/unit/cache/weakRefManager.test.ts rename to packages/core/tests/unit/cache/makeWeakRefManager.test.ts index 1e39677a..8a6250a1 100644 --- a/packages/core/tests/unit/cache/weakRefManager.test.ts +++ b/packages/core/tests/unit/cache/makeWeakRefManager.test.ts @@ -1,14 +1,15 @@ -import { weakRefManager } from '@foscia/core'; +import { makeWeakRefManager } from '@foscia/core'; import { describe, expect, it } from 'vitest'; import PostMock from '../../mocks/models/post.mock'; -describe.concurrent('unit: weakRefCacheMode', () => { +describe.concurrent('unit: makeWeakRefManager', () => { it('should use a weak ref instance', async () => { const post = new PostMock(); - const ref = await weakRefManager.ref(post); + const manager = makeWeakRefManager(); + const ref = await manager.ref(post); expect(ref).toBeInstanceOf(WeakRef); - expect(weakRefManager.value(ref)).toBe(post); + expect(manager.value(ref)).toBe(post); }); }); 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..fb442bcf 100644 --- a/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts +++ b/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts @@ -1,6 +1,11 @@ import { FosciaError } from '@foscia/core'; import consumeRequestConfig from '@foscia/http/actions/context/consumers/consumeRequestConfig'; +/** + * Consume object params. Will throw an exception if string params are configured. + * + * @param context + */ export default (context: {}) => { const config = consumeRequestConfig(context, null); if (typeof config?.params === 'string') { 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..bfe5a9ae 100644 --- a/packages/http/src/actions/context/enhancers/configureRequest.ts +++ b/packages/http/src/actions/context/enhancers/configureRequest.ts @@ -1,14 +1,15 @@ -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'; /** - * 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.). * 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) + * 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 * applied, but other automatic transformation or data passing (params, body, etc.) @@ -17,8 +18,49 @@ import { HttpRequestConfig } from '@foscia/http/types'; * @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); @@ -46,15 +88,4 @@ const configureRequest = ( ], } 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..2113c9b8 100644 --- a/packages/http/src/errors/httpNotFoundError.ts +++ b/packages/http/src/errors/httpNotFoundError.ts @@ -1,6 +1,11 @@ import { NotFoundErrorI } from '@foscia/core'; import HttpInvalidRequestError from '@foscia/http/errors/httpInvalidRequestError'; +/** + * Error thrown on HTTP response status `404 Not Found`. + * + * @group Errors + */ export default class HttpNotFoundError extends HttpInvalidRequestError implements NotFoundErrorI { public readonly $FOSCIA_ERROR_NOT_FOUND = true; } 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 93% rename from packages/http/src/makeHttpAdapterWith.ts rename to packages/http/src/makeHttpAdapter.ts index 84552e89..8eb36fb6 100644 --- a/packages/http/src/makeHttpAdapterWith.ts +++ b/packages/http/src/makeHttpAdapter.ts @@ -27,6 +27,13 @@ import { import clearEndpoint from '@foscia/http/utilities/clearEndpoint'; import { Dictionary, isNil, optionalJoin, sequentialTransform } from '@foscia/shared'; +/** + * Make a {@link HttpAdapter | `HttpAdapter`}. + * + * @param config + * + * @category Factories + */ export default (config: HttpAdapterConfig) => { const transformRequest = ( contextConfig: HttpRequestConfig, @@ -169,11 +176,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) => ( @@ -215,5 +228,5 @@ export default (config: HttpAdapterConfig) => { throw await transformError(contextConfig, makeResponseError(request, response)); }; - 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..eb30e412 100644 --- a/packages/http/src/makeHttpAdapterResponse.ts +++ b/packages/http/src/makeHttpAdapterResponse.ts @@ -1,6 +1,14 @@ import { AdapterResponseI } 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 }, diff --git a/packages/http/src/types.ts b/packages/http/src/types.ts index eea4fd19..c2c8d8f3 100644 --- a/packages/http/src/types.ts +++ b/packages/http/src/types.ts @@ -3,6 +3,8 @@ import { Awaitable, Dictionary, 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,74 @@ export type HttpRequestInitPickKey = /** * Context value representing a HTTP request config. + * + * @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; + /** + * Transforms the {@link !Request | `Request`} object sequentially + * (after adapter transformers). + */ requestTransformers?: RequestTransformer[]; + /** + * Transforms the {@link !Response | `Response`} object sequentially + * (after adapter transformers). + */ responseTransformers?: ResponseTransformer[]; + /** + * Transforms the thrown error sequentially + * (after adapter transformers). + */ errorTransformers?: ErrorTransformer[]; } & Pick; /** * Context containing a HTTP request config. + * + * @internal */ export type ConsumeHttpRequestConfig = { httpRequestConfig: HttpRequestConfig; @@ -61,47 +112,161 @@ 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; /** * The configuration for the HTTP adapter implementation. + * + * @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; + /** + * Transforms the {@link !Request | `Request`} object sequentially. + */ requestTransformers?: RequestTransformer[]; + /** + * Transforms the {@link !Response | `Response`} object sequentially. + */ responseTransformers?: ResponseTransformer[]; + /** + * Transforms the thrown error sequentially. + */ errorTransformers?: ErrorTransformer[]; }; -export interface HttpAdapter extends AdapterI { -} +/** + * HTTP adapter. + * + * @internal + */ +export type HttpAdapter = AdapterI; +/** + * HTTP query params serializer. + * + * @internal + */ export type HttpParamsSerializer = (params: Dictionary) => string | undefined; +/** + * Transforms a {@link !Request | `Request`} object. + * + * @internal + */ export type RequestTransformer = (request: Request) => Awaitable; + +/** + * Transforms a {@link !Response | `Response`} object. + * + * @internal + */ export type ResponseTransformer = (response: Response) => Awaitable; + +/** + * Transforms an unknown error. + * + * @internal + */ 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..cad7ca13 100644 --- a/packages/http/src/utilities/deepParamsSerializer.ts +++ b/packages/http/src/utilities/deepParamsSerializer.ts @@ -1,5 +1,23 @@ import { Dictionary } 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) => { const urlSearchParams = new URLSearchParams(); 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..eb25e676 100644 --- a/packages/jsonapi/src/actions/context/enhancers/fields.ts +++ b/packages/jsonapi/src/actions/context/enhancers/fields.ts @@ -1,11 +1,10 @@ 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'; @@ -14,13 +13,26 @@ import { ArrayableVariadic, isNil } 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); @@ -32,15 +44,4 @@ const fields = ( } return action.use(fieldsFor(model as any, ...fieldset)); -}; - -export default /* @__PURE__ */ appendExtension( - 'fields', - fields, - 'use', -) as WithParsedExtension( - this: Action, - ...fieldset: ArrayableVariadic>> - ): Action; -}>; +}); 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..1d55144e 100644 --- a/packages/jsonapi/src/actions/context/enhancers/sortBy.ts +++ b/packages/jsonapi/src/actions/context/enhancers/sortBy.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 { Arrayable, Dictionary, optionalJoin, uniqueValues, wrap } from '@foscia/shared'; @@ -32,30 +32,10 @@ const serializeSort = ( key: string, 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 +45,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..bc89ebbc 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. + * Append the {@link JsonApiDocument | JSON:API document object} to data object. * Use it as the parameter of `allUsing` and `oneUsing` 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..f4d39818 --- /dev/null +++ b/packages/jsonapi/src/makeJsonApiAdapter.ts @@ -0,0 +1,36 @@ +import { ActionName, consumeAction } from '@foscia/core'; +import { clearEndpoint, deepParamsSerializer } from '@foscia/http'; +import { makeRestAdapter, RestAdapterConfig } 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..88021446 --- /dev/null +++ b/packages/jsonapi/src/makeJsonApiSerializer.ts @@ -0,0 +1,57 @@ +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'; + +/** + * 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>, +) => { + const serializeId = (id: Optional) => (isNil(id) ? undefined : String(id)); + + return makeSerializer({ + 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/types.ts b/packages/jsonapi/src/types.ts index 18b38cde..2f7eb8ef 100644 --- a/packages/jsonapi/src/types.ts +++ b/packages/jsonapi/src/types.ts @@ -16,6 +16,8 @@ import { Arrayable, Awaitable, Dictionary, IdentifiersMap } from '@foscia/shared /** * @see [JSON:API specification](https://jsonapi.org/format/#document-links) + * + * @internal */ export type JsonApiLink = { href: string; @@ -24,16 +26,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 +51,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 +69,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 +90,8 @@ export type JsonApiAbstractResource = { /** * @see [JSON:API specification](https://jsonapi.org/format/#document-resource-objects) + * + * @internal */ export type JsonApiResource = JsonApiAbstractResource & { id: string; @@ -81,6 +99,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 +108,8 @@ export type JsonApiNewResource = JsonApiAbstractResource & { /** * @see [JSON:API specification](https://jsonapi.org/format/#error-objects) + * + * @internal */ export type JsonApiError = { status?: string; @@ -104,6 +126,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 +143,8 @@ export type JsonApiDocument = { /** * Extracted data from a JSON:API backend Response object. + * + * @internal */ export type JsonApiExtractedData = & { @@ -134,6 +160,11 @@ export type JsonApiDeserializedData = & DeserializedData & { document: JsonApiDocument; }; +/** + * Configuration for JSON:API deserializer. + * + * @internal + */ export type JsonApiDeserializerConfig< Record extends JsonApiNewResource, Data extends JsonApiDocument | undefined, @@ -141,12 +172,36 @@ 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, @@ -155,6 +210,11 @@ export type JsonApiDeserializerConfig< } & DeserializerConfig; +/** + * Configuration for JSON:API serializer. + * + * @internal + */ export type JsonApiSerializerConfig< Record extends JsonApiNewResource, Related extends JsonApiResourceIdentifier, 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..f5719d15 100644 --- a/packages/rest/src/makeIncludeParam.ts +++ b/packages/rest/src/makeIncludeParam.ts @@ -1,6 +1,14 @@ 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', diff --git a/packages/rest/src/makeRestAdapter.ts b/packages/rest/src/makeRestAdapter.ts new file mode 100644 index 00000000..edde8f2c --- /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 { toKebabCase } from '@foscia/shared'; + +/** + * Make a REST adapter object. + * + * @param config + * + * @category Factories + * @since 0.13.0 + */ +export default ( + config: Partial> = {}, +) => makeHttpAdapter({ + baseURL: '/api', + modelPathTransformer: toKebabCase, + relationPathTransformer: toKebabCase, + 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..0c464a48 --- /dev/null +++ b/packages/rest/src/makeRestSerializer.ts @@ -0,0 +1,36 @@ +import { RestNewResource, RestSerializerConfig } from '@foscia/rest/types'; +import { makeSerializer, makeSerializerRecordFactory } from '@foscia/serialization'; +import { Arrayable } 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( + (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/types.ts b/packages/rest/src/types.ts index 373c04c4..661b07c7 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -9,22 +9,43 @@ import { } 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. + * + * @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. + * + * @internal + */ export type RestDeserializerConfig< Record, Data, @@ -32,12 +53,36 @@ 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, @@ -46,12 +91,21 @@ export type RestDeserializerConfig< } & DeserializerConfig; +/** + * Configuration for REST serializer. + * + * @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; diff --git a/packages/rest/tests/integration/crud.test.ts b/packages/rest/tests/integration/crud.test.ts index ae192a8f..eefd90c8 100644 --- a/packages/rest/tests/integration/crud.test.ts +++ b/packages/rest/tests/integration/crud.test.ts @@ -19,7 +19,7 @@ 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'; @@ -53,7 +53,7 @@ describe('integration: JSON REST', () => { }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const posts = await action() .use(query(PostMock)) @@ -108,7 +108,7 @@ describe('integration: JSON REST', () => { }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const post = fill(new PostMock(), { id: '1' }); post.$exists = true; @@ -145,7 +145,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] }); @@ -182,7 +182,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' }); @@ -219,7 +219,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'; @@ -262,7 +262,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'; @@ -296,7 +296,7 @@ describe('integration: JSON REST', () => { { id: '1', title: 'Foo' }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const [post] = await action() .use( @@ -324,7 +324,7 @@ describe('integration: JSON REST', () => { { type: 'comments', id: '1', body: 'Foo bar' }, ])); - const action = makeJsonRestActionMock(); + const action = makeRestActionMock(); const [post, comment] = await action() .use( @@ -355,7 +355,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)), @@ -421,7 +421,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/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/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 96% rename from packages/rest/tests/unit/makeJsonRestSerializer.test.ts rename to packages/rest/tests/unit/makeRestSerializer.test.ts index aec7a874..d6ab58de 100644 --- a/packages/rest/tests/unit/makeJsonRestSerializer.test.ts +++ b/packages/rest/tests/unit/makeRestSerializer.test.ts @@ -6,13 +6,12 @@ import { makeComposable, makeModel, makeTransformer, - ModelInstance, } 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 authored = makeComposable({ author: hasOne(), }); @@ -79,7 +78,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { forceFill(user2, { posts: [post2] }); type TestDataProvider = [ - ModelInstance, + typeof post1, Partial>, {}, (assertion: Assertion) => Awaitable, @@ -281,7 +280,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { ] as TestDataProvider[])( 'should serialize instance with configuration', async (instance, config, context, expectation) => { - const { serializer } = makeJsonRestSerializer(config); + const { serializer } = makeRestSerializer(config); const serialize = async () => serializer.serialize( await serializer.serializeInstance(instance, context), context, @@ -328,7 +327,7 @@ describe.concurrent('unit: makeJsonRestSerializer', () => { ] as TestDataProvider[])( 'should serialize relation with configuration', async (instance, config, context, expectation) => { - const { serializer } = makeJsonRestSerializer(config); + const { serializer } = makeRestSerializer(config); const serialize = async () => serializer.serialize( await serializer.serializeRelation( instance, @@ -351,7 +350,7 @@ 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(), diff --git a/packages/serialization/src/errors/serializerCircularRelationError.ts b/packages/serialization/src/errors/serializerCircularRelationError.ts index 5fcd3d8e..98586b54 100644 --- a/packages/serialization/src/errors/serializerCircularRelationError.ts +++ b/packages/serialization/src/errors/serializerCircularRelationError.ts @@ -3,6 +3,8 @@ 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; 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 97% rename from packages/serialization/src/makeDeserializerWith.ts rename to packages/serialization/src/makeDeserializer.ts index d2ba123e..0ee9a7c6 100644 --- a/packages/serialization/src/makeDeserializerWith.ts +++ b/packages/serialization/src/makeDeserializer.ts @@ -22,7 +22,7 @@ import { shouldSync, } from '@foscia/core'; import { - Deserializer, + GenericDeserializer, DeserializerConfig, DeserializerContext, DeserializerExtract, @@ -43,6 +43,13 @@ import { // eslint-disable-next-line max-len /* eslint no-param-reassign: ["error", { "props": true, "ignorePropertyModificationsForRegex": ["^instance"] } ] */ +/** + * Make a {@link Deserializer | `Deserializer`} using the given config. + * + * @param config + * + * @category Factories + */ export default < Record, Data, @@ -73,7 +80,7 @@ export default < // This will also ensure guessed model type matches deserializing record. const guessedModel = await guessContextModel({ queryAs: consumeQueryAs(context, null), - model: (record.parent?.instance.$model ?? consumeModel(context, null)) as Model, + model: (record.parent?.instance.$model ?? consumeModel(context, null)) as Model | null, relation: record.parent?.def ?? consumeRelation(context, null), registry, ensureType: identifier.type, @@ -158,7 +165,7 @@ You should either: instancesMap, )); - let deserializer: Deserializer; + let deserializer: GenericDeserializer; const makeInstancesMapIdentifier = (identifier: DeserializerModelIdentifier) => ( identifier.id ?? identifier.lid ?? NON_IDENTIFIED_LOCAL_ID @@ -327,5 +334,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 95% rename from packages/serialization/src/makeSerializerWith.ts rename to packages/serialization/src/makeSerializer.ts index 04b58db0..3776c9f1 100644 --- a/packages/serialization/src/makeSerializerWith.ts +++ b/packages/serialization/src/makeSerializer.ts @@ -11,13 +11,20 @@ import { import SerializerCircularRelationError from '@foscia/serialization/errors/serializerCircularRelationError'; import { - Serializer, + GenericSerializer, SerializerConfig, SerializerContext, SerializerParents, } from '@foscia/serialization/types'; import { Arrayable, Awaitable, mapArrayable } from '@foscia/shared'; +/** + * Make a {@link Serializer | `Serializer`} using the given config. + * + * @param config + * + * @category Factories + */ export default ( config: SerializerConfig, ) => { @@ -62,7 +69,7 @@ export default ( const circularRelationBehavior = config.circularRelationBehavior ?? (() => 'skip'); - let serializer: Serializer; + let serializer: GenericSerializer; const makeSerializerContext = ( instance: ModelInstance, @@ -148,5 +155,5 @@ export default ( serialize, }; - return serializer; + return { serializer }; }; diff --git a/packages/serialization/src/makeSerializerRecordFactory.ts b/packages/serialization/src/makeSerializerRecordFactory.ts index acfc0989..53db77e3 100644 --- a/packages/serialization/src/makeSerializerRecordFactory.ts +++ b/packages/serialization/src/makeSerializerRecordFactory.ts @@ -2,6 +2,14 @@ import { ModelInstance } from '@foscia/core'; import { SerializerContext, SerializerRecordFactory } from '@foscia/serialization/types'; import { Awaitable } from '@foscia/shared'; +/** + * Make a {@link SerializerRecordFactory | `SerializerRecordFactory`} implementation. + * + * @param initialize + * @param put + * + * @category Factories + */ export default < Record, Related, diff --git a/packages/serialization/src/types.ts b/packages/serialization/src/types.ts index 14861ac8..b505772b 100644 --- a/packages/serialization/src/types.ts +++ b/packages/serialization/src/types.ts @@ -10,6 +10,9 @@ import { } 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 +24,21 @@ export type DeserializerContext< key: string; value: unknown; context: {}; - deserializer: Deserializer; + deserializer: GenericDeserializer; }; +/** + * 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 +46,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 +81,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 +98,106 @@ export type DeserializerRecordFactory< parent?: DeserializerRecordParent, ) => Promise>; +/** + * Deserializer map of instance promises by type and ID. + * + * @internal + */ export type DeserializerInstancesMap = IdentifiersMap>; +/** + * Configuration for deserializer. + * + * @internal + */ export type DeserializerConfig< 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. + * You should pass a {@link makeDeserializerRecordFactory | `makeDeserializerRecordFactory`} + * returned value instead of implementing the factory yourself. + */ 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. + * + * @internal + */ +export type GenericDeserializer = + & { + deserializeRecord( + record: DeserializerRecord, + context: {}, + instancesMap?: DeserializerInstancesMap, + ): Awaitable; + } + & DeserializerI; /** * Serializer context passed to config callbacks. @@ -124,11 +213,13 @@ export type SerializerContext< key: string; value: unknown; context: {}; - serializer: Serializer; + serializer: GenericSerializer; }; /** - * Pending serializer record. + * Pending serializer record which holds properties building. + * + * @internal */ export type SerializerRecord = { /** @@ -145,6 +236,11 @@ export type SerializerRecord = { retrieve(): Awaitable; }; +/** + * Factory to create a pending serializer record. + * + * @internal + */ export type SerializerRecordFactory = ( instance: ModelInstance, context: {}, @@ -153,50 +249,140 @@ export type SerializerRecordFactory = ( /** * Array of previously serialized relationships to avoid circular relations * serialization. + * + * @internal */ export type SerializerParents = { instance: ModelInstance; 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'; +/** + * Configuration for serializer. + * + * @internal + */ export type SerializerConfig = { + /** + * Create a serializer record object which can be hydrated and retrieved. + * You should pass a {@link makeSerializerRecordFactory | `makeSerializerRecordFactory`} + * returned value instead of implementing the factory yourself. + */ 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, 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, 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. + * + * @internal + */ +export type GenericSerializer = + & { + /** + * Serialize a given instance value. + * This overload will handle circular relations using the parents of the instance. + * + * @param instance + * @param context + * @param parents + */ + serializeInstance( + instance: ModelInstance, + context: {}, + parents?: SerializerParents, + ): Awaitable; + } + & SerializerI; diff --git a/packages/serialization/tests/unit/makeSerializerWith.test.ts b/packages/serialization/tests/unit/makeSerializer.test.ts similarity index 87% rename from packages/serialization/tests/unit/makeSerializerWith.test.ts rename to packages/serialization/tests/unit/makeSerializer.test.ts index 32573738..ff9c0687 100644 --- a/packages/serialization/tests/unit/makeSerializerWith.test.ts +++ b/packages/serialization/tests/unit/makeSerializer.test.ts @@ -1,9 +1,9 @@ import { fill, hasMany, hasOne, makeModel } from '@foscia/core'; -import { makeSerializerRecordFactory, makeSerializerWith } from '@foscia/serialization'; +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', { comments: hasMany(), @@ -23,7 +23,7 @@ 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), (record, { key, value }) => { 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..e188db3a 100644 --- a/packages/shared/src/arrays/mapArrayable.ts +++ b/packages/shared/src/arrays/mapArrayable.ts @@ -1,6 +1,14 @@ import isNil from '@foscia/shared/checks/isNil'; import { Arrayable, Awaitable, Optional } from '@foscia/shared/types'; +/** + * Map an optional arrayable value. + * + * @param value + * @param callback + * + * @internal + */ export default async ( value: Optional>, callback: (value: T) => Awaitable, 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/sequentialTransform.ts b/packages/shared/src/arrays/sequentialTransform.ts index 50a7c436..a5666c37 100644 --- a/packages/shared/src/arrays/sequentialTransform.ts +++ b/packages/shared/src/arrays/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/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/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..d2e559f9 100644 --- a/packages/shared/src/configs/mergeConfig.ts +++ b/packages/shared/src/configs/mergeConfig.ts @@ -4,6 +4,8 @@ * @param config * @param newConfig * @param override + * + * @internal */ export default ( config: C, 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..a72567c2 100644 --- a/packages/shared/src/descriptors/makeDescriptorHolder.ts +++ b/packages/shared/src/descriptors/makeDescriptorHolder.ts @@ -1,7 +1,19 @@ import type { DescriptorHolder } from '@foscia/shared/descriptors/types'; +/** + * Symbol for the descriptor holder. + * + * @internal + */ export const SYMBOL_DESCRIPTOR_HOLDER = Symbol('foscia: descriptor holder'); +/** + * Create a descriptor holder. + * + * @param descriptor + * + * @internal + */ export default (descriptor: PropertyDescriptor) => ({ $FOSCIA_TYPE: SYMBOL_DESCRIPTOR_HOLDER, descriptor, diff --git a/packages/shared/src/descriptors/types.ts b/packages/shared/src/descriptors/types.ts index e42eee13..c99d9ec2 100644 --- a/packages/shared/src/descriptors/types.ts +++ b/packages/shared/src/descriptors/types.ts @@ -3,6 +3,8 @@ import { FosciaObject } from '@foscia/shared/types'; /** * Type and descriptor holder for custom properties. + * + * @internal */ export type DescriptorHolder = { descriptor: PropertyDescriptor; diff --git a/packages/shared/src/env.ts b/packages/shared/src/env.ts index 9bd9747d..66704e1c 100644 --- a/packages/shared/src/env.ts +++ b/packages/shared/src/env.ts @@ -20,5 +20,16 @@ const detectEnv = () => { 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/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..21638ee3 100644 --- a/packages/shared/src/identifiers/uniqueId.ts +++ b/packages/shared/src/identifiers/uniqueId.ts @@ -1,3 +1,11 @@ +/** + * Generate a unique ID using generator. + * + * @param generator + * @param notIds + * + * @internal + */ const uniqueId = (generator: () => string, notIds: string[]): string => { const id = generator(); 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..0d64260c 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -11,9 +11,7 @@ import mergeConfig from '@foscia/shared/configs/mergeConfig'; import removeTimezoneOffset from '@foscia/shared/dates/removeTimezoneOffset'; import eachDescriptors from '@foscia/shared/descriptors/eachDescriptors'; import isDescriptorHolder from '@foscia/shared/descriptors/isDescriptorHolder'; -import makeDescriptorHolder, { - SYMBOL_DESCRIPTOR_HOLDER, -} from '@foscia/shared/descriptors/makeDescriptorHolder'; +import makeDescriptorHolder from '@foscia/shared/descriptors/makeDescriptorHolder'; import { IS_DEV, IS_TEST } from '@foscia/shared/env'; import value from '@foscia/shared/functions/value'; import uniqueId from '@foscia/shared/identifiers/uniqueId'; @@ -50,5 +48,4 @@ export { value, wrap, wrapVariadic, - SYMBOL_DESCRIPTOR_HOLDER, }; diff --git a/packages/shared/src/maps/makeIdentifiersMap.ts b/packages/shared/src/maps/makeIdentifiersMap.ts index 7cf310bb..f073cb62 100644 --- a/packages/shared/src/maps/makeIdentifiersMap.ts +++ b/packages/shared/src/maps/makeIdentifiersMap.ts @@ -1,3 +1,8 @@ +/** + * Create an identifiers map object. + * + * @internal + */ export default () => { const values: Map> = new Map(); 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..170d4f1a 100644 --- a/packages/shared/src/strings/pluralize.ts +++ b/packages/shared/src/strings/pluralize.ts @@ -6,6 +6,13 @@ const rules: [RegExp, string][] = [ [/(.)$/i, '$1s'], ]; +/** + * Pluralize a word. + * + * @param word + * + * @internal + */ export default (word: string) => { let pluralizedWord: string | undefined; rules.some(([regexp, replacement]) => { diff --git a/packages/shared/src/strings/toKebabCase.ts b/packages/shared/src/strings/toKebabCase.ts index 0af18aaf..d274f39d 100644 --- a/packages/shared/src/strings/toKebabCase.ts +++ b/packages/shared/src/strings/toKebabCase.ts @@ -1,3 +1,10 @@ +/** + * Convert a string to kebab case. + * + * @param value + * + * @internal + */ 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, diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index d8f8028a..d7995878 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -1,34 +1,109 @@ +/** + * 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 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; +/** + * Transformer function from a type to another. + * + * @internal + */ 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; +/** + * Map identifier with type. + * + * @internal + */ export type IdentifiersMap = { find: (type: Type, id: Id) => T | null; put: (type: Type, id: Id, value: T) => void; @@ -37,6 +112,16 @@ 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; }; diff --git a/packages/test/src/fosciaTestError.ts b/packages/test/src/fosciaTestError.ts new file mode 100644 index 00000000..da4bf2fd --- /dev/null +++ b/packages/test/src/fosciaTestError.ts @@ -0,0 +1,12 @@ +import { FosciaError } from '@foscia/core'; + +/** + * Error which occurs during testing code using a Foscia feature. + * + * @group Errors + * + * @internal + * @experimental + */ +export default class FosciaTestError extends FosciaError { +} diff --git a/packages/test/src/index.ts b/packages/test/src/index.ts index 84feaee9..14751d78 100644 --- a/packages/test/src/index.ts +++ b/packages/test/src/index.ts @@ -1,7 +1,7 @@ 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'; @@ -11,5 +11,5 @@ export { makeActionFactoryMock, mockAction, unmockAction, - UnexpectedMockedRunError, + UnexpectedActionError, }; diff --git a/packages/test/src/makeActionFactoryMock.ts b/packages/test/src/makeActionFactoryMock.ts index d09830af..b2e8b774 100644 --- a/packages/test/src/makeActionFactoryMock.ts +++ b/packages/test/src/makeActionFactoryMock.ts @@ -1,167 +1,130 @@ -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, +} from '@foscia/core'; +import { sequentialTransform, 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. + * + * @experimental + */ +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 runners = await unnestRunners(action, [rootRunner]); + const calls = action.calls(); + const testContext = makeActionTestContext(context, calls, runners); - history.push(makeActionMockedHistoryItem(context)); + const mock = await sequentialTransform(mocks.map((next) => async (prev: ActionMock | null) => { + if (prev) { + return prev; + } - const mockedRun = await findMockedRun(context); - if (!mockedRun) { - throw new UnexpectedMockedRunError(context); + return await next.shouldRun(testContext) ? next : null; + }), null); + if (!mock) { + throw new UnexpectedActionError(context); } + await runHooks(action, 'running', { context, runner: rootRunner }); + try { - const result = await mockedRun.run(context); + const result = await mock.run(testContext); + + await runHooks(action, 'success', { context, result }); - await forgetMockedRun(mockedRun); + history.push({ context: testContext, mock, result, error: undefined }); return result; } catch (error) { - await forgetMockedRun(mockedRun); + await runHooks(action, 'error', { context, 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', { context }); } }; - const makeAction = (...args: A) => new Proxy(factory(...args), { + const make = (...args: A) => new Proxy(factory(...args), { get: (target, property) => ( property === 'run' - ? (...enhancers: any[]) => runMockedRun(target, enhancers) - : target[property as keyof Action] + ? ( + ...enhancers: (ContextEnhancer | ContextRunner)[] + ) => run(target, enhancers) + : target[property as keyof Action] ), }); - const mockResult = ( - result?: ActionMockedResult | ActionMockedRunOptions, - predicate?: ActionMockedPredicate, - ) => { - const mockedRun = makeMockedRun(result, predicate); - - mocks.push(mockedRun); - }; + const mock = (result?: unknown) => { + const newMock = makeActionMock().return(result); - 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; - }; + mocks.push(newMock); - /** - * Reset the ran contexts history. - */ - const resetHistory = () => { - history.length = 0; + 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..503d51d3 100644 --- a/packages/test/src/makeActionFactoryMockable.ts +++ b/packages/test/src/makeActionFactoryMockable.ts @@ -5,17 +5,20 @@ import type { ActionFactoryMock, ActionMockableFactory } from '@foscia/test/type * Creates a proxy of an action factory which can be mocked. * * @param factory + * + * @category Factories + * @experimental */ -export default ( - factory: ActionFactory, -): ActionMockableFactory => { +export default ( + factory: ActionFactory, +): ActionMockableFactory => { const mockableFactory = (...args: A) => ( mockableFactory.$mock - ? mockableFactory.$mock.makeAction(...args) + ? mockableFactory.$mock.make(...args) : mockableFactory.$real(...args) ); - 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..19e20525 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..02bcafc6 100644 --- a/packages/test/src/types.ts +++ b/packages/test/src/types.ts @@ -1,86 +1,293 @@ -import { Action, ActionFactory } from '@foscia/core'; -import { Awaitable } from '@foscia/shared'; +import { Action, ActionCall, ActionFactory } 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. + * + * @param args + * + * @internal + */ + make(...args: A): 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..68885ce1 --- /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 testContext: ActionTestContext; + + public constructor(testContext: 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.testContext = testContext; + } +} 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..e2dbab28 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..3d53861e 100644 --- a/packages/test/tests/unit/mocking.test.ts +++ b/packages/test/tests/unit/mocking.test.ts @@ -1,16 +1,14 @@ -import { context, makeActionClass, AdapterI, raw } from '@foscia/core'; +import { AdapterI, 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(); @@ -18,7 +16,7 @@ describe.concurrent('unit: mocking', () => { return { raw: rawData, read: () => rawData.json() }; }, } as AdapterI, - })))), + })), }; }; @@ -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..61970050 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: version: 7.14.1(eslint@8.57.0)(typescript@5.5.2) '@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.39)(jsdom@24.1.0)(terser@5.32.0)) ansi-colors: specifier: ^4.1.3 version: 4.1.3 @@ -103,7 +103,7 @@ importers: version: 5.5.2 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.39)(jsdom@24.1.0)(terser@5.32.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.5.2 + version: 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@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.5.2 + version: 3.5.2(@algolia/client-search@4.24.0)(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2) '@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.5.2 + version: 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) '@fontsource-variable/onest': - specifier: ^5.0.4 - version: 5.0.4 + specifier: ^5.0.6 + version: 5.0.6 '@fontsource/fira-mono': - specifier: ^5.0.13 - version: 5.0.13 + specifier: ^5.0.15 + version: 5.0.15 '@mdx-js/react': specifier: ^3.0.1 - version: 3.0.1(@types/react@18.3.3)(react@18.3.1) + version: 3.0.1(@types/react@18.3.5)(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.0.5 + version: 1.0.5(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2))) dotenv: specifier: ^16.4.5 version: 16.4.5 prism-react-renderer: - specifier: ^2.3.1 - version: 2.3.1(react@18.3.1) + specifier: ^2.4.0 + version: 2.4.0(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -260,27 +260,30 @@ 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.26.7 + version: 0.26.7(typescript@5.6.2) typedoc-plugin-markdown: - specifier: ^4.0.3 - version: 4.0.3(typedoc@0.25.13(typescript@5.4.5)) + specifier: ^4.2.7 + version: 4.2.7(typedoc@0.26.7(typescript@5.6.2)) + typedoc-plugin-mdn-links: + specifier: ^3.2.12 + version: 3.2.12(typedoc@0.26.7(typescript@5.6.2)) typescript: - specifier: ^5.4.5 - version: 5.4.5 + specifier: ^5.6.2 + version: 5.6.2 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.5.2 + version: 3.5.2(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.3.3 + version: 3.3.3 rimraf: - specifier: ^5.0.7 - version: 5.0.7 + specifier: ^5.0.10 + version: 5.0.10 packages: @@ -304,53 +307,53 @@ packages: '@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/cache-browser-local-storage@4.24.0': + resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==} - '@algolia/cache-common@4.23.3': - resolution: {integrity: sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==} + '@algolia/cache-common@4.24.0': + resolution: {integrity: sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==} - '@algolia/cache-in-memory@4.23.3': - resolution: {integrity: sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==} + '@algolia/cache-in-memory@4.24.0': + resolution: {integrity: sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==} - '@algolia/client-account@4.23.3': - resolution: {integrity: sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==} + '@algolia/client-account@4.24.0': + resolution: {integrity: sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==} - '@algolia/client-analytics@4.23.3': - resolution: {integrity: sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==} + '@algolia/client-analytics@4.24.0': + resolution: {integrity: sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==} - '@algolia/client-common@4.23.3': - resolution: {integrity: sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==} + '@algolia/client-common@4.24.0': + resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==} - '@algolia/client-personalization@4.23.3': - resolution: {integrity: sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==} + '@algolia/client-personalization@4.24.0': + resolution: {integrity: sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==} - '@algolia/client-search@4.23.3': - resolution: {integrity: sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==} + '@algolia/client-search@4.24.0': + resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==} '@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-common@4.24.0': + resolution: {integrity: sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==} - '@algolia/logger-console@4.23.3': - resolution: {integrity: sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==} + '@algolia/logger-console@4.24.0': + resolution: {integrity: sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==} - '@algolia/recommend@4.23.3': - resolution: {integrity: sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==} + '@algolia/recommend@4.24.0': + resolution: {integrity: sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==} - '@algolia/requester-browser-xhr@4.23.3': - resolution: {integrity: sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==} + '@algolia/requester-browser-xhr@4.24.0': + resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==} - '@algolia/requester-common@4.23.3': - resolution: {integrity: sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==} + '@algolia/requester-common@4.24.0': + resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==} - '@algolia/requester-node-http@4.23.3': - resolution: {integrity: sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==} + '@algolia/requester-node-http@4.24.0': + resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==} - '@algolia/transporter@4.23.3': - resolution: {integrity: sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==} + '@algolia/transporter@4.24.0': + resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} @@ -364,14 +367,26 @@ packages: resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.25.4': + resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} + engines: {node: '>=6.9.0'} + '@babel/core@7.24.7': resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} engines: {node: '>=6.9.0'} + '@babel/core@7.25.2': + resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.24.7': resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} engines: {node: '>=6.9.0'} + '@babel/generator@7.25.6': + resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.24.7': resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} engines: {node: '>=6.9.0'} @@ -384,14 +399,18 @@ packages: resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.24.7': - resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==} + '@babel/helper-compilation-targets@7.25.2': + resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.25.4': + resolution: {integrity: sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==} 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.25.2': + resolution: {integrity: sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -413,8 +432,8 @@ packages: 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.24.8': + resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.24.7': @@ -427,22 +446,28 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.25.2': + resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + 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==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.24.7': - resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} + '@babel/helper-plugin-utils@7.24.8': + resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} 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.0': + resolution: {integrity: sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==} 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.25.0': + resolution: {integrity: sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -463,6 +488,10 @@ packages: resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} @@ -471,14 +500,22 @@ packages: resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.24.7': - resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.25.0': + resolution: {integrity: sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==} engines: {node: '>=6.9.0'} '@babel/helpers@7.24.7': resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.25.6': + resolution: {integrity: sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==} + engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} @@ -488,14 +525,25 @@ packages: 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/parser@7.25.6': + resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3': + resolution: {integrity: sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.0': + resolution: {integrity: sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==} 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.0': + resolution: {integrity: sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -506,8 +554,8 @@ packages: 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.0': + resolution: {integrity: sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -544,14 +592,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-assertions@7.24.7': - resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==} + '@babel/plugin-syntax-import-assertions@7.25.6': + resolution: {integrity: sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==} 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==} + '@babel/plugin-syntax-import-attributes@7.25.6': + resolution: {integrity: sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -614,8 +662,8 @@ packages: 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.4': + resolution: {integrity: sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -632,8 +680,8 @@ packages: 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.4': + resolution: {integrity: sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -650,14 +698,14 @@ packages: 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.0': + resolution: {integrity: sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==} 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.4': + resolution: {integrity: sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -668,8 +716,8 @@ packages: peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.24.7': - resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==} + '@babel/plugin-transform-classes@7.25.4': + resolution: {integrity: sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -680,8 +728,8 @@ packages: 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.24.8': + resolution: {integrity: sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -698,6 +746,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.0': + resolution: {integrity: sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/plugin-transform-dynamic-import@7.24.7': resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==} engines: {node: '>=6.9.0'} @@ -722,8 +776,8 @@ packages: 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.1': + resolution: {integrity: sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -734,8 +788,8 @@ packages: 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.2': + resolution: {integrity: sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -758,14 +812,14 @@ packages: 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.24.8': + resolution: {integrity: sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==} 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.0': + resolution: {integrity: sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -818,8 +872,8 @@ packages: 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.24.8': + resolution: {integrity: sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -830,8 +884,8 @@ packages: 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.4': + resolution: {integrity: sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -848,8 +902,8 @@ packages: 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.1': + resolution: {integrity: sha512-SLV/giH/V4SmloZ6Dt40HjTGTAIkxn33TVIHxNGNvo8ezMhrxBkzisj4op1KZYPIOHFLqhv60OHvX+YRu4xbmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -866,8 +920,8 @@ packages: 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.2': + resolution: {integrity: sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -890,8 +944,8 @@ packages: 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.4': + resolution: {integrity: sha512-8hsyG+KUYGY0coX6KUCDancA0Vw225KJ2HJO0yCNr1vq5r+lJTleDaJf0K7iOhjw4SWhu03TMBzYTJ9krmzULQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -920,14 +974,14 @@ packages: 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.24.8': + resolution: {integrity: sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==} 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.25.2': + resolution: {integrity: sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -950,14 +1004,14 @@ packages: 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.4': + resolution: {integrity: sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==} 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.25.4': + resolution: {integrity: sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -982,26 +1036,42 @@ packages: '@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.25.6': + resolution: {integrity: sha512-Gz0Nrobx8szge6kQQ5Z5MX9L3ObqNwCQY1PSwSNzreFL7aHGxv8Fp2j3ETV6/wWdbiV+mW6OSm8oQhg3Tcsniw==} engines: {node: '>=6.9.0'} '@babel/runtime@7.24.7': resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.25.6': + resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} + engines: {node: '>=6.9.0'} + '@babel/template@7.24.7': resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} + '@babel/template@7.25.0': + resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.24.7': resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.25.6': + resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.24.7': resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.25.6': + resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} + engines: {node: '>=6.9.0'} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -1079,11 +1149,11 @@ packages: 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.6.1': + resolution: {integrity: sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==} - '@docsearch/react@3.6.0': - resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} + '@docsearch/react@3.6.1': + resolution: {integrity: sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' react: '>= 16.8.0 < 19.0.0' @@ -1099,93 +1169,95 @@ packages: search-insights: optional: true - '@docusaurus/core@3.4.0': - resolution: {integrity: sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==} + '@docusaurus/core@3.5.2': + resolution: {integrity: sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==} engines: {node: '>=18.0'} hasBin: true peerDependencies: + '@mdx-js/react': ^3.0.0 react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/cssnano-preset@3.4.0': - resolution: {integrity: sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==} + '@docusaurus/cssnano-preset@3.5.2': + resolution: {integrity: sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==} engines: {node: '>=18.0'} - '@docusaurus/logger@3.4.0': - resolution: {integrity: sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==} + '@docusaurus/logger@3.5.2': + resolution: {integrity: sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==} engines: {node: '>=18.0'} - '@docusaurus/mdx-loader@3.4.0': - resolution: {integrity: sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==} + '@docusaurus/mdx-loader@3.5.2': + resolution: {integrity: sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/module-type-aliases@3.4.0': - resolution: {integrity: sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==} + '@docusaurus/module-type-aliases@3.5.2': + resolution: {integrity: sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==} peerDependencies: react: '*' react-dom: '*' - '@docusaurus/plugin-content-blog@3.4.0': - resolution: {integrity: sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==} + '@docusaurus/plugin-content-blog@3.5.2': + resolution: {integrity: sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==} engines: {node: '>=18.0'} peerDependencies: + '@docusaurus/plugin-content-docs': '*' react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-content-docs@3.4.0': - resolution: {integrity: sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==} + '@docusaurus/plugin-content-docs@3.5.2': + resolution: {integrity: sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-content-pages@3.4.0': - resolution: {integrity: sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==} + '@docusaurus/plugin-content-pages@3.5.2': + resolution: {integrity: sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-debug@3.4.0': - resolution: {integrity: sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==} + '@docusaurus/plugin-debug@3.5.2': + resolution: {integrity: sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-google-analytics@3.4.0': - resolution: {integrity: sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==} + '@docusaurus/plugin-google-analytics@3.5.2': + resolution: {integrity: sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-google-gtag@3.4.0': - resolution: {integrity: sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==} + '@docusaurus/plugin-google-gtag@3.5.2': + resolution: {integrity: sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-google-tag-manager@3.4.0': - resolution: {integrity: sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==} + '@docusaurus/plugin-google-tag-manager@3.5.2': + resolution: {integrity: sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/plugin-sitemap@3.4.0': - resolution: {integrity: sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==} + '@docusaurus/plugin-sitemap@3.5.2': + resolution: {integrity: sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/preset-classic@3.4.0': - resolution: {integrity: sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==} + '@docusaurus/preset-classic@3.5.2': + resolution: {integrity: sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 @@ -1196,39 +1268,40 @@ packages: peerDependencies: react: '*' - '@docusaurus/theme-classic@3.4.0': - resolution: {integrity: sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==} + '@docusaurus/theme-classic@3.5.2': + resolution: {integrity: sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/theme-common@3.4.0': - resolution: {integrity: sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==} + '@docusaurus/theme-common@3.5.2': + resolution: {integrity: sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==} engines: {node: '>=18.0'} peerDependencies: + '@docusaurus/plugin-content-docs': '*' react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/theme-search-algolia@3.4.0': - resolution: {integrity: sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==} + '@docusaurus/theme-search-algolia@3.5.2': + resolution: {integrity: sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==} engines: {node: '>=18.0'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/theme-translations@3.4.0': - resolution: {integrity: sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==} + '@docusaurus/theme-translations@3.5.2': + resolution: {integrity: sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==} engines: {node: '>=18.0'} - '@docusaurus/types@3.4.0': - resolution: {integrity: sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==} + '@docusaurus/types@3.5.2': + resolution: {integrity: sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - '@docusaurus/utils-common@3.4.0': - resolution: {integrity: sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==} + '@docusaurus/utils-common@3.5.2': + resolution: {integrity: sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==} engines: {node: '>=18.0'} peerDependencies: '@docusaurus/types': '*' @@ -1236,12 +1309,12 @@ packages: '@docusaurus/types': optional: true - '@docusaurus/utils-validation@3.4.0': - resolution: {integrity: sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==} + '@docusaurus/utils-validation@3.5.2': + resolution: {integrity: sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==} engines: {node: '>=18.0'} - '@docusaurus/utils@3.4.0': - resolution: {integrity: sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==} + '@docusaurus/utils@3.5.2': + resolution: {integrity: sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==} engines: {node: '>=18.0'} peerDependencies: '@docusaurus/types': '*' @@ -1405,11 +1478,11 @@ packages: resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} 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.0.6': + resolution: {integrity: sha512-boKBiDKbt80DZoJ064P0421GdpZAmffM814uFEuOOGFEJEPqEThi+KZTrg/DzTEw1c08SuAGjedQQjx3IGFPHg==} - '@fontsource/fira-mono@5.0.13': - resolution: {integrity: sha512-fZDjR2BdAqmauEbTjcIT62zYzbOgDa5+IQH34D2k8Pxmy1T815mAqQkZciWZVQ9dc/BgdTtTUV9HJ2ulBNwchg==} + '@fontsource/fira-mono@5.0.15': + resolution: {integrity: sha512-wc3TpF2GBbtFDKNbb444BrC3mEKuoPLITSYCKweNIrqBvAalIfJGloY/MVrmSGaMNgaAKUpdgy4eAWPLkUVzaA==} '@foscia/core@0.12.1': resolution: {integrity: sha512-Hgn7TQr9ANpNAR/vQpEP2yu0c4/eCjGLBtc6GLryu2J8ruzbCcLe4oiY0B5auHtir6OpM8WHKIoIN3Ez30gpMw==} @@ -1748,6 +1821,12 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@shikijs/core@1.16.3': + resolution: {integrity: sha512-yETIvrETCeC39gSPIiSADmjri9FwKmxz0QvONMtTIUYlKZe90CJkvcjPksayC2VQOtzOJonEiULUa8v8crUQvA==} + + '@shikijs/vscode-textmate@9.2.2': + resolution: {integrity: sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==} + '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -1889,20 +1968,14 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@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/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} '@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/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} @@ -1925,8 +1998,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==} @@ -1973,8 +2046,8 @@ packages: '@types/node@18.19.39': resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} - '@types/node@20.14.9': - resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==} + '@types/node@22.5.4': + resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2006,8 +2079,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@18.3.5': + resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2030,20 +2103,20 @@ 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.12': + resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} '@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==} @@ -2199,11 +2272,20 @@ packages: resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} + 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==} engines: {node: '>=0.4.0'} hasBin: true + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + add-stream@1.0.0: resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} @@ -2243,13 +2325,16 @@ packages: ajv@8.16.0: resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} - algoliasearch-helper@3.21.0: - resolution: {integrity: sha512-hjVOrL15I3Y3K8xG0icwG1/tWE+MocqBrhW6uVBWpU+/kVEMK0BnM2xdssj6mZM61eJ4iRxHR0djEI3ENOpR8w==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + algoliasearch-helper@3.22.4: + resolution: {integrity: sha512-fvBCywguW9f+939S6awvRMstqMF1XXcd2qs1r1aGqL/PJ1go/DqN06tWmDVmhCDqBJanm++imletrQWf0G2S1g==} peerDependencies: algoliasearch: '>= 3.1 < 6' - algoliasearch@4.23.3: - resolution: {integrity: sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==} + algoliasearch@4.24.0: + resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==} ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -2279,9 +2364,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'} @@ -2359,8 +2441,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 +2455,8 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} - autoprefixer@10.4.19: - resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -2399,8 +2481,8 @@ packages: 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 @@ -2438,8 +2520,8 @@ 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: @@ -2471,6 +2553,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -2530,6 +2617,9 @@ packages: caniuse-lite@1.0.30001636: resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} + caniuse-lite@1.0.30001660: + resolution: {integrity: sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -2858,14 +2948,14 @@ packages: peerDependencies: webpack: ^5.1.0 - core-js-compat@3.37.1: - resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} + core-js-compat@3.38.1: + resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} - core-js-pure@3.37.1: - resolution: {integrity: sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==} + core-js-pure@3.38.1: + resolution: {integrity: sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==} - core-js@3.37.1: - resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==} + core-js@3.38.1: + resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -3069,6 +3159,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -3204,8 +3303,8 @@ 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.0.5: + resolution: {integrity: sha512-mv8LBJYilGOOPLqaIM3vbYc34m4qwOCpb4WfP24DOPFNj2uiTerw8sg9MGvN6Jx2+J8rq9/WMnjcyz3UMqoIIQ==} peerDependencies: typedoc-plugin-markdown: '>=4.0.0' @@ -3262,6 +3361,9 @@ packages: electron-to-chromium@1.4.803: resolution: {integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==} + electron-to-chromium@1.5.18: + resolution: {integrity: sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==} + emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} @@ -3278,17 +3380,25 @@ 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'} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + enhanced-resolve@5.17.0: resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + engines: {node: '>=10.13.0'} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -3325,8 +3435,8 @@ packages: es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - es-module-lexer@1.5.3: - resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==} + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} es-object-atoms@1.0.0: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} @@ -3352,6 +3462,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'} @@ -3485,8 +3599,8 @@ packages: 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.1.2: + resolution: {integrity: sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==} estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} @@ -3532,8 +3646,8 @@ packages: resolution: {integrity: sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==} engines: {node: ^18.19.0 || >=20.5.0} - express@4.19.2: - resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + express@4.20.0: + resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} engines: {node: '>= 0.10.0'} extend-shallow@2.0.1: @@ -3560,6 +3674,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + fast-url-parser@1.1.3: resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} @@ -3638,8 +3755,8 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - 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: '*' @@ -3654,6 +3771,10 @@ packages: resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + fork-ts-checker-webpack-plugin@6.5.3: resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} @@ -3809,6 +3930,10 @@ packages: engines: {node: '>=16 || 14 >=14.18'} hasBin: true + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -4092,6 +4217,10 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + 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==} engines: {node: '>=16.x'} @@ -4119,8 +4248,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.44: + resolution: {integrity: sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==} engines: {node: '>=12'} inflight@1.0.6: @@ -4478,6 +4607,9 @@ packages: 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==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4494,8 +4626,8 @@ packages: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} 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==} @@ -4563,9 +4695,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 +4702,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.11: + resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} hasBin: true keyv@4.5.4: @@ -4592,8 +4721,8 @@ packages: resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} engines: {node: '>=14.16'} - launch-editor@2.7.0: - resolution: {integrity: sha512-KAc66u6LxWL8MifQ94oG3YGKYWDwz/Gi0T15lN//GaQoZe08vQGFJxrXkPAeu50UXgvJPPaRKVGuP1TRUm/aHQ==} + 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==} @@ -4614,6 +4743,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'} @@ -4754,14 +4886,13 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + 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'} - hasBin: true - mdast-util-directive@3.0.0: resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} @@ -4774,8 +4905,8 @@ packages: 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==} @@ -4798,8 +4929,8 @@ packages: mdast-util-mdx-expression@2.0.0: resolution: {integrity: sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==} - mdast-util-mdx-jsx@3.1.2: - resolution: {integrity: sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==} + mdast-util-mdx-jsx@3.1.3: + resolution: {integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==} mdast-util-mdx@3.0.0: resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} @@ -4825,6 +4956,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 +4971,8 @@ packages: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} - merge-descriptors@1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + 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==} @@ -4854,41 +4988,41 @@ packages: micromark-core-commonmark@2.0.1: resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} - micromark-extension-directive@3.0.0: - resolution: {integrity: sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==} + micromark-extension-directive@3.0.1: + resolution: {integrity: sha512-VGV2uxUzhEZmaP7NSFo2vtq7M2nUD+WfmYQD+d8i/1nHbzE+rMy9uzTvUybBbNiVbrhOZibg3gbyoARGqgDWyg==} 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==} @@ -4905,8 +5039,8 @@ packages: micromark-factory-label@2.0.0: resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} - 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==} @@ -4981,6 +5115,10 @@ packages: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + mime-db@1.33.0: resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} engines: {node: '>= 0.6'} @@ -4989,6 +5127,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'} @@ -5018,8 +5160,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.1: + resolution: {integrity: sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 @@ -5121,6 +5263,9 @@ packages: node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + normalize-package-data@6.0.2: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} engines: {node: ^16.14.0 || >=18.0.0} @@ -5211,6 +5356,9 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + oniguruma-to-js@0.3.3: + resolution: {integrity: sha512-m90/WEhgs8g4BxG37+Nu3YrMfJDs2YXtYtIllhsEPR+wP3+K4EZk6dDUvy2v2K4MNFDDOYKL4/yqYPXDqyozTQ==} + open@10.1.0: resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} @@ -5389,8 +5537,8 @@ 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.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} path-to-regexp@1.8.0: resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==} @@ -5418,6 +5566,9 @@ packages: picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -5644,8 +5795,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-selector-parser@6.1.0: - resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} postcss-sort-media-queries@5.2.0: @@ -5679,12 +5830,16 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} + postcss@8.4.45: + resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} + 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.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} engines: {node: '>=14'} hasBin: true @@ -5703,8 +5858,8 @@ packages: 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.0: + resolution: {integrity: sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==} peerDependencies: react: '>=16.0.0' @@ -5749,6 +5904,10 @@ packages: psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -5764,6 +5923,10 @@ packages: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -5838,8 +6001,8 @@ packages: 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 @@ -5914,6 +6077,9 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + regex@4.3.2: + resolution: {integrity: sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw==} + regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -6037,6 +6203,10 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + rimraf@5.0.7: resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} engines: {node: '>=14.18'} @@ -6056,8 +6226,8 @@ packages: rtl-detect@1.1.2: resolution: {integrity: sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==} - 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 @@ -6141,10 +6311,19 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} + 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==} @@ -6155,8 +6334,8 @@ packages: 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.0: + resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} engines: {node: '>= 0.8.0'} set-function-length@1.2.2: @@ -6196,8 +6375,8 @@ packages: engines: {node: '>=4'} hasBin: true - shiki@0.14.7: - resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} + shiki@1.16.3: + resolution: {integrity: sha512-GypUE+fEd06FqDs63LSAVlmq7WsahhPQU62cgZxGF+TJT5LjD2k7HTxXj4/CKOVuMM3+wWQ1t4Y5oooeJFRRBQ==} side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} @@ -6270,6 +6449,10 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -6420,8 +6603,8 @@ packages: style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} - style-to-object@1.0.6: - resolution: {integrity: sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==} + style-to-object@1.0.7: + resolution: {integrity: sha512-uSjr59G5u6fbxUfKbb8GcqMGT3Xs9v5IbPkjb0S16GyOeBLAzSRK0CixBv5YrYvzO6TDLzIS6QCn78tkqWngPw==} stylehacks@6.1.1: resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} @@ -6493,6 +6676,11 @@ packages: engines: {node: '>=10'} hasBin: true + terser@5.32.0: + resolution: {integrity: sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==} + engines: {node: '>=10'} + hasBin: true + test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -6588,6 +6776,9 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -6646,28 +6837,37 @@ 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.2.7: + resolution: {integrity: sha512-bLsQdweSm48P9j6kGqQ3/4GCH5zu2EnURSkkxqirNc+uVFE9YK825ogDw+WbNkRHIV6eZK/1U43gT7YfglyYOg==} + engines: {node: '>= 18'} + peerDependencies: + typedoc: 0.26.x + + typedoc-plugin-mdn-links@3.2.12: + resolution: {integrity: sha512-UT7JinqYE7IQSrpRPkUkHrXXEJw5qbHIZhVq8igutYDA/4fsrYZhYffVo8Uh70K2iXFdAyvt6foQG5ci1VERAw==} peerDependencies: - typedoc: 0.25.x + typedoc: '>= 0.23.14 || 0.24.x || 0.25.x || 0.26.x' - typedoc@0.25.13: - resolution: {integrity: sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ==} - engines: {node: '>= 16'} + typedoc@0.26.7: + resolution: {integrity: sha512-gUeI/Wk99vjXXMi8kanwzyhmeFEGv1LTdTQsiyIsmSYsBebvFxhbcyAx7Zjo4cMbpLGxM4Uz3jVIjksu/I2v6Q==} + engines: {node: '>= 18'} hasBin: true 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 + 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 || 5.5.x || 5.6.x - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + typescript@5.5.2: + resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} engines: {node: '>=14.17'} hasBin: true - typescript@5.5.2: - resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} @@ -6682,6 +6882,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -6706,8 +6909,8 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - unified@11.0.4: - resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} @@ -6755,6 +6958,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + update-notifier@6.0.2: resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} engines: {node: '>=14.16'} @@ -6811,14 +7020,14 @@ 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==} @@ -6878,18 +7087,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: @@ -6941,8 +7144,8 @@ packages: 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.94.0: + resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -7061,6 +7264,18 @@ packages: utf-8-validate: optional: true + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} @@ -7087,6 +7302,11 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -7120,111 +7340,111 @@ 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.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.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.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.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.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.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.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.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.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.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.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@algolia/client-search': 4.24.0 + algoliasearch: 4.24.0 - '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)': + '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': dependencies: - '@algolia/client-search': 4.23.3 - algoliasearch: 4.23.3 + '@algolia/client-search': 4.24.0 + algoliasearch: 4.24.0 - '@algolia/cache-browser-local-storage@4.23.3': + '@algolia/cache-browser-local-storage@4.24.0': dependencies: - '@algolia/cache-common': 4.23.3 + '@algolia/cache-common': 4.24.0 - '@algolia/cache-common@4.23.3': {} + '@algolia/cache-common@4.24.0': {} - '@algolia/cache-in-memory@4.23.3': + '@algolia/cache-in-memory@4.24.0': dependencies: - '@algolia/cache-common': 4.23.3 + '@algolia/cache-common': 4.24.0 - '@algolia/client-account@4.23.3': + '@algolia/client-account@4.24.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/client-search': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-analytics@4.23.3': + '@algolia/client-analytics@4.24.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': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-common@4.23.3': + '@algolia/client-common@4.24.0': dependencies: - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-personalization@4.23.3': + '@algolia/client-personalization@4.24.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/client-search@4.23.3': + '@algolia/client-search@4.24.0': dependencies: - '@algolia/client-common': 4.23.3 - '@algolia/requester-common': 4.23.3 - '@algolia/transporter': 4.23.3 + '@algolia/client-common': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 '@algolia/events@4.0.1': {} - '@algolia/logger-common@4.23.3': {} + '@algolia/logger-common@4.24.0': {} - '@algolia/logger-console@4.23.3': + '@algolia/logger-console@4.24.0': dependencies: - '@algolia/logger-common': 4.23.3 + '@algolia/logger-common': 4.24.0 - '@algolia/recommend@4.23.3': + '@algolia/recommend@4.24.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/cache-browser-local-storage': 4.24.0 + '@algolia/cache-common': 4.24.0 + '@algolia/cache-in-memory': 4.24.0 + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/logger-console': 4.24.0 + '@algolia/requester-browser-xhr': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/requester-node-http': 4.24.0 + '@algolia/transporter': 4.24.0 - '@algolia/requester-browser-xhr@4.23.3': + '@algolia/requester-browser-xhr@4.24.0': dependencies: - '@algolia/requester-common': 4.23.3 + '@algolia/requester-common': 4.24.0 - '@algolia/requester-common@4.23.3': {} + '@algolia/requester-common@4.24.0': {} - '@algolia/requester-node-http@4.23.3': + '@algolia/requester-node-http@4.24.0': dependencies: - '@algolia/requester-common': 4.23.3 + '@algolia/requester-common': 4.24.0 - '@algolia/transporter@4.23.3': + '@algolia/transporter@4.24.0': dependencies: - '@algolia/cache-common': 4.23.3 - '@algolia/logger-common': 4.23.3 - '@algolia/requester-common': 4.23.3 + '@algolia/cache-common': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/requester-common': 4.24.0 '@ampproject/remapping@2.3.0': dependencies: @@ -7238,6 +7458,8 @@ snapshots: '@babel/compat-data@7.24.7': {} + '@babel/compat-data@7.25.4': {} + '@babel/core@7.24.7': dependencies: '@ampproject/remapping': 2.3.0 @@ -7258,6 +7480,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.25.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.6 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helpers': 7.25.6 + '@babel/parser': 7.25.6 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.24.7': dependencies: '@babel/types': 7.24.7 @@ -7265,14 +7507,21 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 + '@babel/generator@7.25.6': + dependencies: + '@babel/types': 7.25.6 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + '@babel/helper-annotate-as-pure@7.24.7': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.6 '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -7284,34 +7533,40 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)': + '@babel/helper-compilation-targets@7.25.2': dependencies: - '@babel/core': 7.24.7 + '@babel/compat-data': 7.25.4 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.25.4(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 '@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-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/traverse': 7.25.6 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.25.2(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 regexpu-core: 5.3.2 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)': + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + debug: 4.3.7 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -7330,10 +7585,10 @@ snapshots: dependencies: '@babel/types': 7.24.7 - '@babel/helper-member-expression-to-functions@7.24.7': + '@babel/helper-member-expression-to-functions@7.24.8': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -7355,27 +7610,37 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.6 + transitivePeerDependencies: + - supports-color + '@babel/helper-optimise-call-expression@7.24.7': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.6 - '@babel/helper-plugin-utils@7.24.7': {} + '@babel/helper-plugin-utils@7.24.8': {} - '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)': + '@babel/helper-remap-async-to-generator@7.25.0(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-wrap-function': 7.24.7 + '@babel/helper-wrap-function': 7.25.0 + '@babel/traverse': 7.25.6 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)': + '@babel/helper-replace-supers@7.25.0(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/traverse': 7.25.6 transitivePeerDependencies: - supports-color @@ -7388,8 +7653,8 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.24.7': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -7399,16 +7664,19 @@ snapshots: '@babel/helper-string-parser@7.24.7': {} + '@babel/helper-string-parser@7.24.8': {} + '@babel/helper-validator-identifier@7.24.7': {} '@babel/helper-validator-option@7.24.7': {} - '@babel/helper-wrap-function@7.24.7': + '@babel/helper-validator-option@7.24.8': {} + + '@babel/helper-wrap-function@7.25.0': 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.0 + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -7417,6 +7685,11 @@ snapshots: '@babel/template': 7.24.7 '@babel/types': 7.24.7 + '@babel/helpers@7.25.6': + dependencies: + '@babel/template': 7.25.0 + '@babel/types': 7.25.6 + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 @@ -7428,650 +7701,682 @@ snapshots: dependencies: '@babel/types': 7.24.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)': + '@babel/parser@7.25.6': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/types': 7.25.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/traverse': 7.25.6 + transitivePeerDependencies: + - supports-color - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.0(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.0(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.0(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/traverse': 7.25.6 + transitivePeerDependencies: + - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-import-assertions@7.25.6(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-import-attributes@7.25.6(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-typescript@7.25.4(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@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.25.2)': 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.25.2 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-async-generator-functions@7.25.4(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-remap-async-to-generator': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.2) + '@babel/traverse': 7.25.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@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/helper-plugin-utils': 7.24.8 + '@babel/helper-remap-async-to-generator': 7.25.0(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-block-scoping@7.25.0(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-class-properties@7.25.4(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-classes@7.25.4(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@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/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) + '@babel/traverse': 7.25.6 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.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/template': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/template': 7.25.0 - '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-destructuring@7.24.8(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.0(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2) + + '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-function-name@7.25.1(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/traverse': 7.25.6 + transitivePeerDependencies: + - supports-color - '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-literals@7.25.2(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-commonjs@7.24.8(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-simple-access': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-systemjs@7.25.0(@babel/core@7.25.2)': 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/core': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 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.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-optional-chaining@7.24.8(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-private-methods@7.25.4(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 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.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@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/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-constant-elements@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-constant-elements@7.25.1(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@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/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) + '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-react-pure-annotations@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 regenerator-transform: 0.15.2 - '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-runtime@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-runtime@7.25.4(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@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/helper-plugin-utils': 7.24.8 + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.2) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.25.2) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.2) 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.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-spread@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-typeof-symbol@7.24.8(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-typescript@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-typescript@7.25.2(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@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) + '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-syntax-typescript': 7.25.4(@babel/core@7.25.2) 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-unicode-escapes@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-transform-unicode-sets-regex@7.25.4(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/preset-env@7.25.4(@babel/core@7.25.2)': + dependencies: + '@babel/compat-data': 7.25.4 + '@babel/core': 7.25.2 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-option': 7.24.8 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.3(@babel/core@7.25.2) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.2) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.2) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.2) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-syntax-import-assertions': 7.25.6(@babel/core@7.25.2) + '@babel/plugin-syntax-import-attributes': 7.25.6(@babel/core@7.25.2) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.2) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.2) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.2) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.2) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.2) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.25.2) + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-async-generator-functions': 7.25.4(@babel/core@7.25.2) + '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-block-scoping': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-transform-class-properties': 7.25.4(@babel/core@7.25.2) + '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-classes': 7.25.4(@babel/core@7.25.2) + '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.25.2) + '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-function-name': 7.25.1(@babel/core@7.25.2) + '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-literals': 7.25.2(@babel/core@7.25.2) + '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) + '@babel/plugin-transform-modules-systemjs': 7.25.0(@babel/core@7.25.2) + '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.25.2) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-private-methods': 7.25.4(@babel/core@7.25.2) + '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-typeof-symbol': 7.24.8(@babel/core@7.25.2) + '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-unicode-sets-regex': 7.25.4(@babel/core@7.25.2) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.25.2) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.2) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.25.2) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.2) + core-js-compat: 3.38.1 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.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/types': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/types': 7.25.6 esutils: 2.0.3 - '@babel/preset-react@7.24.7(@babel/core@7.24.7)': + '@babel/preset-react@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-option': 7.24.8 + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.25.2) + '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-react-pure-annotations': 7.24.7(@babel/core@7.25.2) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.24.7(@babel/core@7.24.7)': + '@babel/preset-typescript@7.24.7(@babel/core@7.25.2)': 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.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-option': 7.24.8 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) + '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2) transitivePeerDependencies: - supports-color '@babel/regjsgen@0.8.0': {} - '@babel/runtime-corejs3@7.24.7': + '@babel/runtime-corejs3@7.25.6': dependencies: - core-js-pure: 3.37.1 + core-js-pure: 3.38.1 regenerator-runtime: 0.14.1 '@babel/runtime@7.24.7': dependencies: regenerator-runtime: 0.14.1 + '@babel/runtime@7.25.6': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.24.7': dependencies: '@babel/code-frame': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 + '@babel/template@7.25.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.25.6 + '@babel/types': 7.25.6 + '@babel/traverse@7.24.7': dependencies: '@babel/code-frame': 7.24.7 @@ -8087,12 +8392,30 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.25.6': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.6 + '@babel/parser': 7.25.6 + '@babel/template': 7.25.0 + '@babel/types': 7.25.6 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/types@7.24.7': dependencies: '@babel/helper-string-parser': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@babel/types@7.25.6': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + '@colors/colors@1.5.0': optional: true @@ -8209,42 +8532,43 @@ snapshots: '@discoveryjs/json-ext@0.5.7': {} - '@docsearch/css@3.6.0': {} + '@docsearch/css@3.6.1': {} - '@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.6.1(@algolia/client-search@4.24.0)(@types/react@18.3.5)(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.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@docsearch/css': 3.6.1 + algoliasearch: 4.24.0 optionalDependencies: - '@types/react': 18.3.3 + '@types/react': 18.3.5 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/core@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/generator': 7.25.6 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-transform-runtime': 7.25.4(@babel/core@7.25.2) + '@babel/preset-env': 7.25.4(@babel/core@7.25.2) + '@babel/preset-react': 7.24.7(@babel/core@7.25.2) + '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) + '@babel/runtime': 7.25.6 + '@babel/runtime-corejs3': 7.25.6 + '@babel/traverse': 7.25.6 + '@docusaurus/cssnano-preset': 3.5.2 + '@docusaurus/logger': 3.5.2 + '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@mdx-js/react': 3.0.1(@types/react@18.3.5)(react@18.3.1) + autoprefixer: 10.4.20(postcss@8.4.45) + babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.94.0) babel-plugin-dynamic-import-node: 2.3.3 boxen: 6.2.1 chalk: 4.1.2 @@ -8253,50 +8577,50 @@ snapshots: 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) + copy-webpack-plugin: 11.0.0(webpack@5.94.0) + core-js: 3.38.1 + css-loader: 6.11.0(webpack@5.94.0) + css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.94.0) + cssnano: 6.1.2(postcss@8.4.45) 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) + file-loader: 6.2.0(webpack@5.94.0) fs-extra: 11.2.0 html-minifier-terser: 7.2.0 html-tags: 3.3.1 - html-webpack-plugin: 5.6.0(webpack@5.92.0) + html-webpack-plugin: 5.6.0(webpack@5.94.0) leven: 3.1.0 lodash: 4.17.21 - mini-css-extract-plugin: 2.9.0(webpack@5.92.0) + mini-css-extract-plugin: 2.9.1(webpack@5.94.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) + postcss: 8.4.45 + postcss-loader: 7.3.4(postcss@8.4.45)(typescript@5.6.2)(webpack@5.94.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.0)(typescript@5.6.2)(webpack@5.94.0) 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-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.94.0) 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 + semver: 7.6.3 serve-handler: 6.1.5 shelljs: 0.8.5 - terser-webpack-plugin: 5.3.10(webpack@5.92.0) - tslib: 2.6.3 + terser-webpack-plugin: 5.3.10(webpack@5.94.0) + tslib: 2.7.0 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 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.94.0))(webpack@5.94.0) + webpack: 5.94.0 webpack-bundle-analyzer: 4.10.2 - webpack-dev-server: 4.15.2(webpack@5.92.0) + webpack-dev-server: 4.15.2(webpack@5.94.0) webpack-merge: 5.10.0 - webpackbar: 5.0.2(webpack@5.92.0) + webpackbar: 5.0.2(webpack@5.94.0) transitivePeerDependencies: - '@docusaurus/types' - '@parcel/css' @@ -8316,28 +8640,28 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/cssnano-preset@3.4.0': + '@docusaurus/cssnano-preset@3.5.2': 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.4.45) + postcss: 8.4.45 + postcss-sort-media-queries: 5.2.0(postcss@8.4.45) + tslib: 2.7.0 - '@docusaurus/logger@3.4.0': + '@docusaurus/logger@3.5.2': dependencies: chalk: 4.1.2 - tslib: 2.6.3 + tslib: 2.7.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/mdx-loader@3.5.2(@docusaurus/types@3.5.2(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.6.2)': 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) + '@docusaurus/logger': 3.5.2 + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) '@mdx-js/mdx': 3.0.1 '@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) + estree-util-value-to-estree: 3.1.2 + file-loader: 6.2.0(webpack@5.94.0) fs-extra: 11.2.0 image-size: 1.1.1 mdast-util-mdx: 3.0.0 @@ -8350,12 +8674,12 @@ 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.7.0 + 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.94.0))(webpack@5.94.0) + vfile: 6.0.3 + webpack: 5.94.0 transitivePeerDependencies: - '@docusaurus/types' - '@swc/core' @@ -8365,11 +8689,11 @@ snapshots: - 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.5.2(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.5.2(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': 18.3.5 '@types/react-router-config': 5.0.11 '@types/react-router-dom': 5.3.3 react: 18.3.1 @@ -8383,15 +8707,17 @@ snapshots: - 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.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + dependencies: + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/logger': 3.5.2 + '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) cheerio: 1.0.0-rc.12 feed: 4.2.2 fs-extra: 11.2.0 @@ -8400,11 +8726,12 @@ snapshots: react-dom: 18.3.1(react@18.3.1) reading-time: 1.5.0 srcset: 4.0.0 - tslib: 2.6.3 + tslib: 2.7.0 unist-util-visit: 5.0.0 utility-types: 3.11.0 - webpack: 5.92.0 + webpack: 5.94.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8422,16 +8749,17 @@ 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.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + dependencies: + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/logger': 3.5.2 + '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/module-type-aliases': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) '@types/react-router-config': 5.0.11 combine-promises: 1.2.0 fs-extra: 11.2.0 @@ -8439,10 +8767,11 @@ snapshots: lodash: 4.17.21 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.7.0 utility-types: 3.11.0 - webpack: 5.92.0 + webpack: 5.94.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8460,19 +8789,20 @@ 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.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': 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) + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) fs-extra: 11.2.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 - webpack: 5.92.0 + tslib: 2.7.0 + webpack: 5.94.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8490,17 +8820,18 @@ 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.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': 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) + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) fs-extra: 11.2.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.7.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8518,15 +8849,16 @@ 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.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': 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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8544,16 +8876,17 @@ 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.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': 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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) '@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.7.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8571,15 +8904,16 @@ 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.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': 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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8597,20 +8931,21 @@ 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.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': 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) + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/logger': 3.5.2 + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) fs-extra: 11.2.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) sitemap: 7.1.2 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8628,25 +8963,26 @@ 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.5.2(@algolia/client-search@4.24.0)(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2)': + dependencies: + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-content-blog': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-content-pages': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-debug': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-google-analytics': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-google-gtag': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-google-tag-manager': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-sitemap': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/theme-classic': 3.5.2(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/theme-search-algolia': 3.5.2(@algolia/client-search@4.24.0)(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2) + '@docusaurus/types': 3.5.2(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' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8668,37 +9004,37 @@ snapshots: '@docusaurus/react-loadable@6.0.0(react@18.3.1)': dependencies: - '@types/react': 18.3.3 + '@types/react': 18.3.5 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.5.2(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + dependencies: + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/module-type-aliases': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-blog': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/plugin-content-pages': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/theme-translations': 3.5.2 + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@mdx-js/react': 3.0.1(@types/react@18.3.5)(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.44 lodash: 4.17.21 nprogress: 0.2.0 - postcss: 8.4.38 - prism-react-renderer: 2.3.1(react@18.3.1) + postcss: 8.4.45 + prism-react-renderer: 2.4.0(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.7.0 utility-types: 3.11.0 transitivePeerDependencies: - '@parcel/css' @@ -8719,67 +9055,56 @@ 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.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2)': 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.5.2(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/module-type-aliases': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(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': 18.3.5 '@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.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.7.0 utility-types: 3.11.0 transitivePeerDependencies: - '@docusaurus/types' - - '@parcel/css' - - '@rspack/core' - '@swc/core' - - '@swc/css' - - bufferutil - - csso - - debug - 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.5.2(@algolia/client-search@4.24.0)(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2)': + dependencies: + '@docsearch/react': 3.6.1(@algolia/client-search@4.24.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0) + '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/logger': 3.5.2 + '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) + '@docusaurus/theme-translations': 3.5.2 + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + algoliasearch: 4.24.0 + algoliasearch-helper: 3.22.4(algoliasearch@4.24.0) clsx: 2.1.1 eta: 2.2.0 fs-extra: 11.2.0 lodash: 4.17.21 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tslib: 2.6.3 + tslib: 2.7.0 utility-types: 3.11.0 transitivePeerDependencies: - '@algolia/client-search' - '@docusaurus/types' + - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' @@ -8799,23 +9124,23 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-translations@3.4.0': + '@docusaurus/theme-translations@3.5.2': dependencies: fs-extra: 11.2.0 - tslib: 2.6.3 + tslib: 2.7.0 - '@docusaurus/types@3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@mdx-js/mdx': 3.0.1 '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 18.3.5 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) utility-types: 3.11.0 - webpack: 5.92.0 + webpack: 5.94.0 webpack-merge: 5.10.0 transitivePeerDependencies: - '@swc/core' @@ -8824,22 +9149,22 @@ snapshots: - 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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: - tslib: 2.6.3 + tslib: 2.7.0 optionalDependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.5.2(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/utils-validation@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': 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)) + '@docusaurus/logger': 3.5.2 + '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) fs-extra: 11.2.0 - joi: 17.13.1 + joi: 17.13.3 js-yaml: 4.1.0 lodash: 4.17.21 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - '@docusaurus/types' - '@swc/core' @@ -8849,13 +9174,13 @@ snapshots: - 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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': 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.5.2 + '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@svgr/webpack': 8.1.0(typescript@5.6.2) escape-string-regexp: 4.0.0 - file-loader: 6.2.0(webpack@5.92.0) + file-loader: 6.2.0(webpack@5.94.0) fs-extra: 11.2.0 github-slugger: 1.5.0 globby: 11.1.0 @@ -8863,16 +9188,16 @@ snapshots: jiti: 1.21.6 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.7.0 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.94.0))(webpack@5.94.0) utility-types: 3.11.0 - webpack: 5.92.0 + webpack: 5.94.0 optionalDependencies: - '@docusaurus/types': 3.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@swc/core' - esbuild @@ -8973,9 +9298,9 @@ snapshots: '@eslint/js@8.57.0': {} - '@fontsource-variable/onest@5.0.4': {} + '@fontsource-variable/onest@5.0.6': {} - '@fontsource/fira-mono@5.0.13': {} + '@fontsource/fira-mono@5.0.15': {} '@foscia/core@0.12.1': dependencies: @@ -9050,8 +9375,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.5.4 + '@types/yargs': 17.0.33 chalk: 4.1.2 '@jridgewell/gen-mapping@0.3.5': @@ -9102,18 +9427,18 @@ snapshots: remark-parse: 11.0.0 remark-rehype: 11.1.0 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: - supports-color - '@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1)': + '@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1)': dependencies: '@types/mdx': 2.0.13 - '@types/react': 18.3.3 + '@types/react': 18.3.5 react: 18.3.1 '@nodelib/fs.scandir@2.1.5': @@ -9330,6 +9655,15 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} + '@shikijs/core@1.16.3': + dependencies: + '@shikijs/vscode-textmate': 9.2.2 + '@types/hast': 3.0.4 + oniguruma-to-js: 0.3.3 + regex: 4.3.2 + + '@shikijs/vscode-textmate@9.2.2': {} + '@sideway/address@4.1.5': dependencies: '@hapi/hoek': 9.3.0 @@ -9354,56 +9688,56 @@ snapshots: 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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@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.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 - '@svgr/babel-preset@8.1.0(@babel/core@7.24.7)': + '@svgr/babel-preset@8.1.0(@babel/core@7.25.2)': 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.25.2 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.25.2) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.25.2) - '@svgr/core@8.1.0(typescript@5.4.5)': + '@svgr/core@8.1.0(typescript@5.6.2)': dependencies: - '@babel/core': 7.24.7 - '@svgr/babel-preset': 8.1.0(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@svgr/babel-preset': 8.1.0(@babel/core@7.25.2) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.4.5) + cosmiconfig: 8.3.6(typescript@5.6.2) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -9411,38 +9745,38 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.6 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.6.2))': 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.25.2 + '@svgr/babel-preset': 8.1.0(@babel/core@7.25.2) + '@svgr/core': 8.1.0(typescript@5.6.2) '@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.6.2))(typescript@5.6.2)': 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.6.2) + cosmiconfig: 8.3.6(typescript@5.6.2) 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.6.2)': 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.25.2 + '@babel/plugin-transform-react-constant-elements': 7.25.1(@babel/core@7.25.2) + '@babel/preset-env': 7.25.4(@babel/core@7.25.2) + '@babel/preset-react': 7.24.7(@babel/core@7.25.2) + '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) + '@svgr/core': 8.1.0(typescript@5.6.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.6.2)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.6.2))(typescript@5.6.2) transitivePeerDependencies: - supports-color - typescript @@ -9462,20 +9796,20 @@ snapshots: '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@types/bonjour@3.5.13': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@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': 4.19.5 + '@types/node': 22.5.4 '@types/connect@3.4.38': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@types/conventional-commits-parser@5.0.0': dependencies: @@ -9485,25 +9819,15 @@ snapshots: dependencies: '@types/ms': 0.7.34 - '@types/eslint-scope@3.7.7': - dependencies: - '@types/eslint': 8.56.10 - '@types/estree': 1.0.5 - - '@types/eslint@8.56.10': - dependencies: - '@types/estree': 1.0.5 - '@types/json-schema': 7.0.15 - '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.5 '@types/estree@1.0.5': {} - '@types/express-serve-static-core@4.19.3': + '@types/express-serve-static-core@4.19.5': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -9511,7 +9835,7 @@ snapshots: '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.3 + '@types/express-serve-static-core': 4.19.5 '@types/qs': 6.9.15 '@types/serve-static': 1.15.7 @@ -9519,7 +9843,7 @@ snapshots: '@types/hast@3.0.4': dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 '@types/history@4.7.11': {} @@ -9529,9 +9853,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.5.4 '@types/istanbul-lib-coverage@2.0.6': {} @@ -9557,7 +9881,7 @@ snapshots: '@types/mdast@4.0.4': dependencies: - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 '@types/mdx@2.0.13': {} @@ -9567,7 +9891,7 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@types/node@17.0.45': {} @@ -9575,9 +9899,9 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@20.14.9': + '@types/node@22.5.4': dependencies: - undici-types: 5.26.5 + undici-types: 6.19.8 '@types/normalize-package-data@2.4.4': {} @@ -9596,21 +9920,21 @@ snapshots: '@types/react-router-config@5.0.11': dependencies: '@types/history': 4.7.11 - '@types/react': 18.3.3 + '@types/react': 18.3.5 '@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': 18.3.5 '@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': 18.3.5 - '@types/react@18.3.3': + '@types/react@18.3.5': dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 @@ -9626,7 +9950,7 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@types/serve-index@1.9.4': dependencies: @@ -9635,24 +9959,24 @@ snapshots: '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@types/send': 0.17.4 '@types/sockjs@0.3.36': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 - '@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.12': dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.32': + '@types/yargs@17.0.33': dependencies: '@types/yargs-parser': 21.0.3 @@ -9739,7 +10063,7 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@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.39)(jsdom@24.1.0)(terser@5.32.0))': dependencies: debug: 4.3.5 istanbul-lib-coverage: 3.2.2 @@ -9750,7 +10074,7 @@ snapshots: magicast: 0.3.4 picocolors: 1.0.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.39)(jsdom@24.1.0)(terser@5.32.0) transitivePeerDependencies: - supports-color @@ -9873,20 +10197,30 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-attributes@1.9.5(acorn@8.12.0): + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: - acorn: 8.12.0 + acorn: 8.12.1 acorn-jsx@5.3.2(acorn@8.12.0): dependencies: acorn: 8.12.0 + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + acorn-walk@8.3.3: dependencies: acorn: 8.12.0 + acorn-walk@8.3.4: + dependencies: + acorn: 8.12.1 + acorn@8.12.0: {} + acorn@8.12.1: {} + add-stream@1.0.0: {} address@1.2.2: {} @@ -9902,17 +10236,17 @@ snapshots: 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: @@ -9929,28 +10263,35 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 - algoliasearch-helper@3.21.0(algoliasearch@4.23.3): + 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 + + algoliasearch-helper@3.22.4(algoliasearch@4.24.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: 4.24.0 + + algoliasearch@4.24.0: + dependencies: + '@algolia/cache-browser-local-storage': 4.24.0 + '@algolia/cache-common': 4.24.0 + '@algolia/cache-in-memory': 4.24.0 + '@algolia/client-account': 4.24.0 + '@algolia/client-analytics': 4.24.0 + '@algolia/client-common': 4.24.0 + '@algolia/client-personalization': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/logger-console': 4.24.0 + '@algolia/recommend': 4.24.0 + '@algolia/requester-browser-xhr': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/requester-node-http': 4.24.0 + '@algolia/transporter': 4.24.0 ansi-align@3.0.1: dependencies: @@ -9972,8 +10313,6 @@ snapshots: ansi-regex@6.0.1: {} - ansi-sequence-parser@1.1.1: {} - ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -10070,7 +10409,7 @@ snapshots: dependencies: tslib: 2.6.3 - astring@1.8.6: {} + astring@1.9.0: {} async-retry@1.3.3: dependencies: @@ -10080,52 +10419,52 @@ snapshots: at-least-node@1.0.0: {} - autoprefixer@10.4.19(postcss@8.4.38): + autoprefixer@10.4.20(postcss@8.4.45): dependencies: - browserslist: 4.23.1 - caniuse-lite: 1.0.30001636 + browserslist: 4.23.3 + caniuse-lite: 1.0.30001660 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.0.1 - postcss: 8.4.38 + picocolors: 1.1.0 + postcss: 8.4.45 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.1.3(@babel/core@7.25.2)(webpack@5.94.0): dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.92.0 + webpack: 5.94.0 babel-plugin-dynamic-import-node@2.3.3: dependencies: object.assign: 4.1.5 - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7): + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.25.2): 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.25.4 + '@babel/core': 7.25.2 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) 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.25.2): 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.25.2 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) + core-js-compat: 3.38.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7): + babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.25.2): dependencies: - '@babel/core': 7.24.7 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) transitivePeerDependencies: - supports-color @@ -10151,7 +10490,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,7 +10500,7 @@ 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 @@ -10217,6 +10556,13 @@ snapshots: node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.1) + browserslist@4.23.3: + dependencies: + caniuse-lite: 1.0.30001660 + electron-to-chromium: 1.5.18 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.23.3) + buffer-from@1.1.2: {} buffer@5.7.1: @@ -10261,7 +10607,7 @@ snapshots: camel-case@4.1.2: dependencies: pascal-case: 3.1.2 - tslib: 2.6.3 + tslib: 2.7.0 camelcase@6.3.0: {} @@ -10269,13 +10615,15 @@ snapshots: caniuse-api@3.0.0: dependencies: - browserslist: 4.23.1 - caniuse-lite: 1.0.30001636 + browserslist: 4.23.3 + caniuse-lite: 1.0.30001660 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 caniuse-lite@1.0.30001636: {} + caniuse-lite@1.0.30001660: {} + ccount@2.0.1: {} chai@4.4.1: @@ -10462,7 +10810,7 @@ snapshots: compressible@2.0.18: dependencies: - mime-db: 1.52.0 + mime-db: 1.53.0 compression@1.7.4: dependencies: @@ -10614,7 +10962,7 @@ snapshots: 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.94.0): dependencies: fast-glob: 3.3.2 glob-parent: 6.0.2 @@ -10622,15 +10970,15 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.2.0 serialize-javascript: 6.0.2 - webpack: 5.92.0 + webpack: 5.94.0 - core-js-compat@3.37.1: + core-js-compat@3.38.1: dependencies: - browserslist: 4.23.1 + browserslist: 4.23.3 - core-js-pure@3.37.1: {} + core-js-pure@3.38.1: {} - core-js@3.37.1: {} + core-js@3.38.1: {} core-util-is@1.0.3: {} @@ -10649,14 +10997,14 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 - cosmiconfig@8.3.6(typescript@5.4.5): + cosmiconfig@8.3.6(typescript@5.6.2): 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.6.2 cosmiconfig@9.0.0(typescript@5.5.2): dependencies: @@ -10677,32 +11025,32 @@ snapshots: dependencies: type-fest: 1.4.0 - css-declaration-sorter@7.2.0(postcss@8.4.38): + css-declaration-sorter@7.2.0(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 - css-loader@6.11.0(webpack@5.92.0): + css-loader@6.11.0(webpack@5.94.0): 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.4.45) + postcss: 8.4.45 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.45) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.45) + postcss-modules-scope: 3.2.0(postcss@8.4.45) + postcss-modules-values: 4.0.0(postcss@8.4.45) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 optionalDependencies: - webpack: 5.92.0 + webpack: 5.94.0 - 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.94.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 - cssnano: 6.1.2(postcss@8.4.38) + cssnano: 6.1.2(postcss@8.4.45) jest-worker: 29.7.0 - postcss: 8.4.38 + postcss: 8.4.45 schema-utils: 4.2.0 serialize-javascript: 6.0.2 - webpack: 5.92.0 + webpack: 5.94.0 optionalDependencies: clean-css: 5.3.3 @@ -10725,71 +11073,71 @@ snapshots: 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: {} 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) + cssnano-preset-advanced@6.1.2(postcss@8.4.45): + dependencies: + autoprefixer: 10.4.20(postcss@8.4.45) + browserslist: 4.23.3 + cssnano-preset-default: 6.1.2(postcss@8.4.45) + postcss: 8.4.45 + postcss-discard-unused: 6.0.5(postcss@8.4.45) + postcss-merge-idents: 6.0.3(postcss@8.4.45) + postcss-reduce-idents: 6.0.3(postcss@8.4.45) + postcss-zindex: 6.0.2(postcss@8.4.45) + + cssnano-preset-default@6.1.2(postcss@8.4.45): + dependencies: + browserslist: 4.23.3 + css-declaration-sorter: 7.2.0(postcss@8.4.45) + cssnano-utils: 4.0.2(postcss@8.4.45) + postcss: 8.4.45 + postcss-calc: 9.0.1(postcss@8.4.45) + postcss-colormin: 6.1.0(postcss@8.4.45) + postcss-convert-values: 6.1.0(postcss@8.4.45) + postcss-discard-comments: 6.0.2(postcss@8.4.45) + postcss-discard-duplicates: 6.0.3(postcss@8.4.45) + postcss-discard-empty: 6.0.3(postcss@8.4.45) + postcss-discard-overridden: 6.0.2(postcss@8.4.45) + postcss-merge-longhand: 6.0.5(postcss@8.4.45) + postcss-merge-rules: 6.1.1(postcss@8.4.45) + postcss-minify-font-values: 6.1.0(postcss@8.4.45) + postcss-minify-gradients: 6.0.3(postcss@8.4.45) + postcss-minify-params: 6.1.0(postcss@8.4.45) + postcss-minify-selectors: 6.0.4(postcss@8.4.45) + postcss-normalize-charset: 6.0.2(postcss@8.4.45) + postcss-normalize-display-values: 6.0.2(postcss@8.4.45) + postcss-normalize-positions: 6.0.2(postcss@8.4.45) + postcss-normalize-repeat-style: 6.0.2(postcss@8.4.45) + postcss-normalize-string: 6.0.2(postcss@8.4.45) + postcss-normalize-timing-functions: 6.0.2(postcss@8.4.45) + postcss-normalize-unicode: 6.1.0(postcss@8.4.45) + postcss-normalize-url: 6.0.2(postcss@8.4.45) + postcss-normalize-whitespace: 6.0.2(postcss@8.4.45) + postcss-ordered-values: 6.0.2(postcss@8.4.45) + postcss-reduce-initial: 6.1.0(postcss@8.4.45) + postcss-reduce-transforms: 6.0.2(postcss@8.4.45) + postcss-svgo: 6.0.3(postcss@8.4.45) + postcss-unique-selectors: 6.0.4(postcss@8.4.45) + + cssnano-utils@4.0.2(postcss@8.4.45): + dependencies: + postcss: 8.4.45 + + cssnano@6.1.2(postcss@8.4.45): + dependencies: + cssnano-preset-default: 6.1.2(postcss@8.4.45) lilconfig: 3.1.2 - postcss: 8.4.38 + postcss: 8.4.45 csso@5.0.5: dependencies: @@ -10848,6 +11196,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.7: + dependencies: + ms: 2.1.3 + decimal.js@10.4.3: {} decode-named-character-reference@1.0.2: @@ -10944,7 +11296,7 @@ snapshots: detect-port@1.6.1: dependencies: address: 1.2.2 - debug: 4.3.5 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -10974,7 +11326,7 @@ snapshots: dependencies: '@mdx-js/mdx': 3.0.1 '@slorber/remark-comment': 1.0.0 - acorn: 8.12.0 + acorn: 8.12.1 chalk: 5.3.0 commander: 11.1.0 globby: 13.2.2 @@ -10987,9 +11339,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.0.5(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2))): dependencies: - typedoc-plugin-markdown: 4.0.3(typedoc@0.25.13(typescript@5.4.5)) + typedoc-plugin-markdown: 4.2.7(typedoc@0.26.7(typescript@5.6.2)) dom-converter@0.2.0: dependencies: @@ -11032,7 +11384,7 @@ snapshots: dot-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 dot-prop@5.3.0: dependencies: @@ -11052,6 +11404,8 @@ snapshots: electron-to-chromium@1.4.803: {} + electron-to-chromium@1.5.18: {} + emoji-regex@10.3.0: {} emoji-regex@8.0.0: {} @@ -11062,15 +11416,22 @@ snapshots: emojis-list@3.0.0: {} - emoticon@4.0.1: {} + emoticon@4.1.0: {} encodeurl@1.0.2: {} + encodeurl@2.0.0: {} + enhanced-resolve@5.17.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 + enhanced-resolve@5.17.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -11155,7 +11516,7 @@ snapshots: isarray: 2.0.5 stop-iteration-iterator: 1.0.0 - es-module-lexer@1.5.3: {} + es-module-lexer@1.5.4: {} es-object-atoms@1.0.0: dependencies: @@ -11205,6 +11566,8 @@ snapshots: escalade@3.1.2: {} + escalade@3.2.0: {} + escape-goat@4.0.0: {} escape-html@1.0.3: {} @@ -11395,18 +11758,17 @@ snapshots: 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.1.2: dependencies: '@types/estree': 1.0.5 - is-plain-obj: 4.1.0 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 +11784,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 require-like: 0.1.2 eventemitter3@4.0.7: {} @@ -11468,34 +11830,34 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.0.2 - express@4.19.2: + express@4.20.0: 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-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 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.10 proxy-addr: 2.0.7 qs: 6.11.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.0 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -11530,6 +11892,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.0.1: {} + fast-url-parser@1.1.3: dependencies: punycode: 1.4.1 @@ -11563,11 +11927,11 @@ snapshots: dependencies: flat-cache: 3.2.0 - file-loader@6.2.0(webpack@5.92.0): + file-loader@6.2.0(webpack@5.94.0): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.92.0 + webpack: 5.94.0 filesize@8.0.7: {} @@ -11622,7 +11986,7 @@ snapshots: flatted@3.3.1: {} - follow-redirects@1.15.6: {} + follow-redirects@1.15.9: {} for-each@0.3.3: dependencies: @@ -11633,7 +11997,12 @@ snapshots: 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): + 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.6.2)(webpack@5.94.0): dependencies: '@babel/code-frame': 7.24.7 '@types/json-schema': 7.0.15 @@ -11646,10 +12015,10 @@ 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.6.2 + webpack: 5.94.0 optionalDependencies: eslint: 8.57.0 @@ -11799,6 +12168,15 @@ snapshots: package-json-from-dist: 1.0.0 path-scurry: 1.11.1 + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -11850,7 +12228,7 @@ snapshots: dependencies: dir-glob: 3.0.1 fast-glob: 3.3.2 - ignore: 5.3.1 + ignore: 5.3.2 merge2: 1.4.1 slash: 4.0.0 @@ -11950,12 +12328,12 @@ snapshots: hast-util-from-parse5@8.0.1: dependencies: '@types/hast': 3.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 devlop: 1.1.0 hastscript: 8.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: @@ -11965,7 +12343,7 @@ snapshots: hast-util-raw@9.0.4: dependencies: '@types/hast': 3.0.4 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 '@ungap/structured-clone': 1.2.0 hast-util-from-parse5: 8.0.1 hast-util-to-parse5: 8.0.0 @@ -11974,7 +12352,7 @@ snapshots: parse5: 7.1.2 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 @@ -11989,7 +12367,7 @@ snapshots: 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-jsx: 3.1.3 mdast-util-mdxjs-esm: 2.0.1 property-information: 6.5.0 space-separated-tokens: 2.0.2 @@ -12003,17 +12381,17 @@ snapshots: dependencies: '@types/estree': 1.0.5 '@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-jsx: 3.1.3 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.7 unist-util-position: 5.0.0 vfile-message: 4.0.2 transitivePeerDependencies: @@ -12047,7 +12425,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.6 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -12085,7 +12463,7 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.31.1 + terser: 5.32.0 html-minifier-terser@7.2.0: dependencies: @@ -12095,13 +12473,13 @@ snapshots: entities: 4.5.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.31.1 + terser: 5.32.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.0(webpack@5.94.0): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -12109,7 +12487,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.2.1 optionalDependencies: - webpack: 5.92.0 + webpack: 5.94.0 htmlparser2@6.1.0: dependencies: @@ -12155,11 +12533,11 @@ snapshots: http-proxy-middleware@2.0.6(@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 +12546,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 @@ -12201,14 +12579,16 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.4.38): + icss-utils@5.1.0(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 ieee754@1.2.1: {} ignore@5.3.1: {} + ignore@5.3.2: {} + image-size@1.1.1: dependencies: queue: 6.0.2 @@ -12228,7 +12608,7 @@ snapshots: indent-string@4.0.0: {} - infima@0.2.0-alpha.43: {} + infima@0.2.0-alpha.44: {} inflight@1.0.6: dependencies: @@ -12542,10 +12922,16 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.9 + '@types/node': 22.5.4 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -12553,20 +12939,20 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 20.14.9 + '@types/node': 22.5.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 jiti@1.21.6: {} - joi@17.13.1: + joi@17.13.3: dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 @@ -12641,8 +13027,6 @@ snapshots: json5@2.2.3: {} - jsonc-parser@3.2.1: {} - jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -12651,7 +13035,7 @@ snapshots: jsonparse@1.3.1: {} - katex@0.16.10: + katex@0.16.11: dependencies: commander: 8.3.0 @@ -12667,9 +13051,9 @@ snapshots: dependencies: package-json: 8.1.1 - launch-editor@2.7.0: + launch-editor@2.9.1: dependencies: - picocolors: 1.0.1 + picocolors: 1.1.0 shell-quote: 1.8.1 leven@3.1.0: {} @@ -12685,6 +13069,10 @@ snapshots: 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: @@ -12769,7 +13157,7 @@ snapshots: lower-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.7.0 lowercase-keys@3.0.0: {} @@ -12803,14 +13191,21 @@ snapshots: 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 - marked@4.3.0: {} + markdown-table@3.0.3: {} 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 @@ -12830,7 +13225,7 @@ snapshots: mdast-util-from-markdown@2.0.1: 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 @@ -12855,7 +13250,7 @@ snapshots: 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 @@ -12903,7 +13298,7 @@ snapshots: mdast-util-gfm@3.0.0: dependencies: mdast-util-from-markdown: 2.0.1 - mdast-util-gfm-autolink-literal: 2.0.0 + 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 @@ -12935,19 +13330,18 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-mdx-jsx@3.1.2: + mdast-util-mdx-jsx@3.1.3: 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 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: @@ -12957,7 +13351,7 @@ snapshots: dependencies: mdast-util-from-markdown: 2.0.1 mdast-util-mdx-expression: 2.0.0 - mdast-util-mdx-jsx: 3.1.2 + mdast-util-mdx-jsx: 3.1.3 mdast-util-mdxjs-esm: 2.0.1 mdast-util-to-markdown: 2.1.0 transitivePeerDependencies: @@ -12989,12 +13383,12 @@ snapshots: 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: 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 @@ -13010,6 +13404,8 @@ snapshots: mdn-data@2.0.30: {} + mdurl@2.0.0: {} + media-typer@0.3.0: {} memfs@3.5.3: @@ -13018,7 +13414,7 @@ snapshots: meow@12.1.1: {} - merge-descriptors@1.0.1: {} + merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -13045,7 +13441,7 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-extension-directive@3.0.0: + micromark-extension-directive@3.0.1: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 @@ -13062,14 +13458,14 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - 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-extension-gfm-footnote@2.0.0: + micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -13080,7 +13476,7 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - 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 @@ -13089,7 +13485,7 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - 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 @@ -13101,7 +13497,7 @@ snapshots: dependencies: micromark-util-types: 2.0.0 - 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 @@ -13111,20 +13507,20 @@ snapshots: 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-extension-gfm-task-list-item: 2.1.0 micromark-util-combine-extensions: 2.0.0 micromark-util-types: 2.0.0 - 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 + katex: 0.16.11 micromark-factory-space: 2.0.0 micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 @@ -13134,22 +13530,23 @@ snapshots: dependencies: '@types/estree': 1.0.5 devlop: 1.1.0 - micromark-factory-mdx-expression: 2.0.1 + micromark-factory-mdx-expression: 2.0.2 micromark-factory-space: 2.0.0 micromark-util-character: 2.1.0 micromark-util-events-to-acorn: 2.0.2 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - 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 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 - micromark-factory-mdx-expression: 2.0.1 + micromark-factory-mdx-expression: 2.0.2 micromark-factory-space: 2.0.0 micromark-util-character: 2.1.0 + micromark-util-events-to-acorn: 2.0.2 micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 vfile-message: 4.0.2 @@ -13172,10 +13569,10 @@ snapshots: micromark-extension-mdxjs@3.0.0: dependencies: - acorn: 8.12.0 - acorn-jsx: 5.3.2(acorn@8.12.0) + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) 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 @@ -13194,10 +13591,11 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 - micromark-factory-mdx-expression@2.0.1: + micromark-factory-mdx-expression@2.0.2: dependencies: '@types/estree': 1.0.5 devlop: 1.1.0 + micromark-factory-space: 2.0.0 micromark-util-character: 2.1.0 micromark-util-events-to-acorn: 2.0.2 micromark-util-symbol: 2.0.0 @@ -13271,7 +13669,7 @@ snapshots: dependencies: '@types/acorn': 4.0.6 '@types/estree': 1.0.5 - '@types/unist': 3.0.2 + '@types/unist': 3.0.3 devlop: 1.1.0 estree-util-visit: 2.0.0 micromark-util-symbol: 2.0.0 @@ -13312,7 +13710,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.5 + debug: 4.3.7 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -13336,10 +13734,17 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + mime-db@1.33.0: {} mime-db@1.52.0: {} + mime-db@1.53.0: {} + mime-types@2.1.18: dependencies: mime-db: 1.33.0 @@ -13358,11 +13763,11 @@ snapshots: mimic-response@4.0.0: {} - mini-css-extract-plugin@2.9.0(webpack@5.92.0): + mini-css-extract-plugin@2.9.1(webpack@5.94.0): dependencies: schema-utils: 4.2.0 tapable: 2.2.1 - webpack: 5.92.0 + webpack: 5.94.0 minimalistic-assert@1.0.1: {} @@ -13429,7 +13834,7 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.6.3 + tslib: 2.7.0 node-domexception@1.0.0: {} @@ -13450,6 +13855,8 @@ snapshots: node-releases@2.0.14: {} + node-releases@2.0.18: {} + normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 @@ -13538,6 +13945,8 @@ snapshots: dependencies: mimic-fn: 4.0.0 + oniguruma-to-js@0.3.3: {} + open@10.1.0: dependencies: default-browser: 5.2.1 @@ -13664,7 +14073,7 @@ snapshots: param-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 parent-module@1.0.1: dependencies: @@ -13672,7 +14081,7 @@ snapshots: parse-entities@4.0.1: dependencies: - '@types/unist': 2.0.10 + '@types/unist': 2.0.11 character-entities: 2.0.2 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 @@ -13730,7 +14139,7 @@ snapshots: pascal-case@3.1.2: dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 path-exists@3.0.0: {} @@ -13753,7 +14162,7 @@ snapshots: lru-cache: 10.2.2 minipass: 7.1.2 - path-to-regexp@0.1.7: {} + path-to-regexp@0.1.10: {} path-to-regexp@1.8.0: dependencies: @@ -13777,6 +14186,8 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.0: {} + picomatch@2.3.1: {} pkg-dir@7.0.0: @@ -13801,215 +14212,215 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-calc@9.0.1(postcss@8.4.38): + postcss-calc@9.0.1(postcss@8.4.45): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.4.45 + postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - postcss-colormin@6.1.0(postcss@8.4.38): + postcss-colormin@6.1.0(postcss@8.4.45): dependencies: - browserslist: 4.23.1 + browserslist: 4.23.3 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-convert-values@6.1.0(postcss@8.4.38): + postcss-convert-values@6.1.0(postcss@8.4.45): dependencies: - browserslist: 4.23.1 - postcss: 8.4.38 + browserslist: 4.23.3 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-discard-comments@6.0.2(postcss@8.4.38): + postcss-discard-comments@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 - postcss-discard-duplicates@6.0.3(postcss@8.4.38): + postcss-discard-duplicates@6.0.3(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 - postcss-discard-empty@6.0.3(postcss@8.4.38): + postcss-discard-empty@6.0.3(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 - postcss-discard-overridden@6.0.2(postcss@8.4.38): + postcss-discard-overridden@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 - postcss-discard-unused@6.0.5(postcss@8.4.38): + postcss-discard-unused@6.0.5(postcss@8.4.45): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.4.45 + postcss-selector-parser: 6.1.2 - postcss-loader@7.3.4(postcss@8.4.38)(typescript@5.4.5)(webpack@5.92.0): + postcss-loader@7.3.4(postcss@8.4.45)(typescript@5.6.2)(webpack@5.94.0): dependencies: - cosmiconfig: 8.3.6(typescript@5.4.5) + cosmiconfig: 8.3.6(typescript@5.6.2) jiti: 1.21.6 - postcss: 8.4.38 - semver: 7.6.2 - webpack: 5.92.0 + postcss: 8.4.45 + semver: 7.6.3 + webpack: 5.94.0 transitivePeerDependencies: - typescript - postcss-merge-idents@6.0.3(postcss@8.4.38): + postcss-merge-idents@6.0.3(postcss@8.4.45): dependencies: - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 + cssnano-utils: 4.0.2(postcss@8.4.45) + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-merge-longhand@6.0.5(postcss@8.4.38): + postcss-merge-longhand@6.0.5(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - stylehacks: 6.1.1(postcss@8.4.38) + stylehacks: 6.1.1(postcss@8.4.45) - postcss-merge-rules@6.1.1(postcss@8.4.38): + postcss-merge-rules@6.1.1(postcss@8.4.45): dependencies: - browserslist: 4.23.1 + browserslist: 4.23.3 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.4.45) + postcss: 8.4.45 + 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.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-minify-gradients@6.0.3(postcss@8.4.38): + postcss-minify-gradients@6.0.3(postcss@8.4.45): 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.4.45) + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-minify-params@6.1.0(postcss@8.4.38): + postcss-minify-params@6.1.0(postcss@8.4.45): dependencies: - browserslist: 4.23.1 - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 + browserslist: 4.23.3 + cssnano-utils: 4.0.2(postcss@8.4.45) + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-minify-selectors@6.0.4(postcss@8.4.38): + postcss-minify-selectors@6.0.4(postcss@8.4.45): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.4.45 + 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.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 - postcss-modules-local-by-default@4.0.5(postcss@8.4.38): + postcss-modules-local-by-default@4.0.5(postcss@8.4.45): 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.4.45) + postcss: 8.4.45 + postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.0(postcss@8.4.38): + postcss-modules-scope@3.2.0(postcss@8.4.45): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.4.45 + postcss-selector-parser: 6.1.2 - postcss-modules-values@4.0.0(postcss@8.4.38): + postcss-modules-values@4.0.0(postcss@8.4.45): dependencies: - icss-utils: 5.1.0(postcss@8.4.38) - postcss: 8.4.38 + icss-utils: 5.1.0(postcss@8.4.45) + postcss: 8.4.45 - postcss-normalize-charset@6.0.2(postcss@8.4.38): + postcss-normalize-charset@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 - postcss-normalize-display-values@6.0.2(postcss@8.4.38): + postcss-normalize-display-values@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-normalize-positions@6.0.2(postcss@8.4.38): + postcss-normalize-positions@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@6.0.2(postcss@8.4.38): + postcss-normalize-repeat-style@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-normalize-string@6.0.2(postcss@8.4.38): + postcss-normalize-string@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@6.0.2(postcss@8.4.38): + postcss-normalize-timing-functions@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@6.1.0(postcss@8.4.38): + postcss-normalize-unicode@6.1.0(postcss@8.4.45): dependencies: - browserslist: 4.23.1 - postcss: 8.4.38 + browserslist: 4.23.3 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-normalize-url@6.0.2(postcss@8.4.38): + postcss-normalize-url@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@6.0.2(postcss@8.4.38): + postcss-normalize-whitespace@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-ordered-values@6.0.2(postcss@8.4.38): + postcss-ordered-values@6.0.2(postcss@8.4.45): dependencies: - cssnano-utils: 4.0.2(postcss@8.4.38) - postcss: 8.4.38 + cssnano-utils: 4.0.2(postcss@8.4.45) + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-reduce-idents@6.0.3(postcss@8.4.38): + postcss-reduce-idents@6.0.3(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-reduce-initial@6.1.0(postcss@8.4.38): + postcss-reduce-initial@6.1.0(postcss@8.4.45): dependencies: - browserslist: 4.23.1 + browserslist: 4.23.3 caniuse-api: 3.0.0 - postcss: 8.4.38 + postcss: 8.4.45 - postcss-reduce-transforms@6.0.2(postcss@8.4.38): + postcss-reduce-transforms@6.0.2(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss-value-parser: 4.2.0 - postcss-selector-parser@6.1.0: + postcss-selector-parser@6.1.2: 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.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 sort-css-media-queries: 2.2.0 - postcss-svgo@6.0.3(postcss@8.4.38): + postcss-svgo@6.0.3(postcss@8.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 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.4.45): dependencies: - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + postcss: 8.4.45 + 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.4.45): dependencies: - postcss: 8.4.38 + postcss: 8.4.45 postcss@8.4.38: dependencies: @@ -14017,9 +14428,15 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postcss@8.4.45: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} - prettier@3.3.2: {} + prettier@3.3.3: {} pretty-error@4.0.0: dependencies: @@ -14038,7 +14455,7 @@ snapshots: pretty-time@1.1.0: {} - prism-react-renderer@2.3.1(react@18.3.1): + prism-react-renderer@2.4.0(react@18.3.1): dependencies: '@types/prismjs': 1.26.4 clsx: 2.1.1 @@ -14096,6 +14513,8 @@ snapshots: psl@1.9.0: {} + punycode.js@2.3.1: {} + punycode@1.4.1: {} punycode@2.3.1: {} @@ -14108,6 +14527,10 @@ snapshots: dependencies: side-channel: 1.0.6 + qs@6.13.0: + dependencies: + side-channel: 1.0.6 + querystringify@2.2.0: {} queue-lit@1.5.2: {} @@ -14142,18 +14565,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.0)(typescript@5.6.2)(webpack@5.94.0): dependencies: '@babel/code-frame': 7.24.7 address: 1.2.2 - browserslist: 4.23.1 + browserslist: 4.23.3 chalk: 4.1.2 cross-spawn: 7.0.3 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.0)(typescript@5.6.2)(webpack@5.94.0) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -14168,9 +14591,9 @@ snapshots: shell-quote: 1.8.1 strip-ansi: 6.0.1 text-table: 0.2.0 - webpack: 5.92.0 + webpack: 5.94.0 optionalDependencies: - typescript: 5.4.5 + typescript: 5.6.2 transitivePeerDependencies: - eslint - supports-color @@ -14188,7 +14611,7 @@ snapshots: 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 + '@babel/runtime': 7.25.6 invariant: 2.2.4 prop-types: 15.8.1 react: 18.3.1 @@ -14207,25 +14630,25 @@ snapshots: 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.94.0): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.6 react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - webpack: 5.92.0 + webpack: 5.94.0 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.25.6 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.25.6 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -14236,7 +14659,7 @@ snapshots: react-router@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.6 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -14304,7 +14727,9 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.6 + + regex@4.3.2: {} regexp.prototype.flags@1.5.2: dependencies: @@ -14338,7 +14763,7 @@ snapshots: dependencies: '@types/hast': 3.0.4 hast-util-raw: 9.0.4 - vfile: 6.0.1 + vfile: 6.0.3 relateurl@0.2.7: {} @@ -14379,25 +14804,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.1 + unified: 11.0.5 transitivePeerDependencies: - supports-color remark-emoji@4.0.1: dependencies: '@types/mdast': 4.0.4 - emoticon: 4.0.1 + emoticon: 4.1.0 mdast-util-find-and-replace: 3.0.1 node-emoji: 2.1.3 - unified: 11.0.4 + 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 +14833,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,8 +14841,8 @@ 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 @@ -14433,7 +14858,7 @@ snapshots: '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.1 micromark-util-types: 2.0.0 - unified: 11.0.4 + unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -14442,14 +14867,14 @@ snapshots: '@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 + unified: 11.0.5 renderkid@3.0.0: dependencies: @@ -14505,6 +14930,10 @@ snapshots: dependencies: glob: 7.2.3 + rimraf@5.0.10: + dependencies: + glob: 10.4.5 + rimraf@5.0.7: dependencies: glob: 10.4.1 @@ -14537,11 +14966,11 @@ snapshots: rtl-detect@1.1.2: {} - 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.0 + postcss: 8.4.45 strip-json-comments: 3.1.1 run-applescript@7.0.0: {} @@ -14600,9 +15029,9 @@ snapshots: schema-utils@4.2.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: {} @@ -14626,6 +15055,8 @@ snapshots: semver@7.6.2: {} + semver@7.6.3: {} + send@0.18.0: dependencies: debug: 2.6.9 @@ -14644,6 +15075,24 @@ snapshots: transitivePeerDependencies: - supports-color + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -14671,7 +15120,7 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.15.0: + serve-static@1.16.0: dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 @@ -14720,12 +15169,11 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 - shiki@0.14.7: + shiki@1.16.3: dependencies: - ansi-sequence-parser: 1.1.1 - jsonc-parser: 3.2.1 - vscode-oniguruma: 1.7.0 - vscode-textmate: 8.0.0 + '@shikijs/core': 1.16.3 + '@shikijs/vscode-textmate': 9.2.2 + '@types/hast': 3.0.4 side-channel@1.0.6: dependencies: @@ -14772,7 +15220,7 @@ snapshots: snake-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.7.0 sockjs@0.3.24: dependencies: @@ -14797,6 +15245,8 @@ snapshots: source-map-js@1.2.0: {} + source-map-js@1.2.1: {} + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -14826,7 +15276,7 @@ snapshots: spdy-transport@3.0.0: dependencies: - debug: 4.3.5 + debug: 4.3.7 detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -14837,7 +15287,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.3.5 + debug: 4.3.7 handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -14953,15 +15403,15 @@ snapshots: dependencies: inline-style-parser: 0.1.1 - style-to-object@1.0.6: + style-to-object@1.0.7: dependencies: inline-style-parser: 0.2.3 - stylehacks@6.1.1(postcss@8.4.38): + stylehacks@6.1.1(postcss@8.4.45): dependencies: - browserslist: 4.23.1 - postcss: 8.4.38 - postcss-selector-parser: 6.1.0 + browserslist: 4.23.3 + postcss: 8.4.45 + postcss-selector-parser: 6.1.2 supports-color@5.5.0: dependencies: @@ -14992,7 +15442,7 @@ snapshots: css-tree: 2.3.1 css-what: 6.1.0 csso: 5.0.5 - picocolors: 1.0.1 + picocolors: 1.1.0 symbol-tree@3.2.4: {} @@ -15005,14 +15455,14 @@ 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.10(webpack@5.94.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 - terser: 5.31.1 - webpack: 5.92.0 + terser: 5.32.0 + webpack: 5.94.0 terser@5.31.1: dependencies: @@ -15021,6 +15471,13 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + terser@5.32.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 + commander: 2.20.3 + source-map-support: 0.5.21 + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 @@ -15106,6 +15563,8 @@ snapshots: tslib@2.6.3: {} + tslib@2.7.0: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -15167,22 +15626,29 @@ snapshots: typedarray@0.0.6: {} - typedoc-plugin-markdown@4.0.3(typedoc@0.25.13(typescript@5.4.5)): + typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2)): dependencies: - typedoc: 0.25.13(typescript@5.4.5) + typedoc: 0.26.7(typescript@5.6.2) - typedoc@0.25.13(typescript@5.4.5): + typedoc-plugin-mdn-links@3.2.12(typedoc@0.26.7(typescript@5.6.2)): dependencies: - lunr: 2.3.9 - marked: 4.3.0 - minimatch: 9.0.4 - shiki: 0.14.7 - typescript: 5.4.5 + typedoc: 0.26.7(typescript@5.6.2) - typescript@5.4.5: {} + typedoc@0.26.7(typescript@5.6.2): + dependencies: + lunr: 2.3.9 + markdown-it: 14.1.0 + minimatch: 9.0.5 + shiki: 1.16.3 + typescript: 5.6.2 + yaml: 2.5.1 typescript@5.5.2: {} + typescript@5.6.2: {} + + uc.micro@2.1.0: {} + ufo@1.5.3: {} uglify-js@3.18.0: @@ -15197,6 +15663,8 @@ snapshots: undici-types@5.26.5: {} + undici-types@6.19.8: {} + unicode-canonical-property-names-ecmascript@2.0.0: {} unicode-emoji-modifier-base@1.0.0: {} @@ -15212,15 +15680,15 @@ snapshots: unicorn-magic@0.1.0: {} - unified@11.0.4: + 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 +15696,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 @@ -15272,6 +15740,12 @@ snapshots: escalade: 3.1.2 picocolors: 1.0.1 + update-browserslist-db@1.1.0(browserslist@4.23.3): + dependencies: + browserslist: 4.23.3 + escalade: 3.2.0 + picocolors: 1.1.0 + update-notifier@6.0.2: dependencies: boxen: 7.1.1 @@ -15285,7 +15759,7 @@ 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 @@ -15310,14 +15784,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.94.0))(webpack@5.94.0): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.92.0 + webpack: 5.94.0 optionalDependencies: - file-loader: 6.2.0(webpack@5.92.0) + file-loader: 6.2.0(webpack@5.94.0) url-parse@1.5.10: dependencies: @@ -15343,29 +15817,28 @@ 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.39)(terser@5.32.0): dependencies: cac: 6.7.14 debug: 4.3.5 pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.3.2(@types/node@18.19.39)(terser@5.31.1) + vite: 5.3.2(@types/node@18.19.39)(terser@5.32.0) transitivePeerDependencies: - '@types/node' - less @@ -15376,7 +15849,7 @@ snapshots: - supports-color - terser - vite@5.3.2(@types/node@18.19.39)(terser@5.31.1): + vite@5.3.2(@types/node@18.19.39)(terser@5.32.0): dependencies: esbuild: 0.21.5 postcss: 8.4.38 @@ -15384,9 +15857,9 @@ snapshots: optionalDependencies: '@types/node': 18.19.39 fsevents: 2.3.3 - terser: 5.31.1 + terser: 5.32.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.39)(jsdom@24.1.0)(terser@5.32.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -15405,8 +15878,8 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.8.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) + vite: 5.3.2(@types/node@18.19.39)(terser@5.32.0) + vite-node: 1.6.0(@types/node@18.19.39)(terser@5.32.0) why-is-node-running: 2.2.2 optionalDependencies: '@types/node': 18.19.39 @@ -15420,15 +15893,11 @@ snapshots: - 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 @@ -15450,31 +15919,31 @@ snapshots: webpack-bundle-analyzer@4.10.2: dependencies: '@discoveryjs/json-ext': 0.5.7 - acorn: 8.12.0 - acorn-walk: 8.3.3 + acorn: 8.12.1 + 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.0 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.94.0): 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 + webpack: 5.94.0 - webpack-dev-server@4.15.2(webpack@5.92.0): + webpack-dev-server@4.15.2(webpack@5.94.0): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -15482,7 +15951,7 @@ 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.12 ansi-html-community: 0.0.8 bonjour-service: 1.2.1 chokidar: 3.6.0 @@ -15490,12 +15959,12 @@ snapshots: compression: 1.7.4 connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.19.2 + express: 4.20.0 graceful-fs: 4.2.11 html-entities: 2.5.2 http-proxy-middleware: 2.0.6(@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 @@ -15504,10 +15973,10 @@ snapshots: 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.94.0) + ws: 8.18.0 optionalDependencies: - webpack: 5.92.0 + webpack: 5.94.0 transitivePeerDependencies: - bufferutil - debug @@ -15522,19 +15991,18 @@ snapshots: webpack-sources@3.2.3: {} - webpack@5.92.0: + webpack@5.94.0: 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 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.23.3 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.17.0 - es-module-lexer: 1.5.3 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -15545,21 +16013,21 @@ 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.10(webpack@5.94.0) + watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpackbar@5.0.2(webpack@5.92.0): + webpackbar@5.0.2(webpack@5.94.0): dependencies: chalk: 4.1.2 consola: 2.15.3 pretty-time: 1.1.0 std-env: 3.7.0 - webpack: 5.92.0 + webpack: 5.94.0 websocket-driver@0.7.4: dependencies: @@ -15656,6 +16124,8 @@ snapshots: ws@8.17.1: {} + ws@8.18.0: {} + xdg-basedir@5.1.0: {} xml-js@1.6.11: @@ -15672,6 +16142,8 @@ snapshots: yaml@1.10.2: {} + yaml@2.5.1: {} + yargs-parser@20.2.9: {} yargs-parser@21.1.1: {} diff --git a/scripts/generate-files.js b/scripts/generate-files.js index 3fff96ad..6bb1ba0e 100644 --- a/scripts/generate-files.js +++ b/scripts/generate-files.js @@ -48,11 +48,11 @@ function renderActionVariadicUse(size) { ...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 = ` @@ -61,7 +61,12 @@ import type { Action, ContextEnhancer } from '@foscia/core/actions/types'; // Do not edit this file, it is generated by \`pnpm generate-files\`. -export type ActionVariadicUse = { +/** + * Variadic action use methods type. + * + * @internal + */ +export type ActionVariadicUse = { ${range(size).map((index) => renderMethod(index + 1)).join('\n')} }; `.trim(); @@ -77,9 +82,9 @@ function renderActionVariadicRun(size) { ...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>;`; @@ -91,7 +96,12 @@ import type { ContextEnhancer, ContextRunner } from '@foscia/core/actions/types' // Do not edit this file, it is generated by \`pnpm generate-files\`. -export type ActionVariadicRun = { +/** + * Variadic action run methods type. + * + * @internal + */ +export type ActionVariadicRun = { ${range(size).map((index) => renderMethod(index + 1)).join('\n')} }; `.trim(); diff --git a/scripts/lint.js b/scripts/lint.js index 2b9d1906..1c1e59c0 100644 --- a/scripts/lint.js +++ b/scripts/lint.js @@ -39,20 +39,24 @@ async function check() { await Promise.all( (await listFiles(path.resolve(rootDirname, 'packages'))).map(async (file) => { const [_, packageName, context] = file.match(/packages\/([a-z-]+)\/(src|tests)/) ?? []; - if (packageName) { + if (packageName && (packageName !== 'cli' || context !== 'tests')) { const errors = []; 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)) { + if (match[1].endsWith('/') && (context !== 'src' || packageName !== importPackageName)) { + console.log(packageName, context) errors.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'))) { + if (context === 'src' && packageName === importPackageName && (!match[1].endsWith('/') || match[1].endsWith('/index'))) { errors.push( `import of ${c.red(`@foscia/${packageName}`)} must use inner package export (instead of root package export)`, ); diff --git a/typedoc.json b/typedoc.json index 0f64f169..4863d96a 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,14 +1,49 @@ { + "$schema": "https://typedoc.org/schema.json", + "readme": "none", "disableGit": true, "sourceLinkTemplate": "https://github.com/foscia-dev/foscia/tree/main/{path}#L{line}", - "groupOrder": [ - "Classes", - "*", - "Namespaces" + "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" ], "categoryOrder": [ "Enhancers", "Runners", + "Utilities", + "Factories", + "Hooks" + ], + "groupOrder": [ + "Functions", + "Classes", + "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..7039f179 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -12,7 +12,6 @@ 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 @@ -48,8 +47,10 @@ way of using actions. ### 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'; @@ -64,9 +65,11 @@ 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'; @@ -82,10 +85,11 @@ const postOrNull = await action().run( ); ``` -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'; @@ -95,17 +99,23 @@ const post = await action().run(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/) 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 @@ -124,9 +134,10 @@ await action().run(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). +You can use [`destroy`](/docs/api/@foscia/core/functions/none) 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'; @@ -148,7 +159,8 @@ 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'; @@ -185,7 +197,9 @@ 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 @@ -208,22 +222,26 @@ 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'; @@ -249,8 +267,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,7 +282,8 @@ 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'; @@ -279,7 +300,8 @@ 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'; @@ -289,7 +311,8 @@ const postOrFail = await action().run(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'; @@ -299,9 +322,10 @@ const cachedPostOrFetch = await action().run(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,7 +333,8 @@ 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'; @@ -317,11 +342,12 @@ import { sortByDesc } from '@foscia/jsonapi'; action().run( 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 +355,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 @@ -383,7 +418,7 @@ 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 +446,13 @@ action().use( ); ``` -#### Enhancers API guide - - - Available enhancers API guide + + Enhancers API reference ### 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: @@ -454,21 +487,23 @@ const posts = await action().run( ); ``` -#### Runners API guide - - - Available runners API guide + + Runners API reference ### Hooks -You may hook on multiple events which occurs on action instance using the hook +You may hook on multiple events which occurs on action 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. +- [`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. @@ -495,8 +530,10 @@ 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`. +[`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'; @@ -515,69 +552,3 @@ provide some library features 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/models.mdx b/website/docs/core-concepts/models.mdx index 87a20125..f3b07783 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -23,23 +23,23 @@ import ShellCommand from '@site/src/components/ShellCommand'; ### 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/type-aliases/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 +57,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/type-aliases/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/type-aliases/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 +105,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,10 +145,10 @@ those. ### Utilities -Foscia proposes you multiple utilities functions to interact with models. +Foscia provides multiple utilities functions to interact with models. - - Read the models' utilities API guide + + Models' utilities API reference ## Definition @@ -153,8 +157,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 +167,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 @@ -193,27 +198,24 @@ id(toString()) .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. | +[`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 @@ -237,29 +239,25 @@ attr(toDateTime()) .sync('pull'); ``` -#### Chained modifiers {#attributes-chained-modifiers} +#### API {#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). | +[`ModelAttritbueFactory`](/docs/api/@foscia/core/type-aliases/ModelAttritbueFactory) +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: @@ -328,6 +326,17 @@ hasMany('comments') +#### Configuration {#relations-configuration} + +Using `config` chained modifier, you can customize the +[`ModelRelationConfig`](/docs/api/@foscia/core/type-aliases/ModelRelationConfig), +which can vary between implementations and used dependencies. + +#### API {#relations-chained-modifiers} + +[`ModelRelationFactory`](/docs/api/@foscia/core/type-aliases/ModelRelationFactory) +provides type definition of `hasMany` and `hasOne` chained modifiers. + #### Polymorphism Defining a polymorphic relation is pretty simple: @@ -355,17 +364,6 @@ must contain a `type` property matching your Foscia models' types. ::: -#### Chained modifiers {#relations-chained-modifiers} - -| 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). | - #### Recommandations ##### Explicit model when not having circular references @@ -419,16 +417,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 @@ -473,7 +461,7 @@ of an ### Using hooks You can hook multiple events on model's instances using hook registration -functions, such as `onCreating`. +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. @@ -490,7 +478,8 @@ const unregisterThisHook = onCreating(User, async (user) => { unregisterThisHook(); ``` -You can also use `unregisterHook` to remove a registered hook from a model. +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'; @@ -506,9 +495,11 @@ onCreating(User, myCreatingHook); 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`. +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'; @@ -522,7 +513,7 @@ const asyncResultOfYourCallback = await withoutHooks(User, async () => { **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. +without model's hooks, as those hooks will also be disabled. ::: @@ -538,18 +529,28 @@ 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 +- [`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`: action to save (create or update) instance was ran successfully +- [`onSaved`](/docs/api/@foscia/core/functions/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. +- [`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: @@ -569,9 +570,10 @@ 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. +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'; @@ -591,10 +593,14 @@ 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). +- [`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: @@ -632,8 +638,9 @@ 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, +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 @@ -660,27 +667,8 @@ and [models factories](/docs/digging-deeper/models/models-composition#factory-us ## 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/type-aliases/Model) +- [`ModelInstance`](/docs/api/@foscia/core/type-aliases/ModelInstance) diff --git a/website/docs/core-concepts/project.md b/website/docs/core-concepts/project.md index fe039e4d..c7c1a485 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. 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/custom-action-enhancers.mdx b/website/docs/digging-deeper/actions/custom-action-enhancers.mdx index 3c476844..17cc08e7 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().run( + 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..0321c2a5 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; -}>; -``` - -:::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..a96c68eb 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 [`RegistryI`](/docs/api/@foscia/core/type-aliases/RegistryI) 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..0a1d7525 --- /dev/null +++ b/website/docs/digging-deeper/implementations/core.md @@ -0,0 +1,160 @@ +--- +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 [`RegistryI`](/docs/api/@foscia/core/type-aliases/RegistryI) and +[`CacheI`](/docs/api/@foscia/core/type-aliases/CacheI) 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 +[`CacheI`](/docs/api/@foscia/core/type-aliases/CacheI) implementation. + +Currently, it uses [`makeRefsCache`](#makerefscache) with +[`makeWeakRefManager`](/docs/api/@foscia/core/functions/makeWeakRefManager). +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 +[`CacheI`](/docs/api/@foscia/core/type-aliases/CacheI) implementation +which stores reference to instances created by a +[`RefManager`](/docs/api/@foscia/core/type-aliases/RefManager). + +The [`RefManager`](/docs/api/@foscia/core/type-aliases/RefManager) is responsible to: + +- Create a ref object for a cached instance. +- Retrieve value for this ref object (may return undefined if the ref is + considered expired). + +Foscia proposes a two implementations of a +[`RefManager`](/docs/api/@foscia/core/type-aliases/RefManager): + +- [`makeWeakRefManager`](/docs/api/@foscia/core/functions/makeWeakRefManager), + 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. +- [`makeTimeoutRefManager`](/docs/api/@foscia/core/functions/makeTimeoutRefManager), + 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 } = makeRefsCacheWith({ + manager: makeWeakRefManager(), + // or... + // manager: makeTimeoutRefManager({ timeout: 5 * 60 * 1000 }), +}); + +cache.put('posts', '1', post); +const cachedPost = cache.find('posts', '1'); +``` + +#### Configuration + +- [`RefsCacheConfig`](/docs/api/@foscia/core/type-aliases/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 +[`RegistryI`](/docs/api/@foscia/core/type-aliases/RegistryI) 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 +[`RegistryI`](/docs/api/@foscia/core/type-aliases/RegistryI) 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/type-aliases/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/reference/implementations/http.md b/website/docs/digging-deeper/implementations/http.md similarity index 97% rename from website/docs/reference/implementations/http.md rename to website/docs/digging-deeper/implementations/http.md index d116dd2d..59145dde 100644 --- a/website/docs/reference/implementations/http.md +++ b/website/docs/digging-deeper/implementations/http.md @@ -10,8 +10,8 @@ description: 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. +the foundation of [JSON:API](/docs/digging-deeper/implementations/jsonapi) and +[REST](/docs/digging-deeper/implementations/rest) implementations. ## Implementations diff --git a/website/docs/reference/implementations/jsonapi.md b/website/docs/digging-deeper/implementations/jsonapi.md similarity index 88% rename from website/docs/reference/implementations/jsonapi.md rename to website/docs/digging-deeper/implementations/jsonapi.md index d2cf3956..348c0145 100644 --- a/website/docs/reference/implementations/jsonapi.md +++ b/website/docs/digging-deeper/implementations/jsonapi.md @@ -21,7 +21,7 @@ using the [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). `makeJsonApiAdapter` uses -[`makeRestAdapterWith`](/docs/reference/implementations/rest#makejsonrestadapter). +[`makeRestAdapterWith`](/docs/digging-deeper/implementations/rest#makerestadapter). #### Usage @@ -43,8 +43,8 @@ const response = await adapter.execute({ `JsonApiAdapter` extends its configuration object from: -- [`makeHttpAdapter`](/docs/reference/implementations/http#makehttpadapter-configuration) -- [`makeRestAdapterWith`](/docs/reference/implementations/rest#makejsonrestadapter-configuration) +- [`makeHttpAdapter`](/docs/digging-deeper/implementations/http#makehttpadapter-configuration) +- [`makeRestAdapterWith`](/docs/digging-deeper/implementations/rest#makerestadapter-configuration) #### Defined in @@ -56,7 +56,7 @@ This implementation of the deserializer extract model instances from JSON:API documents. `makeJsonApiDeserializer` extends the -[`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith). +[`makeDeserializerWith`](/docs/digging-deeper/implementations/serialization#makedeserializerwith).

@@ -150,7 +150,7 @@ const { instances } = await deserializer.deserialize(data, { `makeJsonApiDeserializer` extends its configuration object from: -- [`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith-configuration) +- [`makeDeserializerWith`](/docs/digging-deeper/implementations/serialization#makedeserializerwith-configuration) | Name | Type | Description | |------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| @@ -168,7 +168,7 @@ This implementation of the serializer creates a JSON:API documents from model instance and relations. `makeJsonApiSerializer` extends the -[`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith). +[`makeSerializerWith`](/docs/digging-deeper/implementations/serialization#makeserializerwith).
@@ -230,7 +230,7 @@ const data = await serializer.serializeInstance(instance, { `makeJsonApiSerializer` extends its configuration object from: -- [`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith-configuration) +- [`makeSerializerWith`](/docs/digging-deeper/implementations/serialization#makeserializerwith-configuration) #### Defined in diff --git a/website/docs/digging-deeper/implementations/presentation.md b/website/docs/digging-deeper/implementations/presentation.md new file mode 100644 index 00000000..ebb29ae6 --- /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: + +- [`AdapterI`](/docs/api/@foscia/core/type-aliases/AdapterI) + 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. +- [`DeserializerI`](/docs/api/@foscia/core/type-aliases/DeserializerI) + will deserialize records to instances. It might use the cache and + registry internally. +- [`SerializerI`](/docs/api/@foscia/core/type-aliases/SerializerI) + will serialize instances to the data source format. +- [`CacheI`](/docs/api/@foscia/core/type-aliases/CacheI) + 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. +- [`RegistryI`](/docs/api/@foscia/core/type-aliases/RegistryI) + 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 `CacheI` and `RegistryI`. 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 `AdapterI` to interact with HTTP data +sources. + +- [Adapter through `makeHttpAdapter`](/docs/digging-deeper/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/digging-deeper/implementations/jsonapi#makejsonapiadapter) +- [Serializer through `makeJsonApiSerializer`](/docs/digging-deeper/implementations/jsonapi#makejsonapiserializer) +- [Deserializer through `makeJsonApiDeserializer`](/docs/digging-deeper/implementations/jsonapi#makejsonapideserializer) + +### REST + +`@foscia/rest` provides implementations of `AdapterI`, `SerializerI` and +`DeserializerI` to interact with JSON REST HTTP data sources. + +- [Adapter through `makeRestAdapter`](/docs/digging-deeper/implementations/rest#makerestadapter) +- [Serializer through `makeRestSerializer`](/docs/digging-deeper/implementations/rest#makerestserializer) +- [Deserializer through `makeRestDeserializer`](/docs/digging-deeper/implementations/rest#makerestdeserializer) + +### Serialization + +`@foscia/serialization` provides partial implementations of `SerializerI` and +`DeserializerI` to transform model instances to/from record generic records. + +- [Serializer through `makeSerializer`](/docs/digging-deeper/implementations/serialization#makeserializer) +- [Deserializer through `makeDeserializer`](/docs/digging-deeper/implementations/serialization#makedeserializer) diff --git a/website/docs/reference/implementations/rest.md b/website/docs/digging-deeper/implementations/rest.md similarity index 69% rename from website/docs/reference/implementations/rest.md rename to website/docs/digging-deeper/implementations/rest.md index 5dfa61e2..6ec509a1 100644 --- a/website/docs/reference/implementations/rest.md +++ b/website/docs/digging-deeper/implementations/rest.md @@ -13,23 +13,23 @@ read/write interactions with JSON REST data sources. ## Implementations -### `makeJsonRestAdapter` +### `makeRestAdapter` 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). +`makeRestAdapter` and `makeRestAdapterWith` use +[`makeHttpAdapterWith`](/docs/digging-deeper/implementations/http#makehttpadapter). #### Usage ```typescript import { paramsSerializer } from '@foscia/http'; -import { makeRestAdapterWith, makeJsonRestAdapter } from '@foscia/rest'; +import { makeRestAdapterWith, makeRestAdapter } from '@foscia/rest'; // Using blueprint (preconfigured with sensible defaults). -const { adapter } = makeJsonRestAdapter({ +const { adapter } = makeRestAdapter({ /* ...configuration */ }); // Using constructor (no default configuration provided). @@ -43,11 +43,11 @@ const response = await adapter.execute({ }); ``` -#### Configuration {#makejsonrestadapter-configuration} +#### Configuration {#makerestadapter-configuration} -`makeJsonRestAdapter` and `makeRestAdapterWith` extend its configuration object from: +`makeRestAdapter` and `makeRestAdapterWith` extend its configuration object from: -- [`makeHttpAdapter`](/docs/reference/implementations/http#makehttpadapter-configuration) +- [`makeHttpAdapter`](/docs/digging-deeper/implementations/http#makehttpadapter-configuration) | Name | Type | Description | |-------------------| ------------------------------- |-------------------------------------------------------------------------------------------------------------------------| @@ -55,16 +55,16 @@ const response = await adapter.execute({ #### 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/blueprints/makeRestAdapter.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeRestAdapter.ts) - [`packages/rest/src/makeRestAdapterWith.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/makeRestAdapterWith.ts) -### `makeJsonRestDeserializer` +### `makeRestDeserializer` This implementation of the deserializer extract model instances from object documents. -`makeJsonRestDeserializer` extends the -[`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith). +`makeRestDeserializer` extends the +[`makeDeserializerWith`](/docs/digging-deeper/implementations/serialization#makedeserializerwith).
@@ -74,7 +74,7 @@ Deserialized REST document example -Here is an example of a REST document which `makeJsonRestDeserializer` can deserialize +Here is an example of a REST document which `makeRestDeserializer` can deserialize to model instances. ```json @@ -111,10 +111,10 @@ to model instances. #### Usage ```typescript -import { makeJsonRestDeserializer } from '@foscia/rest'; +import { makeRestDeserializer } from '@foscia/rest'; // Using blueprint (preconfigured with sensible defaults). -const { deserializer } = makeJsonRestDeserializer({ +const { deserializer } = makeRestDeserializer({ /* ...configuration */ }); @@ -125,9 +125,9 @@ const { instances } = await deserializer.deserialize(data, { #### Configuration -`makeJsonRestDeserializer` extends its configuration object from: +`makeRestDeserializer` extends its configuration object from: -- [`makeDeserializerWith`](/docs/reference/implementations/serialization#makedeserializerwith-configuration) +- [`makeDeserializerWith`](/docs/digging-deeper/implementations/serialization#makedeserializerwith-configuration) | Name | Type | Description | |------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| @@ -137,15 +137,15 @@ const { instances } = await deserializer.deserialize(data, { #### Defined in -- [`packages/rest/src/blueprints/makeJsonRestDeserializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeJsonRestDeserializer.ts) +- [`packages/rest/src/blueprints/makeRestDeserializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeRestDeserializer.ts) -### `makeJsonRestSerializer` +### `makeRestSerializer` This implementation of the serializer creates a REST documents from model instance and relations. -`makeJsonRestSerializer` extends the -[`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith). +`makeRestSerializer` extends the +[`makeSerializerWith`](/docs/digging-deeper/implementations/serialization#makeserializerwith).
@@ -155,7 +155,7 @@ Serialized REST document example -Here is an example of a REST document which `makeJsonRestSerializer` can +Here is an example of a REST document which `makeRestSerializer` can create from a model instance. ```json @@ -173,10 +173,10 @@ create from a model instance. #### Usage ```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; +import { makeRestSerializer } from '@foscia/rest'; // Using blueprint (preconfigured with sensible defaults). -const { serializer } = makeJsonRestSerializer({ +const { serializer } = makeRestSerializer({ /* ...configuration */ }); @@ -187,9 +187,9 @@ const data = await serializer.serializeInstance(instance, { #### Configuration -`makeJsonRestSerializer` extends its configuration object from: +`makeRestSerializer` extends its configuration object from: -- [`makeSerializerWith`](/docs/reference/implementations/serialization#makeserializerwith-configuration) +- [`makeSerializerWith`](/docs/digging-deeper/implementations/serialization#makeserializerwith-configuration) | Name | Type | Description | |-----------------|-----------|------------------------------------------------------| @@ -197,4 +197,4 @@ const data = await serializer.serializeInstance(instance, { #### Defined in -[`packages/rest/src/blueprints/makeJsonRestSerializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeJsonRestSerializer.ts) +[`packages/rest/src/blueprints/makeRestSerializer.ts`](https://github.com/foscia-dev/foscia/blob/main/packages/rest/src/blueprints/makeRestSerializer.ts) diff --git a/website/docs/reference/implementations/serialization.md b/website/docs/digging-deeper/implementations/serialization.md similarity index 100% rename from website/docs/reference/implementations/serialization.md rename to website/docs/digging-deeper/implementations/serialization.md 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..3fed9b92 100644 --- a/website/docs/digging-deeper/models/models-changes-tracking.md +++ b/website/docs/digging-deeper/models/models-changes-tracking.md @@ -24,9 +24,11 @@ last synchronization. ## 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/type-aliases/ModelInstance#original) properties of your instance. ```typescript @@ -37,9 +39,11 @@ 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 +[`compareSnapshots`](/docs/api/@foscia/core/functions/compareSnapshots). 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'; @@ -57,8 +61,9 @@ compareSnapshots(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 +76,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..4328221c 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'; @@ -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,14 +203,15 @@ 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} @@ -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..a3358f8e 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,70 +94,10 @@ makeModel('posts'); makeModel({ type: 'posts' }); ``` -#### `path` - -**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. In a SQL database, it would be the -table. - -```typescript title="post.ts" -import { makeModel } from '@foscia/core'; - -makeModel({ - type: 'posts', - path: 'blog-posts', -}); -``` - -#### `guessPath` - -**Default**: no transformation. - -**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`. - -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` - -**Default**: no transformation. - -**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. - -```typescript title="post.ts" -import { makeModel, isManyRelationDef } 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()!, -}); -``` - #### `guessAlias` +##### Description + **Default**: no transformation. **Recommandation**: use this configuration option inside a @@ -157,6 +105,8 @@ makeModel({ `guessAlias` transform a model's property `key` to guess its `alias`. +##### 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: @@ -172,6 +122,8 @@ makeModel({ #### `guessRelationType` +##### Description + **Default**: pluralize key for "to one" relation. **Recommandation**: use this configuration option inside a @@ -181,6 +133,8 @@ 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. +##### 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`; @@ -196,37 +150,10 @@ makeModel({ }); ``` -#### `guessRelationPath` - -**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). - -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, - isManyRelationDef, - ModelClass, - ModelRelation, -} from '@foscia/core'; - -makeModel({ - type: 'posts', - guessRelationPath: (def: ModelRelation) => toKebabCase(def.key), -}); -``` - #### `compareValue` and `cloneValue` +##### Description + **Default**: compare will check for strict equality and clone will return the base value. @@ -236,12 +163,15 @@ 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. 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: @@ -256,16 +186,19 @@ makeModel({ ``` You may change those two functions to really clone values when syncing the -instance state. Keep in mind that: +instance state (such as arrays). 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 +- 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 will break the history because of its default behavior #### `strict` +##### Description + **Default**: `undefined`. **Recommandation**: use this configuration option inside a @@ -283,8 +216,20 @@ strict settings. ::: +##### Example + +```typescript title="post.ts" +import { makeModel } from '@foscia/core'; + +makeModel({ + strict: true, +}); +``` + #### `strictProperties` +##### Description + **Default**: `false`. **Recommandation**: use this configuration option inside a @@ -293,8 +238,20 @@ strict settings. 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. +##### Example + +```typescript title="post.ts" +import { makeModel } from '@foscia/core'; + +makeModel({ + strictReadOnly: true, +}); +``` + #### `strictReadOnly` +##### Description + **Default**: `true`. **Recommandation**: use this configuration option inside a @@ -303,6 +260,16 @@ the value was not retrieved from the store or if the relation is not loaded. When enabled, setting a model's instance readonly property value will throw an error. +##### Example + +```typescript title="post.ts" +import { makeModel } from '@foscia/core'; + +makeModel({ + strictReadOnly: true, +}); +``` + ### HTTP The following configuration options are specific to HTTP models (when @@ -310,8 +277,12 @@ interacting with JSON:API, JSON REST, etc.). #### `baseURL` +##### Description + You may define a `baseURL` configuration option on your models. It will replace -the default base URL define on the adapter. +the default base URL defined on the adapter. + +##### Example ```typescript title="post.ts" import { makeModel } from '@foscia/core'; @@ -321,3 +292,107 @@ makeModel({ baseURL: 'https://example.com/api/v2', }); ``` + +#### `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). + +`guessPath` transform a model's `type` to guess its `path`. + +##### Example + +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). + +`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. + +##### Example + +```typescript title="post.ts" +import { makeModel, isManyRelationDef } 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()!, +}); +``` + +#### `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, + isManyRelationDef, + ModelRelation, +} from '@foscia/core'; + +makeModel({ + type: 'posts', + guessRelationPath: (def: ModelRelation) => toKebabCase(def.key), +}); +``` diff --git a/website/docs/digging-deeper/models/models-reduce-revive.mdx b/website/docs/digging-deeper/models/models-reduce-revive.mdx index e04490b6..a688cf25 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. @@ -58,7 +60,9 @@ const myRevivedPost = revive(JSON.parse(json)); Notice that: -- `makeModelsReviver` must receive an array of revivable models. +- [`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. - 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. @@ -68,17 +72,22 @@ Notice that: ## 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 +118,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/type-aliases/ModelInstance#exists) state, +[`$original`](/docs/api/@foscia/core/type-aliases/ModelInstance#original) +snapshot or other instance properties). ```typescript import { attr, makeModel } from '@foscia/core'; @@ -139,8 +151,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 +188,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..fb047f0e 100644 --- a/website/docs/digging-deeper/models/models-transformers.mdx +++ b/website/docs/digging-deeper/models/models-transformers.mdx @@ -18,7 +18,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 +35,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,9 +58,11 @@ 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" because serializing to a UTC date. This will avoid local date to be serialized to incorrect date due to UTC serialization. @@ -63,7 +70,8 @@ serialization. ### `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 +84,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 +107,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 +127,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 +149,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 +183,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. ```typescript import { attr, makeModel } from '@foscia/core'; 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 99% rename from website/docs/digging-deeper/cli.mdx rename to website/docs/digging-deeper/usages/cli.mdx index adb9e7ca..e9ac1084 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'; diff --git a/website/docs/digging-deeper/http.mdx b/website/docs/digging-deeper/usages/http.mdx similarity index 65% rename from website/docs/digging-deeper/http.mdx rename to website/docs/digging-deeper/usages/http.mdx index c8a891c6..9d8f695e 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'; @@ -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/type-aliases/HttpRequestConfig#headers) and +[`params`](/docs/api/@foscia/http/type-aliases/HttpRequestConfig#params) options +to any other +[`Request` options](https://developer.mozilla.org/docs/Web/API/Request/Request#options): ```typescript import { raw } from '@foscia/core'; @@ -122,7 +128,7 @@ 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'; @@ -143,14 +149,15 @@ 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 +[`headers`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig#headers) +option or implement a request transformer (useful for unstable headers, +such as `Accept-Language` in an internationalized application). ```typescript import { makeHttpAdapter } from '@foscia/http'; @@ -183,10 +190,13 @@ 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). +- [`paramsSerializer`](/docs/api/@foscia/http/functions/paramsSerializer) + (default for [`makeHttpAdapter`](/docs/api/@foscia/http/functions/makeHttpAdapter)) + 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 +210,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 77% rename from website/docs/digging-deeper/jsonapi.md rename to website/docs/digging-deeper/usages/jsonapi.md index 786d039f..09681602 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. --- @@ -34,7 +34,7 @@ const posts = await action().run(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 @@ -54,10 +54,11 @@ 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 @@ -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. @@ -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). @@ -159,19 +160,22 @@ 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), 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/http/type-aliases/HttpAdapterConfig#modelpathtransformer) +and +[`relationPathTransformer`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig#relationpathtransformer) +options. ```typescript import { camelCase } from 'lodash-es'; @@ -188,7 +192,10 @@ 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/serialization/type-aliases/SerializerConfig#serializekey) +and [`deserializeKey`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig#deserializekey) +options. ```typescript import { kebabCase } from 'lodash-es'; @@ -208,7 +215,8 @@ 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/rest/type-aliases/RestDeserializerConfig#pullidentifier) +option. ```typescript import { makeJsonApiDeserializer } from '@foscia/rest'; @@ -225,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/rest.md b/website/docs/digging-deeper/usages/rest.md similarity index 70% rename from website/docs/digging-deeper/rest.md rename to website/docs/digging-deeper/usages/rest.md index 3e2a5534..da4fe3b1 100644 --- a/website/docs/digging-deeper/rest.md +++ b/website/docs/digging-deeper/usages/rest.md @@ -1,5 +1,5 @@ --- -sidebar_label: Using REST +sidebar_label: REST sidebar_position: 4000 description: Using Foscia to interact with a JSON:API. --- @@ -25,7 +25,7 @@ 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) +[configure your REST adapter](/docs/digging-deeper/implementations/rest#makerestadapter) to serialize relationships inclusion in every request. If you need something specific, you can @@ -78,25 +78,27 @@ console.log(results); ## Configuration recipes Here are common configuration for `@foscia/rest` implementation. You can read -[the implementation and configuration guide](/docs/reference/implementations/rest) +[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/http), as +[HTTP usage and common configuration recipes](/docs/digging-deeper/usages/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`. +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` option. +To define a custom query parameter, use the +[`includeParamKey`](/docs/api/@foscia/rest/type-aliases/RestAdapterConfig#includeparamkey) +option. ```typescript -import { makeJsonRestAdapter } from '@foscia/http'; +import { makeRestAdapter } from '@foscia/http'; -const { adapter } = makeJsonRestAdapter({ +const { adapter } = makeRestAdapter({ includeParamKey: 'with', }); ``` @@ -123,13 +125,14 @@ makeRestAdapterWith({ ### 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 +inside a `data` property), you can add +[`extractData`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig#extractdata) option which will extract records data using the given transformation function. ```typescript -import { makeJsonRestDeserializer } from '@foscia/rest'; +import { makeRestDeserializer } from '@foscia/rest'; -makeJsonRestSerializer({ +makeRestDeserializer({ extractData: (data: { data: any }) => ({ records: data.data }), }); ``` @@ -140,9 +143,9 @@ used by Foscia examples), you can also provide more customized behavior: ```typescript import { guessContextModel, consumeModel, consumeRelation } from '@foscia/core'; -import { makeJsonRestDeserializer } from '@foscia/rest'; +import { makeRestDeserializer } from '@foscia/rest'; -makeJsonRestSerializer({ +makeRestDeserializer({ extractData: async (data: any, context) => { // Detect targeted model with context. const model = await guessContextModel({ @@ -165,13 +168,15 @@ This behavior can also be easily implemented when ### 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 +inside a `data` property), you can add +[`createData`](/docs/api/@foscia/serialization/type-aliases/SerializerConfig#createdata) +option which will wrap records data using the given transformation function. ```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; +import { makeRestSerializer } from '@foscia/rest'; -makeJsonRestSerializer({ +makeRestSerializer({ createData: (records, context) => ({ data: records }), }); ``` @@ -180,14 +185,17 @@ makeJsonRestSerializer({ 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/http/type-aliases/HttpAdapterConfig#modelpathtransformer) +and +[`relationPathTransformer`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig#relationpathtransformer) +options. ```typescript import { camelCase } from 'lodash-es'; -import { makeJsonRestAdapter } from '@foscia/rest'; +import { makeRestAdapter } from '@foscia/rest'; -makeJsonRestAdapter({ +makeRestAdapter({ modelPathTransformer: (path) => camelCase(path), relationPathTransformer: (path) => camelCase(path), }); @@ -198,17 +206,20 @@ makeJsonRestAdapter({ 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/serialization/type-aliases/SerializerConfig#serializekey) +and [`deserializeKey`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig#deserializekey) +options. ```typescript import { kebabCase } from 'lodash-es'; -import { makeJsonRestSerializer, makeJsonRestDeserializer } from '@foscia/rest'; +import { makeRestSerializer, makeRestDeserializer } from '@foscia/rest'; -makeJsonRestSerializer({ +makeRestSerializer({ serializeKey: ({ key }) => kebabCase(key), }); -makeJsonRestDeserializer({ +makeRestDeserializer({ deserializeKey: ({ key }) => kebabCase(key), }); ``` @@ -217,15 +228,16 @@ makeJsonRestDeserializer({ By default, REST implementation will only serialize the related IDs as the serialized relation's data. You can customize this behavior using -`serializeRelation` option. +[`serializeRelation`](/docs/api/@foscia/serialization/type-aliases/SerializerConfig#serializerelation) +option. Here is an example which will serialize ID and type to support polymorphic relations: ```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; +import { makeRestSerializer } from '@foscia/rest'; -makeJsonRestSerializer({ +makeRestSerializer({ serializeRelation: (_, related) => ({ type: related.$model.$type, id: related.id }), }); ``` @@ -233,9 +245,9 @@ makeJsonRestSerializer({ Here is another example where we serialize the whole related record: ```typescript -import { makeJsonRestSerializer } from '@foscia/rest'; +import { makeRestSerializer } from '@foscia/rest'; -makeJsonRestSerializer({ +makeRestSerializer({ serializeRelation: ({ context, serializer }, related, parents) => serializer .serializeInstance(related, context, parents) }); @@ -244,8 +256,11 @@ makeJsonRestSerializer({ :::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. +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/serialization/type-aliases/SerializerConfig#serializerelated) +option which have the same signature. ::: @@ -254,12 +269,13 @@ the `serializeRelated` option which have the same signature. 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/rest/type-aliases/RestDeserializerConfig#pullidentifier) +option. ```typescript -import { makeJsonRestDeserializer } from '@foscia/rest'; +import { makeRestDeserializer } from '@foscia/rest'; -makeJsonRestDeserializer({ +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(); @@ -271,4 +287,4 @@ makeJsonRestDeserializer({ ## Reference -- [Implementation and configuration guide](/docs/reference/implementations/rest) +- [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..703030e0 --- /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().run(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/getting-started.mdx b/website/docs/getting-started.mdx index 3d4edff9..ab76e61f 100644 --- a/website/docs/getting-started.mdx +++ b/website/docs/getting-started.mdx @@ -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/type-aliases/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 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,97 +187,42 @@ 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/type-aliases/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 +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. You can also decompose those calls through `use` and `run` individual calls, @@ -299,9 +249,10 @@ 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'; @@ -314,8 +265,13 @@ const post = await action().run( ); ``` -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'; @@ -333,7 +289,29 @@ const createdPost = await action().run( ); ``` - +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().run(destroy(post)); +// Do this instead: +// highlight.addition +await action().run(destroy(post), none()); +``` + + Read the full guide on actions @@ -347,6 +325,11 @@ Using JSON:API or REST blueprints, you can also use Foscia to make non-standard 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'; diff --git a/website/docs/help/faq.md b/website/docs/help/faq.md index 68a5a1d8..03bcc5c4 100644 --- a/website/docs/help/faq.md +++ b/website/docs/help/faq.md @@ -80,10 +80,10 @@ 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. -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..8b0db299 100644 --- a/website/docs/integrations/nuxt.mdx +++ b/website/docs/integrations/nuxt.mdx @@ -45,18 +45,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, }), diff --git a/website/docs/reference/actions-enhancers.mdx b/website/docs/reference/actions-enhancers.mdx deleted file mode 100644 index d27bc800..00000000 --- a/website/docs/reference/actions-enhancers.mdx +++ /dev/null @@ -1,898 +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 - -### `queryAs` - - - -Define models targeted by the query. This will keep the context state when -executing the query, but will take priority when 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`). - -#### Example - -```typescript -import { queryAs } from '@foscia/core'; - -action().use(queryAs(Post)); -action().use(queryAs(Post, Comment)); -action().use(queryAs([Post, Comment])); -``` - -#### Arguments - -- [`ArrayableVariadic`](/docs/reference/api/@foscia/core/type-aliases/Model) `...models` -the model the query is targeting - -### `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 faa54e9a..00000000 --- a/website/docs/reference/actions-extensions.md +++ /dev/null @@ -1,105 +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) -- [`queryAs`](/docs/reference/actions-enhancers#queryas) -- [`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/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/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..2e26ac89 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -4,7 +4,122 @@ toc_max_heading_level: 2 sidebar_position: 5 --- -# Migration +# Migration guides + +## 0.13.x from 0.12.x + +### High impacts changes + +- [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 + +- [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) + +### 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 (`CacheI` +and `RegistryI`) 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` + +### 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). ## 0.12.x from 0.11.x @@ -70,7 +185,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 +215,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 +244,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 +277,8 @@ export default class Post extends makeModel('posts', { ...publishable, // highlight.addition publishable, -}) {} +}) { +} ``` ### `forModel`, `forInstance`, `forRelation`, `forId` and `find` are deprecated @@ -169,7 +287,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..87670005 --- /dev/null +++ b/website/docs/upgrade/support-policy.md @@ -0,0 +1,28 @@ +--- +description: Support policy for Foscia packages. +sidebar_position: 20 +--- + +# Support policy + +**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. + +Currently, only the latest major version of Foscia is actively maintained. + +::: diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 39b21358..2112740f 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,31 @@ 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', + ], + }), + ], ], themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ @@ -79,7 +94,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 +103,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 +139,7 @@ const config = { { position: 'left', label: 'API', - to: '/docs/category/reference', + to: '/docs/category/api', }, { position: 'right', @@ -163,6 +178,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 +197,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..8780f5c2 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", + "@docusaurus/core": "^3.5.2", + "@docusaurus/preset-classic": "^3.5.2", + "@docusaurus/theme-common": "^3.5.2", + "@fontsource-variable/onest": "^5.0.6", + "@fontsource/fira-mono": "^5.0.15", "@mdx-js/react": "^3.0.1", "clsx": "^2.1.1", - "docusaurus-plugin-typedoc": "^1.0.1", + "docusaurus-plugin-typedoc": "^1.0.5", "dotenv": "^16.4.5", - "prism-react-renderer": "^2.3.1", + "prism-react-renderer": "^2.4.0", "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.26.7", + "typedoc-plugin-markdown": "^4.2.7", + "typedoc-plugin-mdn-links": "^3.2.12", + "typescript": "^5.6.2" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^3.4.0", + "@docusaurus/module-type-aliases": "^3.5.2", "docusaurus-mdx-checker": "^3.0.0", - "prettier": "^3.3.1", - "rimraf": "^5.0.7" + "prettier": "^3.3.3", + "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/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..4640358b --- /dev/null +++ b/website/typedoc-plugin.mjs @@ -0,0 +1,77 @@ +// @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`); + } + }); + } + }); +} From b3ee8d1a158b05420cb2af6d871f924aa3fec491 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 9 Jan 2025 22:38:36 +0100 Subject: [PATCH 03/32] feat(core): add action middlewares Add middlewares to action execution. BREAKING CHANGE: actions hooks now contains action instance instead of the context value. --- .../context/consumers/consumeMiddlewares.ts | 11 ++++ .../context/enhancers/hooks/onError.ts | 4 +- .../context/enhancers/hooks/onFinally.ts | 4 +- .../context/enhancers/hooks/onRunning.ts | 4 +- .../context/enhancers/hooks/onSuccess.ts | 4 +- .../middlewares/appendMiddlewares.ts | 30 ++++++++++ .../middlewares/prependMiddlewares.ts | 30 ++++++++++ .../middlewares/replaceMiddlewares.ts | 35 ++++++++++++ packages/core/src/actions/index.ts | 11 +++- .../core/src/actions/makeActionFactory.ts | 24 +++++--- packages/core/src/actions/types.ts | 30 ++++++++-- .../core/tests/typecheck/actions.test-d.ts | 45 ++++++--------- .../unit/actions/makeActionFactory.test.ts | 57 +++++++++++++++---- .../shared/src/arrays/throughMiddlewares.ts | 15 +++++ packages/shared/src/index.ts | 2 + packages/shared/src/types.ts | 14 +++++ packages/test/src/makeActionFactoryMock.ts | 28 ++++++--- packages/test/src/unexpectedActionError.ts | 6 +- website/docs/core-concepts/actions.mdx | 51 +++++++++++++++++ website/docs/upgrade/migration.md | 25 +++++++- 20 files changed, 356 insertions(+), 74 deletions(-) create mode 100644 packages/core/src/actions/context/consumers/consumeMiddlewares.ts create mode 100644 packages/core/src/actions/context/enhancers/middlewares/appendMiddlewares.ts create mode 100644 packages/core/src/actions/context/enhancers/middlewares/prependMiddlewares.ts create mode 100644 packages/core/src/actions/context/enhancers/middlewares/replaceMiddlewares.ts create mode 100644 packages/shared/src/arrays/throughMiddlewares.ts diff --git a/packages/core/src/actions/context/consumers/consumeMiddlewares.ts b/packages/core/src/actions/context/consumers/consumeMiddlewares.ts new file mode 100644 index 00000000..b80dc315 --- /dev/null +++ b/packages/core/src/actions/context/consumers/consumeMiddlewares.ts @@ -0,0 +1,11 @@ +import consumeContext from '@foscia/core/actions/context/consumers/consumeContext'; +import { ActionMiddleware, ConsumeMiddlewares } from '@foscia/core/actions/types'; + +export default ( + context: C & Partial>, + defaultValue?: D, +) => consumeContext(context, 'middlewares', [ + 'appendMiddlewares', + 'prependMiddlewares', + 'replaceMiddlewares', +], defaultValue) as ActionMiddleware[] | D; diff --git a/packages/core/src/actions/context/enhancers/hooks/onError.ts b/packages/core/src/actions/context/enhancers/hooks/onError.ts index 48b2e1eb..aafa1db1 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onError.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onError.ts @@ -16,12 +16,12 @@ import { Awaitable } from '@foscia/shared'; * import { onError } from '@foscia/core'; * * action().use(onError((event) => { - * console.log(event.context, event.error); + * console.log(event.action, event.error); * })); * ``` */ export default /* @__PURE__ */ makeEnhancer('onError', ( - callback: (event: { context: C; error: unknown; }) => Awaitable, + callback: (event: { action: Action; error: unknown; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'error', callback as any); }); diff --git a/packages/core/src/actions/context/enhancers/hooks/onFinally.ts b/packages/core/src/actions/context/enhancers/hooks/onFinally.ts index 5787e366..1caed3d0 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onFinally.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onFinally.ts @@ -16,12 +16,12 @@ import { Awaitable } from '@foscia/shared'; * import { onFinally } from '@foscia/core'; * * action().use(onFinally((event) => { - * console.log(event.context); + * console.log(event.action); * })); * ``` */ export default /* @__PURE__ */ makeEnhancer('onFinally', ( - callback: (event: { context: C; }) => Awaitable, + callback: (event: { action: Action; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'finally', callback as any); }); diff --git a/packages/core/src/actions/context/enhancers/hooks/onRunning.ts b/packages/core/src/actions/context/enhancers/hooks/onRunning.ts index 07dec5e0..30fc1806 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onRunning.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onRunning.ts @@ -16,12 +16,12 @@ import { Awaitable } from '@foscia/shared'; * import { onRunning } from '@foscia/core'; * * action().use(onRunning((event) => { - * console.log(event.context, event.runner); + * console.log(event.action, event.runner); * })); * ``` */ export default /* @__PURE__ */ makeEnhancer('onRunning', ( - callback: (event: { context: C; runner: Function; }) => Awaitable, + callback: (event: { action: Action; runner: Function; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'running', callback as any); }); diff --git a/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts b/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts index 7807d6be..2170dae3 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts @@ -16,12 +16,12 @@ import { Awaitable } from '@foscia/shared'; * import { onSuccess } from '@foscia/core'; * * action().use(onSuccess((event) => { - * console.log(event.context, event.result); + * console.log(event.action, event.result); * })); * ``` */ export default /* @__PURE__ */ makeEnhancer('onSuccess', ( - callback: (event: { context: C; result: unknown; }) => Awaitable, + callback: (event: { action: Action; result: unknown; }) => Awaitable, ) => (action: Action) => { registerHook(action, 'success', callback as any); }); diff --git a/packages/core/src/actions/context/enhancers/middlewares/appendMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/appendMiddlewares.ts new file mode 100644 index 00000000..cb91c81d --- /dev/null +++ b/packages/core/src/actions/context/enhancers/middlewares/appendMiddlewares.ts @@ -0,0 +1,30 @@ +import replaceMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/replaceMiddlewares'; +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 { appendMiddlewares } from '@foscia/core'; + * + * const posts = await action().use(appendMiddlewares( + * (action, next) => { + * // Do something... + * + * return next(); + * }, + * )); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('appendMiddlewares', (( + ...middlewares: ArrayableVariadic> +) => replaceMiddlewares((prev) => [...prev, ...wrapVariadic(...middlewares)]))); diff --git a/packages/core/src/actions/context/enhancers/middlewares/prependMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/prependMiddlewares.ts new file mode 100644 index 00000000..8cf5bd00 --- /dev/null +++ b/packages/core/src/actions/context/enhancers/middlewares/prependMiddlewares.ts @@ -0,0 +1,30 @@ +import replaceMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/replaceMiddlewares'; +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 { prependMiddlewares } from '@foscia/core'; + * + * const posts = await action().use(prependMiddlewares( + * (action, next) => { + * // Do something... + * + * return next(); + * }, + * )); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('prependMiddlewares', (( + ...middlewares: ArrayableVariadic> +) => replaceMiddlewares((prev) => [...wrapVariadic(...middlewares), ...prev]))); diff --git a/packages/core/src/actions/context/enhancers/middlewares/replaceMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/replaceMiddlewares.ts new file mode 100644 index 00000000..ca53d609 --- /dev/null +++ b/packages/core/src/actions/context/enhancers/middlewares/replaceMiddlewares.ts @@ -0,0 +1,35 @@ +import consumeMiddlewares from '@foscia/core/actions/context/consumers/consumeMiddlewares'; +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 factory + * + * @category Enhancers + * @since 0.13.0 + * + * @example + * ```typescript + * import { replaceMiddlewares } from '@foscia/core'; + * + * const posts = await action().use(replaceMiddlewares((previous) => [ + * ...previous, + * (action, next) => { + * // Do something... + * + * return next(); + * }, + * ])); + * ``` + */ +export default /* @__PURE__ */ makeEnhancer('replaceMiddlewares', (( + middlewares: ((prev: ActionMiddleware[]) => Awaitable[]>), +) => async (action: Action) => action.use(context({ + middlewares: await middlewares( + consumeMiddlewares(await action.useContext(), []) as ActionMiddleware[], + ), +})))); diff --git a/packages/core/src/actions/index.ts b/packages/core/src/actions/index.ts index 97b37f6d..5876a29e 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -1,4 +1,5 @@ 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'; @@ -31,6 +32,12 @@ 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 appendMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/appendMiddlewares'; +import prependMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/prependMiddlewares'; +import replaceMiddlewares + from '@foscia/core/actions/context/enhancers/middlewares/replaceMiddlewares'; 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'; @@ -45,7 +52,6 @@ import oneOr, { OneData } 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 isContextFunction from '@foscia/core/actions/checks/isContextFunction'; import makeActionFactory from '@foscia/core/actions/makeActionFactory'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import makeRunner from '@foscia/core/actions/makeRunner'; @@ -90,6 +96,9 @@ export { onSuccess, onError, onFinally, + appendMiddlewares, + prependMiddlewares, + replaceMiddlewares, consumeAction, consumeAdapter, consumeCache, diff --git a/packages/core/src/actions/makeActionFactory.ts b/packages/core/src/actions/makeActionFactory.ts index e53fa03a..da123aff 100644 --- a/packages/core/src/actions/makeActionFactory.ts +++ b/packages/core/src/actions/makeActionFactory.ts @@ -4,7 +4,13 @@ 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, sequentialTransform, value } from '@foscia/shared'; +import { + Dictionary, + Middleware, + sequentialTransform, + throughMiddlewares, + value, +} from '@foscia/shared'; /** * Create an action factory. @@ -58,28 +64,32 @@ export default ( this.use(...enhancers); - const context = await this.useContext(); + const { middlewares, ...context } = await this.useContext() as Dictionary; + this.updateContext(context); - await runHooks(this, 'running', { context, runner }); + 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 withoutHooks(this, async () => this.track(runner)); + 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', { context, result }); + await runHooks(this, 'success', { action: this, result }); return result; } catch (error) { - await runHooks(this, 'error', { context, error }); + await runHooks(this, 'error', { action: this, error }); throw error; } finally { - await runHooks(this, 'finally', { context }); + await runHooks(this, 'finally', { action: this }); } }, async track( diff --git a/packages/core/src/actions/types.ts b/packages/core/src/actions/types.ts index 95fdce86..d817a2cc 100644 --- a/packages/core/src/actions/types.ts +++ b/packages/core/src/actions/types.ts @@ -11,9 +11,9 @@ import { } from '@foscia/core/symbols'; import { Adapter, - InstancesCache, DeserializedData, Deserializer, + InstancesCache, ModelsRegistry, Serializer, } from '@foscia/core/types'; @@ -28,11 +28,10 @@ export * from '@foscia/core/actions/actionVariadicRun'; * @internal */ export type ActionHooksDefinition = { - enhancing: HookCallback<{ enhancer: ContextEnhancer; depth: number; }>; - running: HookCallback<{ context: {}; runner: ContextRunner; }>; - 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; }>; }; /** @@ -96,6 +95,16 @@ export type Action = & ActionVariadicRun & Hookable; +/** + * Middleware to impact an action result or behavior. + * + * @internal + */ +export type ActionMiddleware = ( + value: Action, + next: (value: Action) => Promise, +) => Awaitable; + /** * Action factory. * @@ -288,6 +297,15 @@ export type ConsumeInclude = { include?: string[]; }; +/** + * Define the middlewares to run. + * + * @internal + */ +export type ConsumeMiddlewares = { + middlewares?: ActionMiddleware[]; +}; + /** * Context dependency which can be lazy-loaded. * diff --git a/packages/core/tests/typecheck/actions.test-d.ts b/packages/core/tests/typecheck/actions.test-d.ts index 64933439..865804a8 100644 --- a/packages/core/tests/typecheck/actions.test-d.ts +++ b/packages/core/tests/typecheck/actions.test-d.ts @@ -1,20 +1,22 @@ import { Adapter, - all, + all, appendMiddlewares, associate, attach, cached, cachedOr, - InstancesCache, + context, create, Deserializer, destroy, dissociate, include, + InstancesCache, makeActionFactory, one, oneOrCurrent, oneOrFail, + onRunning, query, queryAs, raw, @@ -207,31 +209,6 @@ test('Actions are type safe', async () => { // @ts-expect-error PostMock not assignable to CommentMock await action().use(attach(new PostMock(), 'comments', [new PostMock()])); - // @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'); - const commentsUsingFunc = await action() .use(query(new PostMock(), 'comments')) .run(all()); @@ -243,4 +220,18 @@ test('Actions are type safe', async () => { expectTypeOf(response).toEqualTypeOf(); // This will ensure `response` is not `any`. 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' }), appendMiddlewares([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/unit/actions/makeActionFactory.test.ts b/packages/core/tests/unit/actions/makeActionFactory.test.ts index 7836227d..f722a7ae 100644 --- a/packages/core/tests/unit/actions/makeActionFactory.test.ts +++ b/packages/core/tests/unit/actions/makeActionFactory.test.ts @@ -1,6 +1,7 @@ import { Action, ActionCall, + appendMiddlewares, cachedOr, context, ContextFunctionType, @@ -13,6 +14,7 @@ import { onFinally, onRunning, onSuccess, + prependMiddlewares, query, SYMBOL_ACTION_CONTEXT_ENHANCER, SYMBOL_ACTION_CONTEXT_RUNNER, @@ -31,8 +33,10 @@ describe('unit: makeActionFactory', () => { const runner = () => 'dummy'; + let action = makeActionFactory()(); + await expect( - makeActionFactory()() + action .use(onRunning(runningMock)) .use(onSuccess(successMock)) .use(onError(errorMock)) @@ -41,13 +45,13 @@ describe('unit: makeActionFactory', () => { ).resolves.toStrictEqual('dummy'); expect(loggerDebugMock.mock.calls).toStrictEqual([ - ['Action running.', [{ context: {}, runner }]], - ['Action success.', [{ context: {}, result: 'dummy' }]], + ['Action running.', [{ action, runner }]], + ['Action success.', [{ action, result: 'dummy' }]], ]); - expect(runningMock.mock.calls).toStrictEqual([[{ context: {}, runner }]]); - expect(successMock.mock.calls).toStrictEqual([[{ context: {}, 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([[{ context: {} }]]); + expect(finallyMock.mock.calls).toStrictEqual([[{ action }]]); loggerDebugMock.mockReset(); runningMock.mockReset(); @@ -60,8 +64,10 @@ describe('unit: makeActionFactory', () => { throw error; }; + action = makeActionFactory()(); + await expect( - makeActionFactory()() + action .use(onRunning(runningMock)) .use(onSuccess(successMock)) .use(onError(errorMock)) @@ -70,17 +76,44 @@ describe('unit: makeActionFactory', () => { ).rejects.toThrowError(error); expect(loggerDebugMock.mock.calls).toStrictEqual([ - ['Action running.', [{ context: {}, runner: failingRunner }]], - ['Action error.', [{ context: {}, error }]], + ['Action running.', [{ action, runner: failingRunner }]], + ['Action error.', [{ action, error }]], ]); - expect(runningMock.mock.calls).toStrictEqual([[{ context: {}, runner: failingRunner }]]); + expect(runningMock.mock.calls).toStrictEqual([[{ action, runner: failingRunner }]]); expect(successMock.mock.calls).toStrictEqual([]); - expect(errorMock.mock.calls).toStrictEqual([[{ context: {}, error }]]); - expect(finallyMock.mock.calls).toStrictEqual([[{ context: {} }]]); + 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(appendMiddlewares([ + 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(prependMiddlewares(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; }>, diff --git a/packages/shared/src/arrays/throughMiddlewares.ts b/packages/shared/src/arrays/throughMiddlewares.ts new file mode 100644 index 00000000..39aedfd1 --- /dev/null +++ b/packages/shared/src/arrays/throughMiddlewares.ts @@ -0,0 +1,15 @@ +import { Middleware, MiddlewareNext } from '@foscia/shared/types'; + +/** + * Execute a callback through a set of middlewares. + * + * @param middlewares + * @param callback + */ +export default ( + middlewares: Middleware[], + callback: MiddlewareNext, +) => middlewares.reduceRight>( + (next, middleware) => (value) => middleware(value, next), + callback, +); diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 0d64260c..a8985713 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,6 +1,7 @@ import mapArrayable from '@foscia/shared/arrays/mapArrayable'; import mapWithKeys from '@foscia/shared/arrays/mapWithKeys'; import sequentialTransform from '@foscia/shared/arrays/sequentialTransform'; +import throughMiddlewares from '@foscia/shared/arrays/throughMiddlewares'; import uniqueValues from '@foscia/shared/arrays/uniqueValues'; import wrap from '@foscia/shared/arrays/wrap'; import wrapVariadic from '@foscia/shared/arrays/wrapVariadic'; @@ -40,6 +41,7 @@ export { optionalJoin, pluralize, sequentialTransform, + throughMiddlewares, removeTimezoneOffset, toKebabCase, unsafeId, diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index d7995878..2f3a8c02 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -99,6 +99,20 @@ export type OmitNever = { export type UnionToIntersection = (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never; +/** + * 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. * diff --git a/packages/test/src/makeActionFactoryMock.ts b/packages/test/src/makeActionFactoryMock.ts index b2e8b774..961bb66d 100644 --- a/packages/test/src/makeActionFactoryMock.ts +++ b/packages/test/src/makeActionFactoryMock.ts @@ -6,8 +6,15 @@ import { FosciaError, isWhenContextFunction, runHooks, + withoutHooks, } from '@foscia/core'; -import { sequentialTransform, value } from '@foscia/shared'; +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'; @@ -59,7 +66,9 @@ export default ( (action as any).use(...enhancers); - const context = await action.useContext(); + const { middlewares, ...context } = await action.useContext() as Dictionary; + action.updateContext(context); + const runners = await unnestRunners(action, [rootRunner]); const calls = action.calls(); const testContext = makeActionTestContext(context, calls, runners); @@ -72,21 +81,24 @@ export default ( return await next.shouldRun(testContext) ? next : null; }), null); if (!mock) { - throw new UnexpectedActionError(context); + throw new UnexpectedActionError(testContext); } - await runHooks(action, 'running', { context, runner: rootRunner }); + await runHooks(action, 'running', { action, runner: rootRunner }); try { - const result = await mock.run(testContext); + const result = await throughMiddlewares( + (middlewares ?? []) as Middleware[], + async (a) => withoutHooks(a, async () => mock.run(testContext)), + )(action); - await runHooks(action, 'success', { context, result }); + await runHooks(action, 'success', { action, result }); history.push({ context: testContext, mock, result, error: undefined }); return result; } catch (error) { - await runHooks(action, 'error', { context, error }); + await runHooks(action, 'error', { action, error }); history.push({ context: testContext, mock, result: undefined, error }); @@ -99,7 +111,7 @@ export default ( } } - await runHooks(action, 'finally', { context }); + await runHooks(action, 'finally', { action }); } }; diff --git a/packages/test/src/unexpectedActionError.ts b/packages/test/src/unexpectedActionError.ts index 68885ce1..4aaa5f60 100644 --- a/packages/test/src/unexpectedActionError.ts +++ b/packages/test/src/unexpectedActionError.ts @@ -10,13 +10,13 @@ import { ActionTestContext } from '@foscia/test/types'; * @internal */ export default class UnexpectedActionError extends FosciaTestError { - public readonly testContext: ActionTestContext; + public readonly context: ActionTestContext; - public constructor(testContext: 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.testContext = testContext; + this.context = context; } } diff --git a/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index 1d706135..c9a5290a 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -552,3 +552,54 @@ provide some library features actions without hooks, as those hooks will also be disable. ::: + +### 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: + +- [`appendMiddlewares`](/docs/api/@foscia/core/functions/appendMiddlewares) + to add new middlewares after already registered ones +- [`prependMiddlewares`](/docs/api/@foscia/core/functions/prependMiddlewares) + 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 { appendMiddlewares } from '@foscia/core'; + +action().use(appendMiddlewares([ + (a, next) => { + // Do something before execution. + return next(a); + }, + async (a, next) => { + const result = await next(a); + // Do something after execution. + return result; + }, +])); +``` + +Finally, when using [`replaceMiddlewares`](/docs/api/@foscia/core/functions/replaceMiddlewares), +you can replace the already configured middlewares by passing a factory function. +This allows to replace or merge middlewares manually. + +```typescript +import { replaceMiddlewares } from '@foscia/core'; + +action().use(replaceMiddlewares((previousMiddlewares) => [ + (a, next) => next(a), + ...previousMiddlewares, +])); +``` diff --git a/website/docs/upgrade/migration.md b/website/docs/upgrade/migration.md index de9741bb..f93f4c76 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -16,6 +16,7 @@ sidebar_position: 5 ### Medium impacts changes +- [Action hooks events now provide action instead of context](#action-hooks-events-now-provide-action-instead-of-context) - [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) @@ -61,8 +62,7 @@ const posts = await action() // highlight.deletion .query(Post).all(); // highlight.addition -. -run(query(Post), all()); + .run(query(Post), all()); ``` If you are using some special types, such as `Action`, `ContextEnhancer` or @@ -109,6 +109,27 @@ In addition, multiple factories functions have been renamed: - `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()); +})); +``` + ### Properties definition are now defined using factories **Likelihood Of Impact: Medium** From 6398e28b35daa4507ac12e907655c698b9015709 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 9 Jan 2025 22:39:14 +0100 Subject: [PATCH 04/32] feat(cli): provide a better and automated integration with nuxt --- packages/cli/src/commands/initCommand.ts | 46 +++++++++++-------- .../cli/src/commands/integrate/nuxtCommand.ts | 9 +++- .../src/commands/make/makeActionCommand.ts | 8 +++- .../cli/src/templates/make/renderAction.ts | 2 +- packages/cli/src/utils/config/config.ts | 10 ++++ .../cli/src/utils/config/validateConfig.ts | 40 ++++++++++++---- .../dependencies/checkMissingDependencies.ts | 18 ++++++-- .../dependencies/warnMissingDependencies.ts | 2 +- .../prompts/promptForActionFactoryOptions.ts | 24 +++++++--- website/docs/integrations/nuxt.mdx | 15 +++++- 10 files changed, 130 insertions(+), 44 deletions(-) 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/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/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/website/docs/integrations/nuxt.mdx b/website/docs/integrations/nuxt.mdx index 8b0db299..23e21b14 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 @@ -90,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`. From 3cb68aecda08d041ef94d60b3c82944bda58522a Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 11 Jan 2025 22:47:06 +0100 Subject: [PATCH 05/32] feat(core): add by ID destroy action --- .../actions/context/enhancers/crud/destroy.ts | 108 ++++++++++++------ packages/shared/src/arrays/mapArrayable.ts | 6 +- packages/shared/src/configs/mergeConfig.ts | 19 +-- packages/shared/src/functions/tap.ts | 17 +++ packages/shared/src/functions/using.ts | 9 ++ packages/shared/src/identifiers/uniqueId.ts | 13 ++- packages/shared/src/index.ts | 16 ++- .../sequentialTransform.ts | 0 .../throughMiddlewares.ts | 2 + packages/shared/src/strings/camelCase.ts | 13 +++ packages/shared/src/strings/capitalize.ts | 8 ++ packages/shared/src/strings/kebabCase.ts | 10 ++ packages/shared/src/strings/makePluralizer.ts | 17 +++ packages/shared/src/strings/pluralize.ts | 26 ++--- packages/shared/src/strings/singularize.ts | 21 ++++ packages/shared/src/strings/toCase.ts | 19 +++ packages/shared/src/strings/toKebabCase.ts | 17 --- .../shared/tests/unit/strings/case.test.ts | 28 +++++ .../tests/unit/strings/pluralization.test.ts | 41 +++++++ .../tests/unit/strings/pluralize.test.ts | 23 ---- .../tests/unit/strings/toKebabCase.test.ts | 16 --- website/docs/core-concepts/actions.mdx | 17 ++- 22 files changed, 314 insertions(+), 132 deletions(-) create mode 100644 packages/shared/src/functions/tap.ts create mode 100644 packages/shared/src/functions/using.ts rename packages/shared/src/{arrays => miscellaneous}/sequentialTransform.ts (100%) rename packages/shared/src/{arrays => miscellaneous}/throughMiddlewares.ts (96%) create mode 100644 packages/shared/src/strings/camelCase.ts create mode 100644 packages/shared/src/strings/capitalize.ts create mode 100644 packages/shared/src/strings/kebabCase.ts create mode 100644 packages/shared/src/strings/makePluralizer.ts create mode 100644 packages/shared/src/strings/singularize.ts create mode 100644 packages/shared/src/strings/toCase.ts delete mode 100644 packages/shared/src/strings/toKebabCase.ts create mode 100644 packages/shared/tests/unit/strings/case.test.ts create mode 100644 packages/shared/tests/unit/strings/pluralization.test.ts delete mode 100644 packages/shared/tests/unit/strings/pluralize.test.ts delete mode 100644 packages/shared/tests/unit/strings/toKebabCase.test.ts diff --git a/packages/core/src/actions/context/enhancers/crud/destroy.ts b/packages/core/src/actions/context/enhancers/crud/destroy.ts index 8f3f0a94..02a6d741 100644 --- a/packages/core/src/actions/context/enhancers/crud/destroy.ts +++ b/packages/core/src/actions/context/enhancers/crud/destroy.ts @@ -4,42 +4,78 @@ 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 makeEnhancer from '@foscia/core/actions/makeEnhancer'; -import { Action } from '@foscia/core/actions/types'; +import { + Action, + ConsumeId, + ConsumeInstance, + ConsumeModel, + ContextEnhancer, +} from '@foscia/core/actions/types'; import runHooks from '@foscia/core/hooks/runHooks'; +import isModel from '@foscia/core/model/checks/isModel'; +import forceFill from '@foscia/core/model/forceFill'; import markSynced from '@foscia/core/model/snapshots/markSynced'; -import { ModelInstance } from '@foscia/core/model/types'; +import { Model, ModelIdType, ModelInstance } from '@foscia/core/model/types'; +import { using } from '@foscia/shared'; -/** - * 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()); - * ``` - */ -export default /* @__PURE__ */ makeEnhancer('destroy', < - C extends {}, - I extends ModelInstance, ->(instance: 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__ */ makeEnhancer('destroy', (( + modelOrInstance: Model | ModelInstance, + id?: ModelIdType, +) => using( + // eslint-disable-next-line new-cap + isModel(modelOrInstance) ? forceFill(new modelOrInstance(), { id }) : modelOrInstance, + (instance) => (action: Action) => 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, + }), + 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); + }), + ), +)) 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/shared/src/arrays/mapArrayable.ts b/packages/shared/src/arrays/mapArrayable.ts index e188db3a..8e7d2483 100644 --- a/packages/shared/src/arrays/mapArrayable.ts +++ b/packages/shared/src/arrays/mapArrayable.ts @@ -17,9 +17,5 @@ export default async ( return Promise.all(value.map((v) => callback(v))); } - if (!isNil(value)) { - return callback(value); - } - - return value; + return isNil(value) ? value : callback(value); }; diff --git a/packages/shared/src/configs/mergeConfig.ts b/packages/shared/src/configs/mergeConfig.ts index d2e559f9..6cea9c5b 100644 --- a/packages/shared/src/configs/mergeConfig.ts +++ b/packages/shared/src/configs/mergeConfig.ts @@ -1,3 +1,5 @@ +import tap from '@foscia/shared/functions/tap'; + /** * Merge two config objects. * @@ -13,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/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/identifiers/uniqueId.ts b/packages/shared/src/identifiers/uniqueId.ts index 21638ee3..8ae2caae 100644 --- a/packages/shared/src/identifiers/uniqueId.ts +++ b/packages/shared/src/identifiers/uniqueId.ts @@ -1,3 +1,5 @@ +import using from '@foscia/shared/functions/using'; + /** * Generate a unique ID using generator. * @@ -6,10 +8,11 @@ * * @internal */ -const uniqueId = (generator: () => string, notIds: string[]): string => { - const id = generator(); - - return notIds.indexOf(id) !== -1 ? uniqueId(generator, notIds) : id; -}; +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/index.ts b/packages/shared/src/index.ts index a8985713..e27bd98c 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,7 +1,5 @@ import mapArrayable from '@foscia/shared/arrays/mapArrayable'; import mapWithKeys from '@foscia/shared/arrays/mapWithKeys'; -import sequentialTransform from '@foscia/shared/arrays/sequentialTransform'; -import throughMiddlewares from '@foscia/shared/arrays/throughMiddlewares'; import uniqueValues from '@foscia/shared/arrays/uniqueValues'; import wrap from '@foscia/shared/arrays/wrap'; import wrapVariadic from '@foscia/shared/arrays/wrapVariadic'; @@ -14,13 +12,19 @@ import eachDescriptors from '@foscia/shared/descriptors/eachDescriptors'; import isDescriptorHolder from '@foscia/shared/descriptors/isDescriptorHolder'; import makeDescriptorHolder 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'; @@ -40,13 +44,17 @@ export { isNone, optionalJoin, pluralize, + singularize, sequentialTransform, throughMiddlewares, removeTimezoneOffset, - toKebabCase, + kebabCase, + camelCase, unsafeId, uniqueId, uniqueValues, + tap, + using, value, wrap, wrapVariadic, diff --git a/packages/shared/src/arrays/sequentialTransform.ts b/packages/shared/src/miscellaneous/sequentialTransform.ts similarity index 100% rename from packages/shared/src/arrays/sequentialTransform.ts rename to packages/shared/src/miscellaneous/sequentialTransform.ts diff --git a/packages/shared/src/arrays/throughMiddlewares.ts b/packages/shared/src/miscellaneous/throughMiddlewares.ts similarity index 96% rename from packages/shared/src/arrays/throughMiddlewares.ts rename to packages/shared/src/miscellaneous/throughMiddlewares.ts index 39aedfd1..a9184c94 100644 --- a/packages/shared/src/arrays/throughMiddlewares.ts +++ b/packages/shared/src/miscellaneous/throughMiddlewares.ts @@ -5,6 +5,8 @@ import { Middleware, MiddlewareNext } from '@foscia/shared/types'; * * @param middlewares * @param callback + * + * @internal */ export default ( middlewares: Middleware[], 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/pluralize.ts b/packages/shared/src/strings/pluralize.ts index 170d4f1a..4666a7ca 100644 --- a/packages/shared/src/strings/pluralize.ts +++ b/packages/shared/src/strings/pluralize.ts @@ -1,10 +1,4 @@ -const rules: [RegExp, string][] = [ - [/(f|fe)$/i, 'ves'], - [/([^aeiouy])y$/i, '$1ies'], - [/([^aeiouy]o)$/i, '$1es'], - [/(s|ch|sh|x|z)$/i, '$1es'], - [/(.)$/i, '$1s'], -]; +import makePluralizer from '@foscia/shared/strings/makePluralizer'; /** * Pluralize a word. @@ -13,13 +7,11 @@ const rules: [RegExp, string][] = [ * * @internal */ -export default (word: string) => { - let pluralizedWord: string | undefined; - rules.some(([regexp, replacement]) => { - pluralizedWord = word.replace(regexp, replacement); - - return word !== pluralizedWord; - }); - - return pluralizedWord ?? word; -}; +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'], +]); 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 d274f39d..00000000 --- a/packages/shared/src/strings/toKebabCase.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Convert a string to kebab case. - * - * @param value - * - * @internal - */ -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/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/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index c9a5290a..e5db3faa 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -134,7 +134,9 @@ await action().run(save(post), none()); ### Deleting records -You can use [`destroy`](/docs/api/@foscia/core/functions/none) to trigger +#### 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). @@ -145,6 +147,19 @@ import { destroy, none } from '@foscia/core'; await action().run(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().run(destroy(Post, '1'), none()); +``` + ### Relationships #### Eager loading relations From de70b1d101052e5e2817aba830fd1487003bac0e Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 11 Jan 2025 22:47:27 +0100 Subject: [PATCH 06/32] chore: refactor and improve docs --- .../context/consumers/consumeAction.ts | 6 ++ .../context/consumers/consumeAdapter.ts | 6 ++ .../actions/context/consumers/consumeCache.ts | 6 ++ .../context/consumers/consumeContext.ts | 12 +++ .../actions/context/consumers/consumeData.ts | 6 ++ .../context/consumers/consumeDeserializer.ts | 6 ++ .../actions/context/consumers/consumeId.ts | 6 ++ .../context/consumers/consumeInclude.ts | 6 ++ .../context/consumers/consumeInstance.ts | 6 ++ .../context/consumers/consumeMiddlewares.ts | 6 ++ .../actions/context/consumers/consumeModel.ts | 6 ++ .../context/consumers/consumeQueryAs.ts | 6 ++ .../context/consumers/consumeRegistry.ts | 6 ++ .../context/consumers/consumeRelation.ts | 6 ++ .../context/consumers/consumeSerializer.ts | 6 ++ .../context/enhancers/crud/instanceData.ts | 2 +- .../context/enhancers/crud/relationData.ts | 2 +- .../context/enhancers/crud/updateRelation.ts | 16 ++-- .../context/guessers/guessContextModel.ts | 26 +++++- .../core/src/actions/context/runners/all.ts | 7 +- .../src/actions/context/runners/cachedOr.ts | 2 +- .../core/src/actions/context/runners/raw.ts | 13 ++- .../context/utils/deserializeInstances.ts | 35 ++++---- .../utils/executeContextThroughAdapter.ts | 7 ++ .../actions/context/utils/normalizeInclude.ts | 33 ++++---- .../context/utils/serializeInstance.ts | 19 +++-- .../context/utils/serializeRelation.ts | 23 ++++-- .../actions/context/utils/serializeWith.ts | 12 --- .../src/actions/makeContextFunctionFactory.ts | 9 ++ packages/core/src/cache/makeCache.ts | 4 +- packages/core/src/cache/makeRefsCache.ts | 48 +++++------ packages/core/src/hooks/mergeHooks.ts | 8 ++ packages/core/src/hooks/registerHook.ts | 7 +- packages/core/src/hooks/withoutHooks.ts | 16 ++-- packages/core/src/index.ts | 4 - packages/core/src/logger/logger.ts | 73 ++++++++++------- .../core/src/model/checks/isAttributeDef.ts | 11 ++- .../core/src/model/checks/isComposable.ts | 7 ++ packages/core/src/model/checks/isIdDef.ts | 11 ++- .../core/src/model/checks/isInstanceUsing.ts | 4 +- .../core/src/model/checks/isModelUsing.ts | 9 ++ .../src/model/checks/isPluralRelationDef.ts | 7 ++ packages/core/src/model/checks/isPropDef.ts | 15 +++- .../core/src/model/checks/isPropDefOfType.ts | 18 ++++ .../core/src/model/checks/isPropFactory.ts | 7 ++ .../core/src/model/checks/isRelationDef.ts | 11 ++- .../src/model/checks/isSingularRelationDef.ts | 11 ++- .../core/src/model/checks/isValuePropDef.ts | 10 --- .../src/model/checks/isValuePropDefOfType.ts | 10 --- packages/core/src/model/fill.ts | 7 +- .../core/src/model/hooks/makeInstanceHook.ts | 7 ++ .../core/src/model/hooks/makeModelHook.ts | 7 ++ .../hooks/properties/makePropertyReadHook.ts | 7 ++ .../hooks/properties/makePropertyWriteHook.ts | 7 ++ packages/core/src/model/makeDefinition.ts | 30 +++---- packages/core/src/model/makeModelClass.ts | 57 +++++++------ packages/core/src/model/makeModelFactory.ts | 17 ++-- .../core/src/model/props/builders/hasMany.ts | 4 +- .../core/src/model/props/builders/hasOne.ts | 4 +- .../props/builders/makeBuilderPropFactory.ts | 10 ++- .../model/props/builders/makePropFactory.ts | 7 ++ .../props/builders/makeValuePropFactory.ts | 10 ++- .../core/src/model/props/builders/relation.ts | 8 ++ .../src/model/props/mappers/mapAttributes.ts | 8 ++ .../core/src/model/props/mappers/mapIds.ts | 12 --- .../core/src/model/props/mappers/mapProps.ts | 16 +++- .../src/model/props/mappers/mapRelations.ts | 8 ++ packages/core/src/model/props/shouldSync.ts | 8 ++ packages/core/src/model/relations/loaded.ts | 6 +- .../model/relations/makeQueryModelLoader.ts | 82 +++++++++---------- .../makeQueryModelLoaderExtractor.ts | 8 +- .../utilities/excludeInstancesAndRelations.ts | 9 ++ .../utilities/groupRelationsByModels.ts | 9 ++ .../utilities/groupRelationsByRoots.ts | 17 ++-- .../relations/utilities/guessRelationType.ts | 7 ++ .../relations/utilities/loadUsingCallback.ts | 9 ++ .../relations/utilities/loadUsingValue.ts | 9 ++ .../shouldExcludeInstanceAndRelation.ts | 8 ++ .../src/model/revivers/makeModelsReducer.ts | 6 +- .../src/model/revivers/makeModelsReviver.ts | 33 ++++---- .../src/model/snapshots/cloneModelValue.ts | 8 ++ .../src/model/snapshots/compareModelValue.ts | 9 ++ .../core/src/model/snapshots/markSynced.ts | 8 +- packages/core/src/model/snapshots/restore.ts | 6 +- .../src/model/snapshots/restoreSnapshot.ts | 12 ++- .../core/src/model/snapshots/takeSnapshot.ts | 11 ++- .../core/src/normalization/normalizeKey.ts | 13 ++- packages/core/src/registry/makeMapRegistry.ts | 6 +- packages/core/src/registry/makeRegistry.ts | 4 +- .../src/transformers/makeDateTransformer.ts | 19 +++-- .../core/src/transformers/makeTransformer.ts | 17 ++-- packages/core/src/transformers/toDate.ts | 13 ++- packages/core/src/transformers/toNumber.ts | 8 +- .../core/tests/typecheck/actions.test-d.ts | 5 ++ .../core/tests/unit/model/composition.test.ts | 2 + .../consumers/consumeRequestObjectParams.ts | 10 +-- .../context/enhancers/configureRequest.ts | 22 ++--- packages/http/src/makeHttpAdapter.ts | 31 +++---- packages/http/src/makeHttpAdapterResponse.ts | 4 +- .../src/utilities/deepParamsSerializer.ts | 12 +-- .../src/actions/context/enhancers/fields.ts | 16 ++-- packages/jsonapi/src/makeJsonApiSerializer.ts | 68 ++++++++------- .../jsonapi/tests/integration/crud.test.ts | 21 +++++ packages/rest/src/makeIncludeParam.ts | 2 +- packages/rest/src/makeRestAdapter.ts | 6 +- packages/rest/src/makeRestSerializer.ts | 11 +-- .../serialization/src/makeDeserializer.ts | 31 +++---- packages/serialization/src/makeSerializer.ts | 13 ++- .../src/makeSerializerRecordFactory.ts | 18 ++-- packages/test/src/index.ts | 2 - packages/test/src/makeActionFactoryMock.ts | 2 +- website/docs/core-concepts/models.mdx | 10 --- .../models/models-transformers.mdx | 4 +- website/docs/examples/jsonapi-blog.mdx | 36 ++++---- website/docs/getting-started.mdx | 6 +- website/docs/help/faq.md | 3 +- website/docs/upgrade/support-policy.md | 8 +- 117 files changed, 933 insertions(+), 574 deletions(-) delete mode 100644 packages/core/src/actions/context/utils/serializeWith.ts create mode 100644 packages/core/src/model/checks/isPropDefOfType.ts delete mode 100644 packages/core/src/model/checks/isValuePropDef.ts delete mode 100644 packages/core/src/model/checks/isValuePropDefOfType.ts delete mode 100644 packages/core/src/model/props/mappers/mapIds.ts 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/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/consumeMiddlewares.ts b/packages/core/src/actions/context/consumers/consumeMiddlewares.ts index b80dc315..8499c363 100644 --- a/packages/core/src/actions/context/consumers/consumeMiddlewares.ts +++ b/packages/core/src/actions/context/consumers/consumeMiddlewares.ts @@ -1,6 +1,12 @@ 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 + */ 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 index 69f2b83a..24167c90 100644 --- a/packages/core/src/actions/context/consumers/consumeQueryAs.ts +++ b/packages/core/src/actions/context/consumers/consumeQueryAs.ts @@ -2,6 +2,12 @@ import consumeContext from '@foscia/core/actions/context/consumers/consumeContex 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, 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/crud/instanceData.ts b/packages/core/src/actions/context/enhancers/crud/instanceData.ts index f20c7949..b18478ed 100644 --- a/packages/core/src/actions/context/enhancers/crud/instanceData.ts +++ b/packages/core/src/actions/context/enhancers/crud/instanceData.ts @@ -17,5 +17,5 @@ export default /* @__PURE__ */ makeEnhancer('instanceData', async ( action: Action>, ) => action.use(context({ - data: await serializeInstance(action, instance), + 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 1ede4582..2296d283 100644 --- a/packages/core/src/actions/context/enhancers/crud/relationData.ts +++ b/packages/core/src/actions/context/enhancers/crud/relationData.ts @@ -26,7 +26,7 @@ export default /* @__PURE__ */ makeEnhancer('relationData', < action: Action>, ) => action.use(context({ data: await serializeRelation( - action, + await action.useContext(), instance, key, instance[key], diff --git a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts index 1586fc60..622e5cdf 100644 --- a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts +++ b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts @@ -18,7 +18,7 @@ import { ModelRelation, ModelRelationKey, } from '@foscia/core/model/types'; -import { wrap } from '@foscia/shared'; +import { using, wrap } from '@foscia/shared'; export type UpdateRelationActionName = | ActionName.UPDATE_RELATION @@ -61,20 +61,18 @@ export default /* @__PURE__ */ makeEnhancer('updateRelation', < relation: K & ModelRelationKey, value: UpdateRelationValue, actionName: UpdateRelationActionName = ActionName.UPDATE_RELATION, -) => async (action: Action>) => { - const wrappedValue = isSingularRelationDef(instance.$model.$schema[relation] as R) - ? value : wrap(value); - - return action.use( +) => async (action: Action>) => using( + isSingularRelationDef(instance.$model.$schema[relation] as R) ? value : wrap(value), + async (wrappedValue) => action.use( query(instance, relation), context({ action: actionName, data: await serializeRelation( - action, + await action.useContext(), instance, relation, wrappedValue as InferModelValuePropType, ), }), - ) as unknown as Action & ConsumeRelation & ConsumeId>; -}); + ) as unknown as Action & ConsumeRelation & ConsumeId>, +)); diff --git a/packages/core/src/actions/context/guessers/guessContextModel.ts b/packages/core/src/actions/context/guessers/guessContextModel.ts index 31ffc13b..2fbeca9d 100644 --- a/packages/core/src/actions/context/guessers/guessContextModel.ts +++ b/packages/core/src/actions/context/guessers/guessContextModel.ts @@ -67,6 +67,30 @@ export default (async ( return guessModelIn(context.model, context.ensureType, multiple); }) as { - (context: GuessContextModelContext, multiple?: false): Promise; + /** + * Guess the model targeted by the given context. + * + * @param context + * + * @internal + */ + (context: GuessContextModelContext): Promise; + /** + * Guess the model targeted by the given context. + * + * @param context + * @param multiple + * + * @internal + */ + (context: GuessContextModelContext, multiple: false): Promise; + /** + * Guess the models targeted by the given context. + * + * @param context + * @param multiple + * + * @internal + */ (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 3546a671..fd774d20 100644 --- a/packages/core/src/actions/context/runners/all.ts +++ b/packages/core/src/actions/context/runners/all.ts @@ -52,12 +52,11 @@ export default /* @__PURE__ */ makeRunner('all', < // 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; diff --git a/packages/core/src/actions/context/runners/cachedOr.ts b/packages/core/src/actions/context/runners/cachedOr.ts index 2efc762a..09a59d3e 100644 --- a/packages/core/src/actions/context/runners/cachedOr.ts +++ b/packages/core/src/actions/context/runners/cachedOr.ts @@ -56,7 +56,7 @@ export default /* @__PURE__ */ makeRunner('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; } diff --git a/packages/core/src/actions/context/runners/raw.ts b/packages/core/src/actions/context/runners/raw.ts index d367ae47..487a7d3c 100644 --- a/packages/core/src/actions/context/runners/raw.ts +++ b/packages/core/src/actions/context/runners/raw.ts @@ -2,7 +2,7 @@ import executeContextThroughAdapter from '@foscia/core/actions/context/utils/executeContextThroughAdapter'; import makeRunner from '@foscia/core/actions/makeRunner'; import { Action, ConsumeAdapter } from '@foscia/core/actions/types'; -import { Awaitable } from '@foscia/shared'; +import { Awaitable, using } from '@foscia/shared'; /** * Run the action and retrieve the raw adapter's data. @@ -19,10 +19,7 @@ import { Awaitable } from '@foscia/shared'; */ 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; -}); +) => 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/utils/deserializeInstances.ts b/packages/core/src/actions/context/utils/deserializeInstances.ts index 8e429387..3c2314bd 100644 --- a/packages/core/src/actions/context/utils/deserializeInstances.ts +++ b/packages/core/src/actions/context/utils/deserializeInstances.ts @@ -1,22 +1,29 @@ import consumeDeserializer from '@foscia/core/actions/context/consumers/consumeDeserializer'; -import { Action, ConsumeDeserializer } from '@foscia/core/actions/types'; +import { ConsumeDeserializer } from '@foscia/core/actions/types'; import { ModelInstance } from '@foscia/core/model/types'; import { DeserializedData } from '@foscia/core/types'; -import { isNil } from '@foscia/shared'; +import { isNil, using } 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); -}; +/** + * Deserialize the instances from the given data. + * + * @param context + * @param data + * + * @internal + */ +export default async ( + context: ConsumeDeserializer, Deserialized>, + data: Data, +) => ( + isNil(data) + ? { instances: [] } + : 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/utils/executeContextThroughAdapter.ts index 72ee6d3f..1214aa5e 100644 --- a/packages/core/src/actions/context/utils/executeContextThroughAdapter.ts +++ b/packages/core/src/actions/context/utils/executeContextThroughAdapter.ts @@ -3,6 +3,13 @@ import consumeAdapter from '@foscia/core/actions/context/consumers/consumeAdapte import { ConsumeAdapter } from '@foscia/core/actions/types'; import { AdapterResponse } from '@foscia/core/types'; +/** + * Execute the given context through the adapter. + * + * @param context + * + * @internal + */ export default async ( context: ConsumeAdapter, ): Promise> => { diff --git a/packages/core/src/actions/context/utils/normalizeInclude.ts b/packages/core/src/actions/context/utils/normalizeInclude.ts index 9b7a0609..12215cea 100644 --- a/packages/core/src/actions/context/utils/normalizeInclude.ts +++ b/packages/core/src/actions/context/utils/normalizeInclude.ts @@ -2,21 +2,26 @@ import consumeRegistry from '@foscia/core/actions/context/consumers/consumeRegis 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[], -) => { - 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; -}; +) => 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/utils/serializeInstance.ts b/packages/core/src/actions/context/utils/serializeInstance.ts index b6e09a70..b1a5941b 100644 --- a/packages/core/src/actions/context/utils/serializeInstance.ts +++ b/packages/core/src/actions/context/utils/serializeInstance.ts @@ -1,11 +1,20 @@ -import serializeWith from '@foscia/core/actions/context/utils/serializeWith'; -import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; +import consumeSerializer from '@foscia/core/actions/context/consumers/consumeSerializer'; +import { ConsumeSerializer } from '@foscia/core/actions/types'; import { ModelInstance } from '@foscia/core/model/types'; +import { using } from '@foscia/shared'; -export default ( - action: Action>, +/** + * Serialize the given instance to a serialized dataset. + * + * @param context + * @param instance + * + * @internal + */ +export default async ( + context: ConsumeSerializer, instance: ModelInstance, -) => serializeWith(action, async (serializer, context) => serializer.serialize( +) => using(await consumeSerializer(context), async (serializer) => 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 index b2038880..172acdc8 100644 --- a/packages/core/src/actions/context/utils/serializeRelation.ts +++ b/packages/core/src/actions/context/utils/serializeRelation.ts @@ -1,5 +1,5 @@ -import serializeWith from '@foscia/core/actions/context/utils/serializeWith'; -import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; +import consumeSerializer from '@foscia/core/actions/context/consumers/consumeSerializer'; +import { ConsumeSerializer } from '@foscia/core/actions/types'; import { InferModelSchemaProp, InferModelValuePropType, @@ -7,10 +7,19 @@ import { ModelRelation, ModelRelationKey, } from '@foscia/core/model/types'; -import { Arrayable } from '@foscia/shared'; +import { Arrayable, using } from '@foscia/shared'; -export default < - C extends {}, +/** + * 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 string, R extends InferModelSchemaProp, @@ -18,11 +27,11 @@ export default < Related, Data, >( - action: Action>, + context: ConsumeSerializer, instance: I, relation: K & ModelRelationKey, value: InferModelValuePropType, -) => serializeWith(action, async (serializer, context) => serializer.serialize( +) => using(await consumeSerializer(context), async (serializer) => serializer.serialize( await serializer.serializeRelation( instance, instance.$model.$schema[relation] as R, 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 dfa29711..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 { Serializer } from '@foscia/core/types'; - -export default async ( - action: Action>, - callback: (serializer: Serializer, context: C) => Return, -) => { - const context = await action.useContext(); - - return callback(await consumeSerializer(context), context); -}; diff --git a/packages/core/src/actions/makeContextFunctionFactory.ts b/packages/core/src/actions/makeContextFunctionFactory.ts index 315903fa..c4c396b6 100644 --- a/packages/core/src/actions/makeContextFunctionFactory.ts +++ b/packages/core/src/actions/makeContextFunctionFactory.ts @@ -7,6 +7,15 @@ type ContextFunctionFactory = | ((...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, diff --git a/packages/core/src/cache/makeCache.ts b/packages/core/src/cache/makeCache.ts index ef3eab09..0d17d41b 100644 --- a/packages/core/src/cache/makeCache.ts +++ b/packages/core/src/cache/makeCache.ts @@ -1,7 +1,7 @@ import makeRefsCache from '@foscia/core/cache/makeRefsCache'; import makeWeakRefManager from '@foscia/core/cache/makeWeakRefManager'; import { InstancesCache } from '@foscia/core/types'; -import { toKebabCase } from '@foscia/shared'; +import { kebabCase } from '@foscia/shared'; /** * Make a default {@link InstancesCache | `InstancesCache`} implementation. @@ -10,6 +10,6 @@ import { toKebabCase } from '@foscia/shared'; */ export default (): { cache: InstancesCache; } => makeRefsCache({ manager: makeWeakRefManager(), - normalizeType: toKebabCase, + normalizeType: kebabCase, normalizeId: (id) => String(id), }); diff --git a/packages/core/src/cache/makeRefsCache.ts b/packages/core/src/cache/makeRefsCache.ts index 79547670..ee18e69a 100644 --- a/packages/core/src/cache/makeRefsCache.ts +++ b/packages/core/src/cache/makeRefsCache.ts @@ -21,37 +21,29 @@ export default (config: RefsCacheConfig) => { normalizeId(id), ); - const forgetAll = async (type: string) => instances.forgetAll(normalizeType(type)); - - const clear = async () => instances.clear(); - - const find = async (type: string, id: ModelIdType) => { - const ref = instances.find(normalizeType(type), normalizeId(id)); - if (ref) { - const instance = await config.manager.value(ref); - if (instance) { - return instance; - } - - await forget(normalizeType(type), normalizeId(id)); - } - - return null; - }; - - const put = async (type: string, id: ModelIdType, instance: ModelInstance) => instances.put( - normalizeType(type), - normalizeId(id), - await config.manager.ref(instance), - ); - return { cache: { forget, - forgetAll, - clear, - find, - put, + 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 config.manager.value(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.manager.ref(instance), + ), } as RefsCache, }; }; 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 5ab7eb43..834a6ed5 100644 --- a/packages/core/src/hooks/registerHook.ts +++ b/packages/core/src/hooks/registerHook.ts @@ -1,6 +1,7 @@ /* 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. @@ -16,10 +17,8 @@ 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/withoutHooks.ts b/packages/core/src/hooks/withoutHooks.ts index f115baf9..8ab20e6f 100644 --- a/packages/core/src/hooks/withoutHooks.ts +++ b/packages/core/src/hooks/withoutHooks.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-param-reassign */ import { Hookable } from '@foscia/core/hooks/types'; /** @@ -12,10 +11,15 @@ import { Hookable } from '@foscia/core/hooks/types'; 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 { @@ -26,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; @@ -39,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 7406dd2e..c9e4b022 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -21,7 +21,6 @@ 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 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 fill from '@foscia/core/model/fill'; @@ -51,7 +50,6 @@ 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 mapRelations from '@foscia/core/model/props/mappers/mapRelations'; import shouldSync from '@foscia/core/model/props/shouldSync'; import loaded from '@foscia/core/model/relations/loaded'; @@ -180,7 +178,6 @@ export { registerHook, unregisterHook, withoutHooks, - isPropDef, isAttributeDef, isRelationDef, isIdDef, @@ -190,7 +187,6 @@ export { isInstance, isModelUsing, isInstanceUsing, - mapIds, mapAttributes, mapRelations, shouldSync, diff --git a/packages/core/src/logger/logger.ts b/packages/core/src/logger/logger.ts index f8819a58..57acba80 100644 --- a/packages/core/src/logger/logger.ts +++ b/packages/core/src/logger/logger.ts @@ -16,7 +16,7 @@ const LOGGER_LEVELS_WEIGHTS = { type LoggerLevel = keyof typeof LOGGER_LEVELS; -const defaultLoggerLevel = (): LoggerLevel | null => { +const makeDefaultLevel = (): LoggerLevel | null => { if (IS_TEST) { return null; } @@ -24,34 +24,47 @@ 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); - } - - public warn(message: string, args: unknown[] = []) { - this.message('warn', message, args); +const makeMessageLog = (level: LoggerLevel) => function log( + this: { level: LoggerLevel | null; }, + 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); } +}; - 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 { + /** + * The minimum level of logged messages. + */ + level: makeDefaultLevel(), + /** + * Log an error message. + * + * @internal + */ + error: makeMessageLog('error'), + /** + * Log a warning message. + * + * @internal + */ + warn: makeMessageLog('warn'), + /** + * Log an info message. + * + * @internal + */ + info: makeMessageLog('info'), + /** + * Log a debug message. + * + * @internal + */ + debug: makeMessageLog('debug'), +}; diff --git a/packages/core/src/model/checks/isAttributeDef.ts b/packages/core/src/model/checks/isAttributeDef.ts index 02da6ef6..4dd93c24 100644 --- a/packages/core/src/model/checks/isAttributeDef.ts +++ b/packages/core/src/model/checks/isAttributeDef.ts @@ -1,7 +1,14 @@ -import isValuePropDefOfType from '@foscia/core/model/checks/isValuePropDefOfType'; +import isPropDefOfType from '@foscia/core/model/checks/isPropDefOfType'; import { ModelAttribute } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_KIND_ATTRIBUTE } from '@foscia/core/symbols'; +/** + * Check if value is an attribute definition. + * + * @param value + * + * @category Utilities + */ export default ( value: unknown, -): value is ModelAttribute => isValuePropDefOfType(value, SYMBOL_MODEL_PROP_KIND_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..617836de 100644 --- a/packages/core/src/model/checks/isComposable.ts +++ b/packages/core/src/model/checks/isComposable.ts @@ -2,6 +2,13 @@ import { ModelComposable } 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); diff --git a/packages/core/src/model/checks/isIdDef.ts b/packages/core/src/model/checks/isIdDef.ts index 1ddd57c5..e9e5461c 100644 --- a/packages/core/src/model/checks/isIdDef.ts +++ b/packages/core/src/model/checks/isIdDef.ts @@ -1,7 +1,14 @@ -import isValuePropDefOfType from '@foscia/core/model/checks/isValuePropDefOfType'; +import isPropDefOfType from '@foscia/core/model/checks/isPropDefOfType'; import { ModelId } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_KIND_ID } from '@foscia/core/symbols'; +/** + * Check if value is an ID definition. + * + * @param value + * + * @category Utilities + */ export default ( value: unknown, -): value is ModelId => isValuePropDefOfType(value, SYMBOL_MODEL_PROP_KIND_ID); +): value is ModelId => isPropDefOfType(value, SYMBOL_MODEL_PROP_KIND_ID); diff --git a/packages/core/src/model/checks/isInstanceUsing.ts b/packages/core/src/model/checks/isInstanceUsing.ts index 7dad92ea..05faf69e 100644 --- a/packages/core/src/model/checks/isInstanceUsing.ts +++ b/packages/core/src/model/checks/isInstanceUsing.ts @@ -14,8 +14,8 @@ import { ModelComposable, ModelInstanceUsing } from '@foscia/core/model/types'; * ```typescript * import { isInstanceUsing } from '@foscia/core'; * - * if (isInstanceUsing(myRecord, publishable)) { - * myRecord.publishedAt = new Date(); + * if (isInstanceUsing(myPost, publishable)) { + * // `myPost` is strictly typed with `publishable` definition. * } * ``` */ diff --git a/packages/core/src/model/checks/isModelUsing.ts b/packages/core/src/model/checks/isModelUsing.ts index 73214634..530860d5 100644 --- a/packages/core/src/model/checks/isModelUsing.ts +++ b/packages/core/src/model/checks/isModelUsing.ts @@ -8,6 +8,15 @@ import { ModelComposable, ModelUsing } from '@foscia/core/model/types'; * @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, diff --git a/packages/core/src/model/checks/isPluralRelationDef.ts b/packages/core/src/model/checks/isPluralRelationDef.ts index a506402d..ad947bae 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 + * + * @category Utilities + */ 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 ef1af1fe..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/isPropFactory.ts b/packages/core/src/model/checks/isPropFactory.ts index 71b604d2..6ee09f2b 100644 --- a/packages/core/src/model/checks/isPropFactory.ts +++ b/packages/core/src/model/checks/isPropFactory.ts @@ -2,6 +2,13 @@ import { ModelPropFactory } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_FACTORY } from '@foscia/core/symbols'; import { isFosciaType } from '@foscia/shared'; +/** + * Check if value is a property definition factory. + * + * @param value + * + * @internal + */ export default ( value: unknown, ): value is ModelPropFactory => isFosciaType(value, SYMBOL_MODEL_PROP_FACTORY); diff --git a/packages/core/src/model/checks/isRelationDef.ts b/packages/core/src/model/checks/isRelationDef.ts index 09ec7387..f6f95a8c 100644 --- a/packages/core/src/model/checks/isRelationDef.ts +++ b/packages/core/src/model/checks/isRelationDef.ts @@ -1,7 +1,14 @@ -import isValuePropDefOfType from '@foscia/core/model/checks/isValuePropDefOfType'; +import isPropDefOfType from '@foscia/core/model/checks/isPropDefOfType'; import { ModelRelation } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; +/** + * Check if value is a relation definition. + * + * @param value + * + * @category Utilities + */ export default ( value: unknown, -): value is ModelRelation => isValuePropDefOfType(value, SYMBOL_MODEL_PROP_KIND_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..7dce8942 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 + * + * @category Utilities + */ export default ( def: ModelRelation, -): boolean => def.$RELATION_TYPE === SYMBOL_MODEL_RELATION_HAS_ONE; +) => !isPluralRelationDef(def); diff --git a/packages/core/src/model/checks/isValuePropDef.ts b/packages/core/src/model/checks/isValuePropDef.ts deleted file mode 100644 index 999b4c4a..00000000 --- a/packages/core/src/model/checks/isValuePropDef.ts +++ /dev/null @@ -1,10 +0,0 @@ -import isAttributeDef from '@foscia/core/model/checks/isAttributeDef'; -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'; - -export default ( - def: unknown, -): def is ModelId | ModelAttribute | ModelRelation => isIdDef(def) - || isAttributeDef(def) - || isRelationDef(def); diff --git a/packages/core/src/model/checks/isValuePropDefOfType.ts b/packages/core/src/model/checks/isValuePropDefOfType.ts deleted file mode 100644 index 37da9c9b..00000000 --- a/packages/core/src/model/checks/isValuePropDefOfType.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ModelAttribute, ModelId, ModelRelation } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP } from '@foscia/core/symbols'; -import { isFosciaType } from '@foscia/shared'; - -export default

| ModelAttribute | ModelRelation>( - def: unknown, - propType: P['$VALUE_PROP_TYPE'], -): def is P => isFosciaType(def, SYMBOL_MODEL_PROP) - && '$VALUE_PROP_TYPE' in def - && def.$VALUE_PROP_TYPE === propType; diff --git a/packages/core/src/model/fill.ts b/packages/core/src/model/fill.ts index 86569e6c..5f649d9e 100644 --- a/packages/core/src/model/fill.ts +++ b/packages/core/src/model/fill.ts @@ -1,4 +1,5 @@ import { ModelInstance, ModelKey, ModelWritableValues } from '@foscia/core/model/types'; +import { tap } from '@foscia/shared'; /** * Fill the instance with given values. @@ -18,11 +19,9 @@ import { ModelInstance, ModelKey, ModelWritableValues } from '@foscia/core/model export default ( instance: I, values: Partial>, -) => { +) => 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/hooks/makeInstanceHook.ts b/packages/core/src/model/hooks/makeInstanceHook.ts index 3a0a9d63..106de929 100644 --- a/packages/core/src/model/hooks/makeInstanceHook.ts +++ b/packages/core/src/model/hooks/makeInstanceHook.ts @@ -9,6 +9,13 @@ import { ModelInstanceUsing, } from '@foscia/core/model/types'; +/** + * Create an instance hook registration function. + * + * @param hook + * + * @internal + */ export default (hook: keyof ModelHooksDefinition): { ( model: Model, diff --git a/packages/core/src/model/hooks/makeModelHook.ts b/packages/core/src/model/hooks/makeModelHook.ts index ec9c57c2..fcb5e48d 100644 --- a/packages/core/src/model/hooks/makeModelHook.ts +++ b/packages/core/src/model/hooks/makeModelHook.ts @@ -9,6 +9,13 @@ import { ModelUsing, } from '@foscia/core/model/types'; +/** + * Create a model hook registration function. + * + * @param hook + * + * @internal + */ export default (hook: keyof ModelHooksDefinition): { ( model: M, diff --git a/packages/core/src/model/hooks/properties/makePropertyReadHook.ts b/packages/core/src/model/hooks/properties/makePropertyReadHook.ts index 0e1017a8..f0f6eb29 100644 --- a/packages/core/src/model/hooks/properties/makePropertyReadHook.ts +++ b/packages/core/src/model/hooks/properties/makePropertyReadHook.ts @@ -8,6 +8,13 @@ import { 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>( model: Model, diff --git a/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts b/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts index 1146ee63..504686ab 100644 --- a/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts +++ b/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts @@ -8,6 +8,13 @@ import { 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>( model: Model, diff --git a/packages/core/src/model/makeDefinition.ts b/packages/core/src/model/makeDefinition.ts index a890f45e..d96f9554 100644 --- a/packages/core/src/model/makeDefinition.ts +++ b/packages/core/src/model/makeDefinition.ts @@ -1,22 +1,24 @@ import isComposable from '@foscia/core/model/checks/isComposable'; import isPropFactory from '@foscia/core/model/checks/isPropFactory'; import { ModelParsedDefinition } from '@foscia/core/model/types'; -import { Dictionary, eachDescriptors, makeDescriptorHolder } from '@foscia/shared'; +import { Dictionary, eachDescriptors, makeDescriptorHolder, tap } from '@foscia/shared'; -const parseDescriptor = (descriptor: PropertyDescriptor) => { - if (descriptor.value && (isComposable(descriptor.value) || isPropFactory(descriptor.value))) { - return descriptor.value; - } - - return makeDescriptorHolder(descriptor); -}; - -export default (definition?: D) => { - const parsedDefinition: Dictionary = {}; +const parseDescriptor = (descriptor: PropertyDescriptor) => ( + descriptor.value && (isComposable(descriptor.value) || isPropFactory(descriptor.value)) + ? descriptor.value + : makeDescriptorHolder(descriptor) +); +/** + * 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] = parseDescriptor(descriptor); }); - - return parsedDefinition as ModelParsedDefinition; -}; +}) as ModelParsedDefinition; diff --git a/packages/core/src/model/makeModelClass.ts b/packages/core/src/model/makeModelClass.ts index fb3561d2..3d1a3c99 100644 --- a/packages/core/src/model/makeModelClass.ts +++ b/packages/core/src/model/makeModelClass.ts @@ -6,7 +6,6 @@ import isComposable from '@foscia/core/model/checks/isComposable'; import isIdDef from '@foscia/core/model/checks/isIdDef'; import isPropFactory from '@foscia/core/model/checks/isPropFactory'; 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 { ExtendableModel, @@ -21,18 +20,29 @@ import { eachDescriptors, mapWithKeys, mergeConfig } from '@foscia/shared'; const { defineProperty } = Object; -const createModelClass = ( +/** + * 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 model = parentModel ? class extends parentModel { } : function ModelConstructor(this: ModelInstance) { defineProperty(this, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_INSTANCE }); defineProperty(this, '$model', { value: this.constructor }); @@ -54,14 +64,14 @@ const createModelClass = ( const propFactories = new Map(); - defineProperty(ModelClass, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_CLASS }); - defineProperty(ModelClass, '$type', { value: type }); - defineProperty(ModelClass, '$config', { value: { ...config } }); - defineProperty(ModelClass, '$composables', { value: [] }); - defineProperty(ModelClass, '$hooks', { writable: true, value: {} }); - 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, '$composables', { value: [] }); + defineProperty(model, '$hooks', { writable: true, value: {} }); + defineProperty(model, '$booted', { writable: true, value: false }); - defineProperty(ModelClass, '$schema', { + defineProperty(model, '$schema', { configurable: true, get() { const $schema = mapWithKeys([...propFactories.entries()], ([key, factory]) => { @@ -90,8 +100,8 @@ const createModelClass = ( }, }); - ModelClass.configure = function configureModel(newConfig?: ModelConfig, override = true) { - return createModelClass( + model.configure = function configureModel(newConfig?: ModelConfig, override = true) { + return makeModelClass( this.$type, mergeConfig(this.$config, newConfig ?? {}, override), mergeHooks(this.$hooks!), @@ -100,8 +110,8 @@ 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!), @@ -114,28 +124,23 @@ const createModelClass = ( currentDefinition: object, ) => eachDescriptors(currentDefinition, (key, descriptor) => { if (isComposable(descriptor.value)) { - ModelClass.$composables.push(descriptor.value); + model.$composables.push(descriptor.value); applyDefinition(descriptor.value.def); - ModelClass.$hooks = mergeHooks(ModelClass.$hooks!, descriptor.value.$hooks!); + model.$hooks = mergeHooks(model.$hooks!, descriptor.value.$hooks!); } else if (isPropFactory(descriptor.value)) { propFactories.set(key, descriptor.value); } else { - defineProperty(ModelClass.prototype, key, descriptor); + defineProperty(model.prototype, key, descriptor); } }); applyDefinition(makeDefinition(definition)); - ModelClass.$hooks = mergeHooks(ModelClass.$hooks!, hooks); + model.$hooks = mergeHooks(model.$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 a392de72..ba44b2ff 100644 --- a/packages/core/src/model/makeModelFactory.ts +++ b/packages/core/src/model/makeModelFactory.ts @@ -1,4 +1,5 @@ import makeModelClass from '@foscia/core/model/makeModelClass'; +import id from '@foscia/core/model/props/builders/id'; import { ModelConfig, ModelFactory, @@ -6,6 +7,7 @@ import { ModelInstance, ModelParsedDefinition, } from '@foscia/core/model/types'; +import { using } from '@foscia/shared'; /** * Create a model factory. @@ -34,19 +36,18 @@ export default ( const factory = ( rawConfig: string | (ModelConfig & { 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, { ...baseConfig, ...config, }, factory.$hooks, { + id: id(), + lid: id(), ...baseRawDefinition, ...rawDefinition, - }); - }; + }), + ); factory.$hooks = {}; diff --git a/packages/core/src/model/props/builders/hasMany.ts b/packages/core/src/model/props/builders/hasMany.ts index 266a76a1..c7b24f71 100644 --- a/packages/core/src/model/props/builders/hasMany.ts +++ b/packages/core/src/model/props/builders/hasMany.ts @@ -10,7 +10,7 @@ import { Awaitable } from '@foscia/shared'; const hasMany: { /** - * Create a has many relation property factory. + * Create a has many relation property factory with types or configuration. * * @param config * @@ -19,7 +19,7 @@ const hasMany: { config?: string | string[] | ModelRelationConfig, ): ModelRelationFactory; /** - * Create a has many relation property factory. + * Create a has many relation property factory with a model resolver. * * @param resolver * diff --git a/packages/core/src/model/props/builders/hasOne.ts b/packages/core/src/model/props/builders/hasOne.ts index 061e49ef..725c3cc7 100644 --- a/packages/core/src/model/props/builders/hasOne.ts +++ b/packages/core/src/model/props/builders/hasOne.ts @@ -10,7 +10,7 @@ import { Awaitable } from '@foscia/shared'; const hasOne: { /** - * Create a has one relation property factory. + * Create a has one relation property factory with types or configuration. * * @param config * @@ -19,7 +19,7 @@ const hasOne: { config?: string | string[] | ModelRelationConfig, ): ModelRelationFactory; /** - * Create a has one relation property factory. + * Create a has one relation property factory with a model resolver. * * @param resolver * diff --git a/packages/core/src/model/props/builders/makeBuilderPropFactory.ts b/packages/core/src/model/props/builders/makeBuilderPropFactory.ts index 3bcbca84..2c39bb0c 100644 --- a/packages/core/src/model/props/builders/makeBuilderPropFactory.ts +++ b/packages/core/src/model/props/builders/makeBuilderPropFactory.ts @@ -1,5 +1,5 @@ -import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; import makePropFactory from '@foscia/core/model/props/builders/makePropFactory'; +import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; import { ModelProp, ModelPropFactory } from '@foscia/core/model/types'; import { Dictionary, mapWithKeys } from '@foscia/shared'; @@ -10,6 +10,14 @@ type BuilderPropFactory< & { [K in keyof M]: (...args: Parameters) => BuilderPropFactory; } & ModelPropFactory

; +/** + * Make a builder property definition factory supporting given modifiers. + * + * @param prop + * @param modifiers + * + * @internal + */ const makeBuilderPropFactory = < P extends ModelProp, M extends Dictionary<(...args: any[]) => Partial

>, diff --git a/packages/core/src/model/props/builders/makePropFactory.ts b/packages/core/src/model/props/builders/makePropFactory.ts index 2d489e0b..f4a7d6c6 100644 --- a/packages/core/src/model/props/builders/makePropFactory.ts +++ b/packages/core/src/model/props/builders/makePropFactory.ts @@ -2,6 +2,13 @@ import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/ty import { ModelProp, ModelPropFactory } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP, SYMBOL_MODEL_PROP_FACTORY } from '@foscia/core/symbols'; +/** + * Make a property definition factory. + * + * @param prop + * + * @internal + */ export default

(prop: ModelPropFactoryDefinition

) => ({ $FOSCIA_TYPE: SYMBOL_MODEL_PROP_FACTORY, make: (parent, key) => ({ diff --git a/packages/core/src/model/props/builders/makeValuePropFactory.ts b/packages/core/src/model/props/builders/makeValuePropFactory.ts index 52a67595..34d81bfe 100644 --- a/packages/core/src/model/props/builders/makeValuePropFactory.ts +++ b/packages/core/src/model/props/builders/makeValuePropFactory.ts @@ -15,6 +15,14 @@ const VALUE_PROP_MODIFIERS = { nullable: () => ({}), }; +/** + * Make a value property definition factory. + * + * @param prop + * @param modifiers + * + * @internal + */ export default < P extends ModelValueProp, M extends Dictionary<(...args: any[]) => Partial

>, @@ -69,7 +77,7 @@ export default < if (this.default !== undefined) { if (this.default && typeof this.default === 'object') { logger.warn( - `Default \`${instance.$model.$type}.${this.key}\` object attribute's values must be defined using a factory function.`, + `Default \`${instance.$model.$type}.${this.key}\` object attribute's value must be defined using a factory function.`, ); } diff --git a/packages/core/src/model/props/builders/relation.ts b/packages/core/src/model/props/builders/relation.ts index a5978806..7e784312 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -6,6 +6,14 @@ import { import { ModelInstance, ModelRelation, ModelRelationType } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; +/** + * Make a relation property definition factory. + * + * @param relationType + * @param config + * + * @internal + */ export default ( relationType: ModelRelationType, config?: ModelRelationFactoryConfig, diff --git a/packages/core/src/model/props/mappers/mapAttributes.ts b/packages/core/src/model/props/mappers/mapAttributes.ts index 8cb4152f..f4409038 100644 --- a/packages/core/src/model/props/mappers/mapAttributes.ts +++ b/packages/core/src/model/props/mappers/mapAttributes.ts @@ -2,6 +2,14 @@ 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'; +/** + * Map all attributes of an instance. + * + * @param instance + * @param callback + * + * @category Utilities + */ export default ( instance: I, callback: (def: ModelAttribute>) => R, 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 09644b38..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, - isIdDef, -) as R[]; diff --git a/packages/core/src/model/props/mappers/mapProps.ts b/packages/core/src/model/props/mappers/mapProps.ts index a937ab24..0ada5a32 100644 --- a/packages/core/src/model/props/mappers/mapProps.ts +++ b/packages/core/src/model/props/mappers/mapProps.ts @@ -1,13 +1,21 @@ import { ModelInstance, ModelProp } from '@foscia/core/model/types'; +import { tap } from '@foscia/shared'; +/** + * Map all properties of an instance. + * + * @param instance + * @param callback + * @param predicate + * + * @internal + */ export default ( instance: I, callback: (def: P) => R, predicate?: (def: ModelProp) => def is P, -) => Object.values(instance.$model.$schema).reduce((stack, def) => { +) => Object.values(instance.$model.$schema).reduce((stack, def) => tap(stack, () => { if (!predicate || predicate(def)) { stack.push(callback(def as P)); } - - return stack; -}, [] as R[]); +}), [] as R[]); diff --git a/packages/core/src/model/props/mappers/mapRelations.ts b/packages/core/src/model/props/mappers/mapRelations.ts index 44cd0e10..f40837d5 100644 --- a/packages/core/src/model/props/mappers/mapRelations.ts +++ b/packages/core/src/model/props/mappers/mapRelations.ts @@ -2,6 +2,14 @@ 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'; +/** + * Map all relations of an instance. + * + * @param instance + * @param callback + * + * @category Utilities + */ export default ( instance: I, callback: (def: ModelRelation>) => R, diff --git a/packages/core/src/model/props/shouldSync.ts b/packages/core/src/model/props/shouldSync.ts index 5f709dff..3c5f4af7 100644 --- a/packages/core/src/model/props/shouldSync.ts +++ b/packages/core/src/model/props/shouldSync.ts @@ -1,5 +1,13 @@ import { ModelPropSync, ModelValueProp } from '@foscia/core/model/types'; +/** + * Check if a value property should be synced depending on the current action. + * + * @param def + * @param actions + * + * @internal + */ export default (def: ModelValueProp, actions: ModelPropSync[]) => ( typeof def.sync === 'string' ? actions.indexOf(def.sync) !== -1 diff --git a/packages/core/src/model/relations/loaded.ts b/packages/core/src/model/relations/loaded.ts index e04359fe..658b14a9 100644 --- a/packages/core/src/model/relations/loaded.ts +++ b/packages/core/src/model/relations/loaded.ts @@ -40,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 c0193784..645d31cb 100644 --- a/packages/core/src/model/relations/makeQueryModelLoader.ts +++ b/packages/core/src/model/relations/makeQueryModelLoader.ts @@ -31,6 +31,7 @@ import { IdentifiersMap, isNil, makeIdentifiersMap, + tap, wrap, } from '@foscia/shared'; @@ -164,39 +165,39 @@ const extractIdsMap = < 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 | 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, @@ -213,18 +214,17 @@ const fetchRelatedMap = async < 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 ExtractedId[]); if (targetedIds.length) { const chunk = (options.chunk ?? ((i) => [i])) as (ids: ExtractedId[]) => ExtractedId[][]; @@ -269,11 +269,7 @@ 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 = ( @@ -282,11 +278,9 @@ const remapRelated = ( ) => 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); }); }); diff --git a/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts b/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts index 4c3f34eb..dc8b81e0 100644 --- a/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts +++ b/packages/core/src/model/relations/makeQueryModelLoaderExtractor.ts @@ -34,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/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 aec36956..692951b0 100644 --- a/packages/core/src/model/relations/utilities/groupRelationsByModels.ts +++ b/packages/core/src/model/relations/utilities/groupRelationsByModels.ts @@ -2,6 +2,15 @@ import { consumeRegistry, guessContextModel } from '@foscia/core/actions'; import FosciaError from '@foscia/core/errors/fosciaError'; 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[]>, 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/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 e5305355..eab1d642 100644 --- a/packages/core/src/model/revivers/makeModelsReducer.ts +++ b/packages/core/src/model/revivers/makeModelsReducer.ts @@ -39,11 +39,7 @@ export default () => { return value.map((item) => reduceValue(item, parents)); } - if (isInstance(value)) { - return reduceInstance(value, parents); - } - - return value; + return isInstance(value) ? reduceInstance(value, parents) : value; }; const reduceValues = (values: Dictionary, parents: Map) => mapWithKeys( diff --git a/packages/core/src/model/revivers/makeModelsReviver.ts b/packages/core/src/model/revivers/makeModelsReviver.ts index b786f521..9aa0d02a 100644 --- a/packages/core/src/model/revivers/makeModelsReviver.ts +++ b/packages/core/src/model/revivers/makeModelsReviver.ts @@ -7,7 +7,7 @@ import { ReducedModelSnapshot, } from '@foscia/core/model/revivers/types'; import { Model, ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; -import { Dictionary, mapWithKeys } from '@foscia/shared'; +import { Dictionary, mapWithKeys, tap } from '@foscia/shared'; /** * Create a models reviver. @@ -32,37 +32,32 @@ export default (options: { models: Model[]; }) => { && 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 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: Map, + ) => tap(parents.get(ref.$ref), (instance) => { if (!instance) { throw new FosciaError('Could not revive not found instance, reduced data might be corrupted.'); } - - return instance; - }; + })!; const reviveValue = (value: unknown, parents: Map): unknown => { if (Array.isArray(value)) { return value.map((item) => reviveValue(item, parents)); } - if ( - isReducedType('instance', value) - || isReducedType('circular', value) - ) { - return reviveInstance(value, parents); - } - - return value; + return isReducedType('instance', value) + || isReducedType('circular', value) + ? reviveInstance(value, parents) + : value; }; const reviveValues = (values: Dictionary, parents: Map) => mapWithKeys( diff --git a/packages/core/src/model/snapshots/cloneModelValue.ts b/packages/core/src/model/snapshots/cloneModelValue.ts index 4b1df993..2530750e 100644 --- a/packages/core/src/model/snapshots/cloneModelValue.ts +++ b/packages/core/src/model/snapshots/cloneModelValue.ts @@ -1,5 +1,13 @@ import { Model } from '@foscia/core/model/types'; +/** + * Clone a value using configured cloner. + * + * @param model + * @param value + * + * @internal + */ export default (model: Model, value: T) => ( model.$config.cloneValue ? model.$config.cloneValue(value) diff --git a/packages/core/src/model/snapshots/compareModelValue.ts b/packages/core/src/model/snapshots/compareModelValue.ts index e2d6fbec..59f0b2ae 100644 --- a/packages/core/src/model/snapshots/compareModelValue.ts +++ b/packages/core/src/model/snapshots/compareModelValue.ts @@ -1,5 +1,14 @@ import { Model } from '@foscia/core/model/types'; +/** + * Compare values using configured comparator. + * + * @param model + * @param nextValue + * @param prevValue + * + * @internal + */ export default ( model: Model, nextValue: unknown, diff --git a/packages/core/src/model/snapshots/markSynced.ts b/packages/core/src/model/snapshots/markSynced.ts index 9ce66019..391e5cbe 100644 --- a/packages/core/src/model/snapshots/markSynced.ts +++ b/packages/core/src/model/snapshots/markSynced.ts @@ -1,7 +1,7 @@ /* 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. @@ -21,7 +21,7 @@ import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; export default ( instance: I, ...only: ArrayableVariadic> -) => { +) => tap(instance, () => { const snapshot = takeSnapshot(instance); const keys = wrapVariadic(...only); if (keys.length) { @@ -35,6 +35,4 @@ export default ( } 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 970281f1..8c7acd79 100644 --- a/packages/core/src/model/snapshots/restore.ts +++ b/packages/core/src/model/snapshots/restore.ts @@ -20,8 +20,4 @@ import { ArrayableVariadic } from '@foscia/shared'; 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 9ae5e03e..7459842c 100644 --- a/packages/core/src/model/snapshots/restoreSnapshot.ts +++ b/packages/core/src/model/snapshots/restoreSnapshot.ts @@ -1,5 +1,5 @@ /* eslint-disable no-param-reassign */ -import isValuePropDef from '@foscia/core/model/checks/isValuePropDef'; +import isPropDef from '@foscia/core/model/checks/isPropDef'; import forceFill from '@foscia/core/model/forceFill'; import mapProps from '@foscia/core/model/props/mappers/mapProps'; import cloneModelValue from '@foscia/core/model/snapshots/cloneModelValue'; @@ -11,7 +11,7 @@ import { ModelValueProp, ModelValues, } from '@foscia/core/model/types'; -import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; +import { ArrayableVariadic, tap, wrapVariadic } from '@foscia/shared'; /** * Restore a specific snapshot on instance. @@ -33,7 +33,7 @@ export default ( instance: I, snapshot: ModelSnapshot, ...only: ArrayableVariadic> -) => { +) => tap(instance, () => { const keys = wrapVariadic(...only); if (!keys.length) { @@ -56,8 +56,6 @@ export default ( } }; - mapProps(instance, restoreForDef, isValuePropDef); + mapProps(instance, restoreForDef, isPropDef as any); markSynced(instance, ...only); - - return instance; -}; +}); diff --git a/packages/core/src/model/snapshots/takeSnapshot.ts b/packages/core/src/model/snapshots/takeSnapshot.ts index 6350dc08..fff03678 100644 --- a/packages/core/src/model/snapshots/takeSnapshot.ts +++ b/packages/core/src/model/snapshots/takeSnapshot.ts @@ -1,6 +1,6 @@ import cloneModelValue from '@foscia/core/model/snapshots/cloneModelValue'; import { ModelInstance, ModelSnapshot, ModelValues } from '@foscia/core/model/types'; -import { mapWithKeys } from '@foscia/shared'; +import { mapWithKeys, using } from '@foscia/shared'; /** * Capture a snapshot of the instance. @@ -23,9 +23,8 @@ export default ( $exists: instance.$exists, $raw: instance.$raw, $loaded: { ...instance.$loaded }, - $values: mapWithKeys(instance.$values, (value, key) => { - const clonedValue = cloneModelValue(instance.$model, value); - - return clonedValue !== undefined ? { [key]: clonedValue } : {}; - }) as Partial>, + $values: mapWithKeys(instance.$values, (value, key) => using( + cloneModelValue(instance.$model, value), + (clonedValue) => (clonedValue !== undefined ? { [key]: clonedValue } : {}), + )) as Partial>, }); diff --git a/packages/core/src/normalization/normalizeKey.ts b/packages/core/src/normalization/normalizeKey.ts index 02182d30..761d8c72 100644 --- a/packages/core/src/normalization/normalizeKey.ts +++ b/packages/core/src/normalization/normalizeKey.ts @@ -1,18 +1,17 @@ import { Model, ModelKey, ModelValueProp } from '@foscia/core/model/types'; +import { using } from '@foscia/shared'; /** * Normalize a property key using its alias or a guessed alias if available. * * @param model * @param key + * + * @internal */ export default ( model: Model, key: ModelKey>, -) => { - const def = model.$schema[key] as unknown as ModelValueProp; - - return def.alias ?? ( - model.$config.guessAlias ? model.$config.guessAlias(def.key) : def.key - ); -}; +) => using(model.$schema[key] as unknown as ModelValueProp, (def) => def.alias ?? ( + model.$config.guessAlias ? model.$config.guessAlias(def.key) : def.key +)); diff --git a/packages/core/src/registry/makeMapRegistry.ts b/packages/core/src/registry/makeMapRegistry.ts index 6cce071d..ebbd28a1 100644 --- a/packages/core/src/registry/makeMapRegistry.ts +++ b/packages/core/src/registry/makeMapRegistry.ts @@ -14,9 +14,9 @@ export default (config: MapRegistryConfig) => { normalizeType(model.$type), model, ])); - const modelFor = async (type: string) => modelsMap.get(normalizeType(type)) ?? null; - return { - registry: { modelFor } as MapRegistry, + registry: { + modelFor: async (type: string) => modelsMap.get(normalizeType(type)) ?? null, + } as MapRegistry, }; }; diff --git a/packages/core/src/registry/makeRegistry.ts b/packages/core/src/registry/makeRegistry.ts index c27ec4cd..e946322c 100644 --- a/packages/core/src/registry/makeRegistry.ts +++ b/packages/core/src/registry/makeRegistry.ts @@ -1,7 +1,7 @@ import { Model } from '@foscia/core/model/types'; import makeMapRegistry from '@foscia/core/registry/makeMapRegistry'; import { ModelsRegistry } from '@foscia/core/types'; -import { toKebabCase } from '@foscia/shared'; +import { kebabCase } from '@foscia/shared'; /** * Make a default {@link ModelsRegistry | `ModelsRegistry`} implementation. @@ -12,5 +12,5 @@ import { toKebabCase } from '@foscia/shared'; */ export default (models: Model[]): { registry: ModelsRegistry; } => makeMapRegistry({ models, - normalizeType: toKebabCase, + normalizeType: kebabCase, }); 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 1e3e500c..7402dde3 100644 --- a/packages/core/src/transformers/makeTransformer.ts +++ b/packages/core/src/transformers/makeTransformer.ts @@ -12,12 +12,11 @@ import { Awaitable, isNil, Optional } from '@foscia/shared'; 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: Optional) => (isNil(value) ? null : deserializeFn(value)), + serialize: (value: T | null) => ( + isNil(value) + ? null + : (serializeFn ?? deserializeFn)(value as any) + ), +} as ObjectTransformer, SR | null>); diff --git a/packages/core/src/transformers/toDate.ts b/packages/core/src/transformers/toDate.ts index c567379d..36e3d3a5 100644 --- a/packages/core/src/transformers/toDate.ts +++ b/packages/core/src/transformers/toDate.ts @@ -1,5 +1,5 @@ import makeDateTransformer from '@foscia/core/transformers/makeDateTransformer'; -import { removeTimezoneOffset } from '@foscia/shared'; +import { removeTimezoneOffset, using } from '@foscia/shared'; /** * Create a date transformer. @@ -8,10 +8,9 @@ import { removeTimezoneOffset } from '@foscia/shared'; */ 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), @@ -19,8 +18,8 @@ export default /* @__PURE__ */ 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/toNumber.ts b/packages/core/src/transformers/toNumber.ts index f053fb7a..be61ba64 100644 --- a/packages/core/src/transformers/toNumber.ts +++ b/packages/core/src/transformers/toNumber.ts @@ -1,16 +1,14 @@ import logger from '@foscia/core/logger/logger'; import makeTransformer from '@foscia/core/transformers/makeTransformer'; +import { tap } from '@foscia/shared'; /** * Create a number transformer. * * @category Factories */ -export default () => makeTransformer((value: unknown) => { - const number = Number(value); +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/tests/typecheck/actions.test-d.ts b/packages/core/tests/typecheck/actions.test-d.ts index 865804a8..2a629ffb 100644 --- a/packages/core/tests/typecheck/actions.test-d.ts +++ b/packages/core/tests/typecheck/actions.test-d.ts @@ -132,6 +132,11 @@ test('Actions are type safe', async () => { .use(destroy(new PostMock())) .run(one()), ).toEqualTypeOf(); + expectTypeOf( + await action() + .use(destroy(PostMock, '123')) + .run(one()), + ).toEqualTypeOf(); expectTypeOf( await action().run( associate(new CommentMock(), 'postedBy', new UserMock()), 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/http/src/actions/context/consumers/consumeRequestObjectParams.ts b/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts index fb442bcf..71c00dc7 100644 --- a/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts +++ b/packages/http/src/actions/context/consumers/consumeRequestObjectParams.ts @@ -1,16 +1,14 @@ import { FosciaError } from '@foscia/core'; import consumeRequestConfig from '@foscia/http/actions/context/consumers/consumeRequestConfig'; +import { Dictionary, tap } from '@foscia/shared'; /** * Consume object params. Will throw an exception if string params are configured. * * @param context */ -export default (context: {}) => { - const config = consumeRequestConfig(context, null); - if (typeof config?.params === 'string') { +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/configureRequest.ts b/packages/http/src/actions/context/enhancers/configureRequest.ts index bfe5a9ae..336769da 100644 --- a/packages/http/src/actions/context/enhancers/configureRequest.ts +++ b/packages/http/src/actions/context/enhancers/configureRequest.ts @@ -1,6 +1,7 @@ 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 HTTP adapter. @@ -62,30 +63,29 @@ import { HttpRequestConfig } from '@foscia/http/types'; */ 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 ?? []), + ...(prevRequestConfig.requestTransformers ?? []), ...(nextConfig?.requestTransformers ?? []), ], responseTransformers: [ - ...(prevRequestConfig?.responseTransformers ?? []), + ...(prevRequestConfig.responseTransformers ?? []), ...(nextConfig?.responseTransformers ?? []), ], errorTransformers: [ - ...(prevRequestConfig?.errorTransformers ?? []), + ...(prevRequestConfig.errorTransformers ?? []), ...(nextConfig?.errorTransformers ?? []), ], } as HttpRequestConfig, - })); -}); + })), +)); diff --git a/packages/http/src/makeHttpAdapter.ts b/packages/http/src/makeHttpAdapter.ts index 8eb36fb6..bc35ebbc 100644 --- a/packages/http/src/makeHttpAdapter.ts +++ b/packages/http/src/makeHttpAdapter.ts @@ -70,12 +70,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); }; @@ -101,15 +103,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); }; @@ -126,11 +128,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(); @@ -200,6 +201,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); diff --git a/packages/http/src/makeHttpAdapterResponse.ts b/packages/http/src/makeHttpAdapterResponse.ts index dad243fa..26e7d64d 100644 --- a/packages/http/src/makeHttpAdapterResponse.ts +++ b/packages/http/src/makeHttpAdapterResponse.ts @@ -14,7 +14,5 @@ export default ( config: { reader: HttpResponseReader }, ): AdapterResponse => ({ read: async () => (response.status === 204 ? undefined : config.reader(response)), - get raw() { - return response; - }, + raw: response, }); diff --git a/packages/http/src/utilities/deepParamsSerializer.ts b/packages/http/src/utilities/deepParamsSerializer.ts index cad7ca13..42c1d209 100644 --- a/packages/http/src/utilities/deepParamsSerializer.ts +++ b/packages/http/src/utilities/deepParamsSerializer.ts @@ -1,4 +1,4 @@ -import { Dictionary } from '@foscia/shared'; +import { Dictionary, tap } from '@foscia/shared'; /** * Deeply serialize given query params (including objects) @@ -18,9 +18,7 @@ import { Dictionary } from '@foscia/shared'; * // search=foo&sort=title&filter[category]=news * ``` */ -export default (params: Dictionary) => { - const urlSearchParams = new URLSearchParams(); - +export default (params: Dictionary) => tap(new URLSearchParams(), (urlParams) => { const appendParam = (key: string, value: unknown) => { if (Array.isArray(value)) { value.forEach((subValue) => appendParam(`${key}[]`, subValue)); @@ -31,7 +29,7 @@ export default (params: Dictionary) => { } else { const finalValue = value; if (finalValue !== undefined) { - urlSearchParams.append(key, String(finalValue)); + urlParams.append(key, String(finalValue)); } } }; @@ -39,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/jsonapi/src/actions/context/enhancers/fields.ts b/packages/jsonapi/src/actions/context/enhancers/fields.ts index eb25e676..d958598f 100644 --- a/packages/jsonapi/src/actions/context/enhancers/fields.ts +++ b/packages/jsonapi/src/actions/context/enhancers/fields.ts @@ -7,7 +7,7 @@ import { ModelKey, } 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) @@ -34,14 +34,12 @@ import { ArrayableVariadic, isNil } from '@foscia/shared'; 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)); + throw new FosciaError( + 'Could not detect context\'s model when applying fieldsets.', + ); }); diff --git a/packages/jsonapi/src/makeJsonApiSerializer.ts b/packages/jsonapi/src/makeJsonApiSerializer.ts index 88021446..70f4b93d 100644 --- a/packages/jsonapi/src/makeJsonApiSerializer.ts +++ b/packages/jsonapi/src/makeJsonApiSerializer.ts @@ -7,6 +7,8 @@ import { 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. * @@ -20,38 +22,34 @@ export default < Data = { data: Arrayable | null }, >( config?: Partial>, -) => { - const serializeId = (id: Optional) => (isNil(id) ? undefined : String(id)); - - return makeSerializer({ - 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, - }); -}; +) => makeSerializer({ + 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/tests/integration/crud.test.ts b/packages/jsonapi/tests/integration/crud.test.ts index e225b595..c5404043 100644 --- a/packages/jsonapi/tests/integration/crud.test.ts +++ b/packages/jsonapi/tests/integration/crud.test.ts @@ -617,6 +617,27 @@ describe('integration: JSON:API', () => { expect(post.comments).toBeUndefined(); }); + 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({ diff --git a/packages/rest/src/makeIncludeParam.ts b/packages/rest/src/makeIncludeParam.ts index f5719d15..14e28aa0 100644 --- a/packages/rest/src/makeIncludeParam.ts +++ b/packages/rest/src/makeIncludeParam.ts @@ -14,7 +14,7 @@ export default async ( 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 index edde8f2c..2c23a14e 100644 --- a/packages/rest/src/makeRestAdapter.ts +++ b/packages/rest/src/makeRestAdapter.ts @@ -1,7 +1,7 @@ import { makeHttpAdapter } from '@foscia/http'; import makeIncludeParam from '@foscia/rest/makeIncludeParam'; import { RestAdapterConfig } from '@foscia/rest/types'; -import { toKebabCase } from '@foscia/shared'; +import { kebabCase } from '@foscia/shared'; /** * Make a REST adapter object. @@ -15,8 +15,8 @@ export default ( config: Partial> = {}, ) => makeHttpAdapter({ baseURL: '/api', - modelPathTransformer: toKebabCase, - relationPathTransformer: toKebabCase, + modelPathTransformer: kebabCase, + relationPathTransformer: kebabCase, appendParams: async (context) => ({ ...await makeIncludeParam(context, config.includeParamKey), ...(await config.appendParams?.(context)), diff --git a/packages/rest/src/makeRestSerializer.ts b/packages/rest/src/makeRestSerializer.ts index 0c464a48..7473263f 100644 --- a/packages/rest/src/makeRestSerializer.ts +++ b/packages/rest/src/makeRestSerializer.ts @@ -1,6 +1,6 @@ import { RestNewResource, RestSerializerConfig } from '@foscia/rest/types'; import { makeSerializer, makeSerializerRecordFactory } from '@foscia/serialization'; -import { Arrayable } from '@foscia/shared'; +import { Arrayable, tap } from '@foscia/shared'; /** * Make a REST serializer object. @@ -18,15 +18,12 @@ export default < config?: Partial>, ) => makeSerializer({ createRecord: makeSerializerRecordFactory( - (instance) => { - const record = { id: instance.id } as Record; - + (instance) => tap({ id: instance.id } as Record, (record) => { if (config?.serializeType) { + // eslint-disable-next-line no-param-reassign 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]; diff --git a/packages/serialization/src/makeDeserializer.ts b/packages/serialization/src/makeDeserializer.ts index d394b156..6f607dff 100644 --- a/packages/serialization/src/makeDeserializer.ts +++ b/packages/serialization/src/makeDeserializer.ts @@ -22,13 +22,13 @@ import { shouldSync, } from '@foscia/core'; import { - RecordDeserializer, DeserializerConfig, DeserializerContext, DeserializerExtract, DeserializerInstancesMap, DeserializerModelIdentifier, DeserializerRecord, + RecordDeserializer, } from '@foscia/serialization/types'; import { type Arrayable, @@ -37,6 +37,7 @@ import { isNone, makeIdentifiersMap, mapArrayable, + using, wrap, } from '@foscia/shared'; @@ -113,23 +114,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, diff --git a/packages/serialization/src/makeSerializer.ts b/packages/serialization/src/makeSerializer.ts index 398d8195..5ca2aff8 100644 --- a/packages/serialization/src/makeSerializer.ts +++ b/packages/serialization/src/makeSerializer.ts @@ -16,7 +16,7 @@ import { 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. @@ -142,15 +142,14 @@ export default ( def: ModelRelation, value: Arrayable | null, context: {}, - ) => { - const serializerContext = makeSerializerContext(instance, def, context); - - return serializeRelationWith( + ) => using( + makeSerializerContext(instance, def, context), + (serializerContext) => serializeRelationWith( { ...serializerContext, value }, serializeRelated, [{ instance, def }], - ); - }, + ), + ), serializeInstance, serialize, }; diff --git a/packages/serialization/src/makeSerializerRecordFactory.ts b/packages/serialization/src/makeSerializerRecordFactory.ts index 53db77e3..de4334fc 100644 --- a/packages/serialization/src/makeSerializerRecordFactory.ts +++ b/packages/serialization/src/makeSerializerRecordFactory.ts @@ -1,6 +1,6 @@ import { ModelInstance } 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. @@ -23,13 +23,9 @@ export default < ): SerializerRecordFactory => async ( instance: ModelInstance, context: {}, -) => { - const record = await initialize(instance, context); - - return { - put: ( - serializerContext: SerializerContext, - ) => put(record, serializerContext), - retrieve: () => record, - }; -}; +) => using(await initialize(instance, context), (record) => ({ + put: ( + serializerContext: SerializerContext, + ) => put(record, serializerContext), + retrieve: () => record, +})); diff --git a/packages/test/src/index.ts b/packages/test/src/index.ts index 14751d78..f133ebc6 100644 --- a/packages/test/src/index.ts +++ b/packages/test/src/index.ts @@ -1,4 +1,3 @@ -import makeActionFactoryMock from '@foscia/test/makeActionFactoryMock'; import makeActionFactoryMockable from '@foscia/test/makeActionFactoryMockable'; import mockAction from '@foscia/test/mockAction'; import UnexpectedActionError from '@foscia/test/unexpectedActionError'; @@ -8,7 +7,6 @@ export * from '@foscia/test/types'; export { makeActionFactoryMockable, - makeActionFactoryMock, mockAction, unmockAction, UnexpectedActionError, diff --git a/packages/test/src/makeActionFactoryMock.ts b/packages/test/src/makeActionFactoryMock.ts index 961bb66d..1761cd13 100644 --- a/packages/test/src/makeActionFactoryMock.ts +++ b/packages/test/src/makeActionFactoryMock.ts @@ -23,7 +23,7 @@ import UnexpectedActionError from '@foscia/test/unexpectedActionError'; /** * Create an action factory mock. * - * @experimental + * @internal */ export default ( factory: ActionFactory, diff --git a/website/docs/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index be538fd8..5c726908 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -446,16 +446,6 @@ 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 diff --git a/website/docs/digging-deeper/models/models-transformers.mdx b/website/docs/digging-deeper/models/models-transformers.mdx index fb047f0e..d9ca9e37 100644 --- a/website/docs/digging-deeper/models/models-transformers.mdx +++ b/website/docs/digging-deeper/models/models-transformers.mdx @@ -62,9 +62,9 @@ makeModel('users', { if a value gets converted to an invalid date. [`toDate`](/docs/api/@foscia/core/functions/toDate) will also -apply an "epoch shift" because serializing to a UTC date. +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. ::: diff --git a/website/docs/examples/jsonapi-blog.mdx b/website/docs/examples/jsonapi-blog.mdx index fb480789..5be4c519 100644 --- a/website/docs/examples/jsonapi-blog.mdx +++ b/website/docs/examples/jsonapi-blog.mdx @@ -72,7 +72,12 @@ import { filterBy, sortByDesc, paginate, usingDocument } from '@foscia/jsonapi'; import action from './action'; import Post from './models/post'; -export default async function fetchAllPost(query = {}) { +type AllPostsQuery = { + search?: string; + page?: number; +}; + +export default async function fetchAllPosts(query: AllPostsQuery = {}) { return action().run( query(Post), when(query.search, (a, s) => a.use(filterBy('search', s))), @@ -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().run( + query(Post, id), + include('tags'), + oneOrFail(), + ); } const post = await fetchOnePost('123-abc'); @@ -102,11 +111,14 @@ 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 { @@ -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) { +export default async function deletePost(post: Post) { await action().run(destroy(post), none()); } -const post = new Post(); - await deletePost(post); ``` @@ -163,17 +172,16 @@ import Post from './models/post'; export default function bestPosts() { return action().run( query(Post), - .makeGet('actions/best-posts'), + makeGet('actions/best-posts'), all(), ); } -export default function publishPost(post, query = {}) { +export default function publishPost(post: Post) { return action().run( 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 ab76e61f..425a91ab 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 ::: @@ -156,7 +156,7 @@ source (adapter, (de)serializer, etc.). You can use 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 targeting, here is what your action factory may @@ -319,7 +319,7 @@ await action().run(destroy(post), none()); #### 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 diff --git a/website/docs/help/faq.md b/website/docs/help/faq.md index 03bcc5c4..36690a23 100644 --- a/website/docs/help/faq.md +++ b/website/docs/help/faq.md @@ -78,7 +78,8 @@ 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 can use method chaining (e.g. `action().query(Post).all()`), because those functions must be imported to be used. diff --git a/website/docs/upgrade/support-policy.md b/website/docs/upgrade/support-policy.md index 87670005..7ebb5229 100644 --- a/website/docs/upgrade/support-policy.md +++ b/website/docs/upgrade/support-policy.md @@ -5,6 +5,12 @@ 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: @@ -23,6 +29,4 @@ 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. -Currently, only the latest major version of Foscia is actively maintained. - ::: From a26ae8cdd98d94b34f93d4a33df50168cb5d2eb9 Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 11 Jan 2025 22:53:06 +0100 Subject: [PATCH 07/32] fix(deserializer): existence state of related records when destroying --- packages/serialization/src/makeDeserializer.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/serialization/src/makeDeserializer.ts b/packages/serialization/src/makeDeserializer.ts index 6f607dff..ccecd067 100644 --- a/packages/serialization/src/makeDeserializer.ts +++ b/packages/serialization/src/makeDeserializer.ts @@ -1,6 +1,7 @@ import { consumeAction, consumeCache, + consumeId, consumeInstance, consumeModel, consumeQueryAs, @@ -10,6 +11,7 @@ import { DeserializerError, forceFill, guessContextModel, + isSame, mapAttributes, mapRelations, markSynced, @@ -241,7 +243,12 @@ You should either: ]); const action = consumeAction(context, null); - instance.$exists = action !== 'destroy'; + + instance.$exists = !(action === 'destroy' && ( + isSame(instance, consumeInstance(context, null)) || ( + instance.$model === consumeModel(context, null) && instance.id === consumeId(context, null) + ) + )); instance.$raw = record.raw; markSynced(instance); From 06f103513a1d0aeeb39b346f3d6e734b1518c73b Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 11 Jan 2025 23:30:36 +0100 Subject: [PATCH 08/32] fix(core): only manage instance state for write action if not done Previously, instance state after a successful write action would be managed by both action's hooks and deserializer. With this fix, state will be synced by hooks only if not already done outside of the hooks (using snapshot comparison). --- .../actions/context/enhancers/crud/create.ts | 19 +++---- .../actions/context/enhancers/crud/destroy.ts | 17 ++---- .../context/enhancers/crud/instanceData.ts | 2 +- .../context/enhancers/crud/relationData.ts | 2 +- .../actions/context/enhancers/crud/update.ts | 17 ++---- .../context/enhancers/crud/updateRelation.ts | 2 +- .../core/src/actions/context/runners/all.ts | 4 +- .../core/src/actions/context/runners/one.ts | 2 +- .../core/src/actions/context/runners/oneOr.ts | 2 +- .../actions/context/runners/oneOrCurrent.ts | 2 +- .../src/actions/context/runners/oneOrFail.ts | 2 +- .../core/src/actions/context/runners/raw.ts | 2 +- .../deserializeInstances.ts | 0 .../executeContextThroughAdapter.ts | 0 .../{utils => utilities}/normalizeInclude.ts | 0 .../utilities/registerWriteActionHooks.ts | 37 +++++++++++++ .../{utils => utilities}/serializeInstance.ts | 0 .../{utils => utilities}/serializeRelation.ts | 0 packages/core/src/index.ts | 2 +- packages/core/src/model/types.ts | 52 ++++++++++++++----- 20 files changed, 101 insertions(+), 63 deletions(-) rename packages/core/src/actions/context/{utils => utilities}/deserializeInstances.ts (100%) rename packages/core/src/actions/context/{utils => utilities}/executeContextThroughAdapter.ts (100%) rename packages/core/src/actions/context/{utils => utilities}/normalizeInclude.ts (100%) create mode 100644 packages/core/src/actions/context/utilities/registerWriteActionHooks.ts rename packages/core/src/actions/context/{utils => utilities}/serializeInstance.ts (100%) rename packages/core/src/actions/context/{utils => utilities}/serializeRelation.ts (100%) diff --git a/packages/core/src/actions/context/enhancers/crud/create.ts b/packages/core/src/actions/context/enhancers/crud/create.ts index fd4de74b..894aaa1e 100644 --- a/packages/core/src/actions/context/enhancers/crud/create.ts +++ b/packages/core/src/actions/context/enhancers/crud/create.ts @@ -1,9 +1,9 @@ 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 registerWriteActionHooks + from '@foscia/core/actions/context/utilities/registerWriteActionHooks'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, @@ -15,8 +15,6 @@ import { ContextEnhancer, InferQueryInstance, } from '@foscia/core/actions/types'; -import runHooks from '@foscia/core/hooks/runHooks'; -import markSynced from '@foscia/core/model/snapshots/markSynced'; import { InferModelSchemaProp, ModelInstance, @@ -37,7 +35,9 @@ export default /* @__PURE__ */ makeEnhancer('create', (< instance: I | RI, throughInstance?: I, throughRelation?: K & ModelRelationKey, -) => (action: Action>) => action.use( +) => ( + action: Action>, +) => registerWriteActionHooks(action.use( query(throughInstance ?? instance, throughRelation as any), context({ action: ActionName.CREATE, @@ -46,14 +46,7 @@ export default /* @__PURE__ */ makeEnhancer('create', (< 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); - }), -)) as { +), instance, ['creating', 'saving'], ['created', 'saved'], true)) as { /** * Prepare context for an instance creation. * diff --git a/packages/core/src/actions/context/enhancers/crud/destroy.ts b/packages/core/src/actions/context/enhancers/crud/destroy.ts index 02a6d741..696335ca 100644 --- a/packages/core/src/actions/context/enhancers/crud/destroy.ts +++ b/packages/core/src/actions/context/enhancers/crud/destroy.ts @@ -1,8 +1,8 @@ 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 registerWriteActionHooks + from '@foscia/core/actions/context/utilities/registerWriteActionHooks'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, @@ -11,10 +11,8 @@ import { ConsumeModel, ContextEnhancer, } from '@foscia/core/actions/types'; -import runHooks from '@foscia/core/hooks/runHooks'; import isModel from '@foscia/core/model/checks/isModel'; import forceFill from '@foscia/core/model/forceFill'; -import markSynced from '@foscia/core/model/snapshots/markSynced'; import { Model, ModelIdType, ModelInstance } from '@foscia/core/model/types'; import { using } from '@foscia/shared'; @@ -24,7 +22,7 @@ export default /* @__PURE__ */ makeEnhancer('destroy', (( ) => using( // eslint-disable-next-line new-cap isModel(modelOrInstance) ? forceFill(new modelOrInstance(), { id }) : modelOrInstance, - (instance) => (action: Action) => action.use( + (instance) => (action: Action) => registerWriteActionHooks(action.use( query(modelOrInstance as any, id as any), context({ action: ActionName.DESTROY, @@ -32,14 +30,7 @@ export default /* @__PURE__ */ makeEnhancer('destroy', (( // even if $exists is false. id: instance.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); - }), - ), + ), instance, 'destroying', 'destroyed', false), )) as { /** * Prepare context for an instance deletion. diff --git a/packages/core/src/actions/context/enhancers/crud/instanceData.ts b/packages/core/src/actions/context/enhancers/crud/instanceData.ts index b18478ed..e4046990 100644 --- a/packages/core/src/actions/context/enhancers/crud/instanceData.ts +++ b/packages/core/src/actions/context/enhancers/crud/instanceData.ts @@ -1,5 +1,5 @@ import context from '@foscia/core/actions/context/enhancers/context'; -import serializeInstance from '@foscia/core/actions/context/utils/serializeInstance'; +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'; diff --git a/packages/core/src/actions/context/enhancers/crud/relationData.ts b/packages/core/src/actions/context/enhancers/crud/relationData.ts index 2296d283..75f0be20 100644 --- a/packages/core/src/actions/context/enhancers/crud/relationData.ts +++ b/packages/core/src/actions/context/enhancers/crud/relationData.ts @@ -1,5 +1,5 @@ import context from '@foscia/core/actions/context/enhancers/context'; -import serializeRelation from '@foscia/core/actions/context/utils/serializeRelation'; +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'; diff --git a/packages/core/src/actions/context/enhancers/crud/update.ts b/packages/core/src/actions/context/enhancers/crud/update.ts index 04407ceb..b236b64a 100644 --- a/packages/core/src/actions/context/enhancers/crud/update.ts +++ b/packages/core/src/actions/context/enhancers/crud/update.ts @@ -1,13 +1,11 @@ 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 registerWriteActionHooks + from '@foscia/core/actions/context/utilities/registerWriteActionHooks'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, ConsumeSerializer } from '@foscia/core/actions/types'; -import runHooks from '@foscia/core/hooks/runHooks'; -import markSynced from '@foscia/core/model/snapshots/markSynced'; import { ModelInstance } from '@foscia/core/model/types'; /** @@ -36,7 +34,7 @@ export default /* @__PURE__ */ makeEnhancer('update', < instance: I, ) => ( action: Action>, -) => action.use( +) => registerWriteActionHooks(action.use( query(instance), context({ action: ActionName.UPDATE, @@ -45,11 +43,4 @@ export default /* @__PURE__ */ makeEnhancer('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); - }), -)); +), 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 622e5cdf..d9cd50d1 100644 --- a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts +++ b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts @@ -1,7 +1,7 @@ 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 serializeRelation from '@foscia/core/actions/context/utilities/serializeRelation'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { Action, diff --git a/packages/core/src/actions/context/runners/all.ts b/packages/core/src/actions/context/runners/all.ts index fd774d20..c5e1f8f8 100644 --- a/packages/core/src/actions/context/runners/all.ts +++ b/packages/core/src/actions/context/runners/all.ts @@ -1,8 +1,8 @@ import deserializeInstances, { DeserializedDataOf, -} from '@foscia/core/actions/context/utils/deserializeInstances'; +} from '@foscia/core/actions/context/utilities/deserializeInstances'; import executeContextThroughAdapter - from '@foscia/core/actions/context/utils/executeContextThroughAdapter'; + from '@foscia/core/actions/context/utilities/executeContextThroughAdapter'; import makeRunner from '@foscia/core/actions/makeRunner'; import { Action, diff --git a/packages/core/src/actions/context/runners/one.ts b/packages/core/src/actions/context/runners/one.ts index 651dfe39..661ea287 100644 --- a/packages/core/src/actions/context/runners/one.ts +++ b/packages/core/src/actions/context/runners/one.ts @@ -1,5 +1,5 @@ import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utils/deserializeInstances'; +import { DeserializedDataOf } 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'; diff --git a/packages/core/src/actions/context/runners/oneOr.ts b/packages/core/src/actions/context/runners/oneOr.ts index e2bee1a0..b3b624d3 100644 --- a/packages/core/src/actions/context/runners/oneOr.ts +++ b/packages/core/src/actions/context/runners/oneOr.ts @@ -1,5 +1,5 @@ import all, { AllData } from '@foscia/core/actions/context/runners/all'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utils/deserializeInstances'; +import { DeserializedDataOf } from '@foscia/core/actions/context/utilities/deserializeInstances'; import makeRunner from '@foscia/core/actions/makeRunner'; import { Action, diff --git a/packages/core/src/actions/context/runners/oneOrCurrent.ts b/packages/core/src/actions/context/runners/oneOrCurrent.ts index f170849c..5c1a2608 100644 --- a/packages/core/src/actions/context/runners/oneOrCurrent.ts +++ b/packages/core/src/actions/context/runners/oneOrCurrent.ts @@ -1,6 +1,6 @@ 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 { DeserializedDataOf } 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'; diff --git a/packages/core/src/actions/context/runners/oneOrFail.ts b/packages/core/src/actions/context/runners/oneOrFail.ts index 63a2427f..c981bf1d 100644 --- a/packages/core/src/actions/context/runners/oneOrFail.ts +++ b/packages/core/src/actions/context/runners/oneOrFail.ts @@ -1,5 +1,5 @@ import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utils/deserializeInstances'; +import { DeserializedDataOf } 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'; diff --git a/packages/core/src/actions/context/runners/raw.ts b/packages/core/src/actions/context/runners/raw.ts index 487a7d3c..49d46749 100644 --- a/packages/core/src/actions/context/runners/raw.ts +++ b/packages/core/src/actions/context/runners/raw.ts @@ -1,5 +1,5 @@ import executeContextThroughAdapter - from '@foscia/core/actions/context/utils/executeContextThroughAdapter'; + 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'; diff --git a/packages/core/src/actions/context/utils/deserializeInstances.ts b/packages/core/src/actions/context/utilities/deserializeInstances.ts similarity index 100% rename from packages/core/src/actions/context/utils/deserializeInstances.ts rename to packages/core/src/actions/context/utilities/deserializeInstances.ts diff --git a/packages/core/src/actions/context/utils/executeContextThroughAdapter.ts b/packages/core/src/actions/context/utilities/executeContextThroughAdapter.ts similarity index 100% rename from packages/core/src/actions/context/utils/executeContextThroughAdapter.ts rename to packages/core/src/actions/context/utilities/executeContextThroughAdapter.ts diff --git a/packages/core/src/actions/context/utils/normalizeInclude.ts b/packages/core/src/actions/context/utilities/normalizeInclude.ts similarity index 100% rename from packages/core/src/actions/context/utils/normalizeInclude.ts rename to packages/core/src/actions/context/utilities/normalizeInclude.ts 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..035096d0 --- /dev/null +++ b/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts @@ -0,0 +1,37 @@ +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 + */ +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/utils/serializeInstance.ts b/packages/core/src/actions/context/utilities/serializeInstance.ts similarity index 100% rename from packages/core/src/actions/context/utils/serializeInstance.ts rename to packages/core/src/actions/context/utilities/serializeInstance.ts diff --git a/packages/core/src/actions/context/utils/serializeRelation.ts b/packages/core/src/actions/context/utilities/serializeRelation.ts similarity index 100% rename from packages/core/src/actions/context/utils/serializeRelation.ts rename to packages/core/src/actions/context/utilities/serializeRelation.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c9e4b022..4032539b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,4 @@ -import normalizeInclude from '@foscia/core/actions/context/utils/normalizeInclude'; +import normalizeInclude from '@foscia/core/actions/context/utilities/normalizeInclude'; import makeCache from '@foscia/core/cache/makeCache'; import makeRefsCache from '@foscia/core/cache/makeRefsCache'; import makeTimeoutRefManager from '@foscia/core/cache/makeTimeoutRefManager'; diff --git a/packages/core/src/model/types.ts b/packages/core/src/model/types.ts index aced1a84..f19d983c 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -405,23 +405,39 @@ export type ModelInstancePropertyWriteHookCallback = SyncHookCallback<{ }>; /** - * Model's hooks definition. + * Model's hooks definition dedicated to a model. * * @internal */ -export type ModelHooksDefinition = +export type ModelHooksDefinitionForModel = { + boot: SyncHookCallback; +}; + +/** + * Model's 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's hooks definition dedicated to an instance property. + * + * @internal + */ +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; @@ -432,6 +448,16 @@ export type ModelHooksDefinition = & Record<`property:writing:${string}`, ModelInstancePropertyWriteHookCallback> & Record<`property:write:${string}`, ModelInstancePropertyWriteHookCallback>; +/** + * Model's hooks definition. + * + * @internal + */ +export type ModelHooksDefinition = + & ModelHooksDefinitionForModel + & ModelHooksDefinitionForInstance + & ModelHooksDefinitionForInstanceProperty; + /** * Model class. * From ead2ea0632b62efe3b553dd3884d354cace291c5 Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 11 Jan 2025 23:55:31 +0100 Subject: [PATCH 09/32] feat(core): store instance instead of model inside snapshots BREAKING CHANGE: `$model` property have been removed from snapshot, use `$instance.$model` instead. --- .../src/model/revivers/makeModelsReducer.ts | 1 - .../src/model/revivers/makeModelsReviver.ts | 10 +++++--- packages/core/src/model/revivers/types.ts | 1 - .../src/model/snapshots/compareSnapshots.ts | 4 ++-- .../core/src/model/snapshots/takeSnapshot.ts | 2 +- packages/core/src/model/types.ts | 2 +- website/docs/upgrade/migration.md | 23 +++++++++++++++++++ 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/core/src/model/revivers/makeModelsReducer.ts b/packages/core/src/model/revivers/makeModelsReducer.ts index eab1d642..b17bd312 100644 --- a/packages/core/src/model/revivers/makeModelsReducer.ts +++ b/packages/core/src/model/revivers/makeModelsReducer.ts @@ -49,7 +49,6 @@ export default () => { const reduceSnapshot = (snapshot: ModelSnapshot, parents: Map) => ({ $FOSCIA_TYPE: 'snapshot', - $model: reduceModel(snapshot.$model), $exists: snapshot.$exists, $raw: snapshot.$raw, $loaded: snapshot.$loaded, diff --git a/packages/core/src/model/revivers/makeModelsReviver.ts b/packages/core/src/model/revivers/makeModelsReviver.ts index 9aa0d02a..42d62165 100644 --- a/packages/core/src/model/revivers/makeModelsReviver.ts +++ b/packages/core/src/model/revivers/makeModelsReviver.ts @@ -65,8 +65,12 @@ export default (options: { models: Model[]; }) => { (value, key) => ({ [key]: reviveValue(value, parents) }), ); - const reviveSnapshot = (snapshot: ReducedModelSnapshot, parents: Map) => ({ - $model: reviveModel(snapshot.$model), + const reviveSnapshot = ( + instance: ModelInstance, + snapshot: ReducedModelSnapshot, + parents: Map, + ) => ({ + $instance: instance, $exists: snapshot.$exists, $raw: snapshot.$raw, $loaded: snapshot.$loaded, @@ -83,7 +87,7 @@ export default (options: { models: Model[]; }) => { instance.$raw = data.$raw; instance.$loaded = data.$loaded; instance.$values = reviveValues(data.$values, parents); - instance.$original = reviveSnapshot(data.$original, parents); + instance.$original = reviveSnapshot(instance, data.$original, parents); /* eslint-enable */ }; diff --git a/packages/core/src/model/revivers/types.ts b/packages/core/src/model/revivers/types.ts index 25611e82..f405c4ee 100644 --- a/packages/core/src/model/revivers/types.ts +++ b/packages/core/src/model/revivers/types.ts @@ -18,7 +18,6 @@ export type ReducedModel = { */ export type ReducedModelSnapshot = { $FOSCIA_TYPE: 'snapshot'; - $model: ReducedModel; $exists: boolean; $raw: any; $loaded: Dictionary; diff --git a/packages/core/src/model/snapshots/compareSnapshots.ts b/packages/core/src/model/snapshots/compareSnapshots.ts index 005f702e..df438038 100644 --- a/packages/core/src/model/snapshots/compareSnapshots.ts +++ b/packages/core/src/model/snapshots/compareSnapshots.ts @@ -25,7 +25,7 @@ export default ( prevSnapshot: ModelSnapshot, ...only: ArrayableVariadic> ) => { - if (nextSnapshot.$model !== prevSnapshot.$model) { + if (nextSnapshot.$instance.$model !== prevSnapshot.$instance.$model) { return false; } @@ -39,7 +39,7 @@ export default ( || 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.$instance.$model, nextSnapshot.$values[key], prevSnapshot.$values[key], ), diff --git a/packages/core/src/model/snapshots/takeSnapshot.ts b/packages/core/src/model/snapshots/takeSnapshot.ts index fff03678..4645a67e 100644 --- a/packages/core/src/model/snapshots/takeSnapshot.ts +++ b/packages/core/src/model/snapshots/takeSnapshot.ts @@ -19,7 +19,7 @@ import { mapWithKeys, using } from '@foscia/shared'; export default ( instance: I, ): ModelSnapshot => ({ - $model: instance.$model, + $instance: instance, $exists: instance.$exists, $raw: instance.$raw, $loaded: { ...instance.$loaded }, diff --git a/packages/core/src/model/types.ts b/packages/core/src/model/types.ts index f19d983c..4a8c8bf8 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -676,7 +676,7 @@ export type ModelInstanceUsing = * Model class or instance snapshot. */ export type ModelSnapshot = { - readonly $model: Model; + readonly $instance: ModelInstance; readonly $exists: boolean; readonly $raw: any; readonly $loaded: Dictionary; diff --git a/website/docs/upgrade/migration.md b/website/docs/upgrade/migration.md index f93f4c76..ac54c1d4 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -20,6 +20,10 @@ sidebar_position: 5 - [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) +### Low impacts changes + +- [`$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** @@ -157,6 +161,25 @@ 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). +### `$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 ### Medium impacts changes From 57dc43b7247f4d474865457cf6ae29c92a0c572e Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 13 Jan 2025 07:25:00 +0100 Subject: [PATCH 10/32] chore: rename new action middlewares enhancers --- ...dlewares.ts => consumeActionMiddlewares.ts} | 8 +++++--- ...ddlewares.ts => appendActionMiddlewares.ts} | 12 ++++++------ ...dlewares.ts => prependActionMiddlewares.ts} | 12 ++++++------ ...dlewares.ts => replaceActionMiddlewares.ts} | 11 ++++++----- packages/core/src/actions/index.ts | 18 +++++++++--------- .../core/tests/typecheck/actions.test-d.ts | 4 ++-- .../unit/actions/makeActionFactory.test.ts | 8 ++++---- website/docs/core-concepts/actions.mdx | 16 ++++++++-------- 8 files changed, 46 insertions(+), 43 deletions(-) rename packages/core/src/actions/context/consumers/{consumeMiddlewares.ts => consumeActionMiddlewares.ts} (82%) rename packages/core/src/actions/context/enhancers/middlewares/{appendMiddlewares.ts => appendActionMiddlewares.ts} (61%) rename packages/core/src/actions/context/enhancers/middlewares/{prependMiddlewares.ts => prependActionMiddlewares.ts} (60%) rename packages/core/src/actions/context/enhancers/middlewares/{replaceMiddlewares.ts => replaceActionMiddlewares.ts} (63%) diff --git a/packages/core/src/actions/context/consumers/consumeMiddlewares.ts b/packages/core/src/actions/context/consumers/consumeActionMiddlewares.ts similarity index 82% rename from packages/core/src/actions/context/consumers/consumeMiddlewares.ts rename to packages/core/src/actions/context/consumers/consumeActionMiddlewares.ts index 8499c363..18129a7b 100644 --- a/packages/core/src/actions/context/consumers/consumeMiddlewares.ts +++ b/packages/core/src/actions/context/consumers/consumeActionMiddlewares.ts @@ -6,12 +6,14 @@ import { ActionMiddleware, ConsumeMiddlewares } from '@foscia/core/actions/types * * @param context * @param defaultValue + * + * @internal */ export default ( context: C & Partial>, defaultValue?: D, ) => consumeContext(context, 'middlewares', [ - 'appendMiddlewares', - 'prependMiddlewares', - 'replaceMiddlewares', + 'appendActionMiddlewares', + 'prependActionMiddlewares', + 'replaceActionMiddlewares', ], defaultValue) as ActionMiddleware[] | D; diff --git a/packages/core/src/actions/context/enhancers/middlewares/appendMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/appendActionMiddlewares.ts similarity index 61% rename from packages/core/src/actions/context/enhancers/middlewares/appendMiddlewares.ts rename to packages/core/src/actions/context/enhancers/middlewares/appendActionMiddlewares.ts index cb91c81d..1aecb59a 100644 --- a/packages/core/src/actions/context/enhancers/middlewares/appendMiddlewares.ts +++ b/packages/core/src/actions/context/enhancers/middlewares/appendActionMiddlewares.ts @@ -1,5 +1,5 @@ -import replaceMiddlewares - from '@foscia/core/actions/context/enhancers/middlewares/replaceMiddlewares'; +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'; @@ -14,9 +14,9 @@ import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; * * @example * ```typescript - * import { appendMiddlewares } from '@foscia/core'; + * import { appendActionMiddlewares } from '@foscia/core'; * - * const posts = await action().use(appendMiddlewares( + * const posts = await action().use(appendActionMiddlewares( * (action, next) => { * // Do something... * @@ -25,6 +25,6 @@ import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; * )); * ``` */ -export default /* @__PURE__ */ makeEnhancer('appendMiddlewares', (( +export default /* @__PURE__ */ makeEnhancer('appendActionMiddlewares', (( ...middlewares: ArrayableVariadic> -) => replaceMiddlewares((prev) => [...prev, ...wrapVariadic(...middlewares)]))); +) => replaceActionMiddlewares((prev) => [...prev, ...wrapVariadic(...middlewares)]))); diff --git a/packages/core/src/actions/context/enhancers/middlewares/prependMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/prependActionMiddlewares.ts similarity index 60% rename from packages/core/src/actions/context/enhancers/middlewares/prependMiddlewares.ts rename to packages/core/src/actions/context/enhancers/middlewares/prependActionMiddlewares.ts index 8cf5bd00..c6de2e99 100644 --- a/packages/core/src/actions/context/enhancers/middlewares/prependMiddlewares.ts +++ b/packages/core/src/actions/context/enhancers/middlewares/prependActionMiddlewares.ts @@ -1,5 +1,5 @@ -import replaceMiddlewares - from '@foscia/core/actions/context/enhancers/middlewares/replaceMiddlewares'; +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'; @@ -14,9 +14,9 @@ import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; * * @example * ```typescript - * import { prependMiddlewares } from '@foscia/core'; + * import { prependActionMiddlewares } from '@foscia/core'; * - * const posts = await action().use(prependMiddlewares( + * const posts = await action().use(prependActionMiddlewares( * (action, next) => { * // Do something... * @@ -25,6 +25,6 @@ import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; * )); * ``` */ -export default /* @__PURE__ */ makeEnhancer('prependMiddlewares', (( +export default /* @__PURE__ */ makeEnhancer('prependActionMiddlewares', (( ...middlewares: ArrayableVariadic> -) => replaceMiddlewares((prev) => [...wrapVariadic(...middlewares), ...prev]))); +) => replaceActionMiddlewares((prev) => [...wrapVariadic(...middlewares), ...prev]))); diff --git a/packages/core/src/actions/context/enhancers/middlewares/replaceMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts similarity index 63% rename from packages/core/src/actions/context/enhancers/middlewares/replaceMiddlewares.ts rename to packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts index ca53d609..515991cb 100644 --- a/packages/core/src/actions/context/enhancers/middlewares/replaceMiddlewares.ts +++ b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts @@ -1,4 +1,5 @@ -import consumeMiddlewares from '@foscia/core/actions/context/consumers/consumeMiddlewares'; +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'; @@ -14,9 +15,9 @@ import { Awaitable } from '@foscia/shared'; * * @example * ```typescript - * import { replaceMiddlewares } from '@foscia/core'; + * import { replaceActionMiddlewares } from '@foscia/core'; * - * const posts = await action().use(replaceMiddlewares((previous) => [ + * const posts = await action().use(replaceActionMiddlewares((previous) => [ * ...previous, * (action, next) => { * // Do something... @@ -26,10 +27,10 @@ import { Awaitable } from '@foscia/shared'; * ])); * ``` */ -export default /* @__PURE__ */ makeEnhancer('replaceMiddlewares', (( +export default /* @__PURE__ */ makeEnhancer('replaceActionMiddlewares', (( middlewares: ((prev: ActionMiddleware[]) => Awaitable[]>), ) => async (action: Action) => action.use(context({ middlewares: await middlewares( - consumeMiddlewares(await action.useContext(), []) as ActionMiddleware[], + consumeActionMiddlewares(await action.useContext(), []) as ActionMiddleware[], ), })))); diff --git a/packages/core/src/actions/index.ts b/packages/core/src/actions/index.ts index 5876a29e..dbc8aed8 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -32,12 +32,12 @@ 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 appendMiddlewares - from '@foscia/core/actions/context/enhancers/middlewares/appendMiddlewares'; -import prependMiddlewares - from '@foscia/core/actions/context/enhancers/middlewares/prependMiddlewares'; -import replaceMiddlewares - from '@foscia/core/actions/context/enhancers/middlewares/replaceMiddlewares'; +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'; @@ -96,9 +96,9 @@ export { onSuccess, onError, onFinally, - appendMiddlewares, - prependMiddlewares, - replaceMiddlewares, + appendActionMiddlewares, + prependActionMiddlewares, + replaceActionMiddlewares, consumeAction, consumeAdapter, consumeCache, diff --git a/packages/core/tests/typecheck/actions.test-d.ts b/packages/core/tests/typecheck/actions.test-d.ts index 2a629ffb..95971761 100644 --- a/packages/core/tests/typecheck/actions.test-d.ts +++ b/packages/core/tests/typecheck/actions.test-d.ts @@ -1,6 +1,6 @@ import { Adapter, - all, appendMiddlewares, + all, appendActionMiddlewares, associate, attach, cached, @@ -231,7 +231,7 @@ test('Actions are type safe', async () => { expectTypeOf((await event.action.useContext()).foo).not.toEqualTypeOf(); })); - action().use(context({ foo: 'bar' }), appendMiddlewares([async (a, next) => { + action().use(context({ foo: 'bar' }), appendActionMiddlewares([async (a, next) => { const ctx = await a.useContext(); expectTypeOf(ctx.foo).toEqualTypeOf(); diff --git a/packages/core/tests/unit/actions/makeActionFactory.test.ts b/packages/core/tests/unit/actions/makeActionFactory.test.ts index f722a7ae..05e0afbf 100644 --- a/packages/core/tests/unit/actions/makeActionFactory.test.ts +++ b/packages/core/tests/unit/actions/makeActionFactory.test.ts @@ -1,7 +1,7 @@ import { Action, ActionCall, - appendMiddlewares, + appendActionMiddlewares, cachedOr, context, ContextFunctionType, @@ -14,7 +14,7 @@ import { onFinally, onRunning, onSuccess, - prependMiddlewares, + prependActionMiddlewares, query, SYMBOL_ACTION_CONTEXT_ENHANCER, SYMBOL_ACTION_CONTEXT_RUNNER, @@ -91,7 +91,7 @@ describe('unit: makeActionFactory', () => { const action = makeActionFactory()(); const result = await action .use(context({ value: 'foo' })) - .use(appendMiddlewares([ + .use(appendActionMiddlewares([ async (a, next) => { a.use(context({ value: `${(await a.useContext()).value}1` })); @@ -107,7 +107,7 @@ describe('unit: makeActionFactory', () => { return `${r}1`; }, ])) - .use(prependMiddlewares(async (a, next) => `${await next(a)}3`)) + .use(prependActionMiddlewares(async (a, next) => `${await next(a)}3`)) .run(() => 'bar'); expect(result).toEqual('bar123'); diff --git a/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index e5db3faa..e112c863 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -578,9 +578,9 @@ their results. To register middlewares, you can use: -- [`appendMiddlewares`](/docs/api/@foscia/core/functions/appendMiddlewares) +- [`appendActionMiddlewares`](/docs/api/@foscia/core/functions/appendActionMiddlewares) to add new middlewares after already registered ones -- [`prependMiddlewares`](/docs/api/@foscia/core/functions/prependMiddlewares) +- [`prependActionMiddlewares`](/docs/api/@foscia/core/functions/prependActionMiddlewares) to add new middlewares before already registered ones Each middleware callback will receive two arguments: @@ -591,9 +591,9 @@ 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 { appendMiddlewares } from '@foscia/core'; +import { appendActionMiddlewares } from '@foscia/core'; -action().use(appendMiddlewares([ +action().use(appendActionMiddlewares([ (a, next) => { // Do something before execution. return next(a); @@ -606,15 +606,15 @@ action().use(appendMiddlewares([ ])); ``` -Finally, when using [`replaceMiddlewares`](/docs/api/@foscia/core/functions/replaceMiddlewares), +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 { replaceMiddlewares } from '@foscia/core'; +import { replaceActionMiddlewares } from '@foscia/core'; -action().use(replaceMiddlewares((previousMiddlewares) => [ +action().use(replaceActionMiddlewares((previousActionMiddlewares) => [ (a, next) => next(a), - ...previousMiddlewares, + ...previousActionMiddlewares, ])); ``` From 4d2c92a3552410f50ba42403cb42cfe07fd69c4d Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 13 Jan 2025 20:07:41 +0100 Subject: [PATCH 11/32] feat(http): add http middlewares to replace transformers BREAKING CHANGE: `requestTransformers`, `responseTransformers` and `errorTransformers` have been removed from HTTP adapter config and request config. You should now be using `middlewares`. --- .../middlewares/replaceActionMiddlewares.ts | 2 +- .../context/enhancers/configureRequest.ts | 18 +---- packages/http/src/makeHttpAdapter.ts | 68 +++++++------------ packages/http/src/types.ts | 38 ++++------- .../tests/mocks/makeJsonApiAction.mock.ts | 4 +- website/docs/digging-deeper/usages/http.mdx | 26 +++---- website/docs/digging-deeper/usages/jsonapi.md | 2 +- website/docs/digging-deeper/usages/rest.md | 2 +- website/docs/upgrade/migration.md | 63 +++++++++++++++++ 9 files changed, 122 insertions(+), 101 deletions(-) diff --git a/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts index 515991cb..bbb46522 100644 --- a/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts +++ b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts @@ -28,7 +28,7 @@ import { Awaitable } from '@foscia/shared'; * ``` */ export default /* @__PURE__ */ makeEnhancer('replaceActionMiddlewares', (( - middlewares: ((prev: ActionMiddleware[]) => Awaitable[]>), + middlewares: (prev: ActionMiddleware[]) => Awaitable[]>, ) => async (action: Action) => action.use(context({ middlewares: await middlewares( consumeActionMiddlewares(await action.useContext(), []) as ActionMiddleware[], diff --git a/packages/http/src/actions/context/enhancers/configureRequest.ts b/packages/http/src/actions/context/enhancers/configureRequest.ts index 336769da..a8551e02 100644 --- a/packages/http/src/actions/context/enhancers/configureRequest.ts +++ b/packages/http/src/actions/context/enhancers/configureRequest.ts @@ -7,12 +7,12 @@ import { using } from '@foscia/shared'; * 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). + * 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. * @@ -74,18 +74,6 @@ export default /* @__PURE__ */ makeEnhancer('configureRequest', ( ...(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, })), )); diff --git a/packages/http/src/makeHttpAdapter.ts b/packages/http/src/makeHttpAdapter.ts index bc35ebbc..3d3df081 100644 --- a/packages/http/src/makeHttpAdapter.ts +++ b/packages/http/src/makeHttpAdapter.ts @@ -25,7 +25,7 @@ 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`}. @@ -35,30 +35,6 @@ import { Dictionary, isNil, optionalJoin, sequentialTransform } from '@foscia/sh * @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) @@ -210,25 +186,29 @@ 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 { adapter: { execute } as HttpAdapter }; diff --git a/packages/http/src/types.ts b/packages/http/src/types.ts index 45fdd870..8d093e1c 100644 --- a/packages/http/src/types.ts +++ b/packages/http/src/types.ts @@ -1,5 +1,5 @@ import { Adapter } from '@foscia/core'; -import { Awaitable, Dictionary, Transformer } from '@foscia/shared'; +import { Awaitable, Dictionary, Middleware, Transformer } from '@foscia/shared'; /** * The HTTP method to use in request. @@ -84,20 +84,13 @@ export type HttpRequestConfig = */ responseReader?: HttpResponseReader; /** - * Transforms the {@link !Request | `Request`} object sequentially - * (after adapter transformers). + * Middlewares to affect requests, responses, and errors. + * If a callback is given, it makes possible to replace globally configured + * middlewares. */ - requestTransformers?: RequestTransformer[]; - /** - * Transforms the {@link !Response | `Response`} object sequentially - * (after adapter transformers). - */ - responseTransformers?: ResponseTransformer[]; - /** - * Transforms the thrown error sequentially - * (after adapter transformers). - */ - errorTransformers?: ErrorTransformer[]; + middlewares?: HttpAdapterMiddleware[] | (( + prev: HttpAdapterMiddleware[], + ) => Awaitable); } & Pick; @@ -145,6 +138,11 @@ export type HttpURLContext = { */ export type HttpResponseReader = (response: Response) => Promise; +/** + * Dedicated middleware for the HTTP adapter implementation. + */ +export type HttpAdapterMiddleware = Middleware>; + /** * The configuration for the HTTP adapter implementation. * @@ -216,17 +214,9 @@ export type HttpAdapterConfig = { */ relationPathTransformer?: Transformer; /** - * Transforms the {@link !Request | `Request`} object sequentially. - */ - requestTransformers?: RequestTransformer[]; - /** - * Transforms the {@link !Response | `Response`} object sequentially. - */ - responseTransformers?: ResponseTransformer[]; - /** - * Transforms the thrown error sequentially. + * Middlewares to affect requests, responses, and errors. */ - errorTransformers?: ErrorTransformer[]; + middlewares?: HttpAdapterMiddleware[]; }; /** 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/website/docs/digging-deeper/usages/http.mdx b/website/docs/digging-deeper/usages/http.mdx index d2303af6..fef8f243 100644 --- a/website/docs/digging-deeper/usages/http.mdx +++ b/website/docs/digging-deeper/usages/http.mdx @@ -105,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()), @@ -156,7 +156,7 @@ for more details. To define a default request headers, use the [`defaultHeaders`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig#defaultheaders) -option or implement a request transformer (useful for unstable headers, +option or implement a middleware (useful for dynamic headers, such as `Accept-Language` in an internationalized application). ```typescript @@ -164,9 +164,9 @@ import { makeHttpAdapter } from '@foscia/http'; const { adapter } = makeHttpAdapter({ defaultHeaders: { 'Accept-Language': 'fr-FR' }, - requestTransformers: [(request) => { + middlewares: [(request, next) => { request.headers.set('Accept-Language', 'en-US'); - return request; + return next(request); }], }); ``` diff --git a/website/docs/digging-deeper/usages/jsonapi.md b/website/docs/digging-deeper/usages/jsonapi.md index 09681602..0f7ac3f2 100644 --- a/website/docs/digging-deeper/usages/jsonapi.md +++ b/website/docs/digging-deeper/usages/jsonapi.md @@ -164,7 +164,7 @@ Here are common configuration for `@foscia/jsonapi` implementation. You can read for more details. You can also take a look at -[HTTP usage and common configuration recipes](/docs/digging-deeper/usages/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 diff --git a/website/docs/digging-deeper/usages/rest.md b/website/docs/digging-deeper/usages/rest.md index 2ceddce7..fa533d53 100644 --- a/website/docs/digging-deeper/usages/rest.md +++ b/website/docs/digging-deeper/usages/rest.md @@ -82,7 +82,7 @@ Here are common configuration for `@foscia/rest` implementation. You can read for more details. You can also take a look at -[HTTP usage and common configuration recipes](/docs/digging-deeper/usages/http), as +[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 diff --git a/website/docs/upgrade/migration.md b/website/docs/upgrade/migration.md index ac54c1d4..b412b025 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -17,6 +17,7 @@ sidebar_position: 5 ### 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) @@ -134,6 +135,68 @@ action.use(onRunning(async (event) => { })); ``` +### 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** From aaadb1fbf9be24b20c828df8d9449f5c02fc2798 Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 13 Jan 2025 21:42:46 +0100 Subject: [PATCH 12/32] feat(core): change transformers to a foscia object BREAKING CHANGE: custom transformer object must now use `makeCustomTransformer` instead of using an object implementing the `ObjectTransformer` type. --- packages/core/src/index.ts | 4 ++ packages/core/src/symbols.ts | 7 ++++ .../core/src/transformers/isTransformer.ts | 7 ++++ .../src/transformers/makeCustomTransformer.ts | 21 ++++++++++ .../core/src/transformers/makeTransformer.ts | 22 +++++----- packages/core/src/transformers/types.ts | 33 ++++----------- .../models/models-transformers.mdx | 32 +++++++------- website/docs/upgrade/migration.md | 42 +++++++++++++++++++ 8 files changed, 116 insertions(+), 52 deletions(-) create mode 100644 packages/core/src/transformers/isTransformer.ts create mode 100644 packages/core/src/transformers/makeCustomTransformer.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4032539b..b3e95ded 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -93,6 +93,8 @@ import { SYMBOL_MODEL_RELATION_HAS_MANY, SYMBOL_MODEL_RELATION_HAS_ONE, } 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'; @@ -156,6 +158,8 @@ export { toNumber, toString, makeTransformer, + makeCustomTransformer, + isTransformer, onBoot, onInit, onRetrieved, diff --git a/packages/core/src/symbols.ts b/packages/core/src/symbols.ts index 5aec4416..fa01e9d6 100644 --- a/packages/core/src/symbols.ts +++ b/packages/core/src/symbols.ts @@ -5,6 +5,13 @@ */ 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. * diff --git a/packages/core/src/transformers/isTransformer.ts b/packages/core/src/transformers/isTransformer.ts new file mode 100644 index 00000000..af564b1b --- /dev/null +++ b/packages/core/src/transformers/isTransformer.ts @@ -0,0 +1,7 @@ +import { SYMBOL_MODEL_PROP_TRANSFORMER } from '@foscia/core/symbols'; +import { ObjectTransformer } from '@foscia/core/transformers/types'; +import { isFosciaType } from '@foscia/shared'; + +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/makeTransformer.ts b/packages/core/src/transformers/makeTransformer.ts index 7402dde3..2d968266 100644 --- a/packages/core/src/transformers/makeTransformer.ts +++ b/packages/core/src/transformers/makeTransformer.ts @@ -1,22 +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. + * Create a transformer with automatic support for `null` + * or `undefined` values. * - * @param deserializeFn - * @param serializeFn + * @param deserialize + * @param serialize * * @category Factories */ export default ( - deserializeFn: (value: DS) => Awaitable, - serializeFn?: (value: T) => Awaitable, -) => ({ - deserialize: (value: Optional) => (isNil(value) ? null : deserializeFn(value)), - serialize: (value: T | null) => ( + deserialize: (value: DS) => Awaitable, + serialize?: (value: T) => Awaitable, +) => makeCustomTransformer( + (value: Optional) => (isNil(value) ? null : deserialize(value)), + (value: T | null) => ( isNil(value) ? null - : (serializeFn ?? deserializeFn)(value as any) + : (serialize ?? deserialize)(value as any) ), -} as ObjectTransformer, SR | null>); +) as ObjectTransformer, SR | null>; diff --git a/packages/core/src/transformers/types.ts b/packages/core/src/transformers/types.ts index 31801b00..80ee73ab 100644 --- a/packages/core/src/transformers/types.ts +++ b/packages/core/src/transformers/types.ts @@ -1,29 +1,12 @@ -import { Awaitable, Optional, Transformer } from '@foscia/shared'; +import { SYMBOL_MODEL_PROP_TRANSFORMER } from '@foscia/core/symbols'; +import { Awaitable, FosciaObject, Transformer } from '@foscia/shared'; /** * Bi-directional object transformer. */ -export type ObjectTransformer = { - deserialize: Transformer>; - serialize: Transformer>; -}; - -/** - * Object transformer factory options. - * - * @internal - */ -export type ObjectTransformerFactoryOptions = { - nullable?: N; -}; - -/** - * Object transformer factory result from options. - * - * @internal - */ -export type ObjectTransformerFactoryResult = ( - options?: ObjectTransformerFactoryOptions, -) => N extends true - ? ObjectTransformer, SR | null> - : ObjectTransformer; +export type ObjectTransformer = + & { + deserialize: Transformer>; + serialize: Transformer>; + } + & FosciaObject; diff --git a/website/docs/digging-deeper/models/models-transformers.mdx b/website/docs/digging-deeper/models/models-transformers.mdx index d9ca9e37..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 @@ -184,7 +185,7 @@ export default () => makeTransformer( ``` The return value of [`makeTransformer`](/docs/api/@foscia/core/functions/makeTransformer) -is a transformer object. +is a transformer object which can be used on model's attributes. ```typescript import { attr, makeModel } from '@foscia/core'; @@ -197,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; } @@ -217,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/upgrade/migration.md b/website/docs/upgrade/migration.md index b412b025..7e353b04 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -23,6 +23,7 @@ sidebar_position: 5 ### 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 @@ -224,6 +225,47 @@ 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). +### 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 +}; +``` + ### `$model` property of snapshots is replaced by `$instance` **Likelihood Of Impact: Low** From bddd783f9b219ccf3c77ecd635d7638c3e9053eb Mon Sep 17 00:00:00 2001 From: paul Date: Wed, 15 Jan 2025 22:13:59 +0100 Subject: [PATCH 13/32] feat(core): change cache refs to factories BREAKING CHANGE: `manager` option of `makeRefsCache` has been replaced by `makeRef`. `makeWeakRefManager` has been replaced by `makeWeakRefFactory`. --- packages/core/src/cache/makeCache.ts | 4 +- packages/core/src/cache/makeRefsCache.ts | 12 ++--- .../core/src/cache/makeTimedRefFactory.ts | 46 ++++++++++++++++++ .../core/src/cache/makeTimeoutRefManager.ts | 47 ------------------- packages/core/src/cache/makeWeakRefFactory.ts | 13 +++++ packages/core/src/cache/makeWeakRefManager.ts | 14 ------ packages/core/src/cache/types.ts | 38 ++++----------- packages/core/src/index.ts | 8 ++-- .../tests/unit/cache/makeRefsCache.test.ts | 25 +++------- .../unit/cache/makeTimedRefFactory.test.ts | 41 ++++++++++++++++ .../unit/cache/makeTimeoutRefManager.test.ts | 32 ------------- .../unit/cache/makeWeakRefFactory.test.ts | 13 +++++ .../unit/cache/makeWeakRefManager.test.ts | 15 ------ website/docs/upgrade/migration.md | 20 ++++++++ 14 files changed, 161 insertions(+), 167 deletions(-) create mode 100644 packages/core/src/cache/makeTimedRefFactory.ts delete mode 100644 packages/core/src/cache/makeTimeoutRefManager.ts create mode 100644 packages/core/src/cache/makeWeakRefFactory.ts delete mode 100644 packages/core/src/cache/makeWeakRefManager.ts create mode 100644 packages/core/tests/unit/cache/makeTimedRefFactory.test.ts delete mode 100644 packages/core/tests/unit/cache/makeTimeoutRefManager.test.ts create mode 100644 packages/core/tests/unit/cache/makeWeakRefFactory.test.ts delete mode 100644 packages/core/tests/unit/cache/makeWeakRefManager.test.ts diff --git a/packages/core/src/cache/makeCache.ts b/packages/core/src/cache/makeCache.ts index 0d17d41b..e992a351 100644 --- a/packages/core/src/cache/makeCache.ts +++ b/packages/core/src/cache/makeCache.ts @@ -1,5 +1,5 @@ import makeRefsCache from '@foscia/core/cache/makeRefsCache'; -import makeWeakRefManager from '@foscia/core/cache/makeWeakRefManager'; +import makeWeakRefFactory from '@foscia/core/cache/makeWeakRefFactory'; import { InstancesCache } from '@foscia/core/types'; import { kebabCase } from '@foscia/shared'; @@ -9,7 +9,7 @@ import { kebabCase } from '@foscia/shared'; * @category Factories */ export default (): { cache: InstancesCache; } => makeRefsCache({ - manager: makeWeakRefManager(), + makeRef: makeWeakRefFactory(), normalizeType: kebabCase, normalizeId: (id) => String(id), }); diff --git a/packages/core/src/cache/makeRefsCache.ts b/packages/core/src/cache/makeRefsCache.ts index ee18e69a..a2b6267a 100644 --- a/packages/core/src/cache/makeRefsCache.ts +++ b/packages/core/src/cache/makeRefsCache.ts @@ -1,17 +1,17 @@ -import { RefsCache, RefsCacheConfig } from '@foscia/core/cache/types'; +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 interacting with instance references - * through configured {@link RefsManager | `RefsManager`}. + * 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 instances = makeIdentifiersMap>(); const normalizeType = config.normalizeType ?? ((t) => t); const normalizeId = config.normalizeId ?? ((v) => v); @@ -29,7 +29,7 @@ export default (config: RefsCacheConfig) => { find: async (type: string, id: ModelIdType) => { const ref = instances.find(normalizeType(type), normalizeId(id)); if (ref) { - const instance = await config.manager.value(ref); + const instance = await ref(); if (instance) { return instance; } @@ -42,7 +42,7 @@ export default (config: RefsCacheConfig) => { put: async (type: string, id: ModelIdType, instance: ModelInstance) => instances.put( normalizeType(type), normalizeId(id), - await config.manager.ref(instance), + await config.makeRef(instance), ), } as RefsCache, }; diff --git a/packages/core/src/cache/makeTimedRefFactory.ts b/packages/core/src/cache/makeTimedRefFactory.ts new file mode 100644 index 00000000..f0716535 --- /dev/null +++ b/packages/core/src/cache/makeTimedRefFactory.ts @@ -0,0 +1,46 @@ +import { tap } from '@foscia/shared'; + +/** + * Config for the timed ref factory. + * + * @internal + */ +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/makeTimeoutRefManager.ts b/packages/core/src/cache/makeTimeoutRefManager.ts deleted file mode 100644 index 045eb21a..00000000 --- a/packages/core/src/cache/makeTimeoutRefManager.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { RefManager, TimeoutRef, TimeoutRefConfig } from '@foscia/core/cache/types'; -import { ModelInstance } from '@foscia/core/model/types'; - -/** - * Create a timeout ref object. - * - * @param instance - * @param config - */ -const makeTimeoutRef = (instance: T, config: TimeoutRefConfig): TimeoutRef => { - let removeTimeout: ReturnType | undefined; - const scheduleRemove = () => { - clearTimeout(removeTimeout); - removeTimeout = setTimeout(() => { - removeTimeout = undefined; - }, config.timeout); - }; - - scheduleRemove(); - - return { - deref: () => { - if (removeTimeout === undefined) { - return undefined; - } - - scheduleRemove(); - - return instance; - }, - }; -}; - -/** - * Make a {@link RefManager | `RefManager`} using - * {@link !setTimeout | `setTimeout`} to retain instance refs. - * - * @param config - * - * @category Factories - * @since 0.13.0 - * @experimental - */ -export default (config: TimeoutRefConfig) => ({ - ref: (instance: ModelInstance) => makeTimeoutRef(instance, config), - value: (ref: TimeoutRef) => ref.deref(), -} as RefManager>); 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/makeWeakRefManager.ts b/packages/core/src/cache/makeWeakRefManager.ts deleted file mode 100644 index bae4d254..00000000 --- a/packages/core/src/cache/makeWeakRefManager.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { RefManager } from '@foscia/core/cache/types'; -import { ModelInstance } from '@foscia/core/model/types'; - -/** - * Make a {@link RefManager | `RefManager`} using - * {@link !WeakRef | `WeakRef`} to retain instance refs. - * - * @category Factories - * @since 0.13.0 - */ -export default () => ({ - ref: (instance: ModelInstance) => new WeakRef(instance), - value: (ref: WeakRef) => ref.deref(), -} as RefManager>); diff --git a/packages/core/src/cache/types.ts b/packages/core/src/cache/types.ts index 96959b71..1ed646a8 100644 --- a/packages/core/src/cache/types.ts +++ b/packages/core/src/cache/types.ts @@ -3,51 +3,31 @@ import { InstancesCache } from '@foscia/core/types'; import { Awaitable, Optional, Transformer } from '@foscia/shared'; /** - * Config for the timeout ref manager. + * Function which stores a reference to a value. + * Calling the function retrieves the value, or `null` if the reference + * expired. * * @internal */ -export type TimeoutRefConfig = { - timeout: number; -}; - -/** - * Timeout ref object. - * - * @internal - */ -export type TimeoutRef = { deref: () => T | undefined; }; +export type RefValue = () => Awaitable; /** - * Reference manager to retain cached instances in cache. + * Factory to create a reference to a value. * * @internal */ -export type RefManager = { - /** - * Create a ref to an instance. - * - * @param instance - */ - ref(instance: ModelInstance): Awaitable; - /** - * Retrieve an instance from a ref. If ref expired, it can return `undefined`. - * - * @param ref - */ - value(ref: R): Awaitable; -}; +export type RefFactory = (value: V) => Awaitable>; /** * Config for refs cache implementation. * * @internal */ -export type RefsCacheConfig = { +export type RefsCacheConfig = { /** - * Manager to use to create and resolve reference to instances. + * Create a reference to a model instance. */ - manager: RefManager; + makeRef: RefFactory; /** * Normalize the type before storing and resolving instances. */ diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b3e95ded..f2c7b62f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,8 +1,8 @@ import normalizeInclude from '@foscia/core/actions/context/utilities/normalizeInclude'; import makeCache from '@foscia/core/cache/makeCache'; import makeRefsCache from '@foscia/core/cache/makeRefsCache'; -import makeTimeoutRefManager from '@foscia/core/cache/makeTimeoutRefManager'; -import makeWeakRefManager from '@foscia/core/cache/makeWeakRefManager'; +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'; @@ -127,8 +127,8 @@ export { makeMapRegistry, makeCache, makeRefsCache, - makeWeakRefManager, - makeTimeoutRefManager, + makeWeakRefFactory, + makeTimedRefFactory, attr, hasMany, hasOne, diff --git a/packages/core/tests/unit/cache/makeRefsCache.test.ts b/packages/core/tests/unit/cache/makeRefsCache.test.ts index 281f40ee..bd7f4ee7 100644 --- a/packages/core/tests/unit/cache/makeRefsCache.test.ts +++ b/packages/core/tests/unit/cache/makeRefsCache.test.ts @@ -1,5 +1,5 @@ -import { makeRefsCache, makeWeakRefManager } 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'; @@ -9,7 +9,7 @@ describe.concurrent('unit: makeRefsCache', () => { const secondPost = new PostMock(); const comment = new CommentMock(); - const { cache } = makeRefsCache({ manager: makeWeakRefManager() }); + 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: makeRefsCache', () => { }); 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 } = makeRefsCache({ 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/makeTimeoutRefManager.test.ts b/packages/core/tests/unit/cache/makeTimeoutRefManager.test.ts deleted file mode 100644 index 90e6924d..00000000 --- a/packages/core/tests/unit/cache/makeTimeoutRefManager.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { makeTimeoutRefManager } from '@foscia/core'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import PostMock from '../../mocks/models/post.mock'; - -describe.concurrent('unit: makeTimeoutRefManager', () => { - beforeEach(() => { - vi.useFakeTimers(); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - it('should use a timeout ref instance', async () => { - const post = new PostMock(); - - const manager = makeTimeoutRefManager({ timeout: 1000 }); - const ref = await manager.ref(post); - - expect(manager.value(ref)).toBe(post); - - vi.advanceTimersByTime(500); - expect(manager.value(ref)).toBe(post); - - // Value reset timers. - vi.advanceTimersByTime(750); - expect(manager.value(ref)).toBe(post); - - vi.advanceTimersByTime(1100); - expect(manager.value(ref)).toBe(undefined); - }); -}); 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/makeWeakRefManager.test.ts b/packages/core/tests/unit/cache/makeWeakRefManager.test.ts deleted file mode 100644 index 8a6250a1..00000000 --- a/packages/core/tests/unit/cache/makeWeakRefManager.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { makeWeakRefManager } from '@foscia/core'; -import { describe, expect, it } from 'vitest'; -import PostMock from '../../mocks/models/post.mock'; - -describe.concurrent('unit: makeWeakRefManager', () => { - it('should use a weak ref instance', async () => { - const post = new PostMock(); - - const manager = makeWeakRefManager(); - const ref = await manager.ref(post); - - expect(ref).toBeInstanceOf(WeakRef); - expect(manager.value(ref)).toBe(post); - }); -}); diff --git a/website/docs/upgrade/migration.md b/website/docs/upgrade/migration.md index 7e353b04..53165049 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -266,6 +266,26 @@ export default makeCustomTransformer( }; ``` +### `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(), +}); +``` + ### `$model` property of snapshots is replaced by `$instance` **Likelihood Of Impact: Low** From ea0f44f4d1a44c644c1df75a637058ccc1fce8b1 Mon Sep 17 00:00:00 2001 From: paul Date: Wed, 15 Jan 2025 22:32:23 +0100 Subject: [PATCH 14/32] feat(core): add output configuration to logger --- packages/core/src/logger/logger.ts | 22 ++++++-- website/docs/core-concepts/environment.md | 69 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 website/docs/core-concepts/environment.md diff --git a/packages/core/src/logger/logger.ts b/packages/core/src/logger/logger.ts index 57acba80..cc89e6b4 100644 --- a/packages/core/src/logger/logger.ts +++ b/packages/core/src/logger/logger.ts @@ -16,6 +16,13 @@ const LOGGER_LEVELS_WEIGHTS = { type LoggerLevel = keyof typeof LOGGER_LEVELS; +type LoggerOutput = { + error: (message: string, ...args: unknown[]) => void; + warn: (message: string, ...args: unknown[]) => void; + info: (message: string, ...args: unknown[]) => void; + debug: (message: string, ...args: unknown[]) => void; +}; + const makeDefaultLevel = (): LoggerLevel | null => { if (IS_TEST) { return null; @@ -25,24 +32,31 @@ const makeDefaultLevel = (): LoggerLevel | null => { }; const makeMessageLog = (level: LoggerLevel) => function log( - this: { level: LoggerLevel | null; }, + this: { level: LoggerLevel | null; output: LoggerOutput | null; }, message: string, args: unknown[] = [], ) { if (this.level && LOGGER_LEVELS_WEIGHTS[level] >= LOGGER_LEVELS_WEIGHTS[this.level] - && typeof console !== 'undefined' - && typeof console[level] === 'function' + && this.output ) { - console[level](`[foscia] ${level}: ${message}`, ...args); + this.output[level](`[foscia] ${level}: ${message}`, ...args); } }; export default { /** * The minimum level of logged messages. + * Defaults to `error` in PROD env, `warn` in DEV env, and `null` in TEST env. */ level: makeDefaultLevel(), + /** + * The output to use for logged messages. + * Defaults to global `console` if available. + */ + output: ( + typeof console !== 'undefined' ? console : null + ) as LoggerOutput | null, /** * Log an error message. * diff --git a/website/docs/core-concepts/environment.md b/website/docs/core-concepts/environment.md new file mode 100644 index 00000000..642a7473 --- /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/variables/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), +}; +``` From 8858e1b65761d94213141ffb832ba39f0d4c1e13 Mon Sep 17 00:00:00 2001 From: paul Date: Wed, 15 Jan 2025 22:32:57 +0100 Subject: [PATCH 15/32] fix(shared): default env to production when detection failed --- packages/shared/src/env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/env.ts b/packages/shared/src/env.ts index 66704e1c..c1885a42 100644 --- a/packages/shared/src/env.ts +++ b/packages/shared/src/env.ts @@ -14,7 +14,7 @@ const detectEnv = () => { return 'production'; } catch { - return false; + return 'production'; } }; From 03cf4c49498f077e7eb82e53ab9305b297d2efb3 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 16 Jan 2025 00:57:33 +0100 Subject: [PATCH 16/32] feat(core): add new model props factories signatures --- packages/core/src/model/makeDefinition.ts | 12 +- .../core/src/model/props/builders/attr.ts | 60 +++- .../core/src/model/props/builders/hasMany.ts | 52 ++- .../core/src/model/props/builders/hasOne.ts | 52 ++- packages/core/src/model/props/builders/id.ts | 57 +++- .../props/builders/parseValuePropConfig.ts | 19 ++ .../core/src/model/props/builders/relation.ts | 49 +-- .../core/src/model/props/builders/types.ts | 39 ++- packages/core/src/model/types.ts | 4 +- .../mocks/composables/commentable.mock.ts | 5 +- .../core/tests/typecheck/models.test-d.ts | 39 ++- website/docs/core-concepts/models.mdx | 296 +++++++++++++----- 12 files changed, 496 insertions(+), 188 deletions(-) create mode 100644 packages/core/src/model/props/builders/parseValuePropConfig.ts diff --git a/packages/core/src/model/makeDefinition.ts b/packages/core/src/model/makeDefinition.ts index d96f9554..aa19a73d 100644 --- a/packages/core/src/model/makeDefinition.ts +++ b/packages/core/src/model/makeDefinition.ts @@ -3,12 +3,6 @@ import isPropFactory from '@foscia/core/model/checks/isPropFactory'; import { ModelParsedDefinition } from '@foscia/core/model/types'; import { Dictionary, eachDescriptors, makeDescriptorHolder, tap } from '@foscia/shared'; -const parseDescriptor = (descriptor: PropertyDescriptor) => ( - descriptor.value && (isComposable(descriptor.value) || isPropFactory(descriptor.value)) - ? descriptor.value - : makeDescriptorHolder(descriptor) -); - /** * Parse each descriptor of a raw definition. * @@ -19,6 +13,10 @@ const parseDescriptor = (descriptor: PropertyDescriptor) => ( export default (definition?: D) => tap({} as Dictionary, (parsedDefinition) => { eachDescriptors(definition ?? {}, (key, descriptor) => { // eslint-disable-next-line no-param-reassign - parsedDefinition[key] = parseDescriptor(descriptor); + parsedDefinition[key] = ( + descriptor.value && (isComposable(descriptor.value) || isPropFactory(descriptor.value)) + ? descriptor.value + : makeDescriptorHolder(descriptor) + ); }); }) as ModelParsedDefinition; diff --git a/packages/core/src/model/props/builders/attr.ts b/packages/core/src/model/props/builders/attr.ts index febaaa35..23a6e190 100644 --- a/packages/core/src/model/props/builders/attr.ts +++ b/packages/core/src/model/props/builders/attr.ts @@ -1,22 +1,54 @@ import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; -import { ModelAttributeFactory } from '@foscia/core/model/props/builders/types'; +import parseValuePropConfig from '@foscia/core/model/props/builders/parseValuePropConfig'; +import { + ModelAttributeFactory, + ModelAttributeFactoryConfig, +} 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'; -/** - * Create an attribute property factory. - * - * @param config - * - * @category Factories - */ -export default ( - config?: ObjectTransformer | T | (() => T), +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, - default: typeof config !== 'object' ? config : undefined, - transformer: typeof config === 'object' ? config : undefined, + ...parseValuePropConfig(config, otherConfig), } as ModelAttribute, { - transform: (transformer: ObjectTransformer) => ({ transformer }), -}) as ModelAttributeFactory; + transform: (transformer: ObjectTransformer) => ({ transformer }), +}) 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 c7b24f71..19ae1f82 100644 --- a/packages/core/src/model/props/builders/hasMany.ts +++ b/packages/core/src/model/props/builders/hasMany.ts @@ -4,31 +4,53 @@ import { 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: { +export default /* @__PURE__ */ relation(SYMBOL_MODEL_RELATION_HAS_MANY) as { /** - * Create a has many relation property factory with types or configuration. + * Create a has many relation property factory. + * Recommended when having circular relations. * - * @param config + * @param type * * @category Factories - */( - config?: string | string[] | ModelRelationConfig, - ): ModelRelationFactory; + * + * @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 with a model resolver. + * 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, - ): ModelRelationFactory[], false>; -} = ( - config?: ModelRelationFactoryConfig, -) => 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 725c3cc7..0cc9c21d 100644 --- a/packages/core/src/model/props/builders/hasOne.ts +++ b/packages/core/src/model/props/builders/hasOne.ts @@ -4,31 +4,53 @@ import { 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: { +export default /* @__PURE__ */ relation(SYMBOL_MODEL_RELATION_HAS_ONE) as { /** - * Create a has one relation property factory with types or configuration. + * Create a has one relation property factory. + * Recommended when having circular relations. * - * @param config + * @param type * * @category Factories - */( - config?: string | string[] | ModelRelationConfig, - ): ModelRelationFactory; + * + * @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 with a model resolver. + * 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, - ): ModelRelationFactory, false>; -} = ( - config?: ModelRelationFactoryConfig, -) => 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 e1540cff..c1777226 100644 --- a/packages/core/src/model/props/builders/id.ts +++ b/packages/core/src/model/props/builders/id.ts @@ -1,22 +1,51 @@ import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; -import { ModelIdFactory } from '@foscia/core/model/props/builders/types'; +import parseValuePropConfig from '@foscia/core/model/props/builders/parseValuePropConfig'; +import { ModelIdFactory, ModelIdFactoryConfig } 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'; -/** - * Create an ID property factory. - * - * @param config - * - * @category Factories - */ -export default ( - config?: ObjectTransformer | T | (() => T), +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, - default: typeof config !== 'object' ? config : undefined, - transformer: typeof config === 'object' ? config : undefined, + ...parseValuePropConfig(config, otherConfig), } as ModelId, { - transform: (transformer: ObjectTransformer) => ({ transformer }), -}) as ModelIdFactory; + transform: (transformer: ObjectTransformer) => ({ transformer }), +}) as ModelIdFactory; + +export default id; 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 7e784312..a5564789 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -3,38 +3,43 @@ import { ModelRelationFactory, ModelRelationFactoryConfig, } from '@foscia/core/model/props/builders/types'; -import { ModelInstance, ModelRelation, ModelRelationType } from '@foscia/core/model/types'; +import { + ModelInstance, + ModelRelation, + ModelRelationConfig, + ModelRelationType, +} from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; +import { Awaitable } from '@foscia/shared'; /** * Make a relation property definition factory. * * @param relationType - * @param config * * @internal */ export default ( relationType: ModelRelationType, - config?: ModelRelationFactoryConfig, -) => { - const resolveConfig = (configValue: ModelRelationFactoryConfig) => { - if (typeof configValue === 'string' || Array.isArray(configValue)) { - return { type: configValue }; - } - - if (typeof configValue === 'function') { - return { model: 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 }; } - return { ...configValue }; - }; - - return makeValuePropFactory({ - $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_RELATION, - $RELATION_TYPE: relationType, - ...resolveConfig(config ?? {}), - } as ModelRelation, { - config: resolveConfig, - }) as ModelRelationFactory; -}; + return typeof config === 'function' + ? { model: config, ...otherConfig } + : config; + })(), +} as ModelRelation, { + config: (newConfig: string | string[] | ModelRelationConfig) => ( + typeof config === 'string' || Array.isArray(newConfig) + ? { type: newConfig } + : newConfig + ) as ModelRelationConfig, +}) as ModelRelationFactory; diff --git a/packages/core/src/model/props/builders/types.ts b/packages/core/src/model/props/builders/types.ts index 62c27763..acd9fa7a 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -1,5 +1,4 @@ import { - Model, ModelAttribute, ModelId, ModelIdType, @@ -10,7 +9,7 @@ import { ModelRelationConfig, } from '@foscia/core/model/types'; import { ObjectTransformer } from '@foscia/core/transformers/types'; -import { Awaitable, Constructor } from '@foscia/shared'; +import { Arrayable, Constructor } from '@foscia/shared'; /** * Default prop factory definition object type. @@ -54,6 +53,14 @@ export type ModelIdFactory = { nullable: () => ModelIdFactory; } & ModelPropFactory>; +/** + * Model ID factory object config. + * + * @internal + */ +export type ModelIdFactoryConfig = + Pick, 'transformer' | 'default' | 'readOnly'>; + /** * Model attribute factory object. * @@ -101,25 +108,22 @@ export type ModelAttributeFactory = { } & ModelPropFactory>; /** - * Infer related instance types from relationship models. + * Model attribute factory object config. * * @internal */ -export type InferModelRelationFactoryInstance = - M extends Constructor[] ? I - : M extends Constructor ? I - : never; +export type ModelAttributeFactoryConfig = + Pick, 'transformer' | 'default' | 'readOnly' | 'alias' | 'sync'>; /** - * Model relationship factory configuration object. + * Infer related instance types from relationship models. * * @internal */ -export type ModelRelationFactoryConfig = - | string - | string[] - | ModelRelationConfig - | (() => Awaitable); +export type InferModelRelationFactoryInstance = + M extends Constructor[] ? I extends object ? I : never + : M extends Constructor ? I extends object ? I : never + : never; /** * Model relationship factory object. @@ -164,3 +168,12 @@ export type ModelRelationFactory = { */ sync: (sync: boolean | ModelPropSync) => ModelRelationFactory; } & ModelPropFactory>; + +/** + * Model relation factory object options. + * + * @internal + */ +export type ModelRelationFactoryConfig | null, R extends boolean> = + & ModelRelationConfig + & Pick, 'default' | 'readOnly' | 'alias' | 'sync'>; diff --git a/packages/core/src/model/types.ts b/packages/core/src/model/types.ts index 4a8c8bf8..494ef1d6 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -197,14 +197,14 @@ export type ModelProp = * The type the property's value is of. * This is a generic type annotation only property and should not be accessed. * - * @private + * @internal */ readonly __type__: T; /** * Tells if the property is read-only. * This is a generic type annotation only property and should not be accessed. * - * @private + * @internal */ readonly __readOnly__: R; } 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/typecheck/models.test-d.ts b/packages/core/tests/typecheck/models.test-d.ts index 8c5affe1..54b7e944 100644 --- a/packages/core/tests/typecheck/models.test-d.ts +++ b/packages/core/tests/typecheck/models.test-d.ts @@ -1,4 +1,6 @@ -import { attr, fill, makeModel, normalizeDotRelations } from '@foscia/core'; +/* eslint-disable max-classes-per-file */ + +import { attr, fill, hasOne, makeModel, normalizeDotRelations, toString } from '@foscia/core'; import { expectTypeOf, test } from 'vitest'; import CommentMock from '../mocks/models/comment.mock'; import FileMock from '../mocks/models/file.mock'; @@ -15,17 +17,22 @@ test('Models are type safe', () => { 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 }); @@ -72,4 +79,34 @@ test('Models are type safe', () => { expectTypeOf(chained.name).toEqualTypeOf(); expectTypeOf(chained.email).toEqualTypeOf(); expectTypeOf(chained.age).toEqualTypeOf(); + + class ModelProps extends makeModel('model-with-object-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(); }); diff --git a/website/docs/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index 5c726908..24df0737 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -179,27 +179,62 @@ 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(); ``` + + + #### API {#ids-chained-modifiers} +[`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. @@ -218,29 +253,62 @@ attributes. You can pass a transformer or a default value to this factory. - `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(); ``` + + + #### 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. @@ -264,93 +332,155 @@ model. You can pass the relation information to this factory. - [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 +hasMany(() => User, { path: 'author' }); +hasMany({ path: 'author' }); +``` + + + + +```typescript +hasOne(() => User) .nullable() - .default(() => null) .readOnly() .alias('author') .sync('pull'); ``` - - + + -```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) +``` + + + + +```typescript +hasMany(() => [Comment, Post]); +hasMany<(Comment | Post)[]>(['comments', 'posts']); +``` + + + + +```typescript +hasMany(() => Comment, { path: 'top-comments' }); +hasMany({ path: 'top-comments' }); +``` + + + + +```typescript +hasMany(() => Comment) .readOnly() - .alias('comments') .sync('pull'); ``` - + -#### Configuration {#relations-configuration} - -Using `config` chained modifier, you can customize the -[`ModelRelationConfig`](/docs/api/@foscia/core/type-aliases/ModelRelationConfig), -which can vary between implementations and used dependencies. + + #### 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 `hasMany` and `hasOne` chained modifiers. +provides type definition of `hasOne` and `hasMany` chained modifiers. -#### Polymorphism - -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']); -``` - -:::warning +#### Polymorphism support Polymorphism may require a specific configuration of your action or your data source. @@ -362,8 +492,6 @@ correct model. When implementing polymorphism with a REST data source, your record JSON object must contain a `type` property matching your Foscia models' types. -::: - #### Recommandations ##### Explicit model when not having circular references From e7dae1cf26b1053e8ddaecc91649a1a69949cc0e Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 16 Jan 2025 01:11:19 +0100 Subject: [PATCH 17/32] docs: fix cache refs factories docs --- .../digging-deeper/implementations/core.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/website/docs/digging-deeper/implementations/core.md b/website/docs/digging-deeper/implementations/core.md index e273cb7c..5c5f80c9 100644 --- a/website/docs/digging-deeper/implementations/core.md +++ b/website/docs/digging-deeper/implementations/core.md @@ -24,7 +24,7 @@ Foscia proposes core implementations of those dependencies. [`InstancesCache`](/docs/api/@foscia/core/type-aliases/InstancesCache) implementation. Currently, it uses [`makeRefsCache`](#makerefscache) with -[`makeWeakRefManager`](/docs/api/@foscia/core/functions/makeWeakRefManager). +[`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. @@ -53,23 +53,21 @@ Since this factory is agnostic of implementation, no configuration is available. [`makeRefsCache`](/docs/api/@foscia/core/functions/makeRefsCache) provides a [`InstancesCache`](/docs/api/@foscia/core/type-aliases/InstancesCache) implementation which stores reference to instances created by a -[`RefManager`](/docs/api/@foscia/core/type-aliases/RefManager). +[`RefFactory`](/docs/api/@foscia/core/type-aliases/RefFactory). -The [`RefManager`](/docs/api/@foscia/core/type-aliases/RefManager) is responsible to: +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. -- Create a ref object for a cached instance. -- Retrieve value for this ref object (may return undefined if the ref is - considered expired). +Foscia proposes two implementations of a +[`RefFactory`](/docs/api/@foscia/core/type-aliases/RefFactory): -Foscia proposes a two implementations of a -[`RefManager`](/docs/api/@foscia/core/type-aliases/RefManager): - -- [`makeWeakRefManager`](/docs/api/@foscia/core/functions/makeWeakRefManager), +- [`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. -- [`makeTimeoutRefManager`](/docs/api/@foscia/core/functions/makeTimeoutRefManager), +- [`makeTimeoutRefFactory`](/docs/api/@foscia/core/functions/makeTimeoutRefFactory), which will store every instance in a special object which expires after a configured timeout. @@ -85,7 +83,7 @@ import { makeRefsCache, makeWeakRefManager } from '@foscia/core'; const { cache } = makeRefsCache({ manager: makeWeakRefManager(), // or... - // manager: makeTimeoutRefManager({ timeout: 5 * 60 * 1000 }), + // manager: makeTimeoutRefManager({ lifetime: 5 * 60 * 1000 }), }); cache.put('posts', '1', post); From 137d670377d1b2084bf28e65bb287d8f8cbca711 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 16 Jan 2025 01:15:42 +0100 Subject: [PATCH 18/32] chore: upgrade dependencies --- package.json | 40 +- pnpm-lock.yaml | 9285 +++++++++++++++++++++++------------------- website/package.json | 30 +- 3 files changed, 5171 insertions(+), 4184 deletions(-) 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/pnpm-lock.yaml b/pnpm-lock.yaml index 61970050..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.32.0)) + 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.32.0) + 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.5.2 - version: 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + 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.5.2 - version: 3.5.2(@algolia/client-search@4.24.0)(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2) + 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.5.2 - version: 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) + 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.6 - version: 5.0.6 + specifier: ^5.1.1 + version: 5.1.1 '@fontsource/fira-mono': - specifier: ^5.0.15 - version: 5.0.15 + specifier: ^5.1.1 + version: 5.1.1 '@mdx-js/react': - specifier: ^3.0.1 - version: 3.0.1(@types/react@18.3.5)(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.5 - version: 1.0.5(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2))) + 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.4.0 - version: 2.4.0(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,302 +260,253 @@ importers: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) typedoc: - specifier: ^0.26.7 - version: 0.26.7(typescript@5.6.2) + specifier: ^0.27.6 + version: 0.27.6(typescript@5.7.3) typedoc-plugin-markdown: - specifier: ^4.2.7 - version: 4.2.7(typedoc@0.26.7(typescript@5.6.2)) + specifier: ^4.4.1 + version: 4.4.1(typedoc@0.27.6(typescript@5.7.3)) typedoc-plugin-mdn-links: - specifier: ^3.2.12 - version: 3.2.12(typedoc@0.26.7(typescript@5.6.2)) + specifier: ^4.0.8 + version: 4.0.8(typedoc@0.27.6(typescript@5.7.3)) typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.7.3 + version: 5.7.3 devDependencies: '@docusaurus/module-type-aliases': - specifier: ^3.5.2 - version: 3.5.2(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.3 - version: 3.3.3 + specifier: ^3.4.2 + version: 3.4.2 rimraf: 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.24.0': - resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==} + '@algolia/client-abtesting@5.19.0': + resolution: {integrity: sha512-dMHwy2+nBL0SnIsC1iHvkBao64h4z+roGelOz11cxrDBrAdASxLxmfVMop8gmodQ2yZSacX0Rzevtxa+9SqxCw==} + engines: {node: '>= 14.0.0'} - '@algolia/cache-common@4.24.0': - resolution: {integrity: sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==} + '@algolia/client-analytics@5.19.0': + resolution: {integrity: sha512-CDW4RwnCHzU10upPJqS6N6YwDpDHno7w6/qXT9KPbPbt8szIIzCHrva4O9KIfx1OhdsHzfGSI5hMAiOOYl4DEQ==} + engines: {node: '>= 14.0.0'} - '@algolia/cache-in-memory@4.24.0': - resolution: {integrity: sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==} + '@algolia/client-common@5.19.0': + resolution: {integrity: sha512-2ERRbICHXvtj5kfFpY5r8qu9pJII/NAHsdgUXnUitQFwPdPL7wXiupcvZJC7DSntOnE8AE0lM7oDsPhrJfj5nQ==} + engines: {node: '>= 14.0.0'} - '@algolia/client-account@4.24.0': - resolution: {integrity: sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==} + '@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.24.0': - resolution: {integrity: sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==} + '@algolia/client-personalization@5.19.0': + resolution: {integrity: sha512-B9eoce/fk8NLboGje+pMr72pw+PV7c5Z01On477heTZ7jkxoZ4X92dobeGuEQop61cJ93Gaevd1of4mBr4hu2A==} + engines: {node: '>= 14.0.0'} - '@algolia/client-common@4.24.0': - resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==} + '@algolia/client-query-suggestions@5.19.0': + resolution: {integrity: sha512-6fcP8d4S8XRDtVogrDvmSM6g5g6DndLc0pEm1GCKe9/ZkAzCmM3ZmW1wFYYPxdjMeifWy1vVEDMJK7sbE4W7MA==} + engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@4.24.0': - resolution: {integrity: sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==} - - '@algolia/client-search@4.24.0': - resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==} + '@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.24.0': - resolution: {integrity: sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==} - - '@algolia/logger-console@4.24.0': - resolution: {integrity: sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==} + '@algolia/ingestion@1.19.0': + resolution: {integrity: sha512-LO7w1MDV+ZLESwfPmXkp+KLeYeFrYEgtbCZG6buWjddhYraPQ9MuQWLhLLiaMlKxZ/sZvFTcZYuyI6Jx4WBhcg==} + engines: {node: '>= 14.0.0'} - '@algolia/recommend@4.24.0': - resolution: {integrity: sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==} + '@algolia/monitoring@1.19.0': + resolution: {integrity: sha512-Mg4uoS0aIKeTpu6iv6O0Hj81s8UHagi5TLm9k2mLIib4vmMtX7WgIAHAcFIaqIZp5D6s5EVy1BaDOoZ7buuJHA==} + engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@4.24.0': - resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==} + '@algolia/recommend@5.19.0': + resolution: {integrity: sha512-PbgrMTbUPlmwfJsxjFhal4XqZO2kpBNRjemLVTkUiti4w/+kzcYO4Hg5zaBgVqPwvFDNQ8JS4SS3TBBem88u+g==} + engines: {node: '>= 14.0.0'} - '@algolia/requester-common@4.24.0': - resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==} + '@algolia/requester-browser-xhr@5.19.0': + resolution: {integrity: sha512-GfnhnQBT23mW/VMNs7m1qyEyZzhZz093aY2x8p0era96MMyNv8+FxGek5pjVX0b57tmSCZPf4EqNCpkGcGsmbw==} + engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@4.24.0': - resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==} + '@algolia/requester-fetch@5.19.0': + resolution: {integrity: sha512-oyTt8ZJ4T4fYvW5avAnuEc6Laedcme9fAFryMD9ndUTIUe/P0kn3BuGcCLFjN3FDmdrETHSFkgPPf1hGy3sLCw==} + engines: {node: '>= 14.0.0'} - '@algolia/transporter@4.24.0': - resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==} + '@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==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.25.4': - resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.24.7': - resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.25.2': - resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.24.7': - resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + '@babel/compat-data@7.26.5': + resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.25.6': - resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==} + '@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-compilation-targets@7.25.2': - resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.25.4': - resolution: {integrity: sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==} + '@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.25.2': - resolution: {integrity: sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==} + '@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.8': - resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==} + '@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-module-transforms@7.25.2': - resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + '@babel/helper-optimise-call-expression@7.25.9': + resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} 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-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.24.8': - resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-remap-async-to-generator@7.25.0': - resolution: {integrity: sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==} + '@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.25.0': - resolution: {integrity: sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==} + '@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==} + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} engines: {node: '>=6.9.0'} - '@babel/helper-split-export-declaration@7.24.7': - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.24.7': - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.24.8': - resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.24.7': - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + '@babel/helper-wrap-function@7.25.9': + resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.24.7': - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.24.8': - resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} - engines: {node: '>=6.9.0'} - - '@babel/helper-wrap-function@7.25.0': - resolution: {integrity: sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.24.7': - resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.25.6': - resolution: {integrity: sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==} + '@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/parser@7.25.6': - resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3': - resolution: {integrity: sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==} + '@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.0': - resolution: {integrity: sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==} + '@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.25.0': - resolution: {integrity: sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==} + '@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.25.0': - resolution: {integrity: sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==} + '@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 @@ -566,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.25.6': - resolution: {integrity: sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==} + '@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-import-attributes@7.25.6': - resolution: {integrity: sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==} + '@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-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-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} 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==} - 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==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.25.4': - resolution: {integrity: sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==} + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -674,344 +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.25.4': - resolution: {integrity: sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==} + '@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.25.0': - resolution: {integrity: sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==} + '@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.25.4': - resolution: {integrity: sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==} + '@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.25.4': - resolution: {integrity: sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==} + '@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.8': - resolution: {integrity: sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==} + '@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-duplicate-named-capturing-groups-regex@7.25.0': - resolution: {integrity: sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==} + '@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.24.7': - resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==} + '@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.25.1': - resolution: {integrity: sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==} + '@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.25.2': - resolution: {integrity: sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==} + '@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.8': - resolution: {integrity: sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==} + '@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.25.0': - resolution: {integrity: sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==} + '@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.8': - resolution: {integrity: sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==} + '@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.25.4': - resolution: {integrity: sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==} + '@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.25.1': - resolution: {integrity: sha512-SLV/giH/V4SmloZ6Dt40HjTGTAIkxn33TVIHxNGNvo8ezMhrxBkzisj4op1KZYPIOHFLqhv60OHvX+YRu4xbmQ==} + '@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.25.2': - resolution: {integrity: sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==} + '@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.25.4': - resolution: {integrity: sha512-8hsyG+KUYGY0coX6KUCDancA0Vw225KJ2HJO0yCNr1vq5r+lJTleDaJf0K7iOhjw4SWhu03TMBzYTJ9krmzULQ==} + '@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.8': - resolution: {integrity: sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==} + '@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.25.2': - resolution: {integrity: sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==} + '@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.25.4': - resolution: {integrity: sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==} + '@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.25.4': - resolution: {integrity: sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==} + '@babel/preset-env@7.26.0': + resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1021,139 +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.25.6': - resolution: {integrity: sha512-Gz0Nrobx8szge6kQQ5Z5MX9L3ObqNwCQY1PSwSNzreFL7aHGxv8Fp2j3ETV6/wWdbiV+mW6OSm8oQhg3Tcsniw==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.24.7': - resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.25.6': - resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} + '@babel/runtime-corejs3@7.26.0': + resolution: {integrity: sha512-YXHu5lN8kJCb1LOb9PgV6pvak43X2h4HvRApcN5SdWeaItQOzfn1hgP6jasD6KWQyJDBxrVmA9o9OivlnNJK/w==} engines: {node: '>=6.9.0'} - '@babel/template@7.24.7': - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} engines: {node: '>=6.9.0'} - '@babel/template@7.25.0': - resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + '@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/traverse@7.25.6': - resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.24.7': - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.25.6': - resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} + '@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.1': - resolution: {integrity: sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==} + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} - '@docsearch/react@3.6.1': - resolution: {integrity: sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==} + '@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' @@ -1169,158 +1298,168 @@ packages: search-insights: optional: true - '@docusaurus/core@3.5.2': - resolution: {integrity: sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==} + '@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: '@mdx-js/react': ^3.0.0 - 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/cssnano-preset@3.5.2': - resolution: {integrity: sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==} + '@docusaurus/cssnano-preset@3.7.0': + resolution: {integrity: sha512-X9GYgruZBSOozg4w4dzv9uOz8oK/EpPVQXkp0MM6Tsgp/nRIU9hJzJ0Pxg1aRa3xCeEQTOimZHcocQFlLwYajQ==} engines: {node: '>=18.0'} - '@docusaurus/logger@3.5.2': - resolution: {integrity: sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==} + '@docusaurus/logger@3.7.0': + resolution: {integrity: sha512-z7g62X7bYxCYmeNNuO9jmzxLQG95q9QxINCwpboVcNff3SJiHJbGrarxxOVMVmAh1MsrSfxWkVGv4P41ktnFsA==} engines: {node: '>=18.0'} - '@docusaurus/mdx-loader@3.5.2': - resolution: {integrity: sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==} + '@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.5.2': - resolution: {integrity: sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==} + '@docusaurus/module-type-aliases@3.7.0': + resolution: {integrity: sha512-g7WdPqDNaqA60CmBrr0cORTrsOit77hbsTj7xE2l71YhBn79sxdm7WMK7wfhcaafkbpIh7jv5ef5TOpf1Xv9Lg==} peerDependencies: react: '*' react-dom: '*' - '@docusaurus/plugin-content-blog@3.5.2': - resolution: {integrity: sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==} + '@docusaurus/plugin-content-blog@3.7.0': + resolution: {integrity: sha512-EFLgEz6tGHYWdPU0rK8tSscZwx+AsyuBW/r+tNig2kbccHYGUJmZtYN38GjAa3Fda4NU+6wqUO5kTXQSRBQD3g==} engines: {node: '>=18.0'} peerDependencies: '@docusaurus/plugin-content-docs': '*' - 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-docs@3.7.0': + resolution: {integrity: sha512-GXg5V7kC9FZE4FkUZA8oo/NrlRb06UwuICzI6tcbzj0+TVgjq/mpUXXzSgKzMS82YByi4dY2Q808njcBCyy6tQ==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 - '@docusaurus/plugin-content-docs@3.5.2': - resolution: {integrity: sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==} + '@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-content-pages@3.5.2': - resolution: {integrity: sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==} + '@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-debug@3.5.2': - resolution: {integrity: sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==} + '@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-analytics@3.5.2': - resolution: {integrity: sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==} + '@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-gtag@3.5.2': - resolution: {integrity: sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==} + '@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-google-tag-manager@3.5.2': - resolution: {integrity: sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==} + '@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/plugin-sitemap@3.5.2': - resolution: {integrity: sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==} + '@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.5.2': - resolution: {integrity: sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==} + '@docusaurus/preset-classic@3.7.0': + resolution: {integrity: sha512-nPHj8AxDLAaQXs+O6+BwILFuhiWbjfQWrdw2tifOClQoNfuXDjfjogee6zfx6NGHWqshR23LrcN115DmkHC91Q==} 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/react-loadable@6.0.0': resolution: {integrity: sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==} peerDependencies: react: '*' - '@docusaurus/theme-classic@3.5.2': - resolution: {integrity: sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==} + '@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.5.2': - resolution: {integrity: sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==} + '@docusaurus/theme-common@3.7.0': + resolution: {integrity: sha512-8eJ5X0y+gWDsURZnBfH0WabdNm8XMCXHv8ENy/3Z/oQKwaB/EHt5lP9VsTDTf36lKEp0V6DjzjFyFIB+CetL0A==} engines: {node: '>=18.0'} peerDependencies: '@docusaurus/plugin-content-docs': '*' - 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-search-algolia@3.5.2': - resolution: {integrity: sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==} + '@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.5.2': - resolution: {integrity: sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==} + '@docusaurus/theme-translations@3.7.0': + resolution: {integrity: sha512-Ewq3bEraWDmienM6eaNK7fx+/lHMtGDHQyd1O+4+3EsDxxUmrzPkV7Ct3nBWTuE0MsoZr3yNwQVKjllzCMuU3g==} engines: {node: '>=18.0'} - '@docusaurus/types@3.5.2': - resolution: {integrity: sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==} + '@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.5.2': - resolution: {integrity: sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==} + '@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.5.2': - resolution: {integrity: sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==} + '@docusaurus/utils-validation@3.7.0': + resolution: {integrity: sha512-w8eiKk8mRdN+bNfeZqC4nyFoxNyI1/VExMKAzD9tqpJfLLbsa46Wfn5wcKH761g9WkKh36RtFV49iL9lh1DYBA==} engines: {node: '>=18.0'} - '@docusaurus/utils@3.5.2': - resolution: {integrity: sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==} + '@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==} @@ -1460,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.6': - resolution: {integrity: sha512-boKBiDKbt80DZoJ064P0421GdpZAmffM814uFEuOOGFEJEPqEThi+KZTrg/DzTEw1c08SuAGjedQQjx3IGFPHg==} + '@fontsource-variable/onest@5.1.1': + resolution: {integrity: sha512-Lb1hWJL7z7VZ9oDiHp5/O4tq/tzSQ0md1Luv+RPRnw3WalFoRBdjE9Xw9ibgteEeVlv8fYQ3UTR6ALDwdWxgzQ==} - '@fontsource/fira-mono@5.0.15': - resolution: {integrity: sha512-wc3TpF2GBbtFDKNbb444BrC3mEKuoPLITSYCKweNIrqBvAalIfJGloY/MVrmSGaMNgaAKUpdgy4eAWPLkUVzaA==} + '@fontsource/fira-mono@5.1.1': + resolution: {integrity: sha512-JDZUQuQQFTvPJgwOftXg44LOa7XYNqwTHHquEpi8rY6SK8m7Q5rWxmMX7nUOvRF2g+jc02mqmK2Y+WmviF9+VQ==} '@foscia/core@0.12.1': resolution: {integrity: sha512-Hgn7TQr9ANpNAR/vQpEP2yu0c4/eCjGLBtc6GLryu2J8ruzbCcLe4oiY0B5auHtir6OpM8WHKIoIN3Ez30gpMw==} @@ -1502,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 @@ -1528,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': @@ -1552,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'} @@ -1566,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.1.0': + resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} - '@mdx-js/mdx@3.0.1': - resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==} - - '@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' @@ -1597,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'} @@ -1613,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==} @@ -1646,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==} @@ -1661,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==} @@ -1674,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 @@ -1698,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 @@ -1729,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 @@ -1738,94 +1887,115 @@ 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/core@1.16.3': - resolution: {integrity: sha512-yETIvrETCeC39gSPIiSADmjri9FwKmxz0QvONMtTIUYlKZe90CJkvcjPksayC2VQOtzOJonEiULUa8v8crUQvA==} + '@shikijs/engine-oniguruma@1.27.2': + resolution: {integrity: sha512-FZYKD1KN7srvpkz4lbGLOYWlyDU4Rd+2RtuKfABTkafAPOFr+J6umfIwY/TzOQqfNtWjL7SAwPAO0dcOraRLaQ==} - '@shikijs/vscode-textmate@9.2.2': - resolution: {integrity: sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==} + '@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==} @@ -1855,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==} @@ -1962,20 +2138,32 @@ 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==} + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@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==} '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/express-serve-static-core@4.19.5': - resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + '@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==} @@ -2043,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@22.5.4': - resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} + '@types/node@22.10.6': + resolution: {integrity: sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2058,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==} @@ -2079,8 +2264,8 @@ packages: '@types/react-router@5.1.20': resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} - '@types/react@18.3.5': - resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==} + '@types/react@19.0.7': + resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2091,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==} @@ -2109,8 +2297,8 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/ws@8.5.12': - resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} + '@types/ws@8.5.13': + resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -2118,8 +2306,8 @@ packages: '@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 @@ -2129,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 @@ -2139,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 @@ -2153,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: '*' @@ -2166,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==} @@ -2199,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==} @@ -2258,31 +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==} - engines: {node: '>=0.4.0'} - 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==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true @@ -2293,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: @@ -2322,19 +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.22.4: - resolution: {integrity: sha512-fvBCywguW9f+939S6awvRMstqMF1XXcd2qs1r1aGqL/PJ1go/DqN06tWmDVmhCDqBJanm++imletrQWf0G2S1g==} + algoliasearch-helper@3.23.0: + resolution: {integrity: sha512-8CK4Gb/ju4OesAYcS+mjBpNiVA7ILWpg7D2vhBZohh0YkG8QT1KZ9LG+8+EntQBUGoKtPy06OFhiwP4f5zzAQg==} peerDependencies: algoliasearch: '>= 3.1 < 6' - algoliasearch@4.24.0: - resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==} + 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==} @@ -2396,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: @@ -2418,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==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} engines: {node: '>= 0.4'} - array.prototype.map@1.0.7: - resolution: {integrity: sha512-XpcFfLoBEAhezrrNw1V+yLXkE7M6uR7xJEsxbG6c/V9v043qurwVJB9r9UTnoSioFDoz1i1VOydpWGmJpfVZbg==} + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} 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: @@ -2455,6 +2623,9 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} + 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} @@ -2466,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 @@ -2476,8 +2647,8 @@ 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 @@ -2486,8 +2657,8 @@ packages: 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 @@ -2524,8 +2695,8 @@ packages: 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==} @@ -2538,6 +2709,10 @@ packages: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -2548,13 +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==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - browserslist@4.23.3: - resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -2564,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'} @@ -2592,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: @@ -2611,20 +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.30001660: - resolution: {integrity: sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==} + 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: @@ -2639,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'} @@ -2680,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'} @@ -2700,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'} @@ -2813,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: @@ -2829,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==} @@ -2839,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==} @@ -2846,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==} @@ -2934,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: @@ -2948,25 +3140,25 @@ packages: peerDependencies: webpack: ^5.1.0 - core-js-compat@3.38.1: - resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} + core-js-compat@3.40.0: + resolution: {integrity: sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==} - core-js-pure@3.38.1: - resolution: {integrity: sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==} + core-js-pure@3.40.0: + resolution: {integrity: sha512-AtDzVIgRrmRKQai62yuSIN5vNiQjcJakJb4fbhVw3ehxx7Lohphvw9SGNWKhLFqSxC4ilD0g/L1huAYFQU3Q6A==} - core-js@3.38.1: - resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} + 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==} @@ -2994,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'} @@ -3041,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==} @@ -3059,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'} @@ -3092,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: @@ -3103,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'} @@ -3115,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: @@ -3150,17 +3363,8 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -3303,10 +3507,10 @@ packages: resolution: {integrity: sha512-xNZcOTPGfhYXoAA5htdmNH9iSUlwu3m9BC5prLA158ZsgB35rsQ9DeWAYRvq7qy0wJ8cwMaqYfh/1HxJpP2r/Q==} hasBin: true - docusaurus-plugin-typedoc@1.0.5: - resolution: {integrity: sha512-mv8LBJYilGOOPLqaIM3vbYc34m4qwOCpb4WfP24DOPFNj2uiTerw8sg9MGvN6Jx2+J8rq9/WMnjcyz3UMqoIIQ==} + 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==} @@ -3331,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==} @@ -3345,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==} @@ -3358,15 +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.18: - resolution: {integrity: sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==} + 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==} @@ -3391,12 +3603,8 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - enhanced-resolve@5.17.0: - resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} - engines: {node: '>=10.13.0'} - - enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + enhanced-resolve@5.18.0: + resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -3417,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.5.4: - resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} - 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'} @@ -3507,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': '*' @@ -3535,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 @@ -3557,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: @@ -3571,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: @@ -3596,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.2: - resolution: {integrity: sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==} + estree-util-value-to-estree@3.2.1: + resolution: {integrity: sha512-Vt2UOjyPbNQQgT5eJh+K5aATti0OjCIAGc9SgMdOFYbohuifsWclR74l0iZTJwePMgWYdX1hlVS+dedH9XV8kw==} estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} @@ -3638,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.20.0: - resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} extend-shallow@2.0.1: @@ -3664,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: @@ -3677,9 +3899,6 @@ packages: fast-uri@3.0.1: resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} - fast-url-parser@1.1.3: - resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} - fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -3694,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==} @@ -3720,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: @@ -3752,8 +3971,8 @@ 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.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} @@ -3767,10 +3986,6 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} - foreground-child@3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} @@ -3793,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'} @@ -3816,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: @@ -3838,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: @@ -3857,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'} @@ -3879,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: @@ -3900,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==} @@ -3920,16 +4144,6 @@ 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'} - hasBin: true - glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -3974,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==} @@ -4014,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==} @@ -4028,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: @@ -4048,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==} @@ -4069,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==} @@ -4119,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 @@ -4151,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 @@ -4175,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: @@ -4187,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 @@ -4213,16 +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==} - engines: {node: '>= 4'} - 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 @@ -4248,8 +4456,8 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - infima@0.2.0-alpha.44: - resolution: {integrity: sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==} + infima@0.2.0-alpha.45: + resolution: {integrity: sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==} engines: {node: '>=12'} inflight@1.0.6: @@ -4277,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: @@ -4316,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==} @@ -4350,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: @@ -4383,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'} @@ -4394,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 @@ -4408,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'} @@ -4423,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: @@ -4455,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'} @@ -4473,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: @@ -4492,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: @@ -4511,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: @@ -4542,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==} @@ -4581,32 +4811,22 @@ 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==} @@ -4622,8 +4842,12 @@ 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.3: @@ -4632,8 +4856,8 @@ packages: 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==} @@ -4646,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 @@ -4655,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: @@ -4702,8 +4927,8 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} - katex@0.16.11: - resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} + katex@0.16.20: + resolution: {integrity: sha512-jjuLaMGD/7P8jUTpdKhA9IoqnH+yMFB3sdAFtq5QdAqeP2PjiSbnC3EaguKPNtv6dXXanHxp1ckwvF4a86LBig==} hasBin: true keyv@4.5.4: @@ -4717,10 +4942,18 @@ 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'} + 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==} @@ -4732,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: @@ -4758,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: @@ -4854,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==} @@ -4868,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==} @@ -4890,17 +5122,24 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true - markdown-table@3.0.3: - resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + 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==} @@ -4926,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.3: - resolution: {integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==} + 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==} @@ -4944,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==} @@ -4971,6 +5210,10 @@ packages: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} + 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==} @@ -4985,11 +5228,11 @@ 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.1: - resolution: {integrity: sha512-VGV2uxUzhEZmaP7NSFo2vtq7M2nUD+WfmYQD+d8i/1nHbzE+rMy9uzTvUybBbNiVbrhOZibg3gbyoARGqgDWyg==} + micromark-extension-directive@3.0.2: + resolution: {integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==} micromark-extension-frontmatter@2.0.0: resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} @@ -5033,11 +5276,11 @@ 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.2: resolution: {integrity: sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw==} @@ -5045,75 +5288,71 @@ packages: 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==} - - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} + micromark@4.0.1: + resolution: {integrity: sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==} micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} @@ -5152,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'} @@ -5160,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.1: - resolution: {integrity: sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==} + 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 @@ -5172,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'} @@ -5187,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==} @@ -5197,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==} @@ -5218,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 @@ -5230,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==} @@ -5244,27 +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.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + 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==} @@ -5290,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: @@ -5330,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: @@ -5356,8 +5592,9 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - oniguruma-to-js@0.3.3: - resolution: {integrity: sha512-m90/WEhgs8g4BxG37+Nu3YrMfJDs2YXtYtIllhsEPR+wP3+K4EZk6dDUvy2v2K4MNFDDOYKL4/yqYPXDqyozTQ==} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} open@10.1.0: resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} @@ -5383,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} @@ -5391,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'} @@ -5435,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: @@ -5446,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'} @@ -5457,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==} @@ -5484,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==} @@ -5493,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==} @@ -5537,14 +5786,14 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@0.1.10: - resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + 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==} @@ -5557,28 +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.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + 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==} @@ -5596,11 +5849,41 @@ 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: ^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==} @@ -5614,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} @@ -5644,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'} @@ -5651,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} @@ -5699,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 @@ -5717,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} @@ -5771,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} @@ -5795,10 +6190,25 @@ packages: peerDependencies: postcss: ^8.4.31 + 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: resolution: {integrity: sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==} engines: {node: '>=14.0.0'} @@ -5826,20 +6236,16 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.4.45: - resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} + 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.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} engines: {node: '>=14'} hasBin: true @@ -5850,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.4.0: - resolution: {integrity: sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==} + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} peerDependencies: react: '>=16.0.0' @@ -5870,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'} @@ -5894,23 +6296,20 @@ 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.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} - punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -5919,10 +6318,6 @@ packages: resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} engines: {node: '>=12.20'} - qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} - qs@6.13.0: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} @@ -5984,17 +6379,6 @@ 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==} @@ -6060,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: @@ -6077,38 +6477,41 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} - regex@4.3.2: - resolution: {integrity: sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw==} - - 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 @@ -6128,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==} @@ -6143,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'} @@ -6174,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: @@ -6190,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'} @@ -6207,24 +6619,16 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rimraf@5.0.7: - resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} - engines: {node: '>=14.18'} - 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.3.0: resolution: {integrity: sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==} @@ -6245,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: @@ -6255,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: @@ -6280,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==} @@ -6306,20 +6714,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true - semver@7.6.3: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true - send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} - send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} @@ -6327,15 +6726,15 @@ packages: 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.16.0: - resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} set-function-length@1.2.2: @@ -6346,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==} @@ -6367,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@1.16.3: - resolution: {integrity: sha512-GypUE+fEd06FqDs63LSAVlmq7WsahhPQU62cgZxGF+TJT5LjD2k7HTxXj4/CKOVuMM3+wWQ1t4Y5oooeJFRRBQ==} + 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: @@ -6433,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: @@ -6445,10 +6858,6 @@ 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==} - engines: {node: '>=0.10.0'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -6479,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==} @@ -6503,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==} @@ -6514,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'} @@ -6537,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==} @@ -6597,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.7: - resolution: {integrity: sha512-uSjr59G5u6fbxUfKbb8GcqMGT3Xs9v5IbPkjb0S16GyOeBLAzSRK0CixBv5YrYvzO6TDLzIS6QCn78tkqWngPw==} + style-to-object@1.0.8: + resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} stylehacks@6.1.1: resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} @@ -6655,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': '*' @@ -6671,13 +7084,8 @@ packages: uglify-js: optional: true - terser@5.31.1: - resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==} - engines: {node: '>=10'} - hasBin: true - - terser@5.32.0: - resolution: {integrity: sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==} + terser@5.37.0: + resolution: {integrity: sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==} engines: {node: '>=10'} hasBin: true @@ -6711,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==} @@ -6726,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'} @@ -6760,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' @@ -6773,18 +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.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + 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: @@ -6807,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: @@ -6837,56 +7241,52 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typedoc-plugin-markdown@4.2.7: - resolution: {integrity: sha512-bLsQdweSm48P9j6kGqQ3/4GCH5zu2EnURSkkxqirNc+uVFE9YK825ogDw+WbNkRHIV6eZK/1U43gT7YfglyYOg==} + typedoc-plugin-markdown@4.4.1: + resolution: {integrity: sha512-fx23nSCvewI9IR8lzIYtzDphETcgTDuxKcmHKGD4lo36oexC+B1k4NaCOY58Snqb4OlE8OXDAGVcQXYYuLRCNw==} engines: {node: '>= 18'} peerDependencies: - typedoc: 0.26.x + typedoc: 0.27.x - typedoc-plugin-mdn-links@3.2.12: - resolution: {integrity: sha512-UT7JinqYE7IQSrpRPkUkHrXXEJw5qbHIZhVq8igutYDA/4fsrYZhYffVo8Uh70K2iXFdAyvt6foQG5ci1VERAw==} + typedoc-plugin-mdn-links@4.0.8: + resolution: {integrity: sha512-HQqGiO1zIHYgWKPkfutYKqnUeDCdf/3SfSz2mgjQnD4/S7NxqemKZbeAZy5Cp2+1qvyo32sf9bN8SmUTB0f6mA==} peerDependencies: - typedoc: '>= 0.23.14 || 0.24.x || 0.25.x || 0.26.x' + typedoc: 0.26.x || 0.27.x - typedoc@0.26.7: - resolution: {integrity: sha512-gUeI/Wk99vjXXMi8kanwzyhmeFEGv1LTdTQsiyIsmSYsBebvFxhbcyAx7Zjo4cMbpLGxM4Uz3jVIjksu/I2v6Q==} + typedoc@0.27.6: + resolution: {integrity: sha512-oBFRoh2Px6jFx366db0lLlihcalq/JzyCVp7Vaq1yphL/tbgx2e+bkpkCgJPunaPvPwoTOXSwasfklWHm7GfAw==} engines: {node: '>= 18'} hasBin: true 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 || 5.5.x || 5.6.x + 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==} - engines: {node: '>=14.17'} - hasBin: true - - typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + 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==} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} - unicode-canonical-property-names-ecmascript@2.0.0: - resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} unicode-emoji-modifier-base@1.0.0: @@ -6897,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: @@ -6909,6 +7309,10 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -6952,14 +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==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - update-browserslist-db@1.1.0: - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + update-browserslist-db@1.1.2: + resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -6968,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: @@ -7034,8 +7432,8 @@ packages: 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: @@ -7043,6 +7441,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -7055,6 +7454,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -7104,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'} @@ -7140,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.94.0: - resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} + webpack@5.97.1: + resolution: {integrity: sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -7154,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 @@ -7176,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: @@ -7196,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 @@ -7205,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==} @@ -7234,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==} @@ -7252,18 +7673,6 @@ packages: utf-8-validate: optional: true - ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -7302,8 +7711,8 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yaml@2.5.1: - resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} engines: {node: '>= 14'} hasBin: true @@ -7331,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: @@ -7340,1145 +7757,940 @@ packages: snapshots: - '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(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.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0) - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@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.24.0)(algoliasearch@4.24.0)(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.24.0)(algoliasearch@4.24.0) + '@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.24.0)(algoliasearch@4.24.0)': + '@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.24.0)(algoliasearch@4.24.0) - '@algolia/client-search': 4.24.0 - algoliasearch: 4.24.0 + '@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.24.0)(algoliasearch@4.24.0)': + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)': dependencies: - '@algolia/client-search': 4.24.0 - algoliasearch: 4.24.0 + '@algolia/client-search': 5.19.0 + algoliasearch: 5.19.0 - '@algolia/cache-browser-local-storage@4.24.0': + '@algolia/client-abtesting@5.19.0': dependencies: - '@algolia/cache-common': 4.24.0 - - '@algolia/cache-common@4.24.0': {} + '@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-in-memory@4.24.0': + '@algolia/client-analytics@5.19.0': dependencies: - '@algolia/cache-common': 4.24.0 + '@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.24.0': - dependencies: - '@algolia/client-common': 4.24.0 - '@algolia/client-search': 4.24.0 - '@algolia/transporter': 4.24.0 + '@algolia/client-common@5.19.0': {} - '@algolia/client-analytics@4.24.0': + '@algolia/client-insights@5.19.0': dependencies: - '@algolia/client-common': 4.24.0 - '@algolia/client-search': 4.24.0 - '@algolia/requester-common': 4.24.0 - '@algolia/transporter': 4.24.0 + '@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.24.0': + '@algolia/client-personalization@5.19.0': dependencies: - '@algolia/requester-common': 4.24.0 - '@algolia/transporter': 4.24.0 + '@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.24.0': + '@algolia/client-query-suggestions@5.19.0': dependencies: - '@algolia/client-common': 4.24.0 - '@algolia/requester-common': 4.24.0 - '@algolia/transporter': 4.24.0 + '@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.24.0': + '@algolia/client-search@5.19.0': dependencies: - '@algolia/client-common': 4.24.0 - '@algolia/requester-common': 4.24.0 - '@algolia/transporter': 4.24.0 + '@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.24.0': {} - - '@algolia/logger-console@4.24.0': + '@algolia/ingestion@1.19.0': dependencies: - '@algolia/logger-common': 4.24.0 + '@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.24.0': + '@algolia/monitoring@1.19.0': dependencies: - '@algolia/cache-browser-local-storage': 4.24.0 - '@algolia/cache-common': 4.24.0 - '@algolia/cache-in-memory': 4.24.0 - '@algolia/client-common': 4.24.0 - '@algolia/client-search': 4.24.0 - '@algolia/logger-common': 4.24.0 - '@algolia/logger-console': 4.24.0 - '@algolia/requester-browser-xhr': 4.24.0 - '@algolia/requester-common': 4.24.0 - '@algolia/requester-node-http': 4.24.0 - '@algolia/transporter': 4.24.0 + '@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.24.0': + '@algolia/recommend@5.19.0': dependencies: - '@algolia/requester-common': 4.24.0 + '@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.24.0': {} + '@algolia/requester-browser-xhr@5.19.0': + dependencies: + '@algolia/client-common': 5.19.0 - '@algolia/requester-node-http@4.24.0': + '@algolia/requester-fetch@5.19.0': dependencies: - '@algolia/requester-common': 4.24.0 + '@algolia/client-common': 5.19.0 - '@algolia/transporter@4.24.0': + '@algolia/requester-node-http@5.19.0': dependencies: - '@algolia/cache-common': 4.24.0 - '@algolia/logger-common': 4.24.0 - '@algolia/requester-common': 4.24.0 + '@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 - - '@babel/compat-data@7.24.7': {} + picocolors: 1.1.1 - '@babel/compat-data@7.25.4': {} - - '@babel/core@7.24.7': + '@babel/code-frame@7.26.2': 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 - convert-source-map: 2.0.0 - debug: 4.3.5 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 - '@babel/core@7.25.2': + '@babel/compat-data@7.26.5': {} + + '@babel/core@7.26.0': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.6 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) - '@babel/helpers': 7.25.6 - '@babel/parser': 7.25.6 - '@babel/template': 7.25.0 - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 + '@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.7 + 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': - dependencies: - '@babel/types': 7.24.7 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - - '@babel/generator@7.25.6': + '@babel/generator@7.26.5': dependencies: - '@babel/types': 7.25.6 - '@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.25.6 - - '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': - dependencies: - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 - transitivePeerDependencies: - - supports-color + jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.24.7': + '@babel/helper-annotate-as-pure@7.25.9': dependencies: - '@babel/compat-data': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - browserslist: 4.23.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/types': 7.26.5 - '@babel/helper-compilation-targets@7.25.2': + '@babel/helper-compilation-targets@7.26.5': dependencies: - '@babel/compat-data': 7.25.4 - '@babel/helper-validator-option': 7.24.8 - browserslist: 4.23.3 + '@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.25.4(@babel/core@7.25.2)': + '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.8 - '@babel/helper-optimise-call-expression': 7.24.7 - '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/traverse': 7.25.6 + '@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.25.2(@babel/core@7.25.2)': + '@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@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.25.2)': + '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - debug: 4.3.7 + '@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 - transitivePeerDependencies: - - supports-color - - '@babel/helper-environment-visitor@7.24.7': - 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.8': - dependencies: - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.24.7': - dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': - 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 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - '@babel/traverse': 7.25.6 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.24.7': - dependencies: - '@babel/types': 7.25.6 - - '@babel/helper-plugin-utils@7.24.8': {} - - '@babel/helper-remap-async-to-generator@7.25.0(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-wrap-function': 7.25.0 - '@babel/traverse': 7.25.6 + resolve: 1.22.10 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.25.0(@babel/core@7.25.2)': + '@babel/helper-member-expression-to-functions@7.25.9': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-member-expression-to-functions': 7.24.8 - '@babel/helper-optimise-call-expression': 7.24.7 - '@babel/traverse': 7.25.6 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 transitivePeerDependencies: - supports-color - '@babel/helper-simple-access@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-skip-transparent-expression-wrappers@7.24.7': + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 + '@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-split-export-declaration@7.24.7': + '@babel/helper-optimise-call-expression@7.25.9': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.26.5 - '@babel/helper-string-parser@7.24.7': {} + '@babel/helper-plugin-utils@7.26.5': {} - '@babel/helper-string-parser@7.24.8': {} - - '@babel/helper-validator-identifier@7.24.7': {} - - '@babel/helper-validator-option@7.24.7': {} - - '@babel/helper-validator-option@7.24.8': {} - - '@babel/helper-wrap-function@7.25.0': - dependencies: - '@babel/template': 7.25.0 - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 - transitivePeerDependencies: - - supports-color - - '@babel/helpers@7.24.7': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 - - '@babel/helpers@7.25.6': - dependencies: - '@babel/template': 7.25.0 - '@babel/types': 7.25.6 - - '@babel/highlight@7.24.7': - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - 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/parser@7.25.6': - dependencies: - '@babel/types': 7.25.6 - - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.3(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/traverse': 7.25.6 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.0(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.0(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.25.2)': + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.25.2) + '@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/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.0(@babel/core@7.25.2)': + '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/traverse': 7.25.6 + '@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/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 + - supports-color - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.2)': + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-string-parser@7.25.9': {} - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-identifier@7.25.9': {} - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-option@7.25.9': {} - '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.25.2)': + '@babel/helper-wrap-function@7.25.9': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-import-assertions@7.25.6(@babel/core@7.25.2)': + '@babel/helpers@7.26.0': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 - '@babel/plugin-syntax-import-attributes@7.25.6(@babel/core@7.25.2)': + '@babel/highlight@7.24.7': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-identifier': 7.25.9 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.2)': + '@babel/parser@7.26.5': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/types': 7.26.5 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.2)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.5 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.2)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.2)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.2)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.2)': + '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.2)': + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-typescript@7.25.4(@babel/core@7.25.2)': + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.25.2)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-async-generator-functions@7.25.4(@babel/core@7.25.2)': + '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-remap-async-to-generator': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.2) - '@babel/traverse': 7.25.6 + '@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.25.2)': + '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-remap-async-to-generator': 7.25.0(@babel/core@7.25.2) + '@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.25.2)': + '@babel/plugin-transform-block-scoped-functions@7.26.5(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-block-scoping@7.25.0(@babel/core@7.25.2)': + '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-class-properties@7.25.4(@babel/core@7.25.2)': + '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.2) + '@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.25.4(@babel/core@7.25.2)': + '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) - '@babel/traverse': 7.25.6 + '@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.25.2)': + '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/template': 7.25.0 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/template': 7.25.9 - '@babel/plugin-transform-destructuring@7.24.8(@babel/core@7.25.2)': + '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.0(@babel/core@7.25.2)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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-dynamic-import@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - 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.25.2)': + '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@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-function-name@7.25.1(@babel/core@7.25.2)': + '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/traverse': 7.25.6 + '@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.25.2)': + '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-literals@7.25.2(@babel/core@7.25.2)': + '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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.8(@babel/core@7.25.2)': + '@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - '@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.25.0(@babel/core@7.25.2)': + '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - '@babel/traverse': 7.25.6 + '@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.25.2)': + '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-nullish-coalescing-operator@7.26.6(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.2) + '@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.25.2)': + '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) + '@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.25.2)': + '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-optional-chaining@7.24.8(@babel/core@7.25.2)': + '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.2) + '@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.25.2)': + '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-private-methods@7.25.4(@babel/core@7.25.2)': + '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.2) + '@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.25.2)': + '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-constant-elements@7.25.1(@babel/core@7.25.2)': + '@babel/plugin-transform-react-constant-elements@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-react-display-name@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.25.2) + '@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.25.2(@babel/core@7.25.2)': + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) - '@babel/types': 7.25.6 + '@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.25.2)': + '@babel/plugin-transform-react-pure-annotations@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@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.25.2)': + '@babel/plugin-transform-regexp-modifiers@7.26.0(@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-reserved-words@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-runtime@7.25.4(@babel/core@7.25.2)': + '@babel/plugin-transform-runtime@7.25.9(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.2) - babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.25.2) - babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.2) + '@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.25.2)': + '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-spread@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-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.25.2 - '@babel/helper-plugin-utils': 7.24.8 + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-spread@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-transform-typescript@7.26.5(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 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 + '@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-sticky-regex@7.24.7(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-typeof-symbol@7.24.8(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-typescript@7.25.2(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.25.4(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/plugin-syntax-typescript': 7.25.4(@babel/core@7.25.2) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-unicode-sets-regex@7.25.4(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2) - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/preset-env@7.25.4(@babel/core@7.25.2)': - dependencies: - '@babel/compat-data': 7.25.4 - '@babel/core': 7.25.2 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.24.8 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.3(@babel/core@7.25.2) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.2) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.2) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.2) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.25.2) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-import-assertions': 7.25.6(@babel/core@7.25.2) - '@babel/plugin-syntax-import-attributes': 7.25.6(@babel/core@7.25.2) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.25.2) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.2) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.25.2) - '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-async-generator-functions': 7.25.4(@babel/core@7.25.2) - '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-block-scoping': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-transform-class-properties': 7.25.4(@babel/core@7.25.2) - '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-classes': 7.25.4(@babel/core@7.25.2) - '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.25.2) - '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-function-name': 7.25.1(@babel/core@7.25.2) - '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-literals': 7.25.2(@babel/core@7.25.2) - '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) - '@babel/plugin-transform-modules-systemjs': 7.25.0(@babel/core@7.25.2) - '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.25.2) - '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-private-methods': 7.25.4(@babel/core@7.25.2) - '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-typeof-symbol': 7.24.8(@babel/core@7.25.2) - '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-unicode-sets-regex': 7.25.4(@babel/core@7.25.2) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.25.2) - babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.2) - babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.25.2) - babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.2) - core-js-compat: 3.38.1 + + '@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.25.2)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/types': 7.25.6 + '@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.25.2)': + '@babel/preset-react@7.26.3(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.24.8 - '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.25.2) - '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-react-pure-annotations': 7.24.7(@babel/core@7.25.2) + '@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.25.2)': + '@babel/preset-typescript@7.26.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/helper-validator-option': 7.24.8 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) - '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2) + '@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.25.6': - dependencies: - core-js-pure: 3.38.1 - regenerator-runtime: 0.14.1 - - '@babel/runtime@7.24.7': + '@babel/runtime-corejs3@7.26.0': dependencies: + core-js-pure: 3.40.0 regenerator-runtime: 0.14.1 - '@babel/runtime@7.25.6': + '@babel/runtime@7.26.0': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.24.7': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - - '@babel/template@7.25.0': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.25.6 - '@babel/types': 7.25.6 - - '@babel/traverse@7.24.7': + '@babel/template@7.25.9': 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 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 - '@babel/traverse@7.25.6': + '@babel/traverse@7.26.5': dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.6 - '@babel/parser': 7.25.6 - '@babel/template': 7.25.0 - '@babel/types': 7.25.6 - debug: 4.3.7 + '@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': - dependencies: - '@babel/helper-string-parser': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 - - '@babel/types@7.25.6': + '@babel/types@7.26.5': dependencies: - '@babel/helper-string-parser': 7.24.8 - '@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 @@ -8486,147 +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.1': {} + '@docsearch/css@3.8.2': {} - '@docsearch/react@3.6.1(@algolia/client-search@4.24.0)(@types/react@18.3.5)(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.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) - '@docsearch/css': 3.6.1 - algoliasearch: 4.24.0 + '@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.5 + '@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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/generator': 7.25.6 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2) - '@babel/plugin-transform-runtime': 7.25.4(@babel/core@7.25.2) - '@babel/preset-env': 7.25.4(@babel/core@7.25.2) - '@babel/preset-react': 7.24.7(@babel/core@7.25.2) - '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) - '@babel/runtime': 7.25.6 - '@babel/runtime-corejs3': 7.25.6 - '@babel/traverse': 7.25.6 - '@docusaurus/cssnano-preset': 3.5.2 - '@docusaurus/logger': 3.5.2 - '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@mdx-js/react': 3.0.1(@types/react@18.3.5)(react@18.3.1) - autoprefixer: 10.4.20(postcss@8.4.45) - babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.94.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.94.0) - core-js: 3.38.1 - css-loader: 6.11.0(webpack@5.94.0) - css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.94.0) - cssnano: 6.1.2(postcss@8.4.45) + 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.94.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.94.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.1(webpack@5.94.0) p-map: 4.0.0 - postcss: 8.4.45 - postcss-loader: 7.3.4(postcss@8.4.45)(typescript@5.6.2)(webpack@5.94.0) prompts: 2.4.2 react: 18.3.1 - react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.6.2)(webpack@5.94.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.94.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.3 - serve-handler: 6.1.5 + serve-handler: 6.1.6 shelljs: 0.8.5 - terser-webpack-plugin: 5.3.10(webpack@5.94.0) - tslib: 2.7.0 + tslib: 2.8.1 update-notifier: 6.0.2 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.94.0))(webpack@5.94.0) - webpack: 5.94.0 + webpack: 5.97.1 webpack-bundle-analyzer: 4.10.2 - webpack-dev-server: 4.15.2(webpack@5.94.0) - webpack-merge: 5.10.0 - webpackbar: 5.0.2(webpack@5.94.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 @@ -8640,30 +9155,30 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/cssnano-preset@3.5.2': + '@docusaurus/cssnano-preset@3.7.0': dependencies: - cssnano-preset-advanced: 6.1.2(postcss@8.4.45) - postcss: 8.4.45 - postcss-sort-media-queries: 5.2.0(postcss@8.4.45) - tslib: 2.7.0 + 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.5.2': + '@docusaurus/logger@3.7.0': dependencies: chalk: 4.1.2 - tslib: 2.7.0 + tslib: 2.8.1 - '@docusaurus/mdx-loader@3.5.2(@docusaurus/types@3.5.2(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.6.2)': + '@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.5.2 - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@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.2 - file-loader: 6.2.0(webpack@5.94.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 @@ -8674,68 +9189,70 @@ snapshots: remark-frontmatter: 5.0.0 remark-gfm: 4.0.0 stringify-object: 3.3.0 - tslib: 2.7.0 + 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.94.0))(webpack@5.94.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.94.0 + webpack: 5.97.1 transitivePeerDependencies: - - '@docusaurus/types' - '@swc/core' + - acorn - esbuild - supports-color - - typescript - uglify-js - webpack-cli - '@docusaurus/module-type-aliases@3.5.2(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.5.2(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.5 + '@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.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': - dependencies: - '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/logger': 3.5.2 - '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@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.7.0 + tslib: 2.8.1 unist-util-visit: 5.0.0 utility-types: 3.11.0 - webpack: 5.94.0 + webpack: 5.97.1 transitivePeerDependencies: + - '@docusaurus/faster' - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8749,33 +9266,35 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': - dependencies: - '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/logger': 3.5.2 - '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/module-type-aliases': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@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.7.0 + tslib: 2.8.1 utility-types: 3.11.0 - webpack: 5.94.0 + webpack: 5.97.1 transitivePeerDependencies: + - '@docusaurus/faster' - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8789,24 +9308,26 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-content-pages@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - 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.7.0 - webpack: 5.94.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 @@ -8820,22 +9341,24 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-debug@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - 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.5.0(react@18.3.1) - tslib: 2.7.0 + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8849,20 +9372,22 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-analytics@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@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.7.0 + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8876,21 +9401,23 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-gtag@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@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.7.0 + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8904,20 +9431,22 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-google-tag-manager@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) + '@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.7.0 + tslib: 2.8.1 transitivePeerDependencies: + - '@docusaurus/faster' - '@mdx-js/react' - '@parcel/css' - '@rspack/core' - '@swc/core' - '@swc/css' + - acorn - bufferutil - csso - debug @@ -8931,25 +9460,60 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/plugin-sitemap@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/logger': 3.5.2 - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - 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.7.0 + 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 @@ -8963,31 +9527,34 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/preset-classic@3.5.2(@algolia/client-search@4.24.0)(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2)': - dependencies: - '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-content-blog': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-content-pages': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-debug': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-google-analytics': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-google-gtag': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-google-tag-manager': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-sitemap': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/theme-classic': 3.5.2(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/theme-search-algolia': 3.5.2(@algolia/client-search@4.24.0)(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2) - '@docusaurus/types': 3.5.2(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 @@ -9004,44 +9571,47 @@ snapshots: '@docusaurus/react-loadable@6.0.0(react@18.3.1)': dependencies: - '@types/react': 18.3.5 + '@types/react': 19.0.7 react: 18.3.1 - '@docusaurus/theme-classic@3.5.2(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': - dependencies: - '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/mdx-loader': 3.5.2(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/module-type-aliases': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-blog': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/plugin-content-pages': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/theme-translations': 3.5.2 - '@docusaurus/types': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@mdx-js/react': 3.0.1(@types/react@18.3.5)(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.44 + infima: 0.2.0-alpha.45 lodash: 4.17.21 nprogress: 0.2.0 - postcss: 8.4.45 - prism-react-renderer: 2.4.0(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.3.0 - tslib: 2.7.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 @@ -9055,61 +9625,61 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-common@3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2)': + '@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.5.2(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/module-type-aliases': 3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(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.5 + '@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.4.0(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.7.0 + tslib: 2.8.1 utility-types: 3.11.0 transitivePeerDependencies: - - '@docusaurus/types' - '@swc/core' + - acorn - esbuild - supports-color - - typescript - uglify-js - webpack-cli - '@docusaurus/theme-search-algolia@3.5.2(@algolia/client-search@4.24.0)(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0)(typescript@5.6.2)': - dependencies: - '@docsearch/react': 3.6.1(@algolia/client-search@4.24.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.14.0) - '@docusaurus/core': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/logger': 3.5.2 - '@docusaurus/plugin-content-docs': 3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@docusaurus/theme-common': 3.5.2(@docusaurus/plugin-content-docs@3.5.2(@mdx-js/react@3.0.1(@types/react@18.3.5)(react@18.3.1))(eslint@8.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2))(@docusaurus/types@3.5.2(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.6.2) - '@docusaurus/theme-translations': 3.5.2 - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-validation': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - algoliasearch: 4.24.0 - algoliasearch-helper: 3.22.4(algoliasearch@4.24.0) + '@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.7.0 + 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 @@ -9124,85 +9694,95 @@ snapshots: - vue-template-compiler - webpack-cli - '@docusaurus/theme-translations@3.5.2': + '@docusaurus/theme-translations@3.7.0': dependencies: - fs-extra: 11.2.0 - tslib: 2.7.0 + fs-extra: 11.3.0 + tslib: 2.8.1 - '@docusaurus/types@3.5.2(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.5 + '@types/react': 19.0.7 commander: 5.1.0 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.94.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.5.2(@docusaurus/types@3.5.2(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.7.0 - optionalDependencies: - '@docusaurus/types': 3.5.2(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.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': + '@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.5.2 - '@docusaurus/utils': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2) - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - fs-extra: 11.2.0 + '@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.7.0 + tslib: 2.8.1 transitivePeerDependencies: - - '@docusaurus/types' - '@swc/core' + - acorn - esbuild + - react + - react-dom - supports-color - - typescript - uglify-js - webpack-cli - '@docusaurus/utils@3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.6.2)': + '@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.5.2 - '@docusaurus/utils-common': 3.5.2(@docusaurus/types@3.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@svgr/webpack': 8.1.0(typescript@5.6.2) + '@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.94.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.8 prompts: 2.4.2 resolve-pathname: 3.0.0 shelljs: 0.8.5 - tslib: 2.7.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.94.0))(webpack@5.94.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.94.0 - optionalDependencies: - '@docusaurus/types': 3.5.2(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 @@ -9275,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 @@ -9296,11 +9876,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} + '@eslint/js@8.57.1': {} - '@fontsource-variable/onest@5.0.6': {} + '@fontsource-variable/onest@5.1.1': {} - '@fontsource/fira-mono@5.0.15': {} + '@fontsource/fira-mono@5.1.1': {} '@foscia/core@0.12.1': dependencies: @@ -9331,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 @@ -9353,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: @@ -9375,7 +9961,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.5.4 + '@types/node': 22.10.6 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -9385,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 @@ -9403,29 +9997,26 @@ 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.5 unist-util-position-from-estree: 2.0.0 @@ -9433,12 +10024,13 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 transitivePeerDependencies: + - acorn - supports-color - '@mdx-js/react@3.0.1(@types/react@18.3.5)(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.5 + '@types/react': 19.0.7 react: 18.3.1 '@nodelib/fs.scandir@2.1.5': @@ -9453,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': @@ -9461,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: @@ -9490,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 @@ -9502,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': @@ -9512,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 @@ -9525,144 +10119,161 @@ 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/core@1.16.3': + '@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': 9.2.2 + '@shikijs/vscode-textmate': 10.0.1 '@types/hast': 3.0.4 - oniguruma-to-js: 0.3.3 - regex: 4.3.2 - '@shikijs/vscode-textmate@9.2.2': {} + '@shikijs/vscode-textmate@10.0.1': {} '@sideway/address@4.1.5': dependencies: @@ -9682,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.25.2)': + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.25.2)': + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.25.2)': + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.25.2)': + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.25.2)': + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.25.2)': + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.25.2)': + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.25.2)': + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 - '@svgr/babel-preset@8.1.0(@babel/core@7.25.2)': + '@svgr/babel-preset@8.1.0(@babel/core@7.26.0)': dependencies: - '@babel/core': 7.25.2 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.25.2) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.25.2) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.25.2) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.25.2) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.25.2) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.25.2) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.25.2) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.25.2) + '@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.6.2)': + '@svgr/core@8.1.0(typescript@5.7.3)': dependencies: - '@babel/core': 7.25.2 - '@svgr/babel-preset': 8.1.0(@babel/core@7.25.2) + '@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.6.2) + cosmiconfig: 8.3.6(typescript@5.7.3) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -9745,38 +10366,38 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.25.6 + '@babel/types': 7.26.5 entities: 4.5.0 - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.6.2))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.7.3))': dependencies: - '@babel/core': 7.25.2 - '@svgr/babel-preset': 8.1.0(@babel/core@7.25.2) - '@svgr/core': 8.1.0(typescript@5.6.2) + '@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.6.2))(typescript@5.6.2)': + '@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.6.2) - cosmiconfig: 8.3.6(typescript@5.6.2) + '@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.6.2)': + '@svgr/webpack@8.1.0(typescript@5.7.3)': dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-transform-react-constant-elements': 7.25.1(@babel/core@7.25.2) - '@babel/preset-env': 7.25.4(@babel/core@7.25.2) - '@babel/preset-react': 7.24.7(@babel/core@7.25.2) - '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) - '@svgr/core': 8.1.0(typescript@5.6.2) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.6.2)) - '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.6.2))(typescript@5.6.2) + '@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 @@ -9791,52 +10412,71 @@ 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': 22.5.4 + '@types/node': 22.10.6 '@types/bonjour@3.5.13': dependencies: - '@types/node': 22.5.4 + '@types/node': 22.10.6 '@types/connect-history-api-fallback@1.5.4': dependencies: - '@types/express-serve-static-core': 4.19.5 - '@types/node': 22.5.4 + '@types/express-serve-static-core': 5.0.5 + '@types/node': 22.10.6 '@types/connect@3.4.38': dependencies: - '@types/node': 22.5.4 + '@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: '@types/ms': 0.7.34 + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.6 + + '@types/eslint@9.6.1': + dependencies: + '@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.5': + '@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': 22.5.4 - '@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.5 - '@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': {} @@ -9855,7 +10495,7 @@ snapshots: '@types/http-proxy@1.17.15': dependencies: - '@types/node': 22.5.4 + '@types/node': 22.10.6 '@types/istanbul-lib-coverage@2.0.6': {} @@ -9891,17 +10531,17 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.5.4 + '@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@22.5.4': + '@types/node@22.10.6': dependencies: - undici-types: 6.19.8 + undici-types: 6.20.0 '@types/normalize-package-data@2.4.4': {} @@ -9909,34 +10549,31 @@ snapshots: '@types/pluralize@0.0.33': {} - '@types/prismjs@1.26.4': {} - - '@types/prop-types@15.7.12': {} + '@types/prismjs@1.26.5': {} - '@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.5 + '@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.5 + '@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.5 + '@types/react': 19.0.7 - '@types/react@18.3.5': + '@types/react@19.0.7': dependencies: - '@types/prop-types': 15.7.12 csstype: 3.1.3 '@types/resolve@1.20.2': {} @@ -9947,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': 22.5.4 + '@types/node': 22.10.6 '@types/serve-index@1.9.4': dependencies: @@ -9959,20 +10598,20 @@ snapshots: '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.5.4 + '@types/node': 22.10.6 '@types/send': 0.17.4 '@types/sockjs@0.3.36': dependencies: - '@types/node': 22.5.4 + '@types/node': 22.10.6 '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} - '@types/ws@8.5.12': + '@types/ws@8.5.13': dependencies: - '@types/node': 22.5.4 + '@types/node': 22.10.6 '@types/yargs-parser@21.0.3': {} @@ -9980,101 +10619,101 @@ snapshots: 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.32.0))': + '@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.32.0) + vitest: 1.6.0(@types/node@18.19.70)(jsdom@24.1.3)(terser@5.37.0) transitivePeerDependencies: - supports-color @@ -10082,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: @@ -10092,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 @@ -10107,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': {} @@ -10197,39 +10836,21 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-attributes@1.9.5(acorn@8.12.1): - dependencies: - acorn: 8.12.1 - - acorn-jsx@5.3.2(acorn@8.12.0): - dependencies: - acorn: 8.12.0 - - acorn-jsx@5.3.2(acorn@8.12.1): - dependencies: - acorn: 8.12.1 - - acorn-walk@8.3.3: + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: - acorn: 8.12.0 + acorn: 8.14.0 acorn-walk@8.3.4: dependencies: - acorn: 8.12.1 - - acorn@8.12.0: {} + acorn: 8.14.0 - acorn@8.12.1: {} + 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: @@ -10256,13 +10877,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.16.0: - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -10270,28 +10884,26 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch-helper@3.22.4(algoliasearch@4.24.0): + algoliasearch-helper@3.23.0(algoliasearch@5.19.0): dependencies: '@algolia/events': 4.0.1 - algoliasearch: 4.24.0 - - algoliasearch@4.24.0: - dependencies: - '@algolia/cache-browser-local-storage': 4.24.0 - '@algolia/cache-common': 4.24.0 - '@algolia/cache-in-memory': 4.24.0 - '@algolia/client-account': 4.24.0 - '@algolia/client-analytics': 4.24.0 - '@algolia/client-common': 4.24.0 - '@algolia/client-personalization': 4.24.0 - '@algolia/client-search': 4.24.0 - '@algolia/logger-common': 4.24.0 - '@algolia/logger-console': 4.24.0 - '@algolia/recommend': 4.24.0 - '@algolia/requester-browser-xhr': 4.24.0 - '@algolia/requester-common': 4.24.0 - '@algolia/requester-node-http': 4.24.0 - '@algolia/transporter': 4.24.0 + 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: @@ -10340,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: {} @@ -10351,63 +10963,53 @@ 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.9.0: {} @@ -10419,52 +11021,57 @@ snapshots: at-least-node@1.0.0: {} - autoprefixer@10.4.20(postcss@8.4.45): + 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.3 - caniuse-lite: 1.0.30001660 + browserslist: 4.24.4 + caniuse-lite: 1.0.30001692 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.1.0 - postcss: 8.4.45 + 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.25.2)(webpack@5.94.0): + babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.97.1): dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.0 find-cache-dir: 4.0.0 - schema-utils: 4.2.0 - webpack: 5.94.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.25.2): + babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): dependencies: - '@babel/compat-data': 7.25.4 - '@babel/core': 7.25.2 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) + '@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.6(@babel/core@7.25.2): + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0): dependencies: - '@babel/core': 7.25.2 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) - core-js-compat: 3.38.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.25.2): + babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0): dependencies: - '@babel/core': 7.25.2 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.25.2) + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) transitivePeerDependencies: - supports-color @@ -10507,7 +11114,7 @@ snapshots: 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 @@ -10536,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 @@ -10549,19 +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) - - browserslist@4.23.3: - dependencies: - caniuse-lite: 1.0.30001660 - electron-to-chromium: 1.5.18 - node-releases: 2.0.18 - update-browserslist-db: 1.1.0(browserslist@4.23.3) + 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: {} @@ -10570,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 @@ -10594,39 +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.7.0 + 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.3 - caniuse-lite: 1.0.30001660 + 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.30001660: {} + 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 @@ -10634,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: @@ -10649,6 +11267,8 @@ snapshots: chalk@5.3.0: {} + chalk@5.4.1: {} + char-regex@1.0.2: {} character-entities-html4@2.1.0: {} @@ -10672,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: @@ -10700,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 @@ -10716,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 @@ -10812,14 +11438,14 @@ snapshots: dependencies: 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 @@ -10839,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: @@ -10860,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: {} @@ -10919,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: @@ -10958,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.94.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.94.0 + webpack: 5.97.1 - core-js-compat@3.38.1: + core-js-compat@3.40.0: dependencies: - browserslist: 4.23.3 + browserslist: 4.24.4 - core-js-pure@3.38.1: {} + core-js-pure@3.40.0: {} - core-js@3.38.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: @@ -10997,23 +11630,23 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 - cosmiconfig@8.3.6(typescript@5.6.2): + 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.6.2 + 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: @@ -11021,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.45): + css-blank-pseudo@7.0.1(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 - css-loader@6.11.0(webpack@5.94.0): + css-declaration-sorter@7.2.0(postcss@8.5.1): dependencies: - icss-utils: 5.1.0(postcss@8.4.45) - postcss: 8.4.45 - postcss-modules-extract-imports: 3.1.0(postcss@8.4.45) - postcss-modules-local-by-default: 4.0.5(postcss@8.4.45) - postcss-modules-scope: 3.2.0(postcss@8.4.45) - postcss-modules-values: 4.0.0(postcss@8.4.45) + postcss: 8.5.1 + + css-has-pseudo@7.0.2(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 + postcss-value-parser: 4.2.0 + + css-loader@6.11.0(webpack@5.97.1): + dependencies: + 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.3 optionalDependencies: - webpack: 5.94.0 + webpack: 5.97.1 - css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.94.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.45) + cssnano: 6.1.2(postcss@8.5.1) jest-worker: 29.7.0 - postcss: 8.4.45 - schema-utils: 4.2.0 + postcss: 8.5.1 + schema-utils: 4.3.0 serialize-javascript: 6.0.2 - webpack: 5.94.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 @@ -11067,7 +11722,7 @@ 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: @@ -11082,105 +11737,106 @@ snapshots: css-what@6.1.0: {} + cssdb@8.2.3: {} + cssesc@3.0.0: {} - cssnano-preset-advanced@6.1.2(postcss@8.4.45): - dependencies: - autoprefixer: 10.4.20(postcss@8.4.45) - browserslist: 4.23.3 - cssnano-preset-default: 6.1.2(postcss@8.4.45) - postcss: 8.4.45 - postcss-discard-unused: 6.0.5(postcss@8.4.45) - postcss-merge-idents: 6.0.3(postcss@8.4.45) - postcss-reduce-idents: 6.0.3(postcss@8.4.45) - postcss-zindex: 6.0.2(postcss@8.4.45) - - cssnano-preset-default@6.1.2(postcss@8.4.45): - dependencies: - browserslist: 4.23.3 - css-declaration-sorter: 7.2.0(postcss@8.4.45) - cssnano-utils: 4.0.2(postcss@8.4.45) - postcss: 8.4.45 - postcss-calc: 9.0.1(postcss@8.4.45) - postcss-colormin: 6.1.0(postcss@8.4.45) - postcss-convert-values: 6.1.0(postcss@8.4.45) - postcss-discard-comments: 6.0.2(postcss@8.4.45) - postcss-discard-duplicates: 6.0.3(postcss@8.4.45) - postcss-discard-empty: 6.0.3(postcss@8.4.45) - postcss-discard-overridden: 6.0.2(postcss@8.4.45) - postcss-merge-longhand: 6.0.5(postcss@8.4.45) - postcss-merge-rules: 6.1.1(postcss@8.4.45) - postcss-minify-font-values: 6.1.0(postcss@8.4.45) - postcss-minify-gradients: 6.0.3(postcss@8.4.45) - postcss-minify-params: 6.1.0(postcss@8.4.45) - postcss-minify-selectors: 6.0.4(postcss@8.4.45) - postcss-normalize-charset: 6.0.2(postcss@8.4.45) - postcss-normalize-display-values: 6.0.2(postcss@8.4.45) - postcss-normalize-positions: 6.0.2(postcss@8.4.45) - postcss-normalize-repeat-style: 6.0.2(postcss@8.4.45) - postcss-normalize-string: 6.0.2(postcss@8.4.45) - postcss-normalize-timing-functions: 6.0.2(postcss@8.4.45) - postcss-normalize-unicode: 6.1.0(postcss@8.4.45) - postcss-normalize-url: 6.0.2(postcss@8.4.45) - postcss-normalize-whitespace: 6.0.2(postcss@8.4.45) - postcss-ordered-values: 6.0.2(postcss@8.4.45) - postcss-reduce-initial: 6.1.0(postcss@8.4.45) - postcss-reduce-transforms: 6.0.2(postcss@8.4.45) - postcss-svgo: 6.0.3(postcss@8.4.45) - postcss-unique-selectors: 6.0.4(postcss@8.4.45) - - cssnano-utils@4.0.2(postcss@8.4.45): - dependencies: - postcss: 8.4.45 - - cssnano@6.1.2(postcss@8.4.45): - dependencies: - cssnano-preset-default: 6.1.2(postcss@8.4.45) - lilconfig: 3.1.2 - postcss: 8.4.45 + 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: {} @@ -11192,11 +11848,7 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.5: - dependencies: - ms: 2.1.2 - - debug@4.3.7: + debug@4.4.0: dependencies: ms: 2.1.3 @@ -11212,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: {} @@ -11239,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: {} @@ -11296,7 +11948,7 @@ snapshots: detect-port@1.6.1: dependencies: address: 1.2.2 - debug: 4.3.7 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -11324,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.1 - chalk: 5.3.0 + acorn: 8.14.0 + chalk: 5.4.1 commander: 11.1.0 globby: 13.2.2 lodash: 4.17.21 @@ -11339,9 +11991,9 @@ snapshots: transitivePeerDependencies: - supports-color - docusaurus-plugin-typedoc@1.0.5(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2))): + 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.2.7(typedoc@0.26.7(typescript@5.6.2)) + typedoc-plugin-markdown: 4.4.1(typedoc@0.27.6(typescript@5.7.3)) dom-converter@0.2.0: dependencies: @@ -11375,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 @@ -11384,7 +12036,7 @@ snapshots: dot-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.7.0 + tslib: 2.8.1 dot-prop@5.3.0: dependencies: @@ -11394,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: {} @@ -11402,12 +12064,12 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.4.803: {} - - electron-to-chromium@1.5.18: {} + 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: {} @@ -11422,12 +12084,7 @@ snapshots: encodeurl@2.0.0: {} - enhanced-resolve@5.17.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - - enhanced-resolve@5.17.1: + enhanced-resolve@5.18.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -11447,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.4: {} + 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 @@ -11532,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: @@ -11586,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 @@ -11679,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 @@ -11706,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 @@ -11724,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 @@ -11744,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: @@ -11755,15 +12417,20 @@ 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.9.0 source-map: 0.7.4 - estree-util-value-to-estree@3.1.2: + estree-util-value-to-estree@3.2.1: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 estree-util-visit@2.0.0: dependencies: @@ -11784,7 +12451,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 22.5.4 + '@types/node': 22.10.6 require-like: 0.1.2 eventemitter3@4.0.7: {} @@ -11793,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 @@ -11803,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 @@ -11815,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.20.0: + express@4.21.2: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 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: 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.3 methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 - path-to-regexp: 0.1.10 + 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.19.0 - serve-static: 1.16.0 + serve-static: 1.16.2 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -11880,13 +12559,13 @@ 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: {} @@ -11894,10 +12573,6 @@ snapshots: fast-uri@3.0.1: {} - fast-url-parser@1.1.3: - dependencies: - punycode: 1.4.1 - fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -11914,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.94.0): + file-loader@6.2.0(webpack@5.97.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.94.0 + webpack: 5.97.1 filesize@8.0.7: {} @@ -11939,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 @@ -11978,13 +12652,13 @@ 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.9: {} @@ -11992,19 +12666,14 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.2.1: - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 - 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.6.2)(webpack@5.94.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 @@ -12017,14 +12686,14 @@ snapshots: schema-utils: 2.7.0 semver: 7.6.3 tapable: 1.1.3 - typescript: 5.6.2 - webpack: 5.94.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 @@ -12032,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 @@ -12064,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: {} @@ -12079,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: {} @@ -12100,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 @@ -12128,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: @@ -12151,23 +12837,6 @@ 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: - dependencies: - foreground-child: 3.2.1 - jackspeak: 3.4.0 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.0 - path-scurry: 1.11.1 - glob@10.4.5: dependencies: foreground-child: 3.3.0 @@ -12213,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 + 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: @@ -12259,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: {} @@ -12299,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: {} @@ -12309,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: {} @@ -12325,12 +12980,12 @@ 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.3 devlop: 1.1.0 - hastscript: 8.0.0 + hastscript: 9.0.0 property-information: 6.5.0 vfile: 6.0.3 vfile-location: 5.0.3 @@ -12340,25 +12995,25 @@ snapshots: 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.3 - '@ungap/structured-clone': 1.2.0 - hast-util-from-parse5: 8.0.1 + '@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.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 @@ -12366,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.3 + 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.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.3 + 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.7 + style-to-object: 1.0.8 unist-util-position: 5.0.0 vfile-message: 4.0.2 transitivePeerDependencies: @@ -12411,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 @@ -12425,7 +13080,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -12438,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: @@ -12463,7 +13118,7 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.32.0 + terser: 5.37.0 html-minifier-terser@7.2.0: dependencies: @@ -12473,13 +13128,13 @@ snapshots: entities: 4.5.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.32.0 + terser: 5.37.0 html-tags@3.3.1: {} html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.0(webpack@5.94.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 @@ -12487,7 +13142,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.2.1 optionalDependencies: - webpack: 5.94.0 + webpack: 5.97.1 htmlparser2@6.1.0: dependencies: @@ -12500,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: {} @@ -12522,16 +13177,16 @@ 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.15 http-proxy: 1.18.1 @@ -12556,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 @@ -12567,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: @@ -12579,17 +13234,15 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.4.45): + icss-utils@5.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.45 + 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 @@ -12608,7 +13261,7 @@ snapshots: indent-string@4.0.0: {} - infima@0.2.0-alpha.44: {} + infima@0.2.0-alpha.45: {} inflight@1.0.6: dependencies: @@ -12627,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 @@ -12648,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: {} @@ -12677,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: {} @@ -12712,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: {} @@ -12734,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: @@ -12753,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: {} @@ -12761,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: {} @@ -12779,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: {} @@ -12791,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: {} @@ -12808,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: @@ -12822,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: {} @@ -12846,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: @@ -12880,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 @@ -12896,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 @@ -12909,19 +13594,6 @@ 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: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -12931,7 +13603,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.5.4 + '@types/node': 22.10.6 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -12939,18 +13611,20 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.5.4 + '@types/node': 22.10.6 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.5.4 + '@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.3: dependencies: @@ -12962,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: @@ -12975,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 @@ -12995,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: {} @@ -13035,7 +13709,7 @@ snapshots: jsonparse@1.3.1: {} - katex@0.16.11: + katex@0.16.20: dependencies: commander: 8.3.0 @@ -13047,14 +13721,20 @@ snapshots: kleur@3.0.3: {} + ky@1.7.4: {} + latest-version@7.0.0: dependencies: package-json: 8.1.1 + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + launch-editor@2.9.1: dependencies: - picocolors: 1.1.0 - shell-quote: 1.8.1 + picocolors: 1.1.1 + shell-quote: 1.8.2 leven@3.1.0: {} @@ -13063,7 +13743,7 @@ 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: {} @@ -13083,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: @@ -13157,13 +13837,13 @@ snapshots: lower-case@2.0.2: dependencies: - tslib: 2.7.0 + 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: @@ -13173,21 +13853,21 @@ 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: {} @@ -13200,41 +13880,47 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - markdown-table@3.0.3: {} + markdown-table@2.0.0: + dependencies: + repeat-string: 1.6.1 + + markdown-table@3.0.4: {} + + math-intrinsics@1.1.0: {} mdast-util-directive@3.0.0: dependencies: '@types/mdast': 4.0.4 '@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.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 @@ -13244,8 +13930,8 @@ 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 @@ -13255,24 +13941,24 @@ snapshots: '@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 @@ -13280,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 @@ -13290,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-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 @@ -13313,24 +13999,24 @@ 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.3: + mdast-util-mdx-jsx@3.2.0: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 @@ -13338,9 +14024,9 @@ snapshots: '@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-stringify-position: 4.0.0 vfile-message: 4.0.2 @@ -13349,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.3 + 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 @@ -13363,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 @@ -13377,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.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.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 @@ -13414,6 +14101,8 @@ snapshots: meow@12.1.1: {} + meow@13.2.0: {} + merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -13422,88 +14111,88 @@ 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.1: + 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.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.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.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.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.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: @@ -13513,93 +14202,93 @@ snapshots: micromark-extension-gfm-table: 2.1.0 micromark-extension-gfm-tagfilter: 2.0.0 micromark-extension-gfm-task-list-item: 2.1.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-extension-math@3.1.0: dependencies: '@types/katex': 0.16.7 devlop: 1.1.0 - katex: 0.16.11 - 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.2 - micromark-factory-space: 2.0.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 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.2 - micromark-factory-space: 2.0.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 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.1 - acorn-jsx: 5.3.2(acorn@8.12.1) + 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.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.2: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 devlop: 1.1.0 - micromark-factory-space: 2.0.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 @@ -13608,132 +14297,127 @@ 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/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.7 + 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: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -13759,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.1(webpack@5.94.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.94.0 + webpack: 5.97.1 minimalistic-assert@1.0.1: {} @@ -13775,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 @@ -13787,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: @@ -13817,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: {} @@ -13834,33 +14516,23 @@ snapshots: no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.7.0 + 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.18: {} + 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: {} @@ -13877,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 + + nwsapi@2.2.16: {} object-assign@4.1.1: {} - object-inspect@1.13.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: {} @@ -13945,7 +14629,9 @@ snapshots: dependencies: mimic-fn: 4.0.0 - oniguruma-to-js@0.3.3: {} + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 open@10.1.0: dependencies: @@ -13995,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: @@ -14018,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: @@ -14043,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 @@ -14063,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.7.0 + 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.11 - character-entities: 2.0.2 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 decode-named-character-reference: 1.0.2 @@ -14099,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 @@ -14121,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 @@ -14139,7 +14849,7 @@ snapshots: pascal-case@3.1.2: dependencies: no-case: 3.0.4 - tslib: 2.7.0 + tslib: 2.8.1 path-exists@3.0.0: {} @@ -14162,13 +14872,13 @@ snapshots: lru-cache: 10.2.2 minipass: 7.1.2 - path-to-regexp@0.1.10: {} + 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: {} @@ -14176,29 +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.0: {} + 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: @@ -14212,231 +14924,442 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-calc@9.0.1(postcss@8.4.45): + 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.4.45 + postcss: 8.5.1 postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - postcss-colormin@6.1.0(postcss@8.4.45): + 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: - browserslist: 4.23.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 + '@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: + '@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.5.1): + dependencies: + browserslist: 4.24.4 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-convert-values@6.1.0(postcss@8.4.45): + postcss-convert-values@6.1.0(postcss@8.5.1): dependencies: - browserslist: 4.23.3 - postcss: 8.4.45 + browserslist: 4.24.4 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-discard-comments@6.0.2(postcss@8.4.45): + 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-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: - postcss: 8.4.45 + postcss: 8.5.1 - postcss-discard-duplicates@6.0.3(postcss@8.4.45): + postcss-discard-duplicates@6.0.3(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 - postcss-discard-empty@6.0.3(postcss@8.4.45): + postcss-discard-empty@6.0.3(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 - postcss-discard-overridden@6.0.2(postcss@8.4.45): + postcss-discard-overridden@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 - postcss-discard-unused@6.0.5(postcss@8.4.45): + postcss-discard-unused@6.0.5(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-selector-parser: 6.1.2 - postcss-loader@7.3.4(postcss@8.4.45)(typescript@5.6.2)(webpack@5.94.0): + 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-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.5.1 + postcss-selector-parser: 7.0.0 + + postcss-font-variant@5.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-gap-properties@6.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-image-set-function@7.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-lab-function@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-loader@7.3.4(postcss@8.5.1)(typescript@5.7.3)(webpack@5.97.1): dependencies: - cosmiconfig: 8.3.6(typescript@5.6.2) - jiti: 1.21.6 - postcss: 8.4.45 + cosmiconfig: 8.3.6(typescript@5.7.3) + jiti: 1.21.7 + postcss: 8.5.1 semver: 7.6.3 - webpack: 5.94.0 + webpack: 5.97.1 transitivePeerDependencies: - typescript - postcss-merge-idents@6.0.3(postcss@8.4.45): + 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.45) - postcss: 8.4.45 + 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.45): + postcss-merge-longhand@6.0.5(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - stylehacks: 6.1.1(postcss@8.4.45) + stylehacks: 6.1.1(postcss@8.5.1) - postcss-merge-rules@6.1.1(postcss@8.4.45): + postcss-merge-rules@6.1.1(postcss@8.5.1): dependencies: - browserslist: 4.23.3 + browserslist: 4.24.4 caniuse-api: 3.0.0 - cssnano-utils: 4.0.2(postcss@8.4.45) - postcss: 8.4.45 + 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.45): + postcss-minify-font-values@6.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-minify-gradients@6.0.3(postcss@8.4.45): + postcss-minify-gradients@6.0.3(postcss@8.5.1): dependencies: colord: 2.9.3 - cssnano-utils: 4.0.2(postcss@8.4.45) - postcss: 8.4.45 + 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.45): + postcss-minify-params@6.1.0(postcss@8.5.1): dependencies: - browserslist: 4.23.3 - cssnano-utils: 4.0.2(postcss@8.4.45) - postcss: 8.4.45 + 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.45): + postcss-minify-selectors@6.0.4(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-selector-parser: 6.1.2 - postcss-modules-extract-imports@3.1.0(postcss@8.4.45): + postcss-modules-extract-imports@3.1.0(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 - postcss-modules-local-by-default@4.0.5(postcss@8.4.45): + postcss-modules-local-by-default@4.2.0(postcss@8.5.1): dependencies: - icss-utils: 5.1.0(postcss@8.4.45) - postcss: 8.4.45 - postcss-selector-parser: 6.1.2 + 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.45): + postcss-modules-scope@3.2.1(postcss@8.5.1): dependencies: - postcss: 8.4.45 - postcss-selector-parser: 6.1.2 + postcss: 8.5.1 + postcss-selector-parser: 7.0.0 + + postcss-modules-values@4.0.0(postcss@8.5.1): + dependencies: + icss-utils: 5.1.0(postcss@8.5.1) + postcss: 8.5.1 - postcss-modules-values@4.0.0(postcss@8.4.45): + postcss-nesting@13.0.1(postcss@8.5.1): dependencies: - icss-utils: 5.1.0(postcss@8.4.45) - postcss: 8.4.45 + '@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-charset@6.0.2(postcss@8.4.45): + postcss-normalize-charset@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 - postcss-normalize-display-values@6.0.2(postcss@8.4.45): + postcss-normalize-display-values@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-positions@6.0.2(postcss@8.4.45): + postcss-normalize-positions@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@6.0.2(postcss@8.4.45): + postcss-normalize-repeat-style@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-string@6.0.2(postcss@8.4.45): + postcss-normalize-string@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@6.0.2(postcss@8.4.45): + postcss-normalize-timing-functions@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@6.1.0(postcss@8.4.45): + postcss-normalize-unicode@6.1.0(postcss@8.5.1): dependencies: - browserslist: 4.23.3 - postcss: 8.4.45 + browserslist: 4.24.4 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-url@6.0.2(postcss@8.4.45): + postcss-normalize-url@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@6.0.2(postcss@8.4.45): + postcss-normalize-whitespace@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-ordered-values@6.0.2(postcss@8.4.45): + postcss-opacity-percentage@3.0.0(postcss@8.5.1): dependencies: - cssnano-utils: 4.0.2(postcss@8.4.45) - postcss: 8.4.45 + postcss: 8.5.1 + + postcss-ordered-values@6.0.2(postcss@8.5.1): + dependencies: + cssnano-utils: 4.0.2(postcss@8.5.1) + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + postcss-overflow-shorthand@6.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 postcss-value-parser: 4.2.0 - postcss-reduce-idents@6.0.3(postcss@8.4.45): + 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.45 + 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.45): + postcss-reduce-initial@6.1.0(postcss@8.5.1): dependencies: - browserslist: 4.23.3 + browserslist: 4.24.4 caniuse-api: 3.0.0 - postcss: 8.4.45 + postcss: 8.5.1 - postcss-reduce-transforms@6.0.2(postcss@8.4.45): + postcss-reduce-transforms@6.0.2(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.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-sort-media-queries@5.2.0(postcss@8.4.45): + 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.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 sort-css-media-queries: 2.2.0 - postcss-svgo@6.0.3(postcss@8.4.45): + postcss-svgo@6.0.3(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-value-parser: 4.2.0 svgo: 3.3.2 - postcss-unique-selectors@6.0.4(postcss@8.4.45): + postcss-unique-selectors@6.0.4(postcss@8.5.1): dependencies: - postcss: 8.4.45 + postcss: 8.5.1 postcss-selector-parser: 6.1.2 postcss-value-parser@4.2.0: {} - postcss-zindex@6.0.2(postcss@8.4.45): - dependencies: - postcss: 8.4.45 - - postcss@8.4.38: + postcss-zindex@6.0.2(postcss@8.5.1): dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + postcss: 8.5.1 - postcss@8.4.45: + postcss@8.5.1: dependencies: - nanoid: 3.3.7 - picocolors: 1.1.0 + nanoid: 3.3.8 + picocolors: 1.1.1 source-map-js: 1.2.1 prelude-ls@1.2.1: {} - prettier@3.3.3: {} + prettier@3.4.2: {} pretty-error@4.0.0: dependencies: @@ -14449,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.4.0(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 @@ -14465,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 @@ -14496,40 +15410,36 @@ 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.js@2.3.1: {} - punycode@1.4.1: {} - punycode@2.3.1: {} pupa@3.1.0: dependencies: escape-goat: 4.0.0 - qs@6.11.0: - dependencies: - side-channel: 1.0.6 - qs@6.13.0: dependencies: - side-channel: 1.0.6 + side-channel: 1.1.0 querystringify@2.2.0: {} @@ -14565,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.6.2)(webpack@5.94.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.3 + 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.6.2)(webpack@5.94.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 @@ -14588,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.94.0 + webpack: 5.97.1 optionalDependencies: - typescript: 5.6.2 + typescript: 5.7.3 transitivePeerDependencies: - eslint - supports-color @@ -14609,23 +15519,6 @@ 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.25.6 - 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: {} @@ -14634,21 +15527,21 @@ snapshots: 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.94.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.25.6 + '@babel/runtime': 7.26.0 react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' - webpack: 5.94.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.25.6 + '@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.25.6 + '@babel/runtime': 7.26.0 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -14659,11 +15552,11 @@ snapshots: react-router@5.3.4(react@18.3.1): dependencies: - '@babel/runtime': 7.25.6 + '@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 @@ -14678,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: @@ -14711,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 @@ -14727,74 +15661,81 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.25.6 - - regex@4.3.2: {} + '@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 + 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 @@ -14804,7 +15745,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 mdast-util-directive: 3.0.0 - micromark-extension-directive: 3.0.1 + micromark-extension-directive: 3.0.2 unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -14813,8 +15754,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 emoticon: 4.1.0 - mdast-util-find-and-replace: 3.0.1 - node-emoji: 2.1.3 + mdast-util-find-and-replace: 3.0.2 + node-emoji: 2.2.0 unified: 11.0.5 remark-frontmatter@5.0.0: @@ -14846,7 +15787,7 @@ snapshots: 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 @@ -14856,13 +15797,13 @@ 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 + 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 @@ -14873,7 +15814,7 @@ snapshots: remark-stringify@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-to-markdown: 2.1.0 + mdast-util-to-markdown: 2.1.2 unified: 11.0.5 renderkid@3.0.0: @@ -14884,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: {} @@ -14902,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 @@ -14922,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: {} @@ -14934,43 +15882,40 @@ snapshots: dependencies: glob: 10.4.5 - rimraf@5.0.7: + rollup@4.30.1: dependencies: - glob: 10.4.1 - - rollup@4.18.0: - 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.3.0: dependencies: escalade: 3.2.0 - picocolors: 1.1.0 - postcss: 8.4.45 + picocolors: 1.1.1 + postcss: 8.5.1 strip-json-comments: 3.1.1 run-applescript@7.0.0: {} @@ -14983,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: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: dependencies: - call-bind: 1.0.7 + call-bound: 1.0.3 es-errors: 1.3.0 - is-regex: 1.1.4 + is-regex: 1.2.1 safer-buffer@2.1.2: {} @@ -15026,7 +15977,7 @@ 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.17.1 @@ -15049,32 +16000,12 @@ 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: - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - send@0.19.0: dependencies: debug: 2.6.9 @@ -15097,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: @@ -15120,12 +16050,12 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.16.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 @@ -15134,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: @@ -15145,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: {} @@ -15161,7 +16097,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.1: {} + shell-quote@1.8.2: {} shelljs@0.8.5: dependencies: @@ -15169,18 +16105,33 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 - shiki@1.16.3: + side-channel-list@1.0.0: dependencies: - '@shikijs/core': 1.16.3 - '@shikijs/vscode-textmate': 9.2.2 - '@types/hast': 3.0.4 + 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-weakmap@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + object-inspect: 1.13.3 + side-channel-map: 1.0.1 - side-channel@1.0.6: + side-channel@1.1.0: dependencies: - call-bind: 1.0.7 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + 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: {} @@ -15190,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 @@ -15220,7 +16171,7 @@ snapshots: snake-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.7.0 + tslib: 2.8.1 sockjs@0.3.24: dependencies: @@ -15228,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 @@ -15243,8 +16194,6 @@ 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: @@ -15263,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.7 + debug: 4.4.0 detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -15287,7 +16236,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.3.7 + debug: 4.4.0 handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -15303,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 @@ -15335,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: @@ -15395,22 +16352,20 @@ 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.7: + 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.45): + stylehacks@6.1.1(postcss@8.5.1): dependencies: - browserslist: 4.23.3 - postcss: 8.4.45 + browserslist: 4.24.4 + postcss: 8.5.1 postcss-selector-parser: 6.1.2 supports-color@5.5.0: @@ -15442,7 +16397,7 @@ snapshots: css-tree: 2.3.1 css-what: 6.1.0 csso: 5.0.5 - picocolors: 1.1.0 + picocolors: 1.1.1 symbol-tree@3.2.4: {} @@ -15455,26 +16410,19 @@ snapshots: ansi-escapes: 5.0.0 supports-hyperlinks: 2.3.0 - terser-webpack-plugin@5.3.10(webpack@5.94.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.32.0 - webpack: 5.94.0 - - terser@5.31.1: - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.12.0 - commander: 2.20.3 - source-map-support: 0.5.21 + terser: 5.37.0 + webpack: 5.97.1 - terser@5.32.0: + terser@5.37.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.12.1 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -15504,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: {} @@ -15514,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 @@ -15526,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 @@ -15541,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: @@ -15561,15 +16509,13 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tslib@2.6.3: {} - - tslib@2.7.0: {} + 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: {} @@ -15581,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: @@ -15626,60 +16573,60 @@ snapshots: typedarray@0.0.6: {} - typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2)): + typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.7.3)): dependencies: - typedoc: 0.26.7(typescript@5.6.2) + typedoc: 0.27.6(typescript@5.7.3) - typedoc-plugin-mdn-links@3.2.12(typedoc@0.26.7(typescript@5.6.2)): + typedoc-plugin-mdn-links@4.0.8(typedoc@0.27.6(typescript@5.7.3)): dependencies: - typedoc: 0.26.7(typescript@5.6.2) + typedoc: 0.27.6(typescript@5.7.3) - typedoc@0.26.7(typescript@5.6.2): + typedoc@0.27.6(typescript@5.7.3): dependencies: + '@gerrit0/mini-shiki': 1.27.0 lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 - shiki: 1.16.3 - typescript: 5.6.2 - yaml: 2.5.1 + typescript: 5.7.3 + yaml: 2.7.0 - typescript@5.5.2: {} - - typescript@5.6.2: {} + typescript@5.7.3: {} 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: {} - undici-types@6.19.8: {} + undici-types@6.20.0: {} - unicode-canonical-property-names-ecmascript@2.0.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: {} + unicorn-magic@0.3.0: {} + unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -15734,22 +16681,16 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.0.16(browserslist@4.23.1): - dependencies: - browserslist: 4.23.1 - escalade: 3.1.2 - picocolors: 1.0.1 - - update-browserslist-db@1.1.0(browserslist@4.23.3): + update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: - browserslist: 4.23.3 + browserslist: 4.24.4 escalade: 3.2.0 - picocolors: 1.1.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 @@ -15763,19 +16704,17 @@ snapshots: 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: @@ -15784,14 +16723,14 @@ snapshots: url-join@5.0.0: {} - url-loader@4.1.1(file-loader@6.2.0(webpack@5.94.0))(webpack@5.94.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.94.0 + webpack: 5.97.1 optionalDependencies: - file-loader: 6.2.0(webpack@5.94.0) + file-loader: 6.2.0(webpack@5.97.1) url-parse@1.5.10: dependencies: @@ -15832,62 +16771,64 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@1.6.0(@types/node@18.19.39)(terser@5.32.0): + 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.32.0) + 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.32.0): + 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.32.0 + terser: 5.37.0 - vitest@1.6.0(@types/node@18.19.39)(jsdom@24.1.0)(terser@5.32.0): + 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.32.0) - vite-node: 1.6.0(@types/node@18.19.39)(terser@5.32.0) - 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 @@ -15912,14 +16853,12 @@ 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.1 + acorn: 8.14.0 acorn-walk: 8.3.4 commander: 7.2.0 debounce: 1.2.1 @@ -15927,23 +16866,23 @@ snapshots: gzip-size: 6.0.0 html-escaper: 2.0.2 opener: 1.5.2 - picocolors: 1.1.0 + 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.94.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.94.0 + schema-utils: 4.3.0 + webpack: 5.97.1 - webpack-dev-server@4.15.2(webpack@5.94.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 @@ -15951,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.12 + '@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.20.0 + 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.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.94.0) + webpack-dev-middleware: 5.3.4(webpack@5.97.1) ws: 8.18.0 optionalDependencies: - webpack: 5.94.0 + webpack: 5.97.1 transitivePeerDependencies: - bufferutil - debug @@ -15989,20 +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.94.0: + webpack@5.97.1: dependencies: - '@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.1 - acorn-import-attributes: 1.9.5(acorn@8.12.1) - browserslist: 4.23.3 + '@types/eslint-scope': 3.7.7 + '@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.1 - es-module-lexer: 1.5.4 + 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 @@ -16013,7 +16958,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(webpack@5.94.0) + terser-webpack-plugin: 5.3.11(webpack@5.97.1) watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -16021,17 +16966,21 @@ snapshots: - esbuild - uglify-js - webpackbar@5.0.2(webpack@5.94.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.94.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 @@ -16043,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: + 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-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 + 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: @@ -16072,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 @@ -16081,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: {} @@ -16111,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: @@ -16122,8 +17107,6 @@ snapshots: ws@7.5.10: {} - ws@8.17.1: {} - ws@8.18.0: {} xdg-basedir@5.1.0: {} @@ -16142,7 +17125,7 @@ snapshots: yaml@1.10.2: {} - yaml@2.5.1: {} + yaml@2.7.0: {} yargs-parser@20.2.9: {} @@ -16161,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 @@ -16172,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/website/package.json b/website/package.json index 8780f5c2..23c0821f 100644 --- a/website/package.json +++ b/website/package.json @@ -16,27 +16,27 @@ "preinstall": "npx only-allow pnpm" }, "dependencies": { - "@docusaurus/core": "^3.5.2", - "@docusaurus/preset-classic": "^3.5.2", - "@docusaurus/theme-common": "^3.5.2", - "@fontsource-variable/onest": "^5.0.6", - "@fontsource/fira-mono": "^5.0.15", - "@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.5", - "dotenv": "^16.4.5", - "prism-react-renderer": "^2.4.0", + "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.26.7", - "typedoc-plugin-markdown": "^4.2.7", - "typedoc-plugin-mdn-links": "^3.2.12", - "typescript": "^5.6.2" + "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.5.2", + "@docusaurus/module-type-aliases": "^3.7.0", "docusaurus-mdx-checker": "^3.0.0", - "prettier": "^3.3.3", + "prettier": "^3.4.2", "rimraf": "^5.0.10" }, "browserslist": { From 7d122333b191c07b34b97c83aa0adf879d6c5c2b Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 16 Jan 2025 01:15:59 +0100 Subject: [PATCH 19/32] docs: fix broken links --- website/docs/digging-deeper/implementations/core.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/digging-deeper/implementations/core.md b/website/docs/digging-deeper/implementations/core.md index 5c5f80c9..815622d0 100644 --- a/website/docs/digging-deeper/implementations/core.md +++ b/website/docs/digging-deeper/implementations/core.md @@ -67,7 +67,7 @@ Foscia proposes two implementations of 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. -- [`makeTimeoutRefFactory`](/docs/api/@foscia/core/functions/makeTimeoutRefFactory), +- [`makeTimedRefFactory`](/docs/api/@foscia/core/functions/makeTimedRefFactory), which will store every instance in a special object which expires after a configured timeout. @@ -83,7 +83,7 @@ import { makeRefsCache, makeWeakRefManager } from '@foscia/core'; const { cache } = makeRefsCache({ manager: makeWeakRefManager(), // or... - // manager: makeTimeoutRefManager({ lifetime: 5 * 60 * 1000 }), + // manager: makeTimedRefFactory({ lifetime: 5 * 60 * 1000 }), }); cache.put('posts', '1', post); From 19724046ed74a65aec5f0412316e2cb7074132b3 Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 18 Jan 2025 22:57:01 +0100 Subject: [PATCH 20/32] chore: change param name in `replaceActionMiddlewares` docs --- .../context/enhancers/middlewares/replaceActionMiddlewares.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts index bbb46522..7c186c24 100644 --- a/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts +++ b/packages/core/src/actions/context/enhancers/middlewares/replaceActionMiddlewares.ts @@ -8,7 +8,7 @@ import { Awaitable } from '@foscia/shared'; /** * Replace existing middlewares with the given middlewares array factory. * - * @param factory + * @param middlewares * * @category Enhancers * @since 0.13.0 From 886ae19bd9cfcdb34c924bd58696f4ad8534211f Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 18 Jan 2025 23:16:07 +0100 Subject: [PATCH 21/32] feat(core): improve snapshots using deep relations state locking --- packages/core/src/index.ts | 8 +- packages/core/src/model/checks/isSnapshot.ts | 14 ++ packages/core/src/model/makeModelClass.ts | 2 +- packages/core/src/model/makeModelFactory.ts | 8 +- .../src/model/props/mappers/mapAttributes.ts | 14 +- .../core/src/model/props/mappers/mapProps.ts | 12 +- .../src/model/props/mappers/mapRelations.ts | 14 +- .../src/model/revivers/makeModelsReducer.ts | 138 ++++++++++------ .../src/model/revivers/makeModelsReviver.ts | 152 +++++++++++------- packages/core/src/model/revivers/types.ts | 104 +++++++++--- packages/core/src/model/snapshots/changed.ts | 4 +- .../src/model/snapshots/cloneModelValue.ts | 15 -- .../src/model/snapshots/compareModelValue.ts | 20 --- ...{compareSnapshots.ts => isSameSnapshot.ts} | 17 +- .../src/model/snapshots/restoreSnapshot.ts | 5 +- .../model/snapshots/takeLimitedSnapshot.ts | 38 +++++ .../core/src/model/snapshots/takeSnapshot.ts | 45 ++++-- .../utilities/captureSnapshotValues.ts | 50 ++++++ packages/core/src/model/types.ts | 83 ++++++++-- .../src/model/utilities/cloneModelValue.ts | 11 ++ .../src/model/utilities/compareModelValues.ts | 29 ++++ packages/core/src/symbols.ts | 7 + .../core/tests/typecheck/models.test-d.ts | 87 +++++++++- .../core/tests/unit/model/snapshots.test.ts | 32 ++-- packages/shared/src/arrays/mapArrayable.ts | 10 +- .../models/models-changes-tracking.md | 18 ++- .../models/models-configuration.md | 89 ++++++---- .../models/models-reduce-revive.mdx | 37 +++-- 28 files changed, 766 insertions(+), 297 deletions(-) create mode 100644 packages/core/src/model/checks/isSnapshot.ts delete mode 100644 packages/core/src/model/snapshots/cloneModelValue.ts delete mode 100644 packages/core/src/model/snapshots/compareModelValue.ts rename packages/core/src/model/snapshots/{compareSnapshots.ts => isSameSnapshot.ts} (62%) create mode 100644 packages/core/src/model/snapshots/takeLimitedSnapshot.ts create mode 100644 packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts create mode 100644 packages/core/src/model/utilities/cloneModelValue.ts create mode 100644 packages/core/src/model/utilities/compareModelValues.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f2c7b62f..df964653 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -68,11 +68,13 @@ import guessRelationType from '@foscia/core/model/relations/utilities/guessRelat 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 makeMapRegistry from '@foscia/core/registry/makeMapRegistry'; @@ -175,7 +177,7 @@ export { onPropertyReading, onPropertyWrite, onPropertyWriting, - compareSnapshots, + isSameSnapshot, restoreSnapshot, takeSnapshot, runHooks, @@ -200,6 +202,8 @@ export { normalizeKey, makeModelsReducer, makeModelsReviver, + cloneModelValue, + compareModelValues, logger, SYMBOL_MODEL_PROP_FACTORY, SYMBOL_MODEL_PROP, 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/makeModelClass.ts b/packages/core/src/model/makeModelClass.ts index 3d1a3c99..81225a5f 100644 --- a/packages/core/src/model/makeModelClass.ts +++ b/packages/core/src/model/makeModelClass.ts @@ -100,7 +100,7 @@ const makeModelClass = ( }, }); - model.configure = function configureModel(newConfig?: ModelConfig, override = true) { + model.configure = function configureModel(newConfig?: Partial, override = true) { return makeModelClass( this.$type, mergeConfig(this.$config, newConfig ?? {}, override), diff --git a/packages/core/src/model/makeModelFactory.ts b/packages/core/src/model/makeModelFactory.ts index ba44b2ff..57fdbb44 100644 --- a/packages/core/src/model/makeModelFactory.ts +++ b/packages/core/src/model/makeModelFactory.ts @@ -7,6 +7,8 @@ import { ModelInstance, ModelParsedDefinition, } 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'; /** @@ -29,16 +31,18 @@ import { using } from '@foscia/shared'; * ``` */ export default ( - baseConfig?: ModelConfig, + baseConfig?: Partial, // eslint-disable-next-line max-len baseRawDefinition?: D & ThisType>>>, ) => { const factory = ( - rawConfig: string | (ModelConfig & { type: string; }), + rawConfig: string | (Partial & { type: string; }), rawDefinition?: object, ) => using( typeof rawConfig === 'string' ? { type: rawConfig } : rawConfig, ({ type, ...config }) => makeModelClass(type, { + compareValues: compareModelValues, + cloneValue: cloneModelValue, ...baseConfig, ...config, }, factory.$hooks, { diff --git a/packages/core/src/model/props/mappers/mapAttributes.ts b/packages/core/src/model/props/mappers/mapAttributes.ts index f4409038..fe8f6059 100644 --- a/packages/core/src/model/props/mappers/mapAttributes.ts +++ b/packages/core/src/model/props/mappers/mapAttributes.ts @@ -1,20 +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, ModelKey } from '@foscia/core/model/types'; /** - * Map all attributes of an instance. + * Map all attributes of a model. * - * @param instance + * @param model * @param callback * * @category Utilities */ -export default ( - instance: I, - callback: (def: ModelAttribute>) => R, +export default ( + model: M, + callback: (def: ModelAttribute>) => R, ) => mapProps( - instance, + model, callback as any, isAttributeDef, ) as R[]; diff --git a/packages/core/src/model/props/mappers/mapProps.ts b/packages/core/src/model/props/mappers/mapProps.ts index 0ada5a32..9244a160 100644 --- a/packages/core/src/model/props/mappers/mapProps.ts +++ b/packages/core/src/model/props/mappers/mapProps.ts @@ -1,20 +1,20 @@ -import { ModelInstance, ModelProp } from '@foscia/core/model/types'; +import { Model, ModelProp } from '@foscia/core/model/types'; import { tap } from '@foscia/shared'; /** - * Map all properties of an instance. + * Map all properties of a model. * - * @param instance + * @param model * @param callback * @param predicate * * @internal */ -export default ( - instance: I, +export default ( + model: M, callback: (def: P) => R, predicate?: (def: ModelProp) => def is P, -) => Object.values(instance.$model.$schema).reduce((stack, def) => tap(stack, () => { +) => Object.values(model.$schema).reduce((stack, def) => tap(stack, () => { if (!predicate || predicate(def)) { stack.push(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 f40837d5..0a3734db 100644 --- a/packages/core/src/model/props/mappers/mapRelations.ts +++ b/packages/core/src/model/props/mappers/mapRelations.ts @@ -1,20 +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, ModelKey, ModelRelation } from '@foscia/core/model/types'; /** - * Map all relations of an instance. + * Map all relations of a model. * - * @param instance + * @param model * @param callback * * @category Utilities */ -export default ( - instance: I, - callback: (def: ModelRelation>) => R, +export default ( + model: M, + callback: (def: ModelRelation>) => R, ) => mapProps( - instance, + model, callback as any, isRelationDef, ) as R[]; diff --git a/packages/core/src/model/revivers/makeModelsReducer.ts b/packages/core/src/model/revivers/makeModelsReducer.ts index b17bd312..a0eb986e 100644 --- a/packages/core/src/model/revivers/makeModelsReducer.ts +++ b/packages/core/src/model/revivers/makeModelsReducer.ts @@ -1,14 +1,24 @@ 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 { Model, 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'; /** * Create a models reducer. @@ -16,82 +26,114 @@ import { Dictionary, mapWithKeys, uniqueId, unsafeId } from '@foscia/shared'; * @category Factories * @since 0.8.6 */ -export default () => { +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: Model) => ({ - $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)); } - return isInstance(value) ? reduceInstance(value, parents) : value; + if (isInstance(value)) { + return reduceInstance(value, parents); + } + + 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', - $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 ? { + $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 42d62165..5f1abbab 100644 --- a/packages/core/src/model/revivers/makeModelsReviver.ts +++ b/packages/core/src/model/revivers/makeModelsReviver.ts @@ -1,27 +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, tap } from '@foscia/shared'; +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 options + * @param config * * @category Factories * @since 0.8.6 */ -export default (options: { models: Model[]; }) => { +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'], @@ -31,7 +40,7 @@ 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 modelsMap = new Map(config.models.map((model) => [model.$type, model])); const reviveModel = ( reducedModel: ReducedModel, ) => tap(modelsMap.get(reducedModel.$type), (model) => { @@ -42,87 +51,116 @@ export default (options: { models: Model[]; }) => { const reviveCircularRef = ( ref: ReducedModelCircularRef, - parents: Map, + 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.'); } })!; - 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)); } - return isReducedType('instance', value) - || isReducedType('circular', value) - ? reviveInstance(value, parents) - : value; + if (isReducedType('circular', value)) { + return reviveCircularRef(value, parents); + } + + if (isReducedType('instance', value)) { + return reviveInstance(value, parents); + } + + 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 = ( - instance: ModelInstance, - snapshot: ReducedModelSnapshot, - parents: Map, - ) => ({ - $instance: instance, - $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(instance, 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); - } + 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 ? { + $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); + /* eslint-enable */ + }, + ); - if (reducedInstance.$custom) { - if (!('$revive' in instance) || typeof instance.$revive !== 'function') { - throw new FosciaError( - `Missing \`$revive\` method inside model with type \`${instance.$model.$type}\`.`, - ); + reviveInstance = makeReferenceableReviver( + (value: ReducedModelInstance) => using( + reviveModel(value.$model), + (RevivedModel) => new RevivedModel(), + ), + (value: ReducedModelInstance, instance: ModelInstance, parents) => { + if (value.$data) { + reviveInstanceData(instance, value.$data, parents); } - instance.$revive(reducedInstance.$custom, { - revive: ( - i: ReducedModelInstance | ReducedModelCircularRef, - ) => reviveInstance(i, 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 f405c4ee..2c8a57f2 100644 --- a/packages/core/src/model/revivers/types.ts +++ b/packages/core/src/model/revivers/types.ts @@ -1,6 +1,46 @@ -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. * @@ -16,13 +56,15 @@ export type ReducedModel = { * * @internal */ -export type ReducedModelSnapshot = { - $FOSCIA_TYPE: 'snapshot'; - $exists: boolean; - $raw: any; - $loaded: Dictionary; - $values: Dictionary; -}; +export type ReducedModelSnapshot = + & { + $FOSCIA_TYPE: 'snapshot'; + $instance: ReducedModelInstance | ReducedModelCircularRef; + $exists: boolean; + $values: Dictionary; + } + & ({ $raw: any; $loaded: Dictionary; } | {}) + & ReviverDereferenceable; /** * Reduced (serialized) model instance data. @@ -42,12 +84,23 @@ export type ReducedModelInstanceData = { * * @internal */ -export type ReducedModelInstance = { - $FOSCIA_TYPE: 'instance'; - $model: ReducedModel; +export type ReducedModelInstance = + & { + $FOSCIA_TYPE: 'instance'; + $model: ReducedModel; + $data?: ReducedModelInstanceData; + $custom?: ReducedModelInstanceCustomData; + } + & ReviverDereferenceable; + +/** + * Reduced (serialized) value reference. + * + * @internal + */ +export type ReducedModelCircularRef = { + $FOSCIA_TYPE: 'circular'; $ref: string; - $data?: ReducedModelInstanceData; - $custom?: ReducedModelInstanceCustomData; }; /** @@ -61,16 +114,6 @@ export type ReducedModelInstanceCustomData = { $data?: ReducedModelInstanceData; }; -/** - * Reduced (serialized) model instance reference. - * - * @internal - */ -export type ReducedModelCircularRef = { - $FOSCIA_TYPE: 'circular'; - $ref: string; -}; - /** * Tools functions available when reducing an instance. */ @@ -119,3 +162,18 @@ export type ModelCanReduceRevive */ $revive(customData: T, tools: ModelReviveTools): void; }; + +/** + * Config for the models reviver. + */ +export type ModelsReviverConfig = { + models: Model[]; + revive?: (value: unknown) => unknown; +}; + +/** + * Config for the models reducer. + */ +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 74baef6f..3f17d4e1 100644 --- a/packages/core/src/model/snapshots/changed.ts +++ b/packages/core/src/model/snapshots/changed.ts @@ -1,4 +1,4 @@ -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'; @@ -23,7 +23,7 @@ import { ArrayableVariadic } from '@foscia/shared'; 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 2530750e..00000000 --- a/packages/core/src/model/snapshots/cloneModelValue.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Model } from '@foscia/core/model/types'; - -/** - * Clone a value using configured cloner. - * - * @param model - * @param value - * - * @internal - */ -export default (model: Model, 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 59f0b2ae..00000000 --- a/packages/core/src/model/snapshots/compareModelValue.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Model } from '@foscia/core/model/types'; - -/** - * Compare values using configured comparator. - * - * @param model - * @param nextValue - * @param prevValue - * - * @internal - */ -export default ( - model: Model, - 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/isSameSnapshot.ts similarity index 62% rename from packages/core/src/model/snapshots/compareSnapshots.ts rename to packages/core/src/model/snapshots/isSameSnapshot.ts index df438038..f9e64c5f 100644 --- a/packages/core/src/model/snapshots/compareSnapshots.ts +++ b/packages/core/src/model/snapshots/isSameSnapshot.ts @@ -1,9 +1,9 @@ -import compareModelValue from '@foscia/core/model/snapshots/compareModelValue'; -import { Model, ModelKey, ModelSnapshot } from '@foscia/core/model/types'; +import { Model, ModelKey, ModelLimitedSnapshot, ModelSnapshot } from '@foscia/core/model/types'; import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; /** - * Compare two snapshots. + * Check if two snapshots are similar (same model, same existence state + * and same values). * * @param nextSnapshot * @param prevSnapshot @@ -13,16 +13,16 @@ import { ArrayableVariadic, wrapVariadic } from '@foscia/shared'; * * @example * ```typescript - * import { compareSnapshot } from '@foscia/core'; + * import { isSameSnapshot } from '@foscia/core'; * - * const titleChanged = compareSnapshot(newSnapshot, oldSnapshot, ['title']); + * const titleChanged = isSameSnapshot(newSnapshot, oldSnapshot, ['title']); * if (titleChanged) { * } * ``` */ export default ( - nextSnapshot: ModelSnapshot, - prevSnapshot: ModelSnapshot, + nextSnapshot: ModelSnapshot | ModelLimitedSnapshot, + prevSnapshot: ModelSnapshot | ModelLimitedSnapshot, ...only: ArrayableVariadic> ) => { if (nextSnapshot.$instance.$model !== prevSnapshot.$instance.$model) { @@ -38,8 +38,7 @@ export default ( 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.$instance.$model, + (key) => nextSnapshot.$instance.$model.$config.compareValues( nextSnapshot.$values[key], prevSnapshot.$values[key], ), diff --git a/packages/core/src/model/snapshots/restoreSnapshot.ts b/packages/core/src/model/snapshots/restoreSnapshot.ts index 7459842c..93a8b94a 100644 --- a/packages/core/src/model/snapshots/restoreSnapshot.ts +++ b/packages/core/src/model/snapshots/restoreSnapshot.ts @@ -2,7 +2,6 @@ import isPropDef from '@foscia/core/model/checks/isPropDef'; 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 { ModelInstance, @@ -49,13 +48,13 @@ export default ( if (Object.prototype.hasOwnProperty.call(snapshot.$values, def.key)) { forceFill(instance, { - [def.key]: cloneModelValue(instance.$model, snapshot.$values[def.key]), + [def.key]: instance.$model.$config.cloneValue(snapshot.$values[def.key]), } as Partial>); } else { delete instance.$values[def.key]; } }; - mapProps(instance, restoreForDef, isPropDef as any); + mapProps(instance.$model, restoreForDef, isPropDef as any); markSynced(instance, ...only); }); diff --git a/packages/core/src/model/snapshots/takeLimitedSnapshot.ts b/packages/core/src/model/snapshots/takeLimitedSnapshot.ts new file mode 100644 index 00000000..5fe25edf --- /dev/null +++ b/packages/core/src/model/snapshots/takeLimitedSnapshot.ts @@ -0,0 +1,38 @@ +import captureSnapshotValues from '@foscia/core/model/snapshots/utilities/captureSnapshotValues'; +import { + ModelInstance, + ModelLimitedSnapshot, + ModelLimitedSnapshotValues, +} 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 ModelLimitedSnapshotValues, +}); + +export default takeLimitedSnapshot; diff --git a/packages/core/src/model/snapshots/takeSnapshot.ts b/packages/core/src/model/snapshots/takeSnapshot.ts index 4645a67e..b0ad8d62 100644 --- a/packages/core/src/model/snapshots/takeSnapshot.ts +++ b/packages/core/src/model/snapshots/takeSnapshot.ts @@ -1,6 +1,32 @@ -import cloneModelValue from '@foscia/core/model/snapshots/cloneModelValue'; -import { ModelInstance, ModelSnapshot, ModelValues } from '@foscia/core/model/types'; -import { mapWithKeys, using } from '@foscia/shared'; +import takeLimitedSnapshot from '@foscia/core/model/snapshots/takeLimitedSnapshot'; +import captureSnapshotValues from '@foscia/core/model/snapshots/utilities/captureSnapshotValues'; +import { ModelInstance, ModelSnapshot, ModelSnapshotValues } from '@foscia/core/model/types'; +import { SYMBOL_MODEL_SNAPSHOT } from '@foscia/core/symbols'; + +const takeFullSnapshot = ( + instance: I, + parents: ModelSnapshot[] = [], +) => { + const snapshot: ModelSnapshot = { + $FOSCIA_TYPE: SYMBOL_MODEL_SNAPSHOT, + $instance: instance, + $exists: instance.$exists, + $raw: instance.$raw, + $loaded: { ...instance.$loaded }, + $values: {}, + }; + + // @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 ModelSnapshotValues; + + return snapshot; +}; /** * Capture a snapshot of the instance. @@ -16,15 +42,4 @@ import { mapWithKeys, using } from '@foscia/shared'; * const snapshot = takeSnapshot(post); * ``` */ -export default ( - instance: I, -): ModelSnapshot => ({ - $instance: instance, - $exists: instance.$exists, - $raw: instance.$raw, - $loaded: { ...instance.$loaded }, - $values: mapWithKeys(instance.$values, (value, key) => using( - cloneModelValue(instance.$model, value), - (clonedValue) => (clonedValue !== undefined ? { [key]: clonedValue } : {}), - )) as Partial>, -}); +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..2f07d406 --- /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.cloneValue(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 494ef1d6..ffb76d13 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -10,6 +10,7 @@ import { 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 { @@ -68,22 +69,28 @@ export type ModelConfig = { */ guessAlias?: Transformer; /** - * Compare two properties values. - * Defaults to strict equality (`next === prev`). - * This is used when comparing snapshot values. + * Compare two properties values when comparing snapshots. + * Defaults to {@link compareModelValues | `compareModelValues`}. * * @param nextValue * @param prevValue */ - compareValue?: (nextValue: unknown, prevValue: unknown) => boolean; + compareValues: (nextValue: unknown, prevValue: unknown) => boolean; /** - * Clone a property value. - * Defaults to no clone at all. - * This is used when creating a snapshot. + * Clone a property value when creating a snapshot. + * Defaults to {@link cloneModelValue | `cloneModelValue`}. * * @param value */ - cloneValue?: (value: T) => T; + cloneValue: (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. @@ -517,7 +524,10 @@ export type ExtendableModel = any * @param config * @param override */ - configure(config: ModelConfig, override?: boolean): ExtendableModel>; + configure( + config: Partial, + override?: boolean, + ): ExtendableModel>; /** * Create a new model class with an extended definition. * @@ -539,7 +549,7 @@ export type ExtendableModel = any export type ModelFactory< D extends {} = {}, > = Hookable & (( - rawConfig: string | (ModelConfig & { type: string; }), + rawConfig: string | (Partial & { type: string; }), // eslint-disable-next-line max-len rawDefinition?: ND & ThisType>>>, // eslint-disable-next-line max-len @@ -621,6 +631,31 @@ export type ModelDefinitionValues = & ModelDefinitionWritableValues & ModelDefinitionReadOnlyValues; +/** + * Model values map for a snapshot (IDs/attributes/relations). + * + * @internal + */ +export type ModelSnapshotDefinitionValues = ModelIdsDefaults & { + [K in keyof D]: D[K] extends ModelPropFactory> + ? T extends (infer I)[] + ? (ModelLimitedSnapshot | ModelSnapshot)[] + : (ModelLimitedSnapshot | ModelSnapshot) + : D[K] extends ModelPropFactory> + ? T + : D[K] extends ModelPropFactory> ? any : never; +}; + +/** + * Model values map for a limited snapshot (IDs/attributes/relations). + * + * @internal + */ +export type ModelLimitedSnapshotDefinitionValues = ModelIdsDefaults & { + [K in keyof ModelSnapshotDefinitionValues]: K extends 'id' | 'lid' + ? ModelSnapshotDefinitionValues[K] : never; +}; + /** * Model descriptors map (only custom properties). * @@ -673,15 +708,25 @@ export type ModelInstanceUsing = ModelInstance>; /** - * Model class or instance snapshot. + * Model instance snapshot which only contains ID and LID + * and is used to track related records inside a snapshot. + */ +export type ModelLimitedSnapshot = { + readonly $instance: ModelInstance; + readonly $exists: boolean; + readonly $values: Partial>>; +} & FosciaObject; + +/** + * Model instance snapshot. */ export type ModelSnapshot = { readonly $instance: ModelInstance; readonly $exists: boolean; readonly $raw: any; readonly $loaded: Dictionary; - readonly $values: Partial>; -}; + readonly $values: Partial>>; +} & FosciaObject; /** * Infer the definition from a model class or model instance. @@ -713,6 +758,18 @@ export type InferModelSchemaProp = ? ModelSchema>[K] : never : never; +/** + * Model snapshot values map (only IDs/attributes/relations). + */ +export type ModelSnapshotValues = ModelSnapshotDefinitionValues>; + +/** + * Model limited snapshot values map (only IDs). + */ +export type ModelLimitedSnapshotValues< + M, +> = ModelLimitedSnapshotDefinitionValues>; + /** * Model class or instance values map (only IDs/attributes/relations). */ 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/symbols.ts b/packages/core/src/symbols.ts index fa01e9d6..71c42f1c 100644 --- a/packages/core/src/symbols.ts +++ b/packages/core/src/symbols.ts @@ -75,6 +75,13 @@ export const SYMBOL_MODEL_INSTANCE: unique symbol = Symbol('foscia:instance'); */ 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. * diff --git a/packages/core/tests/typecheck/models.test-d.ts b/packages/core/tests/typecheck/models.test-d.ts index 54b7e944..81c763c1 100644 --- a/packages/core/tests/typecheck/models.test-d.ts +++ b/packages/core/tests/typecheck/models.test-d.ts @@ -1,6 +1,18 @@ /* eslint-disable max-classes-per-file */ - -import { attr, fill, hasOne, makeModel, normalizeDotRelations, toString } from '@foscia/core'; +/* eslint-disable @typescript-eslint/no-unused-expressions */ + +import { + attr, + fill, + hasOne, + makeModel, + 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'; @@ -9,6 +21,47 @@ 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(); + + 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 post = new PostMock(); expectTypeOf(post.id).toEqualTypeOf(); @@ -36,6 +89,27 @@ test('Models are type safe', () => { // @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).toEqualTypeOf(); @@ -54,6 +128,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 diff --git a/packages/core/tests/unit/model/snapshots.test.ts b/packages/core/tests/unit/model/snapshots.test.ts index fc323ee8..ad78be40 100644 --- a/packages/core/tests/unit/model/snapshots.test.ts +++ b/packages/core/tests/unit/model/snapshots.test.ts @@ -1,6 +1,6 @@ import { changed, - compareSnapshots, + isSameSnapshot, markSynced, restore, restoreSnapshot, @@ -15,9 +15,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); @@ -101,20 +101,20 @@ 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 compareValues = vi.fn((n, p) => n !== p); const ExtendedPostMock = PostMock.configure({ cloneValue, - compareValue, + compareValues, }); expect(cloneValue).not.toHaveBeenCalled(); - expect(compareValue).not.toHaveBeenCalled(); + expect(compareValues).not.toHaveBeenCalled(); const post = new ExtendedPostMock(); expect(cloneValue).not.toHaveBeenCalled(); - expect(compareValue).not.toHaveBeenCalled(); + expect(compareValues).not.toHaveBeenCalled(); post.title = 'foo'; post.comments = []; @@ -122,14 +122,14 @@ describe.concurrent('unit: snapshots', () => { const snapshot = takeSnapshot(post); expect(cloneValue).toHaveBeenCalledTimes(3); - expect(compareValue).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(compareValues).not.toHaveBeenCalled(); + + expect(isSameSnapshot(snapshot, post.$original, 'title')).toStrictEqual(true); + expect(compareValues).toHaveBeenCalledTimes(1); + expect(isSameSnapshot(snapshot, post.$original, 'comments')).toStrictEqual(true); + expect(compareValues).toHaveBeenCalledTimes(2); + expect(isSameSnapshot(snapshot, post.$original, 'commentsCount')).toStrictEqual(true); + expect(compareValues).toHaveBeenCalledTimes(3); expect(cloneValue).toHaveBeenCalledTimes(3); }); diff --git a/packages/shared/src/arrays/mapArrayable.ts b/packages/shared/src/arrays/mapArrayable.ts index 8e7d2483..cda085d8 100644 --- a/packages/shared/src/arrays/mapArrayable.ts +++ b/packages/shared/src/arrays/mapArrayable.ts @@ -1,5 +1,5 @@ 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. @@ -10,12 +10,12 @@ import { Arrayable, Awaitable, Optional } from '@foscia/shared/types'; * @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))); } - return isNil(value) ? value : callback(value); + return isNil(value) ? value : callback(value as NonNullable); }; diff --git a/website/docs/digging-deeper/models/models-changes-tracking.md b/website/docs/digging-deeper/models/models-changes-tracking.md index 3fed9b92..52138194 100644 --- a/website/docs/digging-deeper/models/models-changes-tracking.md +++ b/website/docs/digging-deeper/models/models-changes-tracking.md @@ -22,6 +22,16 @@ 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 @@ -40,23 +50,23 @@ const myPostSnapshot = takeSnapshot(myPost); ## Checking for changes To check for changes between two snapshots, you can use -[`compareSnapshots`](/docs/api/@foscia/core/functions/compareSnapshots). To +[`isSameSnapshot`](/docs/api/@foscia/core/functions/isSameSnapshot). To check for changes between an instance and its original snapshot, you can use [`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 diff --git a/website/docs/digging-deeper/models/models-configuration.md b/website/docs/digging-deeper/models/models-configuration.md index a3358f8e..cdb31b56 100644 --- a/website/docs/digging-deeper/models/models-configuration.md +++ b/website/docs/digging-deeper/models/models-configuration.md @@ -150,51 +150,27 @@ makeModel({ }); ``` -#### `compareValue` and `cloneValue` +#### `limitedSnapshots` ##### Description -**Default**: compare will check for strict equality and clone will return the -base value. - -**Recommandation**: use this configuration option inside a -[custom model factory](/docs/digging-deeper/models/models-composition#factory). - -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/api/@foscia/core/#utilities): -[`changed`](/docs/api/@foscia/core/functions/changed), -[`restore`](/docs/api/@foscia/core/functions/restore), etc. +**Default**: `true`. -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. +Enable storing related records of a snapshot as +[`ModelLimitedSnapshot`](/docs/api/@foscia/core/type-aliases/ModelLimitedSnapshot), +instead of [`ModelSnapshot`](/docs/api/@foscia/core/type-aliases/ModelSnapshot), +to improve memory footprint and performance. ##### Example -The following model configuration is equivalent to the default behavior of -Foscia: - ```typescript title="post.ts" import { makeModel } from '@foscia/core'; makeModel({ - type: 'posts', - compareValue: (newValue, prevValue) => nextValue === prevValue, - cloneValue: (value) => value, + limitedSnapshots: false, }); ``` -You may change those two functions to really clone values when syncing the -instance state (such as arrays). 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 - [`restore`](/docs/api/@foscia/core/functions/restore) utility -- Making a real clone of a value without updating the comparator will break the - history because of its default behavior - #### `strict` ##### Description @@ -270,6 +246,57 @@ makeModel({ }); ``` +#### `compareValues` and `cloneValue` + +##### 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). + +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/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. + +##### Example + +The following model configuration is equivalent to the default behavior of +Foscia: + +```typescript title="post.ts" +import { makeModel, compareModelValues, cloneModelValue } from '@foscia/core'; + +makeModel({ + type: 'posts', + compareValue: compareModelValues, + cloneValue: cloneModelValue, +}); +``` + +:::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 + [`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 + +::: + ### HTTP The following configuration options are specific to HTTP models (when diff --git a/website/docs/digging-deeper/models/models-reduce-revive.mdx b/website/docs/digging-deeper/models/models-reduce-revive.mdx index a688cf25..4ac48d1c 100644 --- a/website/docs/digging-deeper/models/models-reduce-revive.mdx +++ b/website/docs/digging-deeper/models/models-reduce-revive.mdx @@ -48,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(); @@ -58,17 +58,36 @@ const myRevivedPost = revive(JSON.parse(json)); :::info -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. -- 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 From 0a9b073b4575dcece90c789ff388f4d240be63b6 Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 18 Jan 2025 23:16:37 +0100 Subject: [PATCH 22/32] docs: improve models and actions docs --- website/docs/core-concepts/actions.mdx | 12 +- website/docs/core-concepts/models.mdx | 219 +----------------- .../implementations/presentation.md | 6 +- .../digging-deeper/models/models-hooks.md | 219 ++++++++++++++++++ website/typedoc-plugin.mjs | 2 + 5 files changed, 238 insertions(+), 220 deletions(-) create mode 100644 website/docs/digging-deeper/models/models-hooks.md diff --git a/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index e112c863..5066aaed 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -20,14 +20,12 @@ import FunctionInfo from '@site/src/components/FunctionInfo'; ## 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#first-actions). -- 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 diff --git a/website/docs/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index 24df0737..4b137c8a 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -6,7 +6,6 @@ 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,6 +18,15 @@ 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 @@ -574,215 +582,6 @@ export default class User extends makeModel('users', { } ``` -## Hooks - -### Using hooks - -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). - ## Special properties Foscia models provide special properties on both the model class and its diff --git a/website/docs/digging-deeper/implementations/presentation.md b/website/docs/digging-deeper/implementations/presentation.md index b7817423..266aea45 100644 --- a/website/docs/digging-deeper/implementations/presentation.md +++ b/website/docs/digging-deeper/implementations/presentation.md @@ -55,8 +55,8 @@ sources. `Deserializer` to interact with JSON:API data sources. - [Adapter through `makeJsonApiAdapter`](/docs/digging-deeper/implementations/jsonapi#makejsonapiadapter) -- [Serializer through `makeJsonApiSerializer`](/docs/digging-deeper/implementations/jsonapi#makejsonapiserializer) - [Deserializer through `makeJsonApiDeserializer`](/docs/digging-deeper/implementations/jsonapi#makejsonapideserializer) +- [Serializer through `makeJsonApiSerializer`](/docs/digging-deeper/implementations/jsonapi#makejsonapiserializer) ### REST @@ -64,13 +64,13 @@ sources. `Deserializer` to interact with JSON REST HTTP data sources. - [Adapter through `makeRestAdapter`](/docs/digging-deeper/implementations/rest#makerestadapter) -- [Serializer through `makeRestSerializer`](/docs/digging-deeper/implementations/rest#makerestserializer) - [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. -- [Serializer through `makeSerializer`](/docs/digging-deeper/implementations/serialization#makeserializer) - [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/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/typedoc-plugin.mjs b/website/typedoc-plugin.mjs index 4640358b..d05f1758 100644 --- a/website/typedoc-plugin.mjs +++ b/website/typedoc-plugin.mjs @@ -72,6 +72,8 @@ export function load(app) { page.contents = page.contents.replace(matches[0], `\n${chipFactory()}\n`); } }); + + page.contents = page.contents.replace(/\[\\`([^`]+)\\`]\(([^)]+)\)/g, '[`$1`]($2)'); } }); } From 52b21126953f5a76a95a0668a831019e7ae82e2c Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 18 Jan 2025 23:57:00 +0100 Subject: [PATCH 23/32] feat(core): use snapshots inside serializers instead of instances BREAKING CHANGE: signature of `Serializer` is fully changed to receive snapshots instead of instances. --- .../context/guessers/guessContextModel.ts | 14 +- .../context/utilities/serializeInstance.ts | 5 +- .../context/utilities/serializeRelation.ts | 14 +- .../src/model/revivers/makeModelsReducer.ts | 1 + .../src/model/revivers/makeModelsReviver.ts | 5 + packages/core/src/model/revivers/types.ts | 27 +++- .../core/src/model/snapshots/takeSnapshot.ts | 1 + packages/core/src/model/types.ts | 1 + packages/core/src/types.ts | 151 +++++++++++++----- packages/jsonapi/src/makeJsonApiSerializer.ts | 26 +-- packages/rest/src/makeRestSerializer.ts | 4 +- .../tests/unit/makeRestSerializer.test.ts | 33 ++-- .../errors/serializerCircularRelationError.ts | 6 +- .../serialization/src/makeDeserializer.ts | 4 +- packages/serialization/src/makeSerializer.ts | 71 ++++---- .../src/makeSerializerRecordFactory.ts | 8 +- packages/serialization/src/types.ts | 23 +-- .../tests/unit/makeSerializer.test.ts | 17 +- .../implementations/presentation.md | 2 +- .../implementations/serialization.md | 6 +- website/docs/digging-deeper/usages/rest.md | 25 ++- website/docs/upgrade/migration.md | 20 +++ 22 files changed, 306 insertions(+), 158 deletions(-) diff --git a/packages/core/src/actions/context/guessers/guessContextModel.ts b/packages/core/src/actions/context/guessers/guessContextModel.ts index 2fbeca9d..620bec95 100644 --- a/packages/core/src/actions/context/guessers/guessContextModel.ts +++ b/packages/core/src/actions/context/guessers/guessContextModel.ts @@ -67,30 +67,18 @@ export default (async ( return guessModelIn(context.model, context.ensureType, multiple); }) as { - /** - * Guess the model targeted by the given context. - * - * @param context - * - * @internal - */ - (context: GuessContextModelContext): Promise; /** * Guess the model targeted by the given context. * * @param context * @param multiple - * - * @internal */ - (context: GuessContextModelContext, multiple: false): Promise; + (context: GuessContextModelContext, multiple?: false): Promise; /** * Guess the models targeted by the given context. * * @param context * @param multiple - * - * @internal */ (context: GuessContextModelContext, multiple: true): Promise; }; diff --git a/packages/core/src/actions/context/utilities/serializeInstance.ts b/packages/core/src/actions/context/utilities/serializeInstance.ts index b1a5941b..c0a74ff6 100644 --- a/packages/core/src/actions/context/utilities/serializeInstance.ts +++ b/packages/core/src/actions/context/utilities/serializeInstance.ts @@ -1,5 +1,6 @@ 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'; @@ -14,7 +15,7 @@ import { using } from '@foscia/shared'; export default async ( context: ConsumeSerializer, instance: ModelInstance, -) => using(await consumeSerializer(context), async (serializer) => serializer.serialize( - await serializer.serializeInstance(instance, context), +) => 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 index 172acdc8..b93182d4 100644 --- a/packages/core/src/actions/context/utilities/serializeRelation.ts +++ b/packages/core/src/actions/context/utilities/serializeRelation.ts @@ -1,5 +1,6 @@ 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 { InferModelSchemaProp, InferModelValuePropType, @@ -7,7 +8,7 @@ import { ModelRelation, ModelRelationKey, } from '@foscia/core/model/types'; -import { Arrayable, using } from '@foscia/shared'; +import { mapArrayable, using } from '@foscia/shared'; /** * Serialize the given relation's value to a serialized dataset. @@ -31,11 +32,14 @@ export default async < instance: I, relation: K & ModelRelationKey, value: InferModelValuePropType, -) => using(await consumeSerializer(context), async (serializer) => serializer.serialize( - await serializer.serializeRelation( - instance, +) => using(await consumeSerializer(context), async (serializer) => serializer.serializeToData( + await serializer.serializeToRelatedRecords( + takeSnapshot(instance), instance.$model.$schema[relation] as R, - value as Arrayable | null, + await mapArrayable( + value, + (related) => takeSnapshot(related as unknown as ModelInstance), + ), context, ), context, diff --git a/packages/core/src/model/revivers/makeModelsReducer.ts b/packages/core/src/model/revivers/makeModelsReducer.ts index a0eb986e..6d02080d 100644 --- a/packages/core/src/model/revivers/makeModelsReducer.ts +++ b/packages/core/src/model/revivers/makeModelsReducer.ts @@ -105,6 +105,7 @@ export default (config: ModelsReducerConfig = {}) => { $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, } : {}), diff --git a/packages/core/src/model/revivers/makeModelsReviver.ts b/packages/core/src/model/revivers/makeModelsReviver.ts index 5f1abbab..87f1e98c 100644 --- a/packages/core/src/model/revivers/makeModelsReviver.ts +++ b/packages/core/src/model/revivers/makeModelsReviver.ts @@ -122,6 +122,7 @@ export default (config: ModelsReviverConfig) => { $instance: null as any, $values: null as any, ...('$raw' in value ? { + $original: null, $raw: value.$raw, $loaded: value.$loaded, } : {}), @@ -132,6 +133,10 @@ export default (config: ModelsReviverConfig) => { 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 */ }, ); diff --git a/packages/core/src/model/revivers/types.ts b/packages/core/src/model/revivers/types.ts index 2c8a57f2..8d4cf34c 100644 --- a/packages/core/src/model/revivers/types.ts +++ b/packages/core/src/model/revivers/types.ts @@ -52,20 +52,41 @@ export type ReducedModel = { }; /** - * Reduced (serialized) model snapshot. + * Reduced (serialized) model limited snapshot. * * @internal */ -export type ReducedModelSnapshot = +export type ReducedModelLimitedSnapshot = & { $FOSCIA_TYPE: 'snapshot'; $instance: ReducedModelInstance | ReducedModelCircularRef; $exists: boolean; $values: Dictionary; } - & ({ $raw: any; $loaded: 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 = + | ReducedModelLimitedSnapshot + | ReducedModelFullSnapshot; + /** * Reduced (serialized) model instance data. * diff --git a/packages/core/src/model/snapshots/takeSnapshot.ts b/packages/core/src/model/snapshots/takeSnapshot.ts index b0ad8d62..6fd843e1 100644 --- a/packages/core/src/model/snapshots/takeSnapshot.ts +++ b/packages/core/src/model/snapshots/takeSnapshot.ts @@ -9,6 +9,7 @@ const takeFullSnapshot = ( ) => { const snapshot: ModelSnapshot = { $FOSCIA_TYPE: SYMBOL_MODEL_SNAPSHOT, + $original: instance.$original ?? null, $instance: instance, $exists: instance.$exists, $raw: instance.$raw, diff --git a/packages/core/src/model/types.ts b/packages/core/src/model/types.ts index ffb76d13..0b129ad6 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -722,6 +722,7 @@ export type ModelLimitedSnapshot = { */ export type ModelSnapshot = { readonly $instance: ModelInstance; + readonly $original: ModelSnapshot | null; readonly $exists: boolean; readonly $raw: any; readonly $loaded: Dictionary; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f5dcd6a2..17e77f36 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,4 +1,10 @@ -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'; /** @@ -100,69 +106,136 @@ export type Adapter = { 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). + */ +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 `Related` records. + * Usually, it is a wrapper type for `Record` or `RelatedRecord` records. */ -export type Serializer = { +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 should 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 array. - */ -export type DeserializedData = { + serializeToRelatedRecords( + parent: ModelSnapshot, + def: ModelRelation, + snapshots: ModelSnapshot[], + context: {}, + ): Awaitable; /** - * Deserialized instances. + * Serialize related snapshots to related records. + * + * @param parent + * @param def + * @param snapshot + * @param context */ - 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). - */ -export type Deserializer = { + serializeToRelatedRecords( + parent: ModelSnapshot, + def: ModelRelation, + snapshot: null, + context: {}, + ): Awaitable; /** - * Deserialize adapter data to a deserialized array 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: 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/jsonapi/src/makeJsonApiSerializer.ts b/packages/jsonapi/src/makeJsonApiSerializer.ts index 70f4b93d..1b71810f 100644 --- a/packages/jsonapi/src/makeJsonApiSerializer.ts +++ b/packages/jsonapi/src/makeJsonApiSerializer.ts @@ -23,21 +23,12 @@ export default < >( config?: Partial>, ) => makeSerializer({ - 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), + (snapshot) => ({ + type: snapshot.$instance.$model.$type, + id: serializeId(snapshot.$values.id), + lid: serializeId(snapshot.$values.lid), attributes: {}, relationships: {}, } as Record), @@ -51,5 +42,14 @@ export default < } }, ), + 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/rest/src/makeRestSerializer.ts b/packages/rest/src/makeRestSerializer.ts index 7473263f..0a5e50a4 100644 --- a/packages/rest/src/makeRestSerializer.ts +++ b/packages/rest/src/makeRestSerializer.ts @@ -18,10 +18,10 @@ export default < config?: Partial>, ) => makeSerializer({ createRecord: makeSerializerRecordFactory( - (instance) => tap({ id: instance.id } as Record, (record) => { + (snapshot) => tap({ id: snapshot.$values.id } as Record, (record) => { if (config?.serializeType) { // eslint-disable-next-line no-param-reassign - record.type = instance.$model.$type; + record.type = snapshot.$instance.$model.$type; } }), (record, { key, value }) => { diff --git a/packages/rest/tests/unit/makeRestSerializer.test.ts b/packages/rest/tests/unit/makeRestSerializer.test.ts index d6ab58de..12a84fa8 100644 --- a/packages/rest/tests/unit/makeRestSerializer.test.ts +++ b/packages/rest/tests/unit/makeRestSerializer.test.ts @@ -4,14 +4,19 @@ import { hasMany, hasOne, makeComposable, - makeModel, + makeModelFactory, makeTransformer, + takeSnapshot, } from '@foscia/core'; import { makeRestSerializer, RestSerializerConfig } from '@foscia/rest'; import { Awaitable } from '@foscia/shared'; import { Assertion, describe, expect, it } from 'vitest'; describe.concurrent('unit: makeRestSerializer', () => { + const makeModel = makeModelFactory({ + limitedSnapshots: false, + }); + const authored = makeComposable({ author: hasOne(), }); @@ -132,7 +137,7 @@ describe.concurrent('unit: makeRestSerializer', () => { post1, { serializeRelation: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), }, {}, (assertion: Assertion) => assertion.resolves.toStrictEqual({ @@ -169,7 +174,7 @@ describe.concurrent('unit: makeRestSerializer', () => { { serializeType: true, serializeRelation: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), circularRelationBehavior: () => 'keep', }, {}, @@ -270,7 +275,7 @@ describe.concurrent('unit: makeRestSerializer', () => { { circularRelationBehavior: () => 'throw', serializeRelation: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), }, {}, (assertion: Assertion) => assertion.rejects.toThrowError( @@ -281,8 +286,8 @@ describe.concurrent('unit: makeRestSerializer', () => { 'should serialize instance with configuration', async (instance, config, context, expectation) => { const { serializer } = makeRestSerializer(config); - const serialize = async () => serializer.serialize( - await serializer.serializeInstance(instance, context), + const serialize = async () => serializer.serializeToData( + await serializer.serializeToRecords(takeSnapshot(instance), context), context, ); @@ -301,8 +306,8 @@ describe.concurrent('unit: makeRestSerializer', () => { post1, { serializeRelated: (_, related) => ({ - type: related.$model.$type, - id: related.id, + type: related.$instance.$model.$type, + id: related.$values.id, }), }, {}, @@ -315,7 +320,7 @@ describe.concurrent('unit: makeRestSerializer', () => { post1, { serializeRelated: (context, related, parents) => context.serializer - .serializeInstance(related, context, parents), + .serializeToRecords(related, context, parents), }, {}, (assertion: Assertion) => assertion.resolves.toStrictEqual({ @@ -328,11 +333,11 @@ describe.concurrent('unit: makeRestSerializer', () => { 'should serialize relation with configuration', async (instance, config, context, expectation) => { const { serializer } = makeRestSerializer(config); - const serialize = async () => serializer.serialize( - await serializer.serializeRelation( - instance, + const serialize = async () => serializer.serializeToData( + await serializer.serializeToRelatedRecords( + takeSnapshot(instance), instance.$model.$schema.author, - instance.author, + takeSnapshot(instance.author), context, ), context, @@ -356,7 +361,7 @@ describe.concurrent('unit: makeRestSerializer', () => { 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 98586b54..9c720876 100644 --- a/packages/serialization/src/errors/serializerCircularRelationError.ts +++ b/packages/serialization/src/errors/serializerCircularRelationError.ts @@ -1,4 +1,4 @@ -import { ModelInstance, ModelRelation, SerializerError } from '@foscia/core'; +import { ModelRelation, ModelSnapshot, SerializerError } from '@foscia/core'; import { SerializerCircularRelationBehavior } from '@foscia/serialization/types'; /** @@ -10,12 +10,12 @@ 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/makeDeserializer.ts b/packages/serialization/src/makeDeserializer.ts index ccecd067..a70dfa05 100644 --- a/packages/serialization/src/makeDeserializer.ts +++ b/packages/serialization/src/makeDeserializer.ts @@ -212,7 +212,7 @@ You should either: setInstanceId('lid'); await Promise.all([ - ...mapAttributes(instance, async (def) => { + ...mapAttributes(instance.$model, async (def) => { const deserializerContext = await makeDeserializerContext( instance, def, @@ -225,7 +225,7 @@ You should either: }); } }), - ...mapRelations(instance, async (def) => { + ...mapRelations(instance.$model, async (def) => { const deserializerContext = await makeDeserializerContext( instance, def, diff --git a/packages/serialization/src/makeSerializer.ts b/packages/serialization/src/makeSerializer.ts index 5ca2aff8..dc9559f5 100644 --- a/packages/serialization/src/makeSerializer.ts +++ b/packages/serialization/src/makeSerializer.ts @@ -1,10 +1,10 @@ import { - changed, + isSameSnapshot, mapAttributes, mapRelations, ModelAttribute, - ModelInstance, ModelRelation, + ModelSnapshot, normalizeKey, shouldSync, } from '@foscia/core'; @@ -32,38 +32,41 @@ export default ( ?? ((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 @@ -72,21 +75,21 @@ export default ( 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); + ...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); @@ -94,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); @@ -103,7 +106,7 @@ export default ( return; } - throw new SerializerCircularRelationError(instance, def, circularBehavior); + throw new SerializerCircularRelationError(snapshot, def, circularBehavior); } if (await shouldSerialize(serializerContext)) { @@ -111,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 }); @@ -129,30 +132,28 @@ 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: {}, ) => using( - makeSerializerContext(instance, def, context), + makeSerializerContext(parent, def, context), (serializerContext) => serializeRelationWith( { ...serializerContext, value }, serializeRelated, - [{ instance, def }], + [{ model: parent.$instance.$model, def }], ), ), - serializeInstance, - serialize, - }; + 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 de4334fc..66586565 100644 --- a/packages/serialization/src/makeSerializerRecordFactory.ts +++ b/packages/serialization/src/makeSerializerRecordFactory.ts @@ -1,4 +1,4 @@ -import { ModelInstance } from '@foscia/core'; +import { ModelSnapshot } from '@foscia/core'; import { SerializerContext, SerializerRecordFactory } from '@foscia/serialization/types'; import { Awaitable, using } from '@foscia/shared'; @@ -15,15 +15,15 @@ export default < 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: {}, -) => using(await initialize(instance, context), (record) => ({ +) => using(await initialize(snapshot, context), (record) => ({ put: ( serializerContext: SerializerContext, ) => put(record, serializerContext), diff --git a/packages/serialization/src/types.ts b/packages/serialization/src/types.ts index 8d58c274..8721eef7 100644 --- a/packages/serialization/src/types.ts +++ b/packages/serialization/src/types.ts @@ -6,6 +6,7 @@ import { ModelIdType, ModelInstance, ModelRelation, + ModelSnapshot, Serializer, } from '@foscia/core'; import { type Arrayable, Awaitable, IdentifiersMap } from '@foscia/shared'; @@ -208,7 +209,7 @@ export type SerializerContext< Data = unknown, Def = ModelAttribute | ModelRelation, > = { - instance: ModelInstance; + snapshot: ModelSnapshot; def: Def; key: string; value: unknown; @@ -242,7 +243,7 @@ export type SerializerRecord = { * @internal */ export type SerializerRecordFactory = ( - instance: ModelInstance, + snapshot: ModelSnapshot, context: {}, ) => Awaitable>; @@ -252,7 +253,7 @@ export type SerializerRecordFactory = ( * * @internal */ -export type SerializerParents = { instance: ModelInstance; def: ModelRelation }[]; +export type SerializerParents = { model: Model; def: ModelRelation }[]; /** * Available behaviors to apply when encountering a circular relation: @@ -322,7 +323,7 @@ export type SerializerConfig = { */ serializeRelation?: ( serializerContext: SerializerContext, - related: ModelInstance, + related: ModelSnapshot, parents: SerializerParents, ) => Awaitable; /** @@ -336,7 +337,7 @@ export type SerializerConfig = { */ serializeRelated?: ( serializerContext: SerializerContext, - related: ModelInstance, + related: ModelSnapshot, parents: SerializerParents, ) => Awaitable | null>; /** @@ -372,17 +373,17 @@ export type SerializerConfig = { export type RecordSerializer = & { /** - * Serialize a given instance value. - * This overload will handle circular relations using the parents of the instance. + * Serialize snapshots to records. + * This overload handles circular relations using the parents of the instance. * - * @param instance + * @param value * @param context * @param parents */ - serializeInstance( - instance: ModelInstance, + serializeToRecords( + value: ModelSnapshot, context: {}, - parents?: SerializerParents, + parents: SerializerParents, ): Awaitable; } & Serializer; diff --git a/packages/serialization/tests/unit/makeSerializer.test.ts b/packages/serialization/tests/unit/makeSerializer.test.ts index ff9c0687..f529c718 100644 --- a/packages/serialization/tests/unit/makeSerializer.test.ts +++ b/packages/serialization/tests/unit/makeSerializer.test.ts @@ -1,11 +1,18 @@ -import { fill, hasMany, hasOne, makeModel } from '@foscia/core'; +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: 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', { @@ -25,18 +32,18 @@ describe('unit: makeSerializer', () => { 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/website/docs/digging-deeper/implementations/presentation.md b/website/docs/digging-deeper/implementations/presentation.md index 266aea45..d40fec76 100644 --- a/website/docs/digging-deeper/implementations/presentation.md +++ b/website/docs/digging-deeper/implementations/presentation.md @@ -21,7 +21,7 @@ There are 5 kinds of dependency: will deserialize records to instances. It might use the cache and registry internally. - [`Serializer`](/docs/api/@foscia/core/type-aliases/Serializer) - will serialize instances to the data source format. + will serialize instances' snapshots to the data source format. - [`InstancesCache`](/docs/api/@foscia/core/type-aliases/InstancesCache) will store already fetched models instances. It will avoid multiple instances of the same record coexisting and allows you to retrieve already diff --git a/website/docs/digging-deeper/implementations/serialization.md b/website/docs/digging-deeper/implementations/serialization.md index d7b61509..4bfe9397 100644 --- a/website/docs/digging-deeper/implementations/serialization.md +++ b/website/docs/digging-deeper/implementations/serialization.md @@ -68,8 +68,8 @@ implementation which 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 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. @@ -83,7 +83,7 @@ import { makeSerializer, makeSerializerRecordFactory } from '@foscia/serializati const serializer = makeSerializer({ createData: (records) => records, createRecord: makeSerializerRecordFactory( - (instance) => ({ id: instance.id } as Record), + (snapshot) => ({ id: snapshot.$values.id } as Record), (record, { key, value }) => { record[key] = value; }, diff --git a/website/docs/digging-deeper/usages/rest.md b/website/docs/digging-deeper/usages/rest.md index fa533d53..bda0b1bb 100644 --- a/website/docs/digging-deeper/usages/rest.md +++ b/website/docs/digging-deeper/usages/rest.md @@ -229,7 +229,9 @@ makeRestDeserializer({ 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/serialization/type-aliases/SerializerConfig#serializerelation) -option. +option, which will provide the related instance snapshot. + +#### Supporting polymorphism Here is an example which will serialize ID and type to support polymorphic relations: @@ -238,11 +240,28 @@ relations: import { makeRestSerializer } from '@foscia/rest'; makeRestSerializer({ - serializeRelation: (_, related) => ({ type: related.$model.$type, id: related.id }), + serializeRelation: (_, related) => ({ + type: related.$instance.$model.$type, + id: related.$values.id, + }), }); ``` -Here is another example where we serialize the whole related record: +:::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'; diff --git a/website/docs/upgrade/migration.md b/website/docs/upgrade/migration.md index 53165049..0f77f067 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -286,6 +286,26 @@ makeRefsCache({ }); ``` +### 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** From d4f336daf5fa713ef8fc6e3c541bf3d4c0d9480b Mon Sep 17 00:00:00 2001 From: paul Date: Sun, 19 Jan 2025 00:04:03 +0100 Subject: [PATCH 24/32] chore: fix docs broken links --- website/docs/core-concepts/actions.mdx | 2 +- website/docs/digging-deeper/models/models-composition.mdx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index 5066aaed..69ab6a2e 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -561,7 +561,7 @@ const users = await withoutHooks(action(), async (a) => { **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 +([**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/models/models-composition.mdx b/website/docs/digging-deeper/models/models-composition.mdx index 4328221c..6733dc04 100644 --- a/website/docs/digging-deeper/models/models-composition.mdx +++ b/website/docs/digging-deeper/models/models-composition.mdx @@ -74,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. @@ -216,7 +216,7 @@ export default class Post extends makeModel('posts', { ### 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. From db9694b3993394c90c9b918988263a9fd78f74eb Mon Sep 17 00:00:00 2001 From: paul Date: Sun, 19 Jan 2025 00:04:45 +0100 Subject: [PATCH 25/32] chore: refactor logger --- packages/core/src/index.ts | 1 + packages/core/src/logger/constants.ts | 13 +++++ packages/core/src/logger/logger.ts | 59 ++------------------- packages/core/src/logger/types.ts | 64 +++++++++++++++++++++++ website/docs/core-concepts/environment.md | 2 +- 5 files changed, 83 insertions(+), 56 deletions(-) create mode 100644 packages/core/src/logger/constants.ts create mode 100644 packages/core/src/logger/types.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index df964653..0391298b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -109,6 +109,7 @@ 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/props/builders/types'; export * from '@foscia/core/model/revivers/types'; export * from '@foscia/core/model/types'; diff --git a/packages/core/src/logger/constants.ts b/packages/core/src/logger/constants.ts new file mode 100644 index 00000000..00ff77e2 --- /dev/null +++ b/packages/core/src/logger/constants.ts @@ -0,0 +1,13 @@ +export const LOGGER_LEVELS = { + error: 'error', + warn: 'warn', + info: 'info', + debug: 'debug', +}; + +export const LOGGER_LEVELS_WEIGHTS = { + [LOGGER_LEVELS.debug]: 0, + [LOGGER_LEVELS.info]: 10, + [LOGGER_LEVELS.warn]: 100, + [LOGGER_LEVELS.error]: 1000, +}; diff --git a/packages/core/src/logger/logger.ts b/packages/core/src/logger/logger.ts index cc89e6b4..539074d5 100644 --- a/packages/core/src/logger/logger.ts +++ b/packages/core/src/logger/logger.ts @@ -1,28 +1,7 @@ +import { LOGGER_LEVELS_WEIGHTS } from '@foscia/core/logger/constants'; +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 = { - [LOGGER_LEVELS.debug]: 0, - [LOGGER_LEVELS.info]: 10, - [LOGGER_LEVELS.warn]: 100, - [LOGGER_LEVELS.error]: 1000, -}; - -type LoggerLevel = keyof typeof LOGGER_LEVELS; - -type LoggerOutput = { - error: (message: string, ...args: unknown[]) => void; - warn: (message: string, ...args: unknown[]) => void; - info: (message: string, ...args: unknown[]) => void; - debug: (message: string, ...args: unknown[]) => void; -}; - const makeDefaultLevel = (): LoggerLevel | null => { if (IS_TEST) { return null; @@ -45,40 +24,10 @@ const makeMessageLog = (level: LoggerLevel) => function log( }; export default { - /** - * The minimum level of logged messages. - * Defaults to `error` in PROD env, `warn` in DEV env, and `null` in TEST env. - */ level: makeDefaultLevel(), - /** - * The output to use for logged messages. - * Defaults to global `console` if available. - */ - output: ( - typeof console !== 'undefined' ? console : null - ) as LoggerOutput | null, - /** - * Log an error message. - * - * @internal - */ + output: (typeof console !== 'undefined' ? console : null) as LoggerOutput | null, error: makeMessageLog('error'), - /** - * Log a warning message. - * - * @internal - */ warn: makeMessageLog('warn'), - /** - * Log an info message. - * - * @internal - */ info: makeMessageLog('info'), - /** - * Log a debug message. - * - * @internal - */ 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..9ada2138 --- /dev/null +++ b/packages/core/src/logger/types.ts @@ -0,0 +1,64 @@ +import type { LOGGER_LEVELS } from '@foscia/core/logger/constants'; + +/** + * Available logger levels. + * + * @internal + */ +export type LoggerLevel = keyof typeof LOGGER_LEVELS; + +/** + * 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/website/docs/core-concepts/environment.md b/website/docs/core-concepts/environment.md index 642a7473..c8654a5e 100644 --- a/website/docs/core-concepts/environment.md +++ b/website/docs/core-concepts/environment.md @@ -34,7 +34,7 @@ write messages on. ### Configuring minimum log level You can configure the minimum log level by changing the -[`level`](/docs/api/@foscia/core/variables/logger#level) property +[`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. From a55603aa62a8b09bd95aa6d2ce8930d7dbc23ff9 Mon Sep 17 00:00:00 2001 From: paul Date: Tue, 28 Jan 2025 19:02:47 +0100 Subject: [PATCH 26/32] feat: add relation inverse attachment during deserialization closes #3 --- packages/core/src/index.ts | 6 ++ .../core/src/model/props/builders/relation.ts | 13 +-- .../core/src/model/props/builders/types.ts | 51 +++++++++- .../utilities/attachRelationInverse.ts | 65 ++++++++++++ .../utilities/guessRelationInverses.ts | 13 +++ packages/core/src/model/types.ts | 50 +++++----- .../core/tests/typecheck/models.test-d.ts | 44 ++++++++- .../core/tests/unit/model/snapshots.test.ts | 50 ++++++++++ packages/rest/tests/integration/crud.test.ts | 2 + .../mocks/composables/commentable.mock.ts | 2 +- .../rest/tests/mocks/models/comment.mock.ts | 5 +- .../serialization/src/makeDeserializer.ts | 44 ++++++--- packages/serialization/src/types.ts | 11 +++ .../shared/src/maps/makeIdentifiersMap.ts | 45 ++++----- packages/shared/src/types.ts | 1 + website/docs/core-concepts/models.mdx | 98 ++++++++++++++++++- .../models/models-configuration.md | 29 +++++- 17 files changed, 448 insertions(+), 81 deletions(-) create mode 100644 packages/core/src/model/relations/utilities/attachRelationInverse.ts create mode 100644 packages/core/src/model/relations/utilities/guessRelationInverses.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 0391298b..4409e401 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ import isModelUsing from '@foscia/core/model/checks/isModelUsing'; import isPluralRelationDef from '@foscia/core/model/checks/isPluralRelationDef'; 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 fill from '@foscia/core/model/fill'; import filled from '@foscia/core/model/filled'; import forceFill from '@foscia/core/model/forceFill'; @@ -64,6 +65,8 @@ import 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'; @@ -178,6 +181,7 @@ export { onPropertyReading, onPropertyWrite, onPropertyWriting, + isSnapshot, isSameSnapshot, restoreSnapshot, takeSnapshot, @@ -198,6 +202,8 @@ export { mapRelations, shouldSync, guessRelationType, + guessRelationInverses, + attachRelationInverse, normalizeDotRelations, normalizeInclude, normalizeKey, diff --git a/packages/core/src/model/props/builders/relation.ts b/packages/core/src/model/props/builders/relation.ts index a5564789..dae03582 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -2,13 +2,9 @@ import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePro import { ModelRelationFactory, ModelRelationFactoryConfig, + ModelRelationFactorySpecialConfig, } from '@foscia/core/model/props/builders/types'; -import { - ModelInstance, - ModelRelation, - ModelRelationConfig, - ModelRelationType, -} from '@foscia/core/model/types'; +import { ModelInstance, ModelRelation, ModelRelationType } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; import { Awaitable } from '@foscia/shared'; @@ -37,9 +33,10 @@ export default ( : config; })(), } as ModelRelation, { - config: (newConfig: string | string[] | ModelRelationConfig) => ( + config: (newConfig: string | string[] | ModelRelationFactorySpecialConfig) => ( typeof config === 'string' || Array.isArray(newConfig) ? { type: newConfig } : newConfig - ) as ModelRelationConfig, + ) as ModelRelationFactorySpecialConfig, + inverse: (inverse) => ({ inverse }), }) as ModelRelationFactory; diff --git a/packages/core/src/model/props/builders/types.ts b/packages/core/src/model/props/builders/types.ts index acd9fa7a..41d681cf 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -6,7 +6,7 @@ import { ModelPropFactory, ModelPropSync, ModelRelation, - ModelRelationConfig, + ModelRelationKey, } from '@foscia/core/model/types'; import { ObjectTransformer } from '@foscia/core/transformers/types'; import { Arrayable, Constructor } from '@foscia/shared'; @@ -125,6 +125,17 @@ export type InferModelRelationFactoryInstance = : M extends Constructor ? I extends object ? I : never : never; +/** + * Infer a model's relation possible inverse key. + * + * @internal + */ +export type InferModelRelationInverseKey = 0 extends (1 & T) + ? string + : T extends (infer I)[] + ? ModelRelationKey + : ModelRelationKey; + /** * Model relationship factory object. * @@ -136,7 +147,9 @@ export type ModelRelationFactory = { * * @param config */ - config: (config: string | string[] | ModelRelationConfig) => ModelRelationFactory; + config: ( + config: string | string[] | ModelRelationFactorySpecialConfig, + ) => ModelRelationFactory; /** * Define default value. * Object values should be provided with a factory function to avoid @@ -167,6 +180,12 @@ export type ModelRelationFactory = { * @param sync */ sync: (sync: boolean | ModelPropSync) => ModelRelationFactory; + /** + * Define the inverse of the relation. + * + * @param inverse + */ + inverse: (inverse?: InferModelRelationInverseKey | boolean) => ModelRelationFactory; } & ModelPropFactory>; /** @@ -175,5 +194,31 @@ export type ModelRelationFactory = { * @internal */ export type ModelRelationFactoryConfig | null, R extends boolean> = - & ModelRelationConfig + & ModelRelationFactorySpecialConfig & Pick, 'default' | 'readOnly' | 'alias' | 'sync'>; + +/** + * Model relation factory object special options. + * + * @internal + */ +export type ModelRelationFactorySpecialConfig = { + /** + * The related type(s) to help Foscia resolving related models. + */ + type?: string | string[]; + /** + * The inverse relation key on related instances. + */ + inverse?: InferModelRelationInverseKey | 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; +}; 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..7a89f043 --- /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, ModelProp, 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 + ) as ModelProp | 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/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/types.ts b/packages/core/src/model/types.ts index 0b129ad6..2542ff9a 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -63,6 +63,12 @@ export type ModelConfig = { * 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. @@ -294,28 +300,6 @@ export type ModelRelationType = | typeof SYMBOL_MODEL_RELATION_HAS_ONE | typeof SYMBOL_MODEL_RELATION_HAS_MANY; -/** - * Configuration of a model's relation. - * - * @internal - */ -export type ModelRelationConfig = { - /** - * The related type(s) to help Foscia resolving related models. - */ - type?: string | string[]; - - // Specific HTTP config. - - /** - * The path to use when requesting relation's endpoint. - * - * @remarks - * This is specific to HTTP implementations (REST, JSON:API). - */ - path?: string; -}; - /** * Model sync relation property definition. */ @@ -333,9 +317,29 @@ export type ModelRelation = * @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 & ModelValueProp; /** diff --git a/packages/core/tests/typecheck/models.test-d.ts b/packages/core/tests/typecheck/models.test-d.ts index 81c763c1..acecebb4 100644 --- a/packages/core/tests/typecheck/models.test-d.ts +++ b/packages/core/tests/typecheck/models.test-d.ts @@ -4,8 +4,12 @@ import { attr, fill, + hasMany, hasOne, + makeComposable, makeModel, + Model, + ModelIdType, ModelInstance, ModelLimitedSnapshot, ModelSnapshot, @@ -163,7 +167,7 @@ test('Models are type safe', () => { expectTypeOf(chained.email).toEqualTypeOf(); expectTypeOf(chained.age).toEqualTypeOf(); - class ModelProps extends makeModel('model-with-object-props', { + class ModelProps extends makeModel('model-props', { attr1: attr('', { readOnly: true }), attr2: attr(toString(), { default: null }), attr3: attr(toString(), { readOnly: true }), @@ -192,4 +196,42 @@ test('Models are type safe', () => { 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/model/snapshots.test.ts b/packages/core/tests/unit/model/snapshots.test.ts index ad78be40..fcd1ee40 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, + fill, + hasOne, isSameSnapshot, + isSnapshot, + makeModel, markSynced, restore, restoreSnapshot, @@ -133,4 +138,49 @@ describe.concurrent('unit: snapshots', () => { expect(cloneValue).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/rest/tests/integration/crud.test.ts b/packages/rest/tests/integration/crud.test.ts index eefd90c8..d4b30e52 100644 --- a/packages/rest/tests/integration/crud.test.ts +++ b/packages/rest/tests/integration/crud.test.ts @@ -81,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); 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/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/serialization/src/makeDeserializer.ts b/packages/serialization/src/makeDeserializer.ts index a70dfa05..8b9aaf9a 100644 --- a/packages/serialization/src/makeDeserializer.ts +++ b/packages/serialization/src/makeDeserializer.ts @@ -1,4 +1,5 @@ import { + attachRelationInverse, consumeAction, consumeCache, consumeId, @@ -220,9 +221,7 @@ You should either: context, ); if (await shouldDeserialize(deserializerContext)) { - forceFill(instance, { - [def.key]: await deserializeAttributeValue(deserializerContext), - }); + forceFill(instance, { [def.key]: await deserializeAttributeValue(deserializerContext) }); } }), ...mapRelations(instance.$model, async (def) => { @@ -233,11 +232,13 @@ 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)); } }), ]); @@ -251,14 +252,6 @@ You should either: )); 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); - } - return instance; }; @@ -309,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( @@ -326,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; diff --git a/packages/serialization/src/types.ts b/packages/serialization/src/types.ts index 8721eef7..836ee18c 100644 --- a/packages/serialization/src/types.ts +++ b/packages/serialization/src/types.ts @@ -192,6 +192,15 @@ export type DeserializerConfig< */ export type RecordDeserializer = & { + /** + * Deserialize one record. + * + * @param record + * @param context + * @param instancesMap + * + * @internal + */ deserializeRecord( record: DeserializerRecord, context: {}, @@ -379,6 +388,8 @@ export type RecordSerializer = * @param value * @param context * @param parents + * + * @internal */ serializeToRecords( value: ModelSnapshot, diff --git a/packages/shared/src/maps/makeIdentifiersMap.ts b/packages/shared/src/maps/makeIdentifiersMap.ts index f073cb62..43c09185 100644 --- a/packages/shared/src/maps/makeIdentifiersMap.ts +++ b/packages/shared/src/maps/makeIdentifiersMap.ts @@ -6,33 +6,24 @@ 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/types.ts b/packages/shared/src/types.ts index 2f3a8c02..348555da 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -119,6 +119,7 @@ export type Middleware = (value: V, next: MiddlewareNext) => R; * @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; diff --git a/website/docs/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index 4b137c8a..af29546a 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -383,6 +383,17 @@ hasOne(() => [Comment, Post]); hasOne(['comments', 'posts']); ``` + + + +```typescript +hasOne(() => CreditCard, { inverse: true }); +hasOne(() => CreditCard, { inverse: 'owner' }); +``` + User) .nullable() .readOnly() .alias('author') - .sync('pull'); + .sync('pull') + .inverse(); ``` @@ -451,6 +463,17 @@ hasMany(() => [Comment, Post]); hasMany<(Comment | Post)[]>(['comments', 'posts']); ``` + + + +```typescript +hasMany(() => Post, { inverse: true }); +hasMany(() => Post, { inverse: 'author' }); +``` + ({ path: 'top-comments' }); ```typescript hasMany(() => Comment) .readOnly() - .sync('pull'); + .sync('pull') + .inverse(); ``` @@ -500,6 +524,76 @@ 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. + +::: + +##### Configuring an inverse + +To enable inverse of a relation, you can define an +[`inverse` option](/docs/api/@foscia/core/type-aliases/ModelRelationConfig#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 + +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().run(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 ##### Explicit model when not having circular references diff --git a/website/docs/digging-deeper/models/models-configuration.md b/website/docs/digging-deeper/models/models-configuration.md index cdb31b56..a06bcff8 100644 --- a/website/docs/digging-deeper/models/models-configuration.md +++ b/website/docs/digging-deeper/models/models-configuration.md @@ -137,7 +137,7 @@ your models. 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`; +relation, this would guess the type to `blog-posts`: ```typescript title="post.ts" import { makeModel, isPluralRelationDef, ModelRelation } from '@foscia/core'; @@ -150,6 +150,33 @@ makeModel({ }); ``` +#### `guessRelationInverse` + +##### Description + +**Default**: singularize parent model type. + +**Recommandation**: use this configuration option inside a +[custom model factory](/docs/digging-deeper/models/models-composition#factory). + +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, isPluralRelationDef, ModelRelation } from '@foscia/core'; + +makeModel({ + type: 'posts', + guessRelationInverse: (def: ModelRelation) => toCamelCase(singularize(def.parent.$type)), +}); +``` + #### `limitedSnapshots` ##### Description From 883dcc8e379af93d8146f714da0dba16ed37c170 Mon Sep 17 00:00:00 2001 From: paul Date: Tue, 28 Jan 2025 22:03:42 +0100 Subject: [PATCH 27/32] docs: improve docs --- typedoc.json | 7 - website/docs/core-concepts/actions.mdx | 158 +++--------------- website/docs/core-concepts/models.mdx | 12 +- .../digging-deeper/actions/actions-hooks.md | 74 ++++++++ .../actions/actions-middlewares.mdx | 69 ++++++++ website/docusaurus.config.js | 7 + 6 files changed, 176 insertions(+), 151 deletions(-) create mode 100644 website/docs/digging-deeper/actions/actions-hooks.md create mode 100644 website/docs/digging-deeper/actions/actions-middlewares.mdx diff --git a/typedoc.json b/typedoc.json index 4863d96a..d07ee3b8 100644 --- a/typedoc.json +++ b/typedoc.json @@ -29,13 +29,6 @@ "@provideContext", "@requireContext" ], - "categoryOrder": [ - "Enhancers", - "Runners", - "Utilities", - "Factories", - "Hooks" - ], "groupOrder": [ "Functions", "Classes", diff --git a/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index 69ab6a2e..2a8e41d1 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 @@ -13,7 +12,7 @@ import FunctionInfo from '@site/src/components/FunctionInfo'; - Enhancing actions - Running actions - Conditionally enhancing and running actions -- Registering hooks on actions +- Action lifecycle ::: @@ -34,12 +33,13 @@ 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). ::: @@ -403,31 +403,33 @@ action().run( When using [variadic `run` calls](#variadic-run), 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. -); +action() // Creation. + .run( + query(Post), // `query` enhancement. + include('comments'), // `include` enhancement. + all(), // `all` run. + ); ``` The above example is equivalent to and can be decomposed as follows: ```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 @@ -459,10 +461,6 @@ action().use( ); ``` - - Enhancers API reference - - ### Runners An action can be run using the `run` method. The runner can execute @@ -476,7 +474,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() @@ -499,120 +498,3 @@ const posts = await action().run( all(), ); ``` - - - Runners API reference - - -### Hooks - -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(({ 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`](/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.use(query(User)).run(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. - -::: - -### 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; - }, -])); -``` - -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/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index af29546a..46e90dd2 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -3,7 +3,6 @@ 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 ShellCommand from '@site/src/components/ShellCommand'; @@ -153,11 +152,12 @@ those. ### Utilities -Foscia provides multiple utilities functions to interact with models. - - - Models' utilities API reference - +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 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..15814a52 --- /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.use(query(User)).run(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/docusaurus.config.js b/website/docusaurus.config.js index 2112740f..2d498947 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -75,6 +75,13 @@ const config = { '@requireContext', '@example', ], + categoryOrder: [ + 'Enhancers', + 'Runners', + 'Utilities', + 'Factories', + 'Hooks', + ], }), ], ], From 511e37efece5e0991cd0b657547bb1b049c31a32 Mon Sep 17 00:00:00 2001 From: paul Date: Tue, 28 Jan 2025 22:22:17 +0100 Subject: [PATCH 28/32] feat: remove config chained modifier on relations factory BREAKING CHANGE: `config` chained modifier of relations factories is removed. You must use the new call signature to provide config objects. --- .../core/src/model/props/builders/relation.ts | 6 --- .../core/src/model/props/builders/types.ts | 43 +++---------------- website/docs/core-concepts/models.mdx | 2 +- website/docs/upgrade/migration.md | 22 ++++++++++ 4 files changed, 30 insertions(+), 43 deletions(-) diff --git a/packages/core/src/model/props/builders/relation.ts b/packages/core/src/model/props/builders/relation.ts index dae03582..64cf5c07 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -2,7 +2,6 @@ import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePro import { ModelRelationFactory, ModelRelationFactoryConfig, - ModelRelationFactorySpecialConfig, } from '@foscia/core/model/props/builders/types'; import { ModelInstance, ModelRelation, ModelRelationType } from '@foscia/core/model/types'; import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; @@ -33,10 +32,5 @@ export default ( : config; })(), } as ModelRelation, { - config: (newConfig: string | string[] | ModelRelationFactorySpecialConfig) => ( - typeof config === 'string' || Array.isArray(newConfig) - ? { type: newConfig } - : newConfig - ) as ModelRelationFactorySpecialConfig, inverse: (inverse) => ({ inverse }), }) as ModelRelationFactory; diff --git a/packages/core/src/model/props/builders/types.ts b/packages/core/src/model/props/builders/types.ts index 41d681cf..69225751 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -142,14 +142,6 @@ export type InferModelRelationInverseKey = 0 extends (1 & T) * @internal */ export type ModelRelationFactory = { - /** - * Define the related types or the configuration object. - * - * @param config - */ - config: ( - config: string | string[] | ModelRelationFactorySpecialConfig, - ) => ModelRelationFactory; /** * Define default value. * Object values should be provided with a factory function to avoid @@ -194,31 +186,10 @@ export type ModelRelationFactory = { * @internal */ export type ModelRelationFactoryConfig | null, R extends boolean> = - & ModelRelationFactorySpecialConfig - & Pick, 'default' | 'readOnly' | 'alias' | 'sync'>; - -/** - * Model relation factory object special options. - * - * @internal - */ -export type ModelRelationFactorySpecialConfig = { - /** - * The related type(s) to help Foscia resolving related models. - */ - type?: string | string[]; - /** - * The inverse relation key on related instances. - */ - inverse?: InferModelRelationInverseKey | 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; -}; + & { + /** + * The inverse relation key on related instances. + */ + inverse?: InferModelRelationInverseKey | boolean; + } + & Pick, 'type' | 'path' | 'default' | 'readOnly' | 'alias' | 'sync'>; diff --git a/website/docs/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index 46e90dd2..c66efc83 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -543,7 +543,7 @@ during deserialization process. ##### Configuring an inverse To enable inverse of a relation, you can define an -[`inverse` option](/docs/api/@foscia/core/type-aliases/ModelRelationConfig#inverse) +[`inverse` option](/docs/api/@foscia/core/type-aliases/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 diff --git a/website/docs/upgrade/migration.md b/website/docs/upgrade/migration.md index 0f77f067..631ae8bd 100644 --- a/website/docs/upgrade/migration.md +++ b/website/docs/upgrade/migration.md @@ -20,6 +20,7 @@ sidebar_position: 5 - [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 @@ -225,6 +226,27 @@ 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** From 0c8c2b812a84feb164b1271f93203b3214ff16d7 Mon Sep 17 00:00:00 2001 From: paul Date: Tue, 28 Jan 2025 22:31:20 +0100 Subject: [PATCH 29/32] feat(core): add `path` chained modifier to relation --- packages/core/src/model/props/builders/relation.ts | 1 + packages/core/src/model/props/builders/types.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/packages/core/src/model/props/builders/relation.ts b/packages/core/src/model/props/builders/relation.ts index 64cf5c07..5f2d66f9 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -33,4 +33,5 @@ export default ( })(), } as ModelRelation, { 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 69225751..bba414a4 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -178,6 +178,15 @@ export type ModelRelationFactory = { * @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; } & ModelPropFactory>; /** From 9be8ce317ab33ac1cd4f750809f16cf3656a3708 Mon Sep 17 00:00:00 2001 From: paul Date: Wed, 29 Jan 2025 22:07:35 +0100 Subject: [PATCH 30/32] feat(core): action factory variadic calls and more flexible composables --- .../core/src/actions/actionVariadicRun.ts | 27 - .../core/src/actions/actionVariadicUse.ts | 27 - .../context/enhancers/crud/associate.ts | 19 +- .../actions/context/enhancers/crud/attach.ts | 20 +- .../actions/context/enhancers/crud/detach.ts | 20 +- .../context/enhancers/crud/dissociate.ts | 14 +- .../context/enhancers/crud/updateRelation.ts | 54 +- .../context/enhancers/hooks/onError.ts | 1 + .../context/enhancers/hooks/onFinally.ts | 1 + .../context/enhancers/hooks/onRunning.ts | 1 + .../context/enhancers/hooks/onSuccess.ts | 1 + .../context/guessers/guessContextModel.ts | 7 +- .../core/src/actions/context/runners/all.ts | 10 +- .../src/actions/context/runners/cachedOr.ts | 4 + .../src/actions/context/runners/catchIf.ts | 71 +- .../core/src/actions/context/runners/one.ts | 4 +- .../core/src/actions/context/runners/oneOr.ts | 16 +- .../actions/context/runners/oneOrCurrent.ts | 6 +- .../src/actions/context/runners/oneOrFail.ts | 6 +- .../context/utilities/deserializeInstances.ts | 9 +- .../utilities/registerWriteActionHooks.ts | 2 + .../context/utilities/serializeRelation.ts | 12 +- packages/core/src/actions/index.ts | 15 +- .../core/src/actions/makeActionFactory.ts | 18 +- packages/core/src/actions/makeEnhancer.ts | 7 +- packages/core/src/actions/makeRunner.ts | 7 +- packages/core/src/actions/types.ts | 61 +- packages/core/src/actions/variadic.ts | 74 ++ .../core/src/cache/makeTimedRefFactory.ts | 4 +- packages/core/src/cache/types.ts | 9 + .../core/src/errors/flags/isNotFoundError.ts | 12 - packages/core/src/errors/flags/types.ts | 6 - packages/core/src/flags.ts | 8 + packages/core/src/hooks/registerHook.ts | 2 +- packages/core/src/hooks/runHooks.ts | 2 +- packages/core/src/hooks/runHooksSync.ts | 2 +- packages/core/src/index.ts | 18 +- packages/core/src/logger/constants.ts | 13 - packages/core/src/logger/logger.ts | 8 +- packages/core/src/logger/types.ts | 4 +- .../core/src/model/checks/isAttributeDef.ts | 2 +- .../core/src/model/checks/isComposable.ts | 4 +- packages/core/src/model/checks/isIdDef.ts | 2 +- .../core/src/model/checks/isInstanceUsing.ts | 14 +- .../core/src/model/checks/isModelUsing.ts | 14 +- .../src/model/checks/isPluralRelationDef.ts | 2 +- .../core/src/model/checks/isPropFactory.ts | 14 - .../core/src/model/checks/isRelationDef.ts | 2 +- .../src/model/checks/isSingularRelationDef.ts | 2 +- .../src/model/composition/applyDefinition.ts | 35 + .../src/model/composition/makeComposable.ts | 89 +++ .../composition/makeComposableFactory.ts | 37 + .../model/{ => composition}/makeDefinition.ts | 3 +- packages/core/src/model/composition/types.ts | 9 + packages/core/src/model/fill.ts | 4 +- packages/core/src/model/forceFill.ts | 2 +- .../core/src/model/hooks/makeInstanceHook.ts | 4 +- .../core/src/model/hooks/makeModelHook.ts | 4 +- .../hooks/properties/makePropertyReadHook.ts | 67 +- .../hooks/properties/makePropertyWriteHook.ts | 71 +- packages/core/src/model/makeComposable.ts | 32 - packages/core/src/model/makeModelClass.ts | 110 ++- packages/core/src/model/makeModelFactory.ts | 17 +- .../core/src/model/props/builders/attr.ts | 3 +- packages/core/src/model/props/builders/id.ts | 8 +- .../props/builders/makeBuilderPropFactory.ts | 34 - .../builders/makePropChainableFactory.ts | 34 + .../model/props/builders/makePropFactory.ts | 34 +- .../props/builders/makeValuePropFactory.ts | 32 +- .../core/src/model/props/builders/relation.ts | 3 +- .../core/src/model/props/builders/types.ts | 57 +- .../src/model/props/mappers/mapAttributes.ts | 6 +- .../core/src/model/props/mappers/mapProps.ts | 10 +- .../src/model/props/mappers/mapRelations.ts | 6 +- packages/core/src/model/props/shouldSync.ts | 4 +- .../model/relations/makeQueryModelLoader.ts | 22 +- .../relations/makeQueryRelationLoader.ts | 2 +- .../relations/makeRefreshIncludeLoader.ts | 4 +- .../utilities/attachRelationInverse.ts | 4 +- packages/core/src/model/revivers/types.ts | 4 + .../src/model/snapshots/isSameSnapshot.ts | 2 +- .../core/src/model/snapshots/markSynced.ts | 2 + .../src/model/snapshots/restoreSnapshot.ts | 29 +- .../model/snapshots/takeLimitedSnapshot.ts | 8 +- .../core/src/model/snapshots/takeSnapshot.ts | 4 +- .../utilities/captureSnapshotValues.ts | 2 +- packages/core/src/model/types.ts | 675 +++++++++--------- .../normalization/normalizeDotRelations.ts | 12 +- .../core/src/normalization/normalizeKey.ts | 12 +- packages/core/src/registry/types.ts | 4 + .../core/src/transformers/isTransformer.ts | 7 + packages/core/src/transformers/toBoolean.ts | 12 +- packages/core/src/transformers/types.ts | 2 + packages/core/src/types.ts | 10 + packages/core/tests/mocks/models/post.mock.ts | 4 +- .../core/tests/typecheck/actions.test-d.ts | 9 +- .../typecheck/models-composition.test-d.ts | 81 ++- .../core/tests/typecheck/models.test-d.ts | 7 + packages/core/tests/unit/model/hooks.test.ts | 16 +- .../core/tests/unit/model/snapshots.test.ts | 28 +- packages/http/src/errors/httpNotFoundError.ts | 9 +- packages/http/src/makeHttpAdapter.ts | 4 +- packages/http/src/types.ts | 29 +- .../src/actions/context/enhancers/sortBy.ts | 5 +- .../actions/context/runners/usingDocument.ts | 2 +- packages/jsonapi/src/types.ts | 19 +- packages/rest/src/types.ts | 14 +- .../serialization/src/makeDeserializer.ts | 8 +- packages/serialization/src/makeSerializer.ts | 8 +- packages/serialization/src/types.ts | 20 +- packages/shared/src/checks/isFosciaFlag.ts | 19 + .../src/descriptors/makeDescriptorHolder.ts | 4 +- packages/shared/src/descriptors/types.ts | 35 +- packages/shared/src/index.ts | 7 +- packages/shared/src/types.ts | 54 +- .../shared/tests/unit/checks/flags.test.ts | 32 + packages/test/src/fosciaTestError.ts | 1 - packages/test/src/makeActionFactoryMock.ts | 32 +- .../test/src/makeActionFactoryMockable.ts | 18 +- packages/test/src/mockAction.ts | 4 +- packages/test/src/types.ts | 16 +- packages/test/src/unmockAction.ts | 4 +- scripts/generate-files.js | 81 ++- scripts/lint.js | 40 +- website/docs/core-concepts/actions.mdx | 195 ++++- website/docs/core-concepts/models.mdx | 14 +- .../digging-deeper/actions/actions-hooks.md | 2 +- .../actions/custom-action-enhancers.mdx | 2 +- .../actions/custom-action-runners.mdx | 2 +- .../actions/models-registration.mdx | 2 +- .../digging-deeper/implementations/core.md | 16 +- .../digging-deeper/implementations/http.md | 4 +- .../digging-deeper/implementations/jsonapi.md | 16 +- .../implementations/presentation.md | 10 +- .../digging-deeper/implementations/rest.md | 16 +- .../implementations/serialization.md | 8 +- .../models/models-changes-tracking.md | 2 +- .../models/models-configuration.md | 12 +- .../models/models-reduce-revive.mdx | 4 +- website/docs/digging-deeper/usages/cli.mdx | 19 +- website/docs/digging-deeper/usages/http.mdx | 14 +- website/docs/digging-deeper/usages/jsonapi.md | 28 +- website/docs/digging-deeper/usages/rest.md | 25 +- .../docs/digging-deeper/usages/testing.mdx | 2 +- website/docs/examples/jsonapi-blog.mdx | 12 +- website/docs/getting-started.mdx | 30 +- website/docs/integrations/nuxt.mdx | 2 +- website/src/components/HomeFeatures/index.js | 4 +- 148 files changed, 1981 insertions(+), 1260 deletions(-) delete mode 100644 packages/core/src/actions/actionVariadicRun.ts delete mode 100644 packages/core/src/actions/actionVariadicUse.ts create mode 100644 packages/core/src/actions/variadic.ts delete mode 100644 packages/core/src/errors/flags/isNotFoundError.ts delete mode 100644 packages/core/src/errors/flags/types.ts create mode 100644 packages/core/src/flags.ts delete mode 100644 packages/core/src/logger/constants.ts delete mode 100644 packages/core/src/model/checks/isPropFactory.ts create mode 100644 packages/core/src/model/composition/applyDefinition.ts create mode 100644 packages/core/src/model/composition/makeComposable.ts create mode 100644 packages/core/src/model/composition/makeComposableFactory.ts rename packages/core/src/model/{ => composition}/makeDefinition.ts (80%) create mode 100644 packages/core/src/model/composition/types.ts delete mode 100644 packages/core/src/model/makeComposable.ts delete mode 100644 packages/core/src/model/props/builders/makeBuilderPropFactory.ts create mode 100644 packages/core/src/model/props/builders/makePropChainableFactory.ts create mode 100644 packages/shared/src/checks/isFosciaFlag.ts create mode 100644 packages/shared/tests/unit/checks/flags.test.ts diff --git a/packages/core/src/actions/actionVariadicRun.ts b/packages/core/src/actions/actionVariadicRun.ts deleted file mode 100644 index 50408842..00000000 --- a/packages/core/src/actions/actionVariadicRun.ts +++ /dev/null @@ -1,27 +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`. - -/** - * Variadic action run methods type. - * - * @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>; -}; diff --git a/packages/core/src/actions/actionVariadicUse.ts b/packages/core/src/actions/actionVariadicUse.ts deleted file mode 100644 index 622332e0..00000000 --- a/packages/core/src/actions/actionVariadicUse.ts +++ /dev/null @@ -1,27 +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`. - -/** - * Variadic action use methods type. - * - * @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; -}; diff --git a/packages/core/src/actions/context/enhancers/crud/associate.ts b/packages/core/src/actions/context/enhancers/crud/associate.ts index a81a424b..20f70110 100644 --- a/packages/core/src/actions/context/enhancers/crud/associate.ts +++ b/packages/core/src/actions/context/enhancers/crud/associate.ts @@ -1,17 +1,14 @@ -import updateRelation, { - UpdateRelationValue, -} from '@foscia/core/actions/context/enhancers/crud/updateRelation'; +import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; import onSuccess from '@foscia/core/actions/context/enhancers/hooks/onSuccess'; 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 { - InferModelSchemaProp, ModelInstance, - ModelRelation, ModelRelationKey, - ModelWritableValues, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; /** @@ -33,22 +30,22 @@ import { * await action().run(associate(post, 'author', user), none()); * ``` */ + export default /* @__PURE__ */ makeEnhancer('associate', < C extends {}, I extends ModelInstance, - K extends string, - R extends InferModelSchemaProp, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( instance: I, - relation: K & ModelRelationKey, - value: UpdateRelationValue, + relation: K, + value: ModelValues[K], ) => (action: Action>) => action.use( updateRelation(instance, relation, value), onSuccess(() => { - fill(instance, { [relation]: value } as Partial>); + 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 00248752..4b47bd5e 100644 --- a/packages/core/src/actions/context/enhancers/crud/attach.ts +++ b/packages/core/src/actions/context/enhancers/crud/attach.ts @@ -1,14 +1,13 @@ import ActionName from '@foscia/core/actions/actionName'; -import updateRelation, { - UpdateRelationValue, -} from '@foscia/core/actions/context/enhancers/crud/updateRelation'; +import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - InferModelSchemaProp, ModelInstance, - ModelRelation, ModelRelationKey, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; +import { Itemable, wrap } from '@foscia/shared'; /** * Prepare context for a plural relation's update operation. @@ -32,18 +31,17 @@ import { export default /* @__PURE__ */ makeEnhancer('attach', < C extends {}, I extends ModelInstance, - K extends string, - R extends InferModelSchemaProp, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( instance: I, - relation: K & ModelRelationKey, - value: UpdateRelationValue, -) => updateRelation( + relation: K, + value: Itemable[K]>, +) => updateRelation( instance, relation, - value, + wrap(value) as ModelValues[K], ActionName.ATTACH_RELATION, )); diff --git a/packages/core/src/actions/context/enhancers/crud/detach.ts b/packages/core/src/actions/context/enhancers/crud/detach.ts index 9416d0e5..4af8e163 100644 --- a/packages/core/src/actions/context/enhancers/crud/detach.ts +++ b/packages/core/src/actions/context/enhancers/crud/detach.ts @@ -1,14 +1,13 @@ import ActionName from '@foscia/core/actions/actionName'; -import updateRelation, { - UpdateRelationValue, -} from '@foscia/core/actions/context/enhancers/crud/updateRelation'; +import updateRelation from '@foscia/core/actions/context/enhancers/crud/updateRelation'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; import { - InferModelSchemaProp, ModelInstance, - ModelRelation, ModelRelationKey, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; +import { Itemable, wrap } from '@foscia/shared'; /** * Prepare context for a plural relation's update operation. @@ -32,18 +31,17 @@ import { export default /* @__PURE__ */ makeEnhancer('detach', < C extends {}, I extends ModelInstance, - K extends string, - R extends InferModelSchemaProp, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( instance: I, - relation: K & ModelRelationKey, - value: UpdateRelationValue, -) => updateRelation( + relation: K, + value: Itemable[K]>, +) => updateRelation( instance, relation, - value, + wrap(value) as ModelValues[K], ActionName.DETACH_RELATION, )); diff --git a/packages/core/src/actions/context/enhancers/crud/dissociate.ts b/packages/core/src/actions/context/enhancers/crud/dissociate.ts index 731294ce..065bf429 100644 --- a/packages/core/src/actions/context/enhancers/crud/dissociate.ts +++ b/packages/core/src/actions/context/enhancers/crud/dissociate.ts @@ -1,11 +1,6 @@ import associate from '@foscia/core/actions/context/enhancers/crud/associate'; import makeEnhancer from '@foscia/core/actions/makeEnhancer'; -import { - InferModelSchemaProp, - ModelInstance, - ModelRelation, - ModelRelationKey, -} from '@foscia/core/model/types'; +import { ModelInstance, ModelRelationKey, ModelWritableKey } from '@foscia/core/model/types'; /** * Prepare context for a singular relation's update operation. @@ -28,15 +23,14 @@ import { export default /* @__PURE__ */ makeEnhancer('dissociate', < C extends {}, I extends ModelInstance, - K extends string, - R extends InferModelSchemaProp, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( instance: I, - relation: K & ModelRelationKey, -) => associate( + relation: K, +) => associate( instance, relation, null as any, diff --git a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts index d9cd50d1..808dd774 100644 --- a/packages/core/src/actions/context/enhancers/crud/updateRelation.ts +++ b/packages/core/src/actions/context/enhancers/crud/updateRelation.ts @@ -10,22 +10,21 @@ import { ConsumeRelation, ConsumeSerializer, } from '@foscia/core/actions/types'; -import isSingularRelationDef from '@foscia/core/model/checks/isSingularRelationDef'; import { InferModelSchemaProp, - InferModelValuePropType, ModelInstance, ModelRelation, ModelRelationKey, + ModelValues, + ModelWritableKey, } from '@foscia/core/model/types'; -import { using, wrap } from '@foscia/shared'; -export type UpdateRelationActionName = - | ActionName.UPDATE_RELATION - | ActionName.ATTACH_RELATION - | ActionName.DETACH_RELATION; - -export type UpdateRelationValue = R extends ModelRelation +/** + * Infer the relation update possible values. + * + * @internal + */ +export type InferRelationUpdateValue = R extends ModelRelation ? NonNullable extends any[] ? T | NonNullable[number] : T : never; /** @@ -51,28 +50,25 @@ export type UpdateRelationValue = R extends ModelRelation export default /* @__PURE__ */ makeEnhancer('updateRelation', < C extends {}, I extends ModelInstance, - K extends string, - R extends InferModelSchemaProp, + K extends ModelWritableKey & ModelRelationKey, Record, Related, Data, >( instance: I, - relation: K & ModelRelationKey, - value: UpdateRelationValue, - actionName: UpdateRelationActionName = ActionName.UPDATE_RELATION, -) => async (action: Action>) => using( - isSingularRelationDef(instance.$model.$schema[relation] as R) ? value : wrap(value), - async (wrappedValue) => action.use( - query(instance, relation), - context({ - action: actionName, - data: await serializeRelation( - await action.useContext(), - instance, - relation, - wrappedValue as InferModelValuePropType, - ), - }), - ) as unknown as Action & ConsumeRelation & ConsumeId>, -)); + 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/hooks/onError.ts b/packages/core/src/actions/context/enhancers/hooks/onError.ts index aafa1db1..e7aa5ecd 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onError.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onError.ts @@ -10,6 +10,7 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks * * @example * ```typescript diff --git a/packages/core/src/actions/context/enhancers/hooks/onFinally.ts b/packages/core/src/actions/context/enhancers/hooks/onFinally.ts index 1caed3d0..bb55bc7e 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onFinally.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onFinally.ts @@ -10,6 +10,7 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks * * @example * ```typescript diff --git a/packages/core/src/actions/context/enhancers/hooks/onRunning.ts b/packages/core/src/actions/context/enhancers/hooks/onRunning.ts index 30fc1806..695783c0 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onRunning.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onRunning.ts @@ -10,6 +10,7 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks * * @example * ```typescript diff --git a/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts b/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts index 2170dae3..bbd7ad89 100644 --- a/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts +++ b/packages/core/src/actions/context/enhancers/hooks/onSuccess.ts @@ -10,6 +10,7 @@ import { Awaitable } from '@foscia/shared'; * @param callback * * @category Enhancers + * @category Hooks * * @example * ```typescript diff --git a/packages/core/src/actions/context/guessers/guessContextModel.ts b/packages/core/src/actions/context/guessers/guessContextModel.ts index 620bec95..648a94e0 100644 --- a/packages/core/src/actions/context/guessers/guessContextModel.ts +++ b/packages/core/src/actions/context/guessers/guessContextModel.ts @@ -3,7 +3,12 @@ import { Model, ModelRelation } from '@foscia/core/model/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; diff --git a/packages/core/src/actions/context/runners/all.ts b/packages/core/src/actions/context/runners/all.ts index c5e1f8f8..26462d9e 100644 --- a/packages/core/src/actions/context/runners/all.ts +++ b/packages/core/src/actions/context/runners/all.ts @@ -1,5 +1,5 @@ import deserializeInstances, { - DeserializedDataOf, + RetypedDeserializedData, } from '@foscia/core/actions/context/utilities/deserializeInstances'; import executeContextThroughAdapter from '@foscia/core/actions/context/utilities/executeContextThroughAdapter'; @@ -14,6 +14,10 @@ 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, @@ -47,7 +51,7 @@ export default /* @__PURE__ */ makeRunner('all', < Deserialized extends DeserializedData, Next = I[], >(transform?: ( - data: AllData, I>, + data: AllData, I>, ) => Awaitable) => async ( // eslint-disable-next-line max-len action: Action & ConsumeDeserializer, Deserialized>>, @@ -58,7 +62,7 @@ export default /* @__PURE__ */ makeRunner('all', < const deserialized = await deserializeInstances( context, data, - ) as DeserializedDataOf; + ) as RetypedDeserializedData; return ( transform diff --git a/packages/core/src/actions/context/runners/cachedOr.ts b/packages/core/src/actions/context/runners/cachedOr.ts index 09a59d3e..2c29927d 100644 --- a/packages/core/src/actions/context/runners/cachedOr.ts +++ b/packages/core/src/actions/context/runners/cachedOr.ts @@ -17,6 +17,10 @@ import loaded from '@foscia/core/model/relations/loaded'; 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; }; diff --git a/packages/core/src/actions/context/runners/catchIf.ts b/packages/core/src/actions/context/runners/catchIf.ts index 40a9232a..b456558c 100644 --- a/packages/core/src/actions/context/runners/catchIf.ts +++ b/packages/core/src/actions/context/runners/catchIf.ts @@ -2,34 +2,9 @@ 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, it will return null on any 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 - * - * @example - * ```typescript - * import { catchIf, one, query } from '@foscia/core'; - * - * const postOrNull = await action().run( - * query(Post, '123'), - * catchIf(one(), (error) => error instanceof ErrorToCatch), - * ); - * ``` - */ -export default makeRunner('catchIf', ( +export default makeRunner('catchIf', (( runner: ContextRunner>, - catchCallback?: CatchCallback, + catchCallback: (error: unknown) => Awaitable> | boolean>, ) => async (action: Action): Promise => { try { return await action.run(runner); @@ -45,4 +20,46 @@ export default makeRunner('catchIf', ( return null as any; } +}) 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/one.ts b/packages/core/src/actions/context/runners/one.ts index 661ea287..03455afb 100644 --- a/packages/core/src/actions/context/runners/one.ts +++ b/packages/core/src/actions/context/runners/one.ts @@ -1,5 +1,5 @@ import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utilities/deserializeInstances'; +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'; @@ -27,5 +27,5 @@ export default makeRunner('one', < Deserialized extends DeserializedData, Next = I, >( - transform?: (data: OneData, I>) => Awaitable, + 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 b3b624d3..2305d373 100644 --- a/packages/core/src/actions/context/runners/oneOr.ts +++ b/packages/core/src/actions/context/runners/oneOr.ts @@ -1,5 +1,7 @@ import all, { AllData } from '@foscia/core/actions/context/runners/all'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utilities/deserializeInstances'; +import { + RetypedDeserializedData, +} from '@foscia/core/actions/context/utilities/deserializeInstances'; import makeRunner from '@foscia/core/actions/makeRunner'; import { Action, @@ -8,11 +10,15 @@ import { ContextRunner, 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, @@ -48,7 +54,7 @@ export default makeRunner('oneOr', < >( // eslint-disable-next-line max-len nilRunner: ContextRunner & ConsumeDeserializer, Deserialized>, Awaitable>, - transform?: (data: OneData, I>) => Awaitable, + transform?: (data: OneData, I>) => Awaitable, ) => async ( // eslint-disable-next-line max-len action: Action & ConsumeDeserializer, Deserialized>>, @@ -66,7 +72,7 @@ export default makeRunner('oneOr', < return result as Next; } } catch (error) { - if (!isNotFoundError(error)) { + if (!isFosciaFlag(error, FLAG_ERROR_NOT_FOUND)) { throw error; } } diff --git a/packages/core/src/actions/context/runners/oneOrCurrent.ts b/packages/core/src/actions/context/runners/oneOrCurrent.ts index 5c1a2608..cca8f831 100644 --- a/packages/core/src/actions/context/runners/oneOrCurrent.ts +++ b/packages/core/src/actions/context/runners/oneOrCurrent.ts @@ -1,6 +1,8 @@ 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/utilities/deserializeInstances'; +import { + 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'; @@ -30,7 +32,7 @@ export default makeRunner('oneOrCurrent', < Deserialized extends DeserializedData, Next = CI, >( - transform?: (data: OneData, I>) => Awaitable, + transform?: (data: OneData, I>) => Awaitable, ) => oneOr, I, RawData, Data, Deserialized, CI, Next>( async (action) => consumeInstance(await action.useContext()) as Promise, transform, diff --git a/packages/core/src/actions/context/runners/oneOrFail.ts b/packages/core/src/actions/context/runners/oneOrFail.ts index c981bf1d..2e930b09 100644 --- a/packages/core/src/actions/context/runners/oneOrFail.ts +++ b/packages/core/src/actions/context/runners/oneOrFail.ts @@ -1,5 +1,7 @@ import oneOr, { OneData } from '@foscia/core/actions/context/runners/oneOr'; -import { DeserializedDataOf } from '@foscia/core/actions/context/utilities/deserializeInstances'; +import { + 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'; @@ -28,7 +30,7 @@ export default makeRunner('oneOrFail', < Deserialized extends DeserializedData, Next = I, >( - transform?: (data: OneData, I>) => Awaitable, + transform?: (data: OneData, I>) => Awaitable, ) => oneOr(() => { throw new ExpectedRunFailureError( '`oneOrFail` failed. You may handle this error globally as a "not found" record error.', diff --git a/packages/core/src/actions/context/utilities/deserializeInstances.ts b/packages/core/src/actions/context/utilities/deserializeInstances.ts index 3c2314bd..ddaa8ea1 100644 --- a/packages/core/src/actions/context/utilities/deserializeInstances.ts +++ b/packages/core/src/actions/context/utilities/deserializeInstances.ts @@ -4,7 +4,12 @@ import { ModelInstance } from '@foscia/core/model/types'; import { DeserializedData } from '@foscia/core/types'; import { isNil, using } from '@foscia/shared'; -export type DeserializedDataOf = { +/** + * Deserialized data with a strongly retyped instances array. + * + * @internal + */ +export type RetypedDeserializedData

= { instances: I[]; } & Omit; @@ -21,7 +26,7 @@ export default async ( isNil(data) - ? { instances: [] } + ? { instances: [] } as unknown as Deserialized : using( await consumeDeserializer(context), (deserializer) => deserializer.deserialize(data!, context), diff --git a/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts b/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts index 035096d0..3bd5a01d 100644 --- a/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts +++ b/packages/core/src/actions/context/utilities/registerWriteActionHooks.ts @@ -14,6 +14,8 @@ import { Arrayable, using } from '@foscia/shared'; * @param runningHooks * @param successHooks * @param exists + * + * @internal */ export default ( action: Action, diff --git a/packages/core/src/actions/context/utilities/serializeRelation.ts b/packages/core/src/actions/context/utilities/serializeRelation.ts index b93182d4..aa3edafa 100644 --- a/packages/core/src/actions/context/utilities/serializeRelation.ts +++ b/packages/core/src/actions/context/utilities/serializeRelation.ts @@ -2,11 +2,10 @@ import consumeSerializer from '@foscia/core/actions/context/consumers/consumeSer import { ConsumeSerializer } from '@foscia/core/actions/types'; import takeSnapshot from '@foscia/core/model/snapshots/takeSnapshot'; import { - InferModelSchemaProp, - InferModelValuePropType, ModelInstance, ModelRelation, ModelRelationKey, + ModelValues, } from '@foscia/core/model/types'; import { mapArrayable, using } from '@foscia/shared'; @@ -22,20 +21,19 @@ import { mapArrayable, using } from '@foscia/shared'; */ export default async < I extends ModelInstance, - K extends string, - R extends InferModelSchemaProp, + K extends ModelRelationKey, Record, Related, Data, >( context: ConsumeSerializer, instance: I, - relation: K & ModelRelationKey, - value: InferModelValuePropType, + relation: K, + value: ModelValues[K], ) => using(await consumeSerializer(context), async (serializer) => serializer.serializeToData( await serializer.serializeToRelatedRecords( takeSnapshot(instance), - instance.$model.$schema[relation] as R, + instance.$model.$schema[relation] as ModelRelation, await mapArrayable( value, (related) => takeSnapshot(related as unknown as ModelInstance), diff --git a/packages/core/src/actions/index.ts b/packages/core/src/actions/index.ts index dbc8aed8..a10a9eb0 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -41,14 +41,14 @@ import 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'; @@ -57,13 +57,6 @@ 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, diff --git a/packages/core/src/actions/makeActionFactory.ts b/packages/core/src/actions/makeActionFactory.ts index da123aff..2229cd1b 100644 --- a/packages/core/src/actions/makeActionFactory.ts +++ b/packages/core/src/actions/makeActionFactory.ts @@ -1,4 +1,10 @@ -import { Action, ActionCall, ContextEnhancer, ContextRunner } 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'; @@ -21,7 +27,9 @@ import { */ export default ( initialContext?: Context | (() => Context), -) => () => { +): ActionFactory => ( + ...immediateEnhancers: (ContextEnhancer | ContextRunner)[] +) => { const currentCalls: ActionCall[] = []; let currentCall: ActionCall | null = null; @@ -38,7 +46,7 @@ export default ( await sequentialTransform(enhancements); }; - const action: Action = { + const action = { $hooks: {}, async useContext() { await dequeueEnhancers(this); @@ -116,5 +124,7 @@ export default ( registerHook(action, 'success', (event) => logger.debug('Action success.', [event])); registerHook(action, 'error', (event) => logger.debug('Action error.', [event])); - return action; + return immediateEnhancers.length + ? action.run(...immediateEnhancers) + : action; }; diff --git a/packages/core/src/actions/makeEnhancer.ts b/packages/core/src/actions/makeEnhancer.ts index 6de7396d..068a56aa 100644 --- a/packages/core/src/actions/makeEnhancer.ts +++ b/packages/core/src/actions/makeEnhancer.ts @@ -2,10 +2,6 @@ import makeContextFunctionFactory from '@foscia/core/actions/makeContextFunction import { ContextEnhancer } from '@foscia/core/actions/types'; import { SYMBOL_ACTION_CONTEXT_ENHANCER } from '@foscia/core/symbols'; -type ContextEnhancerFactory = - | ((...args: any[]) => ContextEnhancer) - | ((...args: never[]) => ContextEnhancer); - /** * Make a context enhancer factory with incorporated metadata (name, arguments). * @@ -14,7 +10,8 @@ type ContextEnhancerFactory = * * @category Factories */ -export default (( +// 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 index 02cd9279..ef337470 100644 --- a/packages/core/src/actions/makeRunner.ts +++ b/packages/core/src/actions/makeRunner.ts @@ -2,10 +2,6 @@ import makeContextFunctionFactory from '@foscia/core/actions/makeContextFunction import { ContextRunner } from '@foscia/core/actions/types'; import { SYMBOL_ACTION_CONTEXT_RUNNER } from '@foscia/core/symbols'; -type ContextRunnerFactory = - | ((...args: any[]) => ContextRunner) - | ((...args: never[]) => ContextRunner); - /** * Make a context runner factory with incorporated metadata (name, arguments). * @@ -14,7 +10,8 @@ type ContextRunnerFactory = * * @category Factories */ -export default ( +// 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 d817a2cc..44e3e9f9 100644 --- a/packages/core/src/actions/types.ts +++ b/packages/core/src/actions/types.ts @@ -1,6 +1,21 @@ 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 { @@ -19,8 +34,16 @@ import { } from '@foscia/core/types'; import { Awaitable, Constructor, FosciaObject } from '@foscia/shared'; -export * from '@foscia/core/actions/actionVariadicUse'; -export * from '@foscia/core/actions/actionVariadicRun'; +export type { + AllData, + OneData, + CachedData, + RetypedDeserializedData, + GuessContextModelContext, + InferRelationUpdateValue, +}; + +export * from '@foscia/core/actions/variadic'; /** * Action hooks definition. @@ -38,6 +61,8 @@ export type ActionHooksDefinition = { * Action. * * @typeParam Context The current context of the action. + * + * @interface */ export type Action = & { @@ -96,23 +121,23 @@ export type Action = & Hookable; /** - * Middleware to impact an action result or behavior. + * Action factory. * * @internal */ -export type ActionMiddleware = ( - value: Action, - next: (value: Action) => Promise, -) => Awaitable; +export type ActionFactory = + & (() => Action) + & ActionFactoryVariadicRun; /** - * Action factory. + * Middleware to impact an action result or behavior. * * @internal */ -export type ActionFactory = ( - ...args: Args -) => Action; +export type ActionMiddleware = ( + value: Action, + next: (value: Action) => Promise, +) => Awaitable; /** * Action call (enhancer or runner) tree node. @@ -209,9 +234,9 @@ export type ContextFunctionFactory = */ export type InferQueryInstance = C extends { queryAs: Constructor[] } ? I extends ModelInstance ? I : never - : C extends { relation: ModelRelation> } + : C extends { relation: ModelRelation> } ? 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; @@ -226,9 +251,9 @@ export type InferQueryInstance = */ export type InferQueryModelOrInstance = C extends { queryAs: Constructor[] } ? I extends ModelInstance ? I : never - : C extends { relation: ModelRelation> } + : C extends { relation: ModelRelation> } ? 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; @@ -311,7 +336,7 @@ export type ConsumeMiddlewares = { * * @internal */ -type ResolvableContextDependency = T | (() => Awaitable); +export type ResolvableContextDependency = T | (() => Awaitable); /** * Define the cache implementation to use. 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/cache/makeTimedRefFactory.ts b/packages/core/src/cache/makeTimedRefFactory.ts index f0716535..daf7fb06 100644 --- a/packages/core/src/cache/makeTimedRefFactory.ts +++ b/packages/core/src/cache/makeTimedRefFactory.ts @@ -3,9 +3,11 @@ import { tap } from '@foscia/shared'; /** * Config for the timed ref factory. * + * @interface + * * @internal */ -type TimedRefFactoryConfig = { +export type TimedRefFactoryConfig = { /** * Lifetime of a reference before expiration in seconds. * Defaults to `300` (5 minutes). diff --git a/packages/core/src/cache/types.ts b/packages/core/src/cache/types.ts index 1ed646a8..1014f155 100644 --- a/packages/core/src/cache/types.ts +++ b/packages/core/src/cache/types.ts @@ -1,7 +1,12 @@ +import type { TimedRefFactoryConfig } from '@foscia/core/cache/makeTimedRefFactory'; import { ModelIdType, ModelInstance } from '@foscia/core/model/types'; import { InstancesCache } from '@foscia/core/types'; import { Awaitable, Optional, Transformer } from '@foscia/shared'; +export type { + TimedRefFactoryConfig, +}; + /** * Function which stores a reference to a value. * Calling the function retrieves the value, or `null` if the reference @@ -21,6 +26,8 @@ export type RefFactory = (value: V) => Awaitable>; /** * Config for refs cache implementation. * + * @interface + * * @internal */ export type RefsCacheConfig = { @@ -41,6 +48,8 @@ export type RefsCacheConfig = { /** * Cache implementation using references. * + * @interface + * * @internal */ export type RefsCache = InstancesCache; diff --git a/packages/core/src/errors/flags/isNotFoundError.ts b/packages/core/src/errors/flags/isNotFoundError.ts deleted file mode 100644 index ccc184dd..00000000 --- a/packages/core/src/errors/flags/isNotFoundError.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NotFoundErrorI } from '@foscia/core/errors/flags/types'; - -/** - * Check if an error should be interpreted by Foscia as a "Record not found" - * error. - * - * @param error - */ -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/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/registerHook.ts b/packages/core/src/hooks/registerHook.ts index 834a6ed5..ac4e61ed 100644 --- a/packages/core/src/hooks/registerHook.ts +++ b/packages/core/src/hooks/registerHook.ts @@ -11,7 +11,7 @@ import { tap } from '@foscia/shared'; * @param key * @param callback * - * @category Hooks + * @internal */ export default ( hookable: Hookable, diff --git a/packages/core/src/hooks/runHooks.ts b/packages/core/src/hooks/runHooks.ts index 84097c62..3d740be8 100644 --- a/packages/core/src/hooks/runHooks.ts +++ b/packages/core/src/hooks/runHooks.ts @@ -8,7 +8,7 @@ import { Arrayable, sequentialTransform, wrap } from '@foscia/shared'; * @param hooks * @param event * - * @category Hooks + * @internal */ export default ( hookable: Hookable, diff --git a/packages/core/src/hooks/runHooksSync.ts b/packages/core/src/hooks/runHooksSync.ts index 25fa66c3..7b8a196b 100644 --- a/packages/core/src/hooks/runHooksSync.ts +++ b/packages/core/src/hooks/runHooksSync.ts @@ -8,7 +8,7 @@ import { Arrayable, wrap } from '@foscia/shared'; * @param hooks * @param event * - * @category Hooks + * @internal */ export default ( hookable: Hookable, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4409e401..7dd4ab06 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,9 +6,9 @@ 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'; @@ -24,6 +24,10 @@ import isPluralRelationDef from '@foscia/core/model/checks/isPluralRelationDef'; 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'; @@ -43,7 +47,6 @@ import onPropertyReading from '@foscia/core/model/hooks/properties/onPropertyRea import onPropertyWrite from '@foscia/core/model/hooks/properties/onPropertyWrite'; import onPropertyWriting from '@foscia/core/model/hooks/properties/onPropertyWriting'; 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 attr from '@foscia/core/model/props/builders/attr'; @@ -95,8 +98,10 @@ import { 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'; @@ -110,9 +115,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'; @@ -128,7 +133,6 @@ export { DeserializerError, SerializerError, ExpectedRunFailureError, - isNotFoundError, makeRegistry, makeMapRegistry, makeCache, @@ -147,7 +151,10 @@ export { changed, restore, markSynced, + applyDefinition, + makeDefinition, makeComposable, + makeComposableFactory, makeModel, makeModelFactory, makeQueryModelLoader, @@ -212,7 +219,9 @@ export { cloneModelValue, compareModelValues, logger, + 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, @@ -222,6 +231,7 @@ export { SYMBOL_MODEL_CLASS, SYMBOL_MODEL_INSTANCE, SYMBOL_MODEL_COMPOSABLE, + SYMBOL_MODEL_SNAPSHOT, SYMBOL_ACTION_CONTEXT_WHEN, SYMBOL_ACTION_CONTEXT_ENHANCER, SYMBOL_ACTION_CONTEXT_RUNNER, diff --git a/packages/core/src/logger/constants.ts b/packages/core/src/logger/constants.ts deleted file mode 100644 index 00ff77e2..00000000 --- a/packages/core/src/logger/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const LOGGER_LEVELS = { - error: 'error', - warn: 'warn', - info: 'info', - debug: 'debug', -}; - -export const LOGGER_LEVELS_WEIGHTS = { - [LOGGER_LEVELS.debug]: 0, - [LOGGER_LEVELS.info]: 10, - [LOGGER_LEVELS.warn]: 100, - [LOGGER_LEVELS.error]: 1000, -}; diff --git a/packages/core/src/logger/logger.ts b/packages/core/src/logger/logger.ts index 539074d5..ba5a57fc 100644 --- a/packages/core/src/logger/logger.ts +++ b/packages/core/src/logger/logger.ts @@ -1,7 +1,13 @@ -import { LOGGER_LEVELS_WEIGHTS } from '@foscia/core/logger/constants'; import { Logger, LoggerLevel, LoggerOutput } from '@foscia/core/logger/types'; import { IS_DEV, IS_TEST } from '@foscia/shared'; +const LOGGER_LEVELS_WEIGHTS: Record = { + debug: 0, + info: 10, + warn: 100, + error: 1000, +}; + const makeDefaultLevel = (): LoggerLevel | null => { if (IS_TEST) { return null; diff --git a/packages/core/src/logger/types.ts b/packages/core/src/logger/types.ts index 9ada2138..c2783125 100644 --- a/packages/core/src/logger/types.ts +++ b/packages/core/src/logger/types.ts @@ -1,11 +1,9 @@ -import type { LOGGER_LEVELS } from '@foscia/core/logger/constants'; - /** * Available logger levels. * * @internal */ -export type LoggerLevel = keyof typeof LOGGER_LEVELS; +export type LoggerLevel = 'error' | 'warn' | 'info' | 'debug'; /** * Output to use on a logger. diff --git a/packages/core/src/model/checks/isAttributeDef.ts b/packages/core/src/model/checks/isAttributeDef.ts index 4dd93c24..e31c85f8 100644 --- a/packages/core/src/model/checks/isAttributeDef.ts +++ b/packages/core/src/model/checks/isAttributeDef.ts @@ -7,7 +7,7 @@ import { SYMBOL_MODEL_PROP_KIND_ATTRIBUTE } from '@foscia/core/symbols'; * * @param value * - * @category Utilities + * @internal */ export default ( value: unknown, diff --git a/packages/core/src/model/checks/isComposable.ts b/packages/core/src/model/checks/isComposable.ts index 617836de..01e3500a 100644 --- a/packages/core/src/model/checks/isComposable.ts +++ b/packages/core/src/model/checks/isComposable.ts @@ -1,4 +1,4 @@ -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'; @@ -11,4 +11,4 @@ import { isFosciaType } from '@foscia/shared'; */ 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 e9e5461c..3f92a18c 100644 --- a/packages/core/src/model/checks/isIdDef.ts +++ b/packages/core/src/model/checks/isIdDef.ts @@ -7,7 +7,7 @@ import { SYMBOL_MODEL_PROP_KIND_ID } from '@foscia/core/symbols'; * * @param value * - * @category Utilities + * @internal */ export default ( value: unknown, diff --git a/packages/core/src/model/checks/isInstanceUsing.ts b/packages/core/src/model/checks/isInstanceUsing.ts index 05faf69e..096fdd72 100644 --- a/packages/core/src/model/checks/isInstanceUsing.ts +++ b/packages/core/src/model/checks/isInstanceUsing.ts @@ -1,6 +1,10 @@ -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. @@ -20,6 +24,6 @@ import { ModelComposable, ModelInstanceUsing } from '@foscia/core/model/types'; * ``` */ 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/isModelUsing.ts b/packages/core/src/model/checks/isModelUsing.ts index 530860d5..eb689c70 100644 --- a/packages/core/src/model/checks/isModelUsing.ts +++ b/packages/core/src/model/checks/isModelUsing.ts @@ -1,5 +1,9 @@ -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. @@ -19,6 +23,6 @@ import { ModelComposable, ModelUsing } from '@foscia/core/model/types'; * ``` */ 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/isPluralRelationDef.ts b/packages/core/src/model/checks/isPluralRelationDef.ts index ad947bae..27b4f6de 100644 --- a/packages/core/src/model/checks/isPluralRelationDef.ts +++ b/packages/core/src/model/checks/isPluralRelationDef.ts @@ -6,7 +6,7 @@ import { SYMBOL_MODEL_RELATION_HAS_MANY } from '@foscia/core/symbols'; * * @param def * - * @category Utilities + * @internal */ export default ( def: ModelRelation, diff --git a/packages/core/src/model/checks/isPropFactory.ts b/packages/core/src/model/checks/isPropFactory.ts deleted file mode 100644 index 6ee09f2b..00000000 --- a/packages/core/src/model/checks/isPropFactory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ModelPropFactory } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP_FACTORY } from '@foscia/core/symbols'; -import { isFosciaType } from '@foscia/shared'; - -/** - * Check if value is a property definition factory. - * - * @param value - * - * @internal - */ -export default ( - value: unknown, -): value is ModelPropFactory => isFosciaType(value, SYMBOL_MODEL_PROP_FACTORY); diff --git a/packages/core/src/model/checks/isRelationDef.ts b/packages/core/src/model/checks/isRelationDef.ts index f6f95a8c..bea01f95 100644 --- a/packages/core/src/model/checks/isRelationDef.ts +++ b/packages/core/src/model/checks/isRelationDef.ts @@ -7,7 +7,7 @@ import { SYMBOL_MODEL_PROP_KIND_RELATION } from '@foscia/core/symbols'; * * @param value * - * @category Utilities + * @internal */ export default ( value: unknown, diff --git a/packages/core/src/model/checks/isSingularRelationDef.ts b/packages/core/src/model/checks/isSingularRelationDef.ts index 7dce8942..abf152de 100644 --- a/packages/core/src/model/checks/isSingularRelationDef.ts +++ b/packages/core/src/model/checks/isSingularRelationDef.ts @@ -6,7 +6,7 @@ import { ModelRelation } from '@foscia/core/model/types'; * * @param def * - * @category Utilities + * @internal */ export default ( def: ModelRelation, 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/makeDefinition.ts b/packages/core/src/model/composition/makeDefinition.ts similarity index 80% rename from packages/core/src/model/makeDefinition.ts rename to packages/core/src/model/composition/makeDefinition.ts index aa19a73d..fbae8b4f 100644 --- a/packages/core/src/model/makeDefinition.ts +++ b/packages/core/src/model/composition/makeDefinition.ts @@ -1,5 +1,4 @@ import isComposable from '@foscia/core/model/checks/isComposable'; -import isPropFactory from '@foscia/core/model/checks/isPropFactory'; import { ModelParsedDefinition } from '@foscia/core/model/types'; import { Dictionary, eachDescriptors, makeDescriptorHolder, tap } from '@foscia/shared'; @@ -14,7 +13,7 @@ export default (definition?: D) => tap({} as Dictionary, (par eachDescriptors(definition ?? {}, (key, descriptor) => { // eslint-disable-next-line no-param-reassign parsedDefinition[key] = ( - descriptor.value && (isComposable(descriptor.value) || isPropFactory(descriptor.value)) + descriptor.value && isComposable(descriptor.value) ? descriptor.value : makeDescriptorHolder(descriptor) ); 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 5f649d9e..aa5780b7 100644 --- a/packages/core/src/model/fill.ts +++ b/packages/core/src/model/fill.ts @@ -1,4 +1,4 @@ -import { ModelInstance, ModelKey, ModelWritableValues } from '@foscia/core/model/types'; +import { ModelInstance, ModelKey, ModelValues, ModelWritableKey } from '@foscia/core/model/types'; import { tap } from '@foscia/shared'; /** @@ -18,7 +18,7 @@ import { tap } from '@foscia/shared'; */ export default ( instance: I, - values: Partial>, + values: Partial, ModelWritableKey>>, ) => tap(instance, () => { Object.entries(values).forEach(([key, value]) => { // eslint-disable-next-line no-param-reassign diff --git a/packages/core/src/model/forceFill.ts b/packages/core/src/model/forceFill.ts index bde190c4..867d4cc4 100644 --- a/packages/core/src/model/forceFill.ts +++ b/packages/core/src/model/forceFill.ts @@ -27,7 +27,7 @@ export default ( try { instance.$model.$config.strictReadOnly = false; - return fill(instance, values); + return fill(instance, values as any); } finally { instance.$model.$config.strictReadOnly = strictReadOnly; } diff --git a/packages/core/src/model/hooks/makeInstanceHook.ts b/packages/core/src/model/hooks/makeInstanceHook.ts index 106de929..26c141cd 100644 --- a/packages/core/src/model/hooks/makeInstanceHook.ts +++ b/packages/core/src/model/hooks/makeInstanceHook.ts @@ -2,7 +2,7 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { HookCallback, SyncHookCallback } from '@foscia/core/hooks/types'; import { Model, - ModelComposable, + ModelComposableFactory, ModelFactory, ModelHooksDefinition, ModelInstance, @@ -21,7 +21,7 @@ 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 fcb5e48d..264c2354 100644 --- a/packages/core/src/model/hooks/makeModelHook.ts +++ b/packages/core/src/model/hooks/makeModelHook.ts @@ -2,7 +2,7 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { SyncHookCallback } from '@foscia/core/hooks/types'; import { Model, - ModelComposable, + ModelComposableFactory, ModelFactory, ModelHooksDefinition, ModelInstance, @@ -21,7 +21,7 @@ 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/properties/makePropertyReadHook.ts b/packages/core/src/model/hooks/properties/makePropertyReadHook.ts index f0f6eb29..2e558d67 100644 --- a/packages/core/src/model/hooks/properties/makePropertyReadHook.ts +++ b/packages/core/src/model/hooks/properties/makePropertyReadHook.ts @@ -1,10 +1,13 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { + InferModelSchemaProp, Model, ModelComposable, + ModelComposableFactory, ModelFactory, ModelInstance, ModelInstancePropertyReadHookCallback, + ModelInstanceUsing, ModelValues, } from '@foscia/core/model/types'; @@ -16,32 +19,64 @@ import { * @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 504686ab..c3b8412f 100644 --- a/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts +++ b/packages/core/src/model/hooks/properties/makePropertyWriteHook.ts @@ -1,10 +1,13 @@ import registerHook from '@foscia/core/hooks/registerHook'; import { + InferModelSchemaProp, Model, ModelComposable, + ModelComposableFactory, ModelFactory, ModelInstance, ModelInstancePropertyWriteHookCallback, + ModelInstanceUsing, ModelValues, } from '@foscia/core/model/types'; @@ -16,49 +19,81 @@ import { * @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/makeComposable.ts b/packages/core/src/model/makeComposable.ts deleted file mode 100644 index 4fbd44bc..00000000 --- a/packages/core/src/model/makeComposable.ts +++ /dev/null @@ -1,32 +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 - * - * @category Factories - * - * @example - * ```typescript - * import { makeComposable } from '@foscia/core'; - * - * export default makeComposable({ - * // Definition... - * }); - * ``` - */ -export default ( - rawDefinition?: D & ThisType>>>, -) => ({ - $FOSCIA_TYPE: SYMBOL_MODEL_COMPOSABLE, - def: makeDefinition(rawDefinition), - $hooks: {}, -} as ModelComposable>); diff --git a/packages/core/src/model/makeModelClass.ts b/packages/core/src/model/makeModelClass.ts index 81225a5f..6d9beb8b 100644 --- a/packages/core/src/model/makeModelClass.ts +++ b/packages/core/src/model/makeModelClass.ts @@ -2,21 +2,22 @@ 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 isComposable from '@foscia/core/model/checks/isComposable'; -import isIdDef from '@foscia/core/model/checks/isIdDef'; -import isPropFactory from '@foscia/core/model/checks/isPropFactory'; -import makeDefinition from '@foscia/core/model/makeDefinition'; -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, - ModelPropFactory, + ModelSnapshot, } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_CLASS, SYMBOL_MODEL_INSTANCE } from '@foscia/core/symbols'; -import { eachDescriptors, mapWithKeys, mergeConfig } from '@foscia/shared'; +import { + SYMBOL_MODEL_CLASS, + SYMBOL_MODEL_INSTANCE, + SYMBOL_MODEL_SNAPSHOT, +} from '@foscia/core/symbols'; +import { mergeConfig } from '@foscia/shared'; const { defineProperty } = Object; @@ -42,6 +43,21 @@ const makeModelClass = ( throw new FosciaError('Model type cannot be an empty string.'); } + 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) { defineProperty(this, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_INSTANCE }); @@ -50,56 +66,50 @@ const makeModelClass = ( 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: takeSnapshot(this) }); - - Object.values(this.$model.$schema).forEach((def) => def.bind?.(this)); + 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; - const propFactories = new Map(); - defineProperty(model, '$FOSCIA_TYPE', { value: SYMBOL_MODEL_CLASS }); defineProperty(model, '$type', { value: type }); defineProperty(model, '$config', { value: { ...config } }); - defineProperty(model, '$composables', { value: [] }); - defineProperty(model, '$hooks', { writable: true, value: {} }); + defineProperty(model, '$parsed', { writable: true, value: false }); defineProperty(model, '$booted', { writable: true, value: false }); - defineProperty(model, '$schema', { + const defineOverwrittenProperty = (property: string) => defineProperty(model, property, { configurable: true, get() { - const $schema = mapWithKeys([...propFactories.entries()], ([key, factory]) => { - if (key === 'type') { - throw new FosciaError( - '`type` is forbidden as a definition key because it may be used with some implementations.', - ); - } - - const def = factory.make(this, key); - - if ((key === 'id' || key === 'lid') && !isIdDef(def)) { - throw new FosciaError( - `\`id\` and \`lid\` must be defined with \`id()\` factory (found \`${key}\`).`, - ); - } - - def.boot?.(this); + parseModel(this); - return { [key]: def }; - }); - - defineProperty(this, '$schema', { get: () => $schema }); - - return $schema; + return this[property]; }, }); + defineOverwrittenProperty('$composables'); + defineOverwrittenProperty('$schema'); + defineOverwrittenProperty('$hooks'); + model.configure = function configureModel(newConfig?: Partial, override = true) { return makeModelClass( this.$type, @@ -115,31 +125,11 @@ const 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 (isComposable(descriptor.value)) { - model.$composables.push(descriptor.value); - - applyDefinition(descriptor.value.def); - - model.$hooks = mergeHooks(model.$hooks!, descriptor.value.$hooks!); - } else if (isPropFactory(descriptor.value)) { - propFactories.set(key, descriptor.value); - } else { - defineProperty(model.prototype, key, descriptor); - } - }); - - applyDefinition(makeDefinition(definition)); - - model.$hooks = mergeHooks(model.$hooks!, hooks); - return model; }; diff --git a/packages/core/src/model/makeModelFactory.ts b/packages/core/src/model/makeModelFactory.ts index 57fdbb44..121fded9 100644 --- a/packages/core/src/model/makeModelFactory.ts +++ b/packages/core/src/model/makeModelFactory.ts @@ -1,11 +1,11 @@ +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, ModelInstance, - ModelParsedDefinition, + ModelParsedFlattenDefinition, } from '@foscia/core/model/types'; import cloneModelValue from '@foscia/core/model/utilities/cloneModelValue'; import compareModelValues from '@foscia/core/model/utilities/compareModelValues'; @@ -32,8 +32,7 @@ import { using } from '@foscia/shared'; */ export default ( baseConfig?: Partial, - // eslint-disable-next-line max-len - baseRawDefinition?: D & ThisType>>>, + baseRawDefinition?: D & ThisType>>, ) => { const factory = ( rawConfig: string | (Partial & { type: string; }), @@ -41,19 +40,19 @@ export default ( ) => using( typeof rawConfig === 'string' ? { type: rawConfig } : rawConfig, ({ type, ...config }) => makeModelClass(type, { - compareValues: compareModelValues, - cloneValue: cloneModelValue, + compareSnapshotValues: compareModelValues, + cloneSnapshotValue: cloneModelValue, ...baseConfig, ...config, }, factory.$hooks, { id: id(), lid: id(), - ...baseRawDefinition, - ...rawDefinition, + ...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/attr.ts b/packages/core/src/model/props/builders/attr.ts index 23a6e190..8bf6c150 100644 --- a/packages/core/src/model/props/builders/attr.ts +++ b/packages/core/src/model/props/builders/attr.ts @@ -3,6 +3,7 @@ import parseValuePropConfig from '@foscia/core/model/props/builders/parseValuePr 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'; @@ -47,7 +48,7 @@ const attr: { ) => makeValuePropFactory({ $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_ATTRIBUTE, ...parseValuePropConfig(config, otherConfig), -} as ModelAttribute, { +} as ModelPendingProp, { transform: (transformer: ObjectTransformer) => ({ transformer }), }) as ModelAttributeFactory; diff --git a/packages/core/src/model/props/builders/id.ts b/packages/core/src/model/props/builders/id.ts index c1777226..0fe4b967 100644 --- a/packages/core/src/model/props/builders/id.ts +++ b/packages/core/src/model/props/builders/id.ts @@ -1,6 +1,10 @@ import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; import parseValuePropConfig from '@foscia/core/model/props/builders/parseValuePropConfig'; -import { ModelIdFactory, ModelIdFactoryConfig } from '@foscia/core/model/props/builders/types'; +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'; @@ -44,7 +48,7 @@ const id: { ) => makeValuePropFactory({ $VALUE_PROP_TYPE: SYMBOL_MODEL_PROP_KIND_ID, ...parseValuePropConfig(config, otherConfig), -} as ModelId, { +} as ModelPendingProp, { transform: (transformer: ObjectTransformer) => ({ transformer }), }) as ModelIdFactory; diff --git a/packages/core/src/model/props/builders/makeBuilderPropFactory.ts b/packages/core/src/model/props/builders/makeBuilderPropFactory.ts deleted file mode 100644 index 2c39bb0c..00000000 --- a/packages/core/src/model/props/builders/makeBuilderPropFactory.ts +++ /dev/null @@ -1,34 +0,0 @@ -import makePropFactory from '@foscia/core/model/props/builders/makePropFactory'; -import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; -import { ModelProp, ModelPropFactory } from '@foscia/core/model/types'; -import { Dictionary, mapWithKeys } from '@foscia/shared'; - -type BuilderPropFactory< - P extends ModelProp, - M extends Dictionary<(...args: any[]) => Partial

>, -> = - & { [K in keyof M]: (...args: Parameters) => BuilderPropFactory; } - & ModelPropFactory

; - -/** - * Make a builder property definition factory supporting given modifiers. - * - * @param prop - * @param modifiers - * - * @internal - */ -const makeBuilderPropFactory = < - P extends ModelProp, - M extends Dictionary<(...args: any[]) => Partial

>, ->(prop: ModelPropFactoryDefinition

, modifiers: M): BuilderPropFactory => ({ - ...mapWithKeys(modifiers, (modifier, key) => ({ - [key]: (...args: Parameters) => makeBuilderPropFactory({ - ...prop, - ...modifier(...args), - }, modifiers), - })), - ...makePropFactory(prop), -} as BuilderPropFactory); - -export default makeBuilderPropFactory; 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 index f4a7d6c6..aa2dd40a 100644 --- a/packages/core/src/model/props/builders/makePropFactory.ts +++ b/packages/core/src/model/props/builders/makePropFactory.ts @@ -1,20 +1,28 @@ -import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; -import { ModelProp, ModelPropFactory } from '@foscia/core/model/types'; -import { SYMBOL_MODEL_PROP, SYMBOL_MODEL_PROP_FACTORY } from '@foscia/core/symbols'; +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 definition factory. + * Make a property factory. * - * @param prop + * @param pendingProp + * @param properties * * @internal */ -export default

(prop: ModelPropFactoryDefinition

) => ({ - $FOSCIA_TYPE: SYMBOL_MODEL_PROP_FACTORY, - make: (parent, key) => ({ - ...(prop ?? {}), +export default

( + pendingProp: ModelPendingProp

, + properties?: U, +) => makeComposableFactory({ + composable: { $FOSCIA_TYPE: SYMBOL_MODEL_PROP, - parent, - key, - }), -} as ModelPropFactory

); + ...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 index 34d81bfe..954b6f41 100644 --- a/packages/core/src/model/props/builders/makeValuePropFactory.ts +++ b/packages/core/src/model/props/builders/makeValuePropFactory.ts @@ -2,23 +2,15 @@ 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 makeBuilderPropFactory from '@foscia/core/model/props/builders/makeBuilderPropFactory'; -import { ModelPropFactoryDefinition } from '@foscia/core/model/props/builders/types'; +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'; -const VALUE_PROP_MODIFIERS = { - default: (defaultValue: unknown | (() => unknown)) => ({ default: defaultValue }), - alias: (alias: string) => ({ alias }), - readOnly: (readOnly?: boolean) => ({ readOnly }), - sync: (sync: boolean | ModelPropSync) => ({ sync }), - nullable: () => ({}), -}; - /** * Make a value property definition factory. * - * @param prop + * @param pendingProp * @param modifiers * * @internal @@ -26,9 +18,11 @@ const VALUE_PROP_MODIFIERS = { export default < P extends ModelValueProp, M extends Dictionary<(...args: any[]) => Partial

>, ->(prop: ModelPropFactoryDefinition

, modifiers: M) => makeBuilderPropFactory({ - ...prop, - bind(instance) { +>( + pendingProp: ModelPendingProp

, + modifiers: M, +) => makePropChainableFactory({ + init(instance) { Object.defineProperty(instance, this.key, { enumerable: true, get: () => { @@ -84,4 +78,12 @@ export default < forceFill(instance, { [this.key]: value(this.default) }); } }, -}, { ...VALUE_PROP_MODIFIERS, ...modifiers }); + ...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/relation.ts b/packages/core/src/model/props/builders/relation.ts index 5f2d66f9..eb8f2bfd 100644 --- a/packages/core/src/model/props/builders/relation.ts +++ b/packages/core/src/model/props/builders/relation.ts @@ -1,5 +1,6 @@ import makeValuePropFactory from '@foscia/core/model/props/builders/makeValuePropFactory'; import { + ModelPendingProp, ModelRelationFactory, ModelRelationFactoryConfig, } from '@foscia/core/model/props/builders/types'; @@ -31,7 +32,7 @@ export default ( ? { model: config, ...otherConfig } : config; })(), -} as ModelRelation, { +} 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 bba414a4..35671e4e 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -1,29 +1,41 @@ import { ModelAttribute, + ModelComposableFactory, ModelId, ModelIdType, ModelProp, - ModelPropFactory, ModelPropSync, ModelRelation, ModelRelationKey, } from '@foscia/core/model/types'; import { ObjectTransformer } from '@foscia/core/transformers/types'; -import { Arrayable, Constructor } from '@foscia/shared'; +import { Arrayable, Constructor, Dictionary, IfAny } from '@foscia/shared'; /** - * Default prop factory definition object type. + * Pending property. * * @internal */ -export type ModelPropFactoryDefinition

= - Partial

& ThisType

; +export type ModelPendingProp

= + Omit & ThisType

; /** - * Model ID factory object. + * 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

; + +/** + * Model ID factory. + * + * @interface + */ export type ModelIdFactory = { /** * Define a transformer. @@ -51,20 +63,22 @@ export type ModelIdFactory = { * Mark nullable. */ nullable: () => ModelIdFactory; -} & ModelPropFactory>; +} & ModelComposableFactory>; /** * Model ID factory object config. * + * @interface + * * @internal */ export type ModelIdFactoryConfig = - Pick, 'transformer' | 'default' | 'readOnly'>; + Pick, 'transformer' | 'default' | 'readOnly'>; /** - * Model attribute factory object. + * Model attribute factory. * - * @internal + * @interface */ export type ModelAttributeFactory = { /** @@ -105,15 +119,17 @@ export type ModelAttributeFactory = { * @param sync */ sync: (sync: boolean | ModelPropSync) => ModelAttributeFactory; -} & ModelPropFactory>; +} & ModelComposableFactory>; /** * Model attribute factory object config. * + * @interface + * * @internal */ export type ModelAttributeFactoryConfig = - Pick, 'transformer' | 'default' | 'readOnly' | 'alias' | 'sync'>; + Pick, 'transformer' | 'default' | 'readOnly' | 'alias' | 'sync'>; /** * Infer related instance types from relationship models. @@ -130,16 +146,13 @@ export type InferModelRelationFactoryInstance = * * @internal */ -export type InferModelRelationInverseKey = 0 extends (1 & T) - ? string - : T extends (infer I)[] - ? ModelRelationKey - : ModelRelationKey; +export type InferModelRelationInverseKey = + IfAny>; /** - * Model relationship factory object. + * Model relationship factory. * - * @internal + * @interface */ export type ModelRelationFactory = { /** @@ -187,11 +200,13 @@ export type ModelRelationFactory = { * This is specific to HTTP implementations (REST, JSON:API). */ path: (path: string) => ModelRelationFactory; -} & ModelPropFactory>; +} & ModelComposableFactory>; /** * Model relation factory object options. * + * @interface + * * @internal */ export type ModelRelationFactoryConfig | null, R extends boolean> = @@ -201,4 +216,4 @@ export type ModelRelationFactoryConfig | null, R ext */ inverse?: InferModelRelationInverseKey | boolean; } - & Pick, 'type' | 'path' | 'default' | 'readOnly' | 'alias' | 'sync'>; + & Pick, 'type' | 'path' | 'default' | 'readOnly' | 'alias' | 'sync'>; diff --git a/packages/core/src/model/props/mappers/mapAttributes.ts b/packages/core/src/model/props/mappers/mapAttributes.ts index fe8f6059..15931ab4 100644 --- a/packages/core/src/model/props/mappers/mapAttributes.ts +++ b/packages/core/src/model/props/mappers/mapAttributes.ts @@ -1,6 +1,6 @@ import isAttributeDef from '@foscia/core/model/checks/isAttributeDef'; import mapProps from '@foscia/core/model/props/mappers/mapProps'; -import { Model, ModelAttribute, ModelKey } from '@foscia/core/model/types'; +import { Model, ModelAttribute } from '@foscia/core/model/types'; /** * Map all attributes of a model. @@ -12,9 +12,9 @@ import { Model, ModelAttribute, ModelKey } from '@foscia/core/model/types'; */ export default ( model: M, - callback: (def: ModelAttribute>) => R, + callback: (def: ModelAttribute) => R, ) => mapProps( model, callback as any, isAttributeDef, -) as R[]; +); diff --git a/packages/core/src/model/props/mappers/mapProps.ts b/packages/core/src/model/props/mappers/mapProps.ts index 9244a160..e5411988 100644 --- a/packages/core/src/model/props/mappers/mapProps.ts +++ b/packages/core/src/model/props/mappers/mapProps.ts @@ -1,5 +1,5 @@ import { Model, ModelProp } from '@foscia/core/model/types'; -import { tap } from '@foscia/shared'; +import { mapWithKeys } from '@foscia/shared'; /** * Map all properties of a model. @@ -14,8 +14,6 @@ export default ( model: M, callback: (def: P) => R, predicate?: (def: ModelProp) => def is P, -) => Object.values(model.$schema).reduce((stack, def) => tap(stack, () => { - if (!predicate || predicate(def)) { - stack.push(callback(def as P)); - } -}), [] as R[]); +) => 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 0a3734db..d6f71a58 100644 --- a/packages/core/src/model/props/mappers/mapRelations.ts +++ b/packages/core/src/model/props/mappers/mapRelations.ts @@ -1,6 +1,6 @@ import isRelationDef from '@foscia/core/model/checks/isRelationDef'; import mapProps from '@foscia/core/model/props/mappers/mapProps'; -import { Model, ModelKey, ModelRelation } from '@foscia/core/model/types'; +import { Model, ModelRelation } from '@foscia/core/model/types'; /** * Map all relations of a model. @@ -12,9 +12,9 @@ import { Model, ModelKey, ModelRelation } from '@foscia/core/model/types'; */ export default ( model: M, - callback: (def: ModelRelation>) => R, + callback: (def: ModelRelation) => R, ) => mapProps( model, callback as any, isRelationDef, -) as R[]; +); diff --git a/packages/core/src/model/props/shouldSync.ts b/packages/core/src/model/props/shouldSync.ts index 3c5f4af7..ad289fde 100644 --- a/packages/core/src/model/props/shouldSync.ts +++ b/packages/core/src/model/props/shouldSync.ts @@ -1,4 +1,4 @@ -import { ModelPropSync, ModelValueProp } from '@foscia/core/model/types'; +import { ModelProp, ModelPropSync } from '@foscia/core/model/types'; /** * Check if a value property should be synced depending on the current action. @@ -8,7 +8,7 @@ import { ModelPropSync, ModelValueProp } from '@foscia/core/model/types'; * * @internal */ -export default (def: ModelValueProp, actions: ModelPropSync[]) => ( +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/makeQueryModelLoader.ts b/packages/core/src/model/relations/makeQueryModelLoader.ts index 645d31cb..0221c04a 100644 --- a/packages/core/src/model/relations/makeQueryModelLoader.ts +++ b/packages/core/src/model/relations/makeQueryModelLoader.ts @@ -35,8 +35,6 @@ import { wrap, } from '@foscia/shared'; -type ExtractedId = { id: ModelIdType; type?: string; }; - /** * Configuration for the {@link makeQueryModelLoader | `makeQueryModelLoader`} factory. * @@ -87,7 +85,7 @@ export type QueryModelLoaderOptions< 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. @@ -166,7 +164,7 @@ const extractIdsMap = < instances: I[], relations: Map, string[]>, ) => tap( - new Map, Arrayable | null>>(), + new Map, Arrayable<{ id: ModelIdType; type?: string; }> | null>>(), (extractedIdsMap) => { const { exclude } = options; const extract = options.extract ?? makeQueryModelLoaderExtractor( @@ -206,10 +204,10 @@ const fetchRelatedMap = async < C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, I extends ModelInstance, >( - action: ActionFactory<[], C>, + action: ActionFactory, options: QueryModelLoaderOptions, relations: Map[]; nested: string[] }>, - ids: Map, Arrayable | null>>, + ids: Map, Arrayable<{ id: ModelIdType; type?: string; }> | null>>, ) => { const related = makeIdentifiersMap(); @@ -224,10 +222,12 @@ const fetchRelatedMap = async < ); } }), - ), [] 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() @@ -260,7 +260,7 @@ const fetchRelatedMap = async < }; const extractRelated = ( - ids: Arrayable | null, + ids: Arrayable<{ id: ModelIdType; type?: string; }> | null, related: IdentifiersMap, ) => { if (Array.isArray(ids)) { @@ -273,7 +273,7 @@ const extractRelated = ( }; const remapRelated = ( - ids: Map, Arrayable | null>>, + ids: Map, Arrayable<{ id: ModelIdType; type?: string; }> | null>>, related: IdentifiersMap, ) => ids.forEach((idsForInstance, instance) => { idsForInstance.forEach((extractIds, relation) => { @@ -310,7 +310,7 @@ export default < Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, >( - action: ActionFactory<[], C>, + action: ActionFactory, options: QueryModelLoaderOptions = {}, ) => async ( instances: Arrayable, diff --git a/packages/core/src/model/relations/makeQueryRelationLoader.ts b/packages/core/src/model/relations/makeQueryRelationLoader.ts index df2afe3b..38d76d53 100644 --- a/packages/core/src/model/relations/makeQueryRelationLoader.ts +++ b/packages/core/src/model/relations/makeQueryRelationLoader.ts @@ -78,7 +78,7 @@ export default < Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, >( - action: ActionFactory<[], C>, + action: ActionFactory, options: QueryRelationLoaderOptions = {}, ) => async ( instances: Arrayable, diff --git a/packages/core/src/model/relations/makeRefreshIncludeLoader.ts b/packages/core/src/model/relations/makeRefreshIncludeLoader.ts index 39944fe3..c6f3cb72 100644 --- a/packages/core/src/model/relations/makeRefreshIncludeLoader.ts +++ b/packages/core/src/model/relations/makeRefreshIncludeLoader.ts @@ -107,7 +107,7 @@ const refreshLoad = async < C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, I extends ModelInstance, >( - action: ActionFactory<[], C>, + action: ActionFactory, options: RefreshIncludeLoaderOptions, instances: I[], relations: ModelRelationDotKey[], @@ -169,7 +169,7 @@ export default < Deserialized extends DeserializedData, C extends ConsumeAdapter & ConsumeDeserializer, Deserialized>, >( - action: ActionFactory<[], C>, + action: ActionFactory, options: RefreshIncludeLoaderOptions = {}, ) => async ( instances: Arrayable, diff --git a/packages/core/src/model/relations/utilities/attachRelationInverse.ts b/packages/core/src/model/relations/utilities/attachRelationInverse.ts index 7a89f043..04e29c52 100644 --- a/packages/core/src/model/relations/utilities/attachRelationInverse.ts +++ b/packages/core/src/model/relations/utilities/attachRelationInverse.ts @@ -3,7 +3,7 @@ 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, ModelProp, ModelRelation } from '@foscia/core/model/types'; +import { ModelInstance, ModelRelation } from '@foscia/core/model/types'; import { Arrayable, isNil, using, wrap } from '@foscia/shared'; /** @@ -39,7 +39,7 @@ export default ( if (instances.some((instance) => { const inverse = ( inverseKey ? instance.$model.$schema[inverseKey] : undefined - ) as ModelProp | undefined; + ); if (inverse && isRelationDef(inverse)) { if (isSingularRelationDef(inverse)) { return false; diff --git a/packages/core/src/model/revivers/types.ts b/packages/core/src/model/revivers/types.ts index 8d4cf34c..e7f723d1 100644 --- a/packages/core/src/model/revivers/types.ts +++ b/packages/core/src/model/revivers/types.ts @@ -186,6 +186,8 @@ export type ModelCanReduceRevive /** * Config for the models reviver. + * + * @internal */ export type ModelsReviverConfig = { models: Model[]; @@ -194,6 +196,8 @@ export type ModelsReviverConfig = { /** * Config for the models reducer. + * + * @internal */ export type ModelsReducerConfig = { reduce?: (value: unknown) => unknown; diff --git a/packages/core/src/model/snapshots/isSameSnapshot.ts b/packages/core/src/model/snapshots/isSameSnapshot.ts index f9e64c5f..47b89290 100644 --- a/packages/core/src/model/snapshots/isSameSnapshot.ts +++ b/packages/core/src/model/snapshots/isSameSnapshot.ts @@ -38,7 +38,7 @@ export default ( 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.compareValues( + (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 391e5cbe..4e6ca25f 100644 --- a/packages/core/src/model/snapshots/markSynced.ts +++ b/packages/core/src/model/snapshots/markSynced.ts @@ -27,8 +27,10 @@ export default ( 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]; } }); diff --git a/packages/core/src/model/snapshots/restoreSnapshot.ts b/packages/core/src/model/snapshots/restoreSnapshot.ts index 93a8b94a..3e2ae2c3 100644 --- a/packages/core/src/model/snapshots/restoreSnapshot.ts +++ b/packages/core/src/model/snapshots/restoreSnapshot.ts @@ -1,16 +1,35 @@ /* 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 markSynced from '@foscia/core/model/snapshots/markSynced'; import { ModelInstance, ModelKey, + ModelLimitedSnapshot, + ModelProp, ModelSnapshot, - ModelValueProp, ModelValues, } from '@foscia/core/model/types'; -import { ArrayableVariadic, tap, 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. @@ -41,14 +60,14 @@ export default ( instance.$loaded = snapshot.$loaded; } - const restoreForDef = (def: ModelValueProp>) => { - 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]: instance.$model.$config.cloneValue(snapshot.$values[def.key]), + [def.key]: restoreSnapshotValue(snapshot, def), } as Partial>); } else { delete instance.$values[def.key]; diff --git a/packages/core/src/model/snapshots/takeLimitedSnapshot.ts b/packages/core/src/model/snapshots/takeLimitedSnapshot.ts index 5fe25edf..579ad272 100644 --- a/packages/core/src/model/snapshots/takeLimitedSnapshot.ts +++ b/packages/core/src/model/snapshots/takeLimitedSnapshot.ts @@ -1,9 +1,5 @@ import captureSnapshotValues from '@foscia/core/model/snapshots/utilities/captureSnapshotValues'; -import { - ModelInstance, - ModelLimitedSnapshot, - ModelLimitedSnapshotValues, -} from '@foscia/core/model/types'; +import { ModelInstance, ModelLimitedSnapshot } from '@foscia/core/model/types'; import { SYMBOL_MODEL_SNAPSHOT } from '@foscia/core/symbols'; /** @@ -32,7 +28,7 @@ const takeLimitedSnapshot = ( instance, takeLimitedSnapshot, ['id', 'lid'], - ) as ModelLimitedSnapshotValues, + ) 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 6fd843e1..84cb03e7 100644 --- a/packages/core/src/model/snapshots/takeSnapshot.ts +++ b/packages/core/src/model/snapshots/takeSnapshot.ts @@ -1,6 +1,6 @@ import takeLimitedSnapshot from '@foscia/core/model/snapshots/takeLimitedSnapshot'; import captureSnapshotValues from '@foscia/core/model/snapshots/utilities/captureSnapshotValues'; -import { ModelInstance, ModelSnapshot, ModelSnapshotValues } from '@foscia/core/model/types'; +import { ModelInstance, ModelSnapshot } from '@foscia/core/model/types'; import { SYMBOL_MODEL_SNAPSHOT } from '@foscia/core/symbols'; const takeFullSnapshot = ( @@ -24,7 +24,7 @@ const takeFullSnapshot = ( ? takeLimitedSnapshot(related) : takeFullSnapshot(related, [...parents, snapshot]) ) - )) as ModelSnapshotValues; + )) as ModelSnapshot['$values']; return snapshot; }; diff --git a/packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts b/packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts index 2f07d406..51a8fe76 100644 --- a/packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts +++ b/packages/core/src/model/snapshots/utilities/captureSnapshotValues.ts @@ -24,7 +24,7 @@ const captureSnapshotValue = ( parent: ModelInstance, ) => ModelSnapshot | ModelLimitedSnapshot, ) => using( - instance.$model.$config.cloneValue(value), + instance.$model.$config.cloneSnapshotValue(value), (clone) => using(instance.$model.$schema[key], (def) => ( isRelationDef(def) && !isNil(clone) ? captureSnapshotRelation(instance, clone as Arrayable, takeSnapshot) diff --git a/packages/core/src/model/types.ts b/packages/core/src/model/types.ts index 2542ff9a..9c75ee23 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -4,7 +4,6 @@ import { SYMBOL_MODEL_COMPOSABLE, SYMBOL_MODEL_INSTANCE, SYMBOL_MODEL_PROP, - SYMBOL_MODEL_PROP_FACTORY, SYMBOL_MODEL_PROP_KIND_ATTRIBUTE, SYMBOL_MODEL_PROP_KIND_ID, SYMBOL_MODEL_PROP_KIND_RELATION, @@ -17,11 +16,14 @@ import { Awaitable, Constructor, DescriptorHolder, + DescriptorHolderOf, Dictionary, FosciaObject, + IfAny, OmitNever, Optional, Prev, + RestoreDescriptorHolder, Transformer, UnionToIntersection, } from '@foscia/shared'; @@ -81,14 +83,14 @@ export type ModelConfig = { * @param nextValue * @param prevValue */ - compareValues: (nextValue: unknown, prevValue: unknown) => boolean; + compareSnapshotValues: (nextValue: unknown, prevValue: unknown) => boolean; /** * Clone a property value when creating a snapshot. * Defaults to {@link cloneModelValue | `cloneModelValue`}. * * @param value */ - cloneValue: (value: T) => T; + cloneSnapshotValue: (value: T) => T; /** * Tells if snapshots of related instances should be using * {@link ModelSnapshot | `ModelSnapshot`} or @@ -141,23 +143,54 @@ export type ModelConfig = { }; /** - * Model composable which can be included on any model definition. + * Model composable which adds features and typings to a model. * - * @typeParam D Definition of the composable. + * @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 of the composable. - * - * @internal + * Create and bind the composable to a model. */ - readonly def: D; + readonly bind: (ctx: { parent: Model, key: string }) => C; } - & FosciaObject - & Hookable; + & FosciaObject; /** * Model instance ID default typing. @@ -165,106 +198,58 @@ export type ModelComposable = export type ModelIdType = string | number; /** - * Factory for a model property. - * - * @typeParam P Model property created by the factory. + * 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 ModelPropFactory

= ModelProp> = - & { - /** - * Create the model property for a model and key. - */ - readonly make: (parent: Model, key: string) => P; - } - & FosciaObject; +export type ModelPropSync = 'pull' | 'push'; /** - * Model property appended to a model schema. + * Model property. * * @internal */ -export type ModelProp = - & { - /** - * Boot the property before it is appended to a model schema. - * At this step, the model schema is not complete and should not be accessed. - */ - readonly boot?: (model: Model) => void; - /** - * Bind the property to the given instance. - * Usually, this will use `Object.defineProperty` to configure - * the property behavior. - */ - readonly bind?: (instance: ModelInstance) => void; - /** - * The model the property is attached to. - */ - readonly parent: Model; - /** - * The key the property uses in the model schema and instances. - */ - readonly key: K; - /** - * The type the property's value is of. - * This is a generic type annotation only property and should not be accessed. - * - * @internal - */ - readonly __type__: T; - /** - * Tells if the property is read-only. - * This is a generic type annotation only property and should not be accessed. - * - * @internal - */ - readonly __readOnly__: R; - } - & FosciaObject; +export interface ModelProp + extends ModelComposable, FosciaObject { + /** + * Alias of the property (might be used when (de)serializing). + */ + alias?: string | undefined; + /** + * Tells if the property should be synced with the data store. + */ + sync?: boolean | ModelPropSync; + /** + * Tells if the property is read-only. + */ + readonly readOnly?: R; -/** - * 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'; + readonly _type: R extends false ? Record : Readonly>; +} /** - * Model property which holds a value that can be exchanged with the data source. + * Model value property stored inside the instance internal values. + * + * @interface * * @internal */ -export type ModelValueProp< - K = string, - T = unknown, - R extends boolean = boolean, -> = +export type ModelValueProp = & { - /** - * Tells if the property is read-only. - */ - readonly readOnly?: R; /** * Default value for the property. */ default?: T | (() => T) | undefined; - /** - * Alias of the property (might be used when (de)serializing). - */ - alias?: string | undefined; - /** - * Tells if the property should be synced with the data store. - */ - sync?: boolean | ModelPropSync; } - & ModelProp; + & ModelProp; /** - * Model ID property definition. + * Model ID property. + * + * @interface */ -export type ModelId = +export type ModelId = { /** * Type of property. @@ -274,12 +259,14 @@ export type ModelId; } - & ModelValueProp; + & ModelValueProp; /** - * Model attribute property definition. + * Model attribute property. + * + * @interface */ -export type ModelAttribute = +export type ModelAttribute = { /** * Type of property. @@ -289,7 +276,7 @@ export type ModelAttribute = readonly $VALUE_PROP_TYPE: typeof SYMBOL_MODEL_PROP_KIND_ATTRIBUTE; transformer?: ObjectTransformer; } - & ModelValueProp; + & ModelValueProp; /** * Available model relation types. @@ -301,9 +288,11 @@ export type ModelRelationType = | typeof SYMBOL_MODEL_RELATION_HAS_MANY; /** - * Model sync relation property definition. + * Model relation property. + * + * @interface */ -export type ModelRelation = +export type ModelRelation = { /** * Type of property. @@ -340,57 +329,7 @@ export type ModelRelation = */ path?: string; } - & ModelValueProp; - -/** - * Infer a model's property type from the property definition. - * - * @internal - */ -export type InferModelValuePropType

+ + + +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: creation, enhancements and execution (run). ```typescript -action() // Creation. - .run( - 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() // Creation. @@ -487,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( diff --git a/website/docs/core-concepts/models.mdx b/website/docs/core-concepts/models.mdx index c66efc83..92f53e87 100644 --- a/website/docs/core-concepts/models.mdx +++ b/website/docs/core-concepts/models.mdx @@ -39,7 +39,7 @@ You can generate a new model using [`@foscia/cli`](/docs/digging-deeper/usages/c 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/type-aliases/Model) constructor: +[`Model`](/docs/api/@foscia/core/interfaces/Model) constructor: - The `type` string or a [configuration object](/docs/digging-deeper/models/models-configuration). @@ -65,14 +65,14 @@ export default makeModel('posts', { ### Extending a model class [`makeModel`](/docs/api/@foscia/core/functions/makeModel) will return a -[`Model`](/docs/api/@foscia/core/type-aliases/Model) constructor +[`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`](/docs/api/@foscia/core/type-aliases/Model) constructor +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). @@ -543,7 +543,7 @@ during deserialization process. ##### Configuring an inverse To enable inverse of a relation, you can define an -[`inverse` option](/docs/api/@foscia/core/type-aliases/ModelRelationFactoryConfig#inverse) +[`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 @@ -584,7 +584,7 @@ with included `comments` relation, each `Comment` will have its `post` relation filled with the parent `Post` instance. ```typescript -const posts = await action().run(query(Post), include('comments'), all()); +const posts = await action(query(Post), include('comments'), all()); posts.forEach((post) => { post.comments.forEach((comment) => { @@ -681,5 +681,5 @@ export default class User extends makeModel('users', { Foscia models provide special properties on both the model class and its instances, which you can discover inside the API reference: -- [`Model`](/docs/api/@foscia/core/type-aliases/Model) -- [`ModelInstance`](/docs/api/@foscia/core/type-aliases/ModelInstance) +- [`Model`](/docs/api/@foscia/core/interfaces/Model) +- [`ModelInstance`](/docs/api/@foscia/core/interfaces/ModelInstance) diff --git a/website/docs/digging-deeper/actions/actions-hooks.md b/website/docs/digging-deeper/actions/actions-hooks.md index 15814a52..e87cb3fc 100644 --- a/website/docs/digging-deeper/actions/actions-hooks.md +++ b/website/docs/digging-deeper/actions/actions-hooks.md @@ -60,7 +60,7 @@ 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()); + return await a.run(query(User), all()); }); ``` diff --git a/website/docs/digging-deeper/actions/custom-action-enhancers.mdx b/website/docs/digging-deeper/actions/custom-action-enhancers.mdx index 17cc08e7..6a6dcc4b 100644 --- a/website/docs/digging-deeper/actions/custom-action-enhancers.mdx +++ b/website/docs/digging-deeper/actions/custom-action-enhancers.mdx @@ -71,7 +71,7 @@ import action from './action'; import searchBy from './enhancers/searchBy'; import Post from './models/post'; -const posts = await action().run( +const posts = await action( query(Post), searchBy('hello'), all(), diff --git a/website/docs/digging-deeper/actions/custom-action-runners.mdx b/website/docs/digging-deeper/actions/custom-action-runners.mdx index 0321c2a5..24999e14 100644 --- a/website/docs/digging-deeper/actions/custom-action-runners.mdx +++ b/website/docs/digging-deeper/actions/custom-action-runners.mdx @@ -71,5 +71,5 @@ import action from './action'; import first from './runners/first'; import Post from './models/post'; -const post = await action().run(query(Post), first()); +const post = await action(query(Post), first()); ``` diff --git a/website/docs/digging-deeper/actions/models-registration.mdx b/website/docs/digging-deeper/actions/models-registration.mdx index af6fc117..09199e17 100644 --- a/website/docs/digging-deeper/actions/models-registration.mdx +++ b/website/docs/digging-deeper/actions/models-registration.mdx @@ -17,7 +17,7 @@ import ShellCommand from '@site/src/components/ShellCommand'; ## When should you use a registry? -A [`ModelsRegistry`](/docs/api/@foscia/core/type-aliases/ModelsRegistry) is a simple +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. diff --git a/website/docs/digging-deeper/implementations/core.md b/website/docs/digging-deeper/implementations/core.md index 815622d0..69f7536a 100644 --- a/website/docs/digging-deeper/implementations/core.md +++ b/website/docs/digging-deeper/implementations/core.md @@ -11,8 +11,8 @@ description: Core implementations are implementations of actions' dependencies which can be used when using Foscia for any purpose. -Since [`ModelsRegistry`](/docs/api/@foscia/core/type-aliases/ModelsRegistry) and -[`InstancesCache`](/docs/api/@foscia/core/type-aliases/InstancesCache) can be agnostic of +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. @@ -21,7 +21,7 @@ Foscia proposes core implementations of those dependencies. ### `makeCache` [`makeCache`](/docs/api/@foscia/core/functions/makeCache) provides a -[`InstancesCache`](/docs/api/@foscia/core/type-aliases/InstancesCache) implementation. +[`InstancesCache`](/docs/api/@foscia/core/interfaces/InstancesCache) implementation. Currently, it uses [`makeRefsCache`](#makerefscache) with [`makeWeakRefFactory`](/docs/api/@foscia/core/functions/makeWeakRefFactory). @@ -51,7 +51,7 @@ Since this factory is agnostic of implementation, no configuration is available. ### `makeRefsCache` [`makeRefsCache`](/docs/api/@foscia/core/functions/makeRefsCache) provides a -[`InstancesCache`](/docs/api/@foscia/core/type-aliases/InstancesCache) implementation +[`InstancesCache`](/docs/api/@foscia/core/interfaces/InstancesCache) implementation which stores reference to instances created by a [`RefFactory`](/docs/api/@foscia/core/type-aliases/RefFactory). @@ -92,7 +92,7 @@ const cachedPost = cache.find('posts', '1'); #### Configuration -- [`RefsCacheConfig`](/docs/api/@foscia/core/type-aliases/RefsCacheConfig) +- [`RefsCacheConfig`](/docs/api/@foscia/core/interfaces/RefsCacheConfig) #### Defined in @@ -101,7 +101,7 @@ const cachedPost = cache.find('posts', '1'); ### `makeRegistry` [`makeRegistry`](/docs/api/@foscia/core/functions/makeRegistry) provides a -[`ModelsRegistry`](/docs/api/@foscia/core/type-aliases/ModelsRegistry) implementation. +[`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 @@ -129,7 +129,7 @@ Since this factory is agnostic of implementation, no configuration is available. ### `makeMapRegistry` [`makeRegistry`](/docs/api/@foscia/core/functions/makeRegistry) provides a -[`ModelsRegistry`](/docs/api/@foscia/core/type-aliases/ModelsRegistry) implementation +[`ModelsRegistry`](/docs/api/@foscia/core/interfaces/ModelsRegistry) implementation which stores a map of models keyed by their type. Type normalization function can be configured @@ -151,7 +151,7 @@ const PostModel = registry.modelFor('posts'); #### Configuration -- [`MapRegistryConfig`](/docs/api/@foscia/core/type-aliases/MapRegistryConfig) +- [`MapRegistryConfig`](/docs/api/@foscia/core/interfaces/MapRegistryConfig) #### Defined in diff --git a/website/docs/digging-deeper/implementations/http.md b/website/docs/digging-deeper/implementations/http.md index 8a8bd19e..402acaf5 100644 --- a/website/docs/digging-deeper/implementations/http.md +++ b/website/docs/digging-deeper/implementations/http.md @@ -20,7 +20,7 @@ the foundation of [JSON:API](/docs/digging-deeper/implementations/jsonapi) and [`makeHttpAdapter`](/docs/api/@foscia/http/functions/makeHttpAdapter) provides a -[`Adapter`](/docs/api/@foscia/core/type-aliases/Adapter) implementation which +[`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). @@ -55,7 +55,7 @@ const response = await adapter.execute({ #### Configuration {#makehttpadapter-configuration} -- [`HttpAdapterConfig`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig) +- [`HttpAdapterConfig`](/docs/api/@foscia/http/interfaces/HttpAdapterConfig) #### Defined in diff --git a/website/docs/digging-deeper/implementations/jsonapi.md b/website/docs/digging-deeper/implementations/jsonapi.md index c7659c86..75c5ae6a 100644 --- a/website/docs/digging-deeper/implementations/jsonapi.md +++ b/website/docs/digging-deeper/implementations/jsonapi.md @@ -17,7 +17,7 @@ implementations to support read/write interactions with ### `makeJsonApiAdapter` [`makeJsonApiAdapter`](/docs/api/@foscia/jsonapi/functions/makeJsonApiAdapter) -provides a [`Adapter`](/docs/api/@foscia/core/type-aliases/Adapter) +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). @@ -40,9 +40,7 @@ const response = await adapter.execute({ #### Configuration -- [`JsonApiAdapterConfig`](/docs/api/@foscia/jsonapi/type-aliases/JsonApiAdapterConfig) -- [`RestAdapterConfig`](/docs/api/@foscia/rest/type-aliases/RestAdapterConfig) -- [`HttpAdapterConfig`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig) +- [`JsonApiAdapterConfig`](/docs/api/@foscia/jsonapi/interfaces/JsonApiAdapterConfig) #### Defined in @@ -51,7 +49,7 @@ const response = await adapter.execute({ ### `makeJsonApiDeserializer` [`makeJsonApiDeserializer`](/docs/api/@foscia/jsonapi/functions/makeJsonApiDeserializer) -provides a [`Deserializer`](/docs/api/@foscia/core/type-aliases/Deserializer) +provides a [`Deserializer`](/docs/api/@foscia/core/interfaces/Deserializer) implementation which will extract model instances from JSON:API documents. `makeJsonApiDeserializer` uses @@ -146,8 +144,7 @@ const { instances } = await deserializer.deserialize(data, { #### Configuration -- [`JsonApiDeserializerConfig`](/docs/api/@foscia/jsonapi/type-aliases/JsonApiDeserializerConfig) -- [`DeserializerConfig`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig) +- [`JsonApiDeserializerConfig`](/docs/api/@foscia/jsonapi/interfaces/JsonApiDeserializerConfig) #### Defined in @@ -156,7 +153,7 @@ const { instances } = await deserializer.deserialize(data, { ### `makeJsonApiSerializer` [`makeJsonApiSerializer`](/docs/api/@foscia/jsonapi/functions/makeJsonApiSerializer) -provides a [`Serializer`](/docs/api/@foscia/core/type-aliases/Serializer) +provides a [`Serializer`](/docs/api/@foscia/core/interfaces/Serializer) implementation which will create JSON:API documents from model instance and relations. @@ -220,8 +217,7 @@ const data = await serializer.serializeInstance(instance, { #### Configuration -- [`JsonApiSerializerConfig`](/docs/api/@foscia/jsonapi/type-aliases/JsonApiSerializerConfig) -- [`SerializerConfig`](/docs/api/@foscia/serialization/type-aliases/SerializerConfig) +- [`JsonApiSerializerConfig`](/docs/api/@foscia/jsonapi/interfaces/JsonApiSerializerConfig) #### Defined in diff --git a/website/docs/digging-deeper/implementations/presentation.md b/website/docs/digging-deeper/implementations/presentation.md index d40fec76..f82cc0da 100644 --- a/website/docs/digging-deeper/implementations/presentation.md +++ b/website/docs/digging-deeper/implementations/presentation.md @@ -13,20 +13,20 @@ process. There are 5 kinds of dependency: -- [`Adapter`](/docs/api/@foscia/core/type-aliases/Adapter) +- [`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/type-aliases/Deserializer) +- [`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/type-aliases/Serializer) +- [`Serializer`](/docs/api/@foscia/core/interfaces/Serializer) will serialize instances' snapshots to the data source format. -- [`InstancesCache`](/docs/api/@foscia/core/type-aliases/InstancesCache) +- [`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/type-aliases/ModelsRegistry) +- [`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. diff --git a/website/docs/digging-deeper/implementations/rest.md b/website/docs/digging-deeper/implementations/rest.md index 37d9d939..19a0b4d7 100644 --- a/website/docs/digging-deeper/implementations/rest.md +++ b/website/docs/digging-deeper/implementations/rest.md @@ -16,7 +16,7 @@ read/write interactions with JSON REST data sources. ### `makeRestAdapter` [`makeRestAdapter`](/docs/api/@foscia/rest/functions/makeRestAdapter) -provides a [`Adapter`](/docs/api/@foscia/core/type-aliases/Adapter) +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). @@ -26,7 +26,6 @@ implementation which will execute context through HTTP requests using the #### Usage ```typescript -import { paramsSerializer } from '@foscia/http'; import { makeRestAdapter } from '@foscia/rest'; const { adapter } = makeRestAdapter({ @@ -40,8 +39,7 @@ const response = await adapter.execute({ #### Configuration {#makerestadapter-configuration} -- [`RestAdapterConfig`](/docs/api/@foscia/rest/type-aliases/RestAdapterConfig) -- [`HttpAdapterConfig`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig) +- [`RestAdapterConfig`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig) #### Defined in @@ -50,7 +48,7 @@ const response = await adapter.execute({ ### `makeRestDeserializer` [`makeRestDeserializer`](/docs/api/@foscia/rest/functions/makeRestDeserializer) -provides a [`Deserializer`](/docs/api/@foscia/core/type-aliases/Deserializer) +provides a [`Deserializer`](/docs/api/@foscia/core/interfaces/Deserializer) implementation which will extract model instances from object documents. `makeRestDeserializer` uses @@ -114,8 +112,7 @@ const { instances } = await deserializer.deserialize(data, { #### Configuration -- [`RestDeserializerConfig`](/docs/api/@foscia/rest/type-aliases/RestDeserializerConfig) -- [`DeserializerConfig`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig) +- [`RestDeserializerConfig`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig) #### Defined in @@ -124,7 +121,7 @@ const { instances } = await deserializer.deserialize(data, { ### `makeRestSerializer` [`makeRestSerializer`](/docs/api/@foscia/rest/functions/makeRestSerializer) -provides a [`Serializer`](/docs/api/@foscia/core/type-aliases/Serializer) +provides a [`Serializer`](/docs/api/@foscia/core/interfaces/Serializer) implementation which will create REST object documents from model instance and relations. @@ -170,8 +167,7 @@ const data = await serializer.serializeInstance(instance, { #### Configuration -- [`RestSerializerConfig`](/docs/api/@foscia/rest/type-aliases/RestSerializerConfig) -- [`SerializerConfig`](/docs/api/@foscia/serialization/type-aliases/SerializerConfig) +- [`RestSerializerConfig`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig) #### Defined in diff --git a/website/docs/digging-deeper/implementations/serialization.md b/website/docs/digging-deeper/implementations/serialization.md index 4bfe9397..d48d9d34 100644 --- a/website/docs/digging-deeper/implementations/serialization.md +++ b/website/docs/digging-deeper/implementations/serialization.md @@ -17,7 +17,7 @@ for serializer and deserializer dependencies. ### `makeDeserializer` [`makeDeserializer`](/docs/api/@foscia/serialization/functions/makeDeserializer) -provides a [`Deserializer`](/docs/api/@foscia/core/type-aliases/Deserializer) +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: @@ -54,7 +54,7 @@ const { instances } = await deserializer.deserialize(data, { #### Configuration {#makedeserializer-configuration} -- [`DeserializerConfig`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig) +- [`RecordDeserializerConfig`](/docs/api/@foscia/serialization/interfaces/RecordDeserializerConfig) #### Defined in @@ -63,7 +63,7 @@ const { instances } = await deserializer.deserialize(data, { ### `makeSerializer` [`makeSerializer`](/docs/api/@foscia/serialization/functions/makeSerializer) -provides a [`Serializer`](/docs/api/@foscia/core/type-aliases/Serializer) +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: @@ -97,7 +97,7 @@ const data = await serializer.serializeInstance(instance, { #### Configuration {#makeserializer-configuration} -- [`SerializerConfig`](/docs/api/@foscia/serialization/type-aliases/SerializerConfig) +- [`RecordSerializerConfig`](/docs/api/@foscia/serialization/interfaces/RecordSerializerConfig) #### Defined in diff --git a/website/docs/digging-deeper/models/models-changes-tracking.md b/website/docs/digging-deeper/models/models-changes-tracking.md index 52138194..5d52fe28 100644 --- a/website/docs/digging-deeper/models/models-changes-tracking.md +++ b/website/docs/digging-deeper/models/models-changes-tracking.md @@ -38,7 +38,7 @@ 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`](/docs/api/@foscia/core/type-aliases/ModelInstance#original) properties of +[`$original`](/docs/api/@foscia/core/interfaces/ModelInstance#original) properties of your instance. ```typescript diff --git a/website/docs/digging-deeper/models/models-configuration.md b/website/docs/digging-deeper/models/models-configuration.md index a06bcff8..bbef89bc 100644 --- a/website/docs/digging-deeper/models/models-configuration.md +++ b/website/docs/digging-deeper/models/models-configuration.md @@ -140,13 +140,11 @@ Here is an example of a type guesser using hypothetical `toKebabCase` and relation, this would guess the type to `blog-posts`: ```typescript title="post.ts" -import { makeModel, isPluralRelationDef, ModelRelation } from '@foscia/core'; +import { makeModel, ModelRelation } from '@foscia/core'; makeModel({ type: 'posts', - guessRelationType: (def: ModelRelation) => ( - isPluralRelationDef(def) ? def.key : pluralize(def.key) - ), + guessRelationType: (def: ModelRelation) => pluralize(def.key), }); ``` @@ -169,7 +167,7 @@ Here is an example of an inverse guesser using hypothetical `toCamelCase` and relation, this would guess the inverse to `post`: ```typescript title="post.ts" -import { makeModel, isPluralRelationDef, ModelRelation } from '@foscia/core'; +import { makeModel, ModelRelation } from '@foscia/core'; makeModel({ type: 'posts', @@ -184,8 +182,8 @@ makeModel({ **Default**: `true`. Enable storing related records of a snapshot as -[`ModelLimitedSnapshot`](/docs/api/@foscia/core/type-aliases/ModelLimitedSnapshot), -instead of [`ModelSnapshot`](/docs/api/@foscia/core/type-aliases/ModelSnapshot), +[`ModelLimitedSnapshot`](/docs/api/@foscia/core/interfaces/ModelLimitedSnapshot), +instead of [`ModelSnapshot`](/docs/api/@foscia/core/interfaces/ModelSnapshot), to improve memory footprint and performance. ##### Example diff --git a/website/docs/digging-deeper/models/models-reduce-revive.mdx b/website/docs/digging-deeper/models/models-reduce-revive.mdx index 4ac48d1c..450562c8 100644 --- a/website/docs/digging-deeper/models/models-reduce-revive.mdx +++ b/website/docs/digging-deeper/models/models-reduce-revive.mdx @@ -141,8 +141,8 @@ inside your [`$reduce`](/docs/api/@foscia/core/type-aliases/ModelCanReduceRevive 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`](/docs/api/@foscia/core/type-aliases/ModelInstance#exists) state, -[`$original`](/docs/api/@foscia/core/type-aliases/ModelInstance#original) +[`$exists`](/docs/api/@foscia/core/interfaces/ModelInstance#exists) state, +[`$original`](/docs/api/@foscia/core/interfaces/ModelInstance#original) snapshot or other instance properties). ```typescript diff --git a/website/docs/digging-deeper/usages/cli.mdx b/website/docs/digging-deeper/usages/cli.mdx index e9ac1084..fd1f9341 100644 --- a/website/docs/digging-deeper/usages/cli.mdx +++ b/website/docs/digging-deeper/usages/cli.mdx @@ -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/usages/http.mdx b/website/docs/digging-deeper/usages/http.mdx index fef8f243..03bc0754 100644 --- a/website/docs/digging-deeper/usages/http.mdx +++ b/website/docs/digging-deeper/usages/http.mdx @@ -70,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!' }, }), @@ -84,8 +84,8 @@ const data = await action().run( ### Advanced usage You can pass a lot of options to each request enhancers, from -[`headers`](/docs/api/@foscia/http/type-aliases/HttpRequestConfig#headers) and -[`params`](/docs/api/@foscia/http/type-aliases/HttpRequestConfig#params) options +[`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): @@ -96,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!' }, }, { @@ -135,7 +135,7 @@ 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: {}, @@ -155,7 +155,7 @@ for more details. ### Defining default headers To define a default request headers, use the -[`defaultHeaders`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig#defaultheaders) +[`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). diff --git a/website/docs/digging-deeper/usages/jsonapi.md b/website/docs/digging-deeper/usages/jsonapi.md index 0f7ac3f2..f0810688 100644 --- a/website/docs/digging-deeper/usages/jsonapi.md +++ b/website/docs/digging-deeper/usages/jsonapi.md @@ -28,7 +28,7 @@ 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 @@ -41,7 +41,7 @@ both supports key-value or object parameters. 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), @@ -65,7 +65,7 @@ ascending sorting by default. 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']), @@ -93,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'), @@ -117,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 }), @@ -145,7 +145,7 @@ a global search endpoint: import { queryAs, all } from '@foscia/core'; import { paginate, usingDocument } from '@foscia/jsonapi'; -const results = await action().run( +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]), @@ -172,14 +172,14 @@ the JSON:API adapter is based on HTTP adapter. 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/http/type-aliases/HttpAdapterConfig#modelpathtransformer) +[`modelPathTransformer`](/docs/api/@foscia/jsonapi/interfaces/JsonApiAdapterConfig#modelpathtransformer) and -[`relationPathTransformer`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig#relationpathtransformer) +[`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), @@ -193,13 +193,13 @@ 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/serialization/type-aliases/SerializerConfig#serializekey) -and [`deserializeKey`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig#deserializekey) +[`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), @@ -215,11 +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`](/docs/api/@foscia/rest/type-aliases/RestDeserializerConfig#pullidentifier) +[`pullIdentifier`](/docs/api/@foscia/jsonapi/interfaces/JsonApiDeserializerConfig#pullidentifier) option. ```typescript -import { makeJsonApiDeserializer } from '@foscia/rest'; +import { makeJsonApiDeserializer } from '@foscia/jsonapi'; makeJsonApiDeserializer({ pullIdentifier: (record) => { diff --git a/website/docs/digging-deeper/usages/rest.md b/website/docs/digging-deeper/usages/rest.md index bda0b1bb..c8c34dae 100644 --- a/website/docs/digging-deeper/usages/rest.md +++ b/website/docs/digging-deeper/usages/rest.md @@ -61,9 +61,8 @@ a global search endpoint. In combination with ```typescript import { queryAs, all } from '@foscia/core'; -import { paginate, usingDocument } from '@foscia/jsonapi'; -const results = await action().run( +const results = await action( // Notice the `queryAs` instead of `query`, this will // GET `/api/search` instead of `/api/posts/search`. queryAs([Post, Comment, User]), @@ -92,11 +91,11 @@ If your REST API supports eager loading relations, you can use 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/type-aliases/RestAdapterConfig#includeparamkey) +[`includeParamKey`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig#includeparamkey) option. ```typescript -import { makeRestAdapter } from '@foscia/http'; +import { makeRestAdapter } from '@foscia/rest'; const { adapter } = makeRestAdapter({ includeParamKey: 'with', @@ -126,7 +125,7 @@ makeRestAdapter({ 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/serialization/type-aliases/DeserializerConfig#extractdata) option which will extract +[`extractData`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig#extractdata) option which will extract records data using the given transformation function. ```typescript @@ -169,7 +168,7 @@ This behavior can also be easily implemented when 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/serialization/type-aliases/SerializerConfig#createdata) +[`createData`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#createdata) option which will wrap records data using the given transformation function. @@ -186,9 +185,9 @@ makeRestSerializer({ 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/http/type-aliases/HttpAdapterConfig#modelpathtransformer) +[`modelPathTransformer`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig#modelpathtransformer) and -[`relationPathTransformer`](/docs/api/@foscia/http/type-aliases/HttpAdapterConfig#relationpathtransformer) +[`relationPathTransformer`](/docs/api/@foscia/rest/interfaces/RestAdapterConfig#relationpathtransformer) options. ```typescript @@ -207,8 +206,8 @@ 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/serialization/type-aliases/SerializerConfig#serializekey) -and [`deserializeKey`](/docs/api/@foscia/serialization/type-aliases/DeserializerConfig#deserializekey) +[`serializeKey`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#serializekey) +and [`deserializeKey`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig#deserializekey) options. ```typescript @@ -228,7 +227,7 @@ makeRestDeserializer({ 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/serialization/type-aliases/SerializerConfig#serializerelation) +[`serializeRelation`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#serializerelation) option, which will provide the related instance snapshot. #### Supporting polymorphism @@ -278,7 +277,7 @@ 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/serialization/type-aliases/SerializerConfig#serializerelated) +[`serializeRelated`](/docs/api/@foscia/rest/interfaces/RestSerializerConfig#serializerelated) option which have the same signature. ::: @@ -288,7 +287,7 @@ option which have the same signature. 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/type-aliases/RestDeserializerConfig#pullidentifier) +[`pullIdentifier`](/docs/api/@foscia/rest/interfaces/RestDeserializerConfig#pullidentifier) option. ```typescript diff --git a/website/docs/digging-deeper/usages/testing.mdx b/website/docs/digging-deeper/usages/testing.mdx index 703030e0..df026b7e 100644 --- a/website/docs/digging-deeper/usages/testing.mdx +++ b/website/docs/digging-deeper/usages/testing.mdx @@ -120,7 +120,7 @@ export default function registerUser( acceptedTermsAt: new Date(), }); - return action().run(create(user), oneOrCurrent()); + return action(create(user), oneOrCurrent()); } ``` diff --git a/website/docs/examples/jsonapi-blog.mdx b/website/docs/examples/jsonapi-blog.mdx index 5be4c519..70b7bb6d 100644 --- a/website/docs/examples/jsonapi-blog.mdx +++ b/website/docs/examples/jsonapi-blog.mdx @@ -78,7 +78,7 @@ type AllPostsQuery = { }; export default async function fetchAllPosts(query: AllPostsQuery = {}) { - return action().run( + return action( query(Post), when(query.search, (a, s) => a.use(filterBy('search', s))), sortByDesc('createdAt'), @@ -98,7 +98,7 @@ import action from './action'; import Post from './models/post'; export default async function fetchOnePost(id: string) { - return action().run( + return action( query(Post, id), include('tags'), oneOrFail(), @@ -122,7 +122,7 @@ export default async function savePost( fill(post, values); try { - await action().run( + await action( save(post), when(!post.$exists || changed(post), oneOrCurrent(), () => instance), ); @@ -150,7 +150,7 @@ import { destroy, none } from '@foscia/core'; import action from './action'; export default async function deletePost(post: Post) { - await action().run(destroy(post), none()); + await action(destroy(post), none()); } await deletePost(post); @@ -170,7 +170,7 @@ import action from './action'; import Post from './models/post'; export default function bestPosts() { - return action().run( + return action( query(Post), makeGet('actions/best-posts'), all(), @@ -178,7 +178,7 @@ export default function bestPosts() { } export default function publishPost(post: Post) { - return action().run( + return action( query(post), makePost('actions/publish', { data: { publishedAt: new Date(), }, diff --git a/website/docs/getting-started.mdx b/website/docs/getting-started.mdx index 425a91ab..27d721ce 100644 --- a/website/docs/getting-started.mdx +++ b/website/docs/getting-started.mdx @@ -88,7 +88,7 @@ type safe interactions with your data source. 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/type-aliases/Model) constructor: +[`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. @@ -217,16 +217,18 @@ export default makeActionFactory({ ### Running simple actions To run an action, you can initialize a new -[`Action`](/docs/api/@foscia/core/type-aliases/Action) object by calling your +[`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'; @@ -234,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(), ); @@ -259,7 +261,7 @@ 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(), ); @@ -283,7 +285,7 @@ const post = fill(new Post(), { description: 'Your first post', }); -const createdPost = await action().run( +const createdPost = await action( create(post), oneOrCurrent(), ); @@ -302,10 +304,10 @@ import action from './action'; // This has no effect an may failed because `destroy` is not a runner! // highlight.deletion -await action().run(destroy(post)); +await action(destroy(post)); // Do this instead: // highlight.addition -await action().run(destroy(post), none()); +await action(destroy(post), none()); ``` action().run(query(Post), all()), + () => action(query(Post), all()), ); diff --git a/website/src/components/HomeFeatures/index.js b/website/src/components/HomeFeatures/index.js index c597129d..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 Date: Sat, 8 Feb 2025 15:12:00 +0100 Subject: [PATCH 31/32] chore: use another install method for corepack --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From daa6a24eef041ff806b4bd7aa8d9ad389bb27ae2 Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 8 Feb 2025 14:39:17 +0100 Subject: [PATCH 32/32] feat(core): model assembled property --- packages/core/src/index.ts | 2 + .../src/model/props/builders/assembled.ts | 132 ++++++++++++++++++ .../core/src/model/props/builders/types.ts | 40 ++++++ packages/core/src/model/types.ts | 17 +++ .../core/tests/unit/model/assembled.test.ts | 100 +++++++++++++ 5 files changed, 291 insertions(+) create mode 100644 packages/core/src/model/props/builders/assembled.ts create mode 100644 packages/core/tests/unit/model/assembled.test.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 7dd4ab06..d235eaf1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -49,6 +49,7 @@ import onPropertyWriting from '@foscia/core/model/hooks/properties/onPropertyWri import isSame from '@foscia/core/model/isSame'; 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'; @@ -143,6 +144,7 @@ export { hasMany, hasOne, id, + assembled, loaded, fill, forceFill, 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/types.ts b/packages/core/src/model/props/builders/types.ts index 35671e4e..158f5036 100644 --- a/packages/core/src/model/props/builders/types.ts +++ b/packages/core/src/model/props/builders/types.ts @@ -1,4 +1,5 @@ import { + ModelAssembled, ModelAttribute, ModelComposableFactory, ModelId, @@ -217,3 +218,42 @@ export type ModelRelationFactoryConfig | null, R ext 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/types.ts b/packages/core/src/model/types.ts index 9c75ee23..2de92bd7 100644 --- a/packages/core/src/model/types.ts +++ b/packages/core/src/model/types.ts @@ -244,6 +244,23 @@ export type ModelValueProp = } & ModelProp; +/** + * Model assembled/memoized property. + * + * @interface + * + * @since 0.13.0 + * @experimental + */ +export type ModelAssembled = + & { + /** + * Tells if the assembled property getter is memoized (enabled by default). + */ + memo?: boolean; + } + & ModelProp; + /** * Model ID property. * 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'); + }); +});

= P extends ModelValueProp ? T : never; - -/** - * The parsed model definition with non composables/attributes/relations - * properties' descriptors wrapped in holders. - * - * @internal - */ -export type ModelParsedDefinition = { - [K in keyof D]: D[K] extends ModelComposable | ModelPropFactory | DescriptorHolder - ? D[K] : DescriptorHolder -}; - -/** - * The flatten and parsed model definition with composables properties - * flattened to the definition root. - * - * @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] }>; - -/** - * The flatten and parsed model definition from a composable object type. - * - * @internal - */ -export type ModelDefinitionFromComposable> = ModelFlattenDefinition<{ - __composable__: C; -}>; - -/** - * Extract model's properties from definition factories. - * - * @internal - */ -export type ModelSchema = { - readonly [K in keyof D]: D[K] extends ModelPropFactory - ? P & { key: K; } : never; -}; + & ModelValueProp; /** * Model instance read property generic hook callback function. @@ -399,7 +338,7 @@ export type ModelSchema = { */ export type ModelInstancePropertyReadHookCallback = SyncHookCallback<{ readonly instance: ModelInstance; - readonly def: ModelValueProp; + readonly def: ModelProp; readonly value: unknown; }>; @@ -410,13 +349,13 @@ export type ModelInstancePropertyReadHookCallback = SyncHookCallback<{ */ export type ModelInstancePropertyWriteHookCallback = SyncHookCallback<{ readonly instance: ModelInstance; - readonly def: ModelValueProp; + readonly def: ModelProp; readonly prev: unknown; readonly next: unknown; }>; /** - * Model's hooks definition dedicated to a model. + * Model hooks definition dedicated to a model. * * @internal */ @@ -425,7 +364,7 @@ export type ModelHooksDefinitionForModel = { }; /** - * Model's hooks definition dedicated to an instance. + * Model hooks definition dedicated to an instance. * * @internal */ @@ -443,7 +382,7 @@ export type ModelHooksDefinitionForInstance = { }; /** - * Model's hooks definition dedicated to an instance property. + * Model hooks definition dedicated to an instance property. * * @internal */ @@ -460,7 +399,7 @@ export type ModelHooksDefinitionForInstanceProperty = & Record<`property:write:${string}`, ModelInstancePropertyWriteHookCallback>; /** - * Model's hooks definition. + * Model hooks definition. * * @internal */ @@ -469,11 +408,65 @@ export type ModelHooksDefinition = & ModelHooksDefinitionForInstance & ModelHooksDefinitionForInstanceProperty; +/** + * Model instance. + * + * @typeParam D Flatten definition of the model. + * + * @interface + */ +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 Definition of the model. + * @typeParam D Flatten definition of the model. * @typeParam I Instance of the model. + * + * @interface */ export type Model = any> = & { @@ -488,8 +481,8 @@ export type Model = any> = readonly $config: ModelConfig; /** * Schema of the model. - * It is parsed once on first property get (usually on first constructor call) - * and will be cached afterward. It should not be updated afterward. + * + * @internal */ readonly $schema: ModelSchema; /** @@ -498,32 +491,44 @@ export type Model = any> = * @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; } & Constructor & Hookable & FosciaObject; /** - * Model class using a given composable. - */ -export type ModelUsing = - Model, ModelInstanceUsing>; - -/** - * Model class which can be configured or extended. + * Model class which can be configured or extended to another model. * * @internal */ export type ExtendableModel = any> = & { /** - * Create a new model class with an extended configuration. + * Create a new model class with an overwritten configuration. * * @param config * @param override @@ -538,10 +543,9 @@ export type ExtendableModel = any * @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; @@ -554,269 +558,288 @@ export type ModelFactory< D extends {} = {}, > = Hookable & (( rawConfig: string | (Partial & { type: string; }), + rawDefinition?: ND & ThisType>>, // eslint-disable-next-line max-len - rawDefinition?: ND & ThisType>>>, - // eslint-disable-next-line max-len -) => ExtendableModel>, ModelInstance>>>); +) => ExtendableModel, ModelInstance>>); /** - * Model defaults IDs typing when not defined by definition. + * Model instance snapshot. * - * @internal + * @interface */ -export type ModelIdsDefaults = - & (D extends { id: any } ? {} : { id: ModelIdType | null }) - & (D extends { lid: any } ? {} : { lid: ModelIdType | null }); +export type ModelSnapshot = { + readonly $instance: ModelInstance; + readonly $original: ModelSnapshot | null; + readonly $exists: boolean; + readonly $raw: any; + readonly $loaded: Dictionary; + readonly $values: Partial>>>; +} & FosciaObject; /** - * Model properties map (IDs/attributes/relations/custom props). + * Model instance snapshot which only contains ID and LID + * and is used to track related records inside a snapshot. * - * @internal + * @interface */ -export type ModelDefinitionProperties = ModelIdsDefaults & { - [K in keyof D]: D[K] extends ModelPropFactory> - ? T : D[K] extends DescriptorHolder - ? T : D[K]; -}; +export type ModelLimitedSnapshot = { + readonly $instance: ModelInstance; + readonly $exists: boolean; + readonly $values: Partial>]: K extends 'id' | 'lid' + ? ModelSnapshotValues>[K] + : never; + }>>>; +} & FosciaObject; /** - * Model properties key (only writable IDs/attributes/relations). - * - * @internal + * Model class using a given composable or composable factory. */ -export type ModelDefinitionWritableKey = { - [K in keyof D]: D[K] extends DescriptorHolder - ? never - : D[K] extends ModelPropFactory> ? K : never; -}[keyof D] | keyof ModelIdsDefaults; +export type ModelUsing = + Model, ModelInstanceUsing>; /** - * Model properties key (only readonly IDs/attributes/relations). + * Model instance using a given composable or composable factory. + */ +export type ModelInstanceUsing = + ModelInstance>; + +/** + * Parsed model definition with non-composable properties stored into + * descriptor holders. * * @internal */ -export type ModelDefinitionReadOnlyKey = { - [K in keyof D]: D[K] extends DescriptorHolder - ? never - : D[K] extends ModelPropFactory> ? K : never; -}[keyof D]; +export type ModelParsedDefinition = { + [K in keyof D]: D[K] extends ModelComposableFactory | DescriptorHolder + ? D[K] : DescriptorHolderOf; +}; /** - * Model descriptors key (only custom properties). + * Flatten non-properties composables to the definition's root. * * @internal */ -export type ModelDefinitionDescriptorKey = { - [K in keyof D]: D[K] extends DescriptorHolder ? K : never; -}[keyof D]; +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 map (only writable IDs/attributes/relations). + * Extract root properties composables or descriptors. * * @internal */ -export type ModelDefinitionWritableValues = - Pick, ModelDefinitionWritableKey>; +export type ModelRootProperties = OmitNever<{ + [K in keyof D]: D[K] extends ModelComposableFactory + ? C extends ModelProp ? (D[K] & { key: K; }) : never : D[K]; +}>; /** - * Model properties map (only readonly IDs/attributes/relations). + * Flatten definition typing with non-properties composable typings resolved to + * the definition root. * * @internal */ -export type ModelDefinitionReadOnlyValues = - Readonly, ModelDefinitionReadOnlyKey>>; +export type ModelFlattenDefinition = + & ModelFlattenComposables + & ModelRootProperties; /** - * Model properties map (IDs/attributes/relations). + * {@link ModelParsedDefinition | `ModelParsedDefinition`} of a model + * {@link ModelFlattenDefinition | `ModelFlattenDefinition`}. * * @internal */ -export type ModelDefinitionValues = - & ModelDefinitionWritableValues - & ModelDefinitionReadOnlyValues; +export type ModelParsedFlattenDefinition = + ModelParsedDefinition>; /** - * Model values map for a snapshot (IDs/attributes/relations). + * Model real schema from definition. * * @internal */ -export type ModelSnapshotDefinitionValues = ModelIdsDefaults & { - [K in keyof D]: D[K] extends ModelPropFactory> - ? T extends (infer I)[] - ? (ModelLimitedSnapshot | ModelSnapshot)[] - : (ModelLimitedSnapshot | ModelSnapshot) - : D[K] extends ModelPropFactory> - ? T - : D[K] extends ModelPropFactory> ? any : never; -}; +export type ModelRealSchema = OmitNever<{ + [K in keyof D]: D[K] extends ModelComposableFactory + ? C extends ModelProp ? C : never : never; +}>; /** - * Model values map for a limited snapshot (IDs/attributes/relations). + * Model default schema from definition. * * @internal */ -export type ModelLimitedSnapshotDefinitionValues = ModelIdsDefaults & { - [K in keyof ModelSnapshotDefinitionValues]: K extends 'id' | 'lid' - ? ModelSnapshotDefinitionValues[K] : never; -}; +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. * - * @typeParam D Definition of the model instance. + * @internal */ -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; - /** - * Tells which relation is considered to be loaded. - */ - $loaded: Dictionary; - /** - * Internal values of the instance's properties. - */ - $values: Partial>; - /** - * Raw value of the data source record. - */ - $raw: any; - /** - * Latest synced snapshot. - */ - $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 instance snapshot which only contains ID and LID - * and is used to track related records inside a snapshot. + * Model properties from definition. + * + * @internal */ -export type ModelLimitedSnapshot = { - readonly $instance: ModelInstance; - readonly $exists: boolean; - readonly $values: Partial>>; -} & FosciaObject; +export type ModelDefinitionProperties = + & IfAny, ModelDefinitionRealProperties> + & IfAny>; /** - * Model instance snapshot. + * Model real snapshot values from definition. + * + * @internal */ -export type ModelSnapshot = { - readonly $instance: ModelInstance; - readonly $original: ModelSnapshot | null; - readonly $exists: boolean; - readonly $raw: any; - readonly $loaded: Dictionary; - readonly $values: Partial>>; -} & FosciaObject; +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; +}>; /** - * Infer the definition from a model class or model instance. + * Model default snapshot values from definition. * * @internal */ -export type InferModelDefinition = M extends Model - ? D : M extends ModelInstance ? D - : M extends {} - ? M - : never; +export type ModelDefaultSnapshotValues = + & (ModelRealSchema extends { id: any } ? {} : { id: ModelIdType | null; }) + & (ModelRealSchema extends { lid: any } ? {} : { lid: ModelIdType | null; }); /** - * Infer the schema from a model class or model instance. + * Model snapshot values from definition. * * @internal */ -export type InferModelSchema = ModelSchema>; +export type ModelSnapshotValues = + IfAny & ModelDefaultSnapshotValues>; /** - * Infer a schema property from a model class or model instance - * and ensure it extends the given `P` generic type. + * Infer the definition from a composable or a composable factory. * * @internal */ -export type InferModelSchemaProp = - K extends keyof ModelSchema> - ? ModelSchema>[K] extends P - ? ModelSchema>[K] - : never : never; +export type InferModelComposableDefinition = + C extends ModelComposableFactory ? T['_type'] + : C extends ModelComposable ? C['_type'] + : {}; /** - * Model snapshot values map (only IDs/attributes/relations). + * Infer the definition from a model class, a model instance or a definition. + * + * @internal */ -export type ModelSnapshotValues = ModelSnapshotDefinitionValues>; +export type InferModelDefinition = + M extends FosciaObject & { + readonly _definition: infer D + } ? D extends {} ? D : never : M extends {} ? M : never; /** - * Model limited snapshot values map (only IDs). + * Infer the schema from a model class, a model instance or a definition. + * + * @internal */ -export type ModelLimitedSnapshotValues< - M, -> = ModelLimitedSnapshotDefinitionValues>; +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 InferModelSchema - & keyof ModelValues; + & 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 InferModelSchema extends infer K - ? K extends ModelKey - ? InferModelSchema[K] extends never - ? never - : InferModelSchema[K] extends ModelRelation - ? K - : InferModelSchema[K] extends ModelProp - ? 0 extends (1 & T) ? K - : never : 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']; @@ -824,14 +847,12 @@ export type ModelRelationKey = export type ModelRelationDotKey = [Depth] extends [0] ? never - : keyof InferModelSchema extends infer K + : ModelKey extends infer K ? K extends ModelKey ? InferModelSchema[K] extends never ? never - : InferModelSchema[K] extends ModelRelation - ? T extends any[] - ? K | `${K}.${ModelRelationDotKey}` - : K | `${K}.${ModelRelationDotKey}` - : InferModelSchema[K] extends ModelProp - ? 0 extends (1 & T) ? K : never + : InferModelSchema[K] extends ModelRelation + ? K | `${K}.${ModelRelationDotKey}` + : InferModelSchema[K] extends ModelProp + ? IfAny : never : never : never; diff --git a/packages/core/src/normalization/normalizeDotRelations.ts b/packages/core/src/normalization/normalizeDotRelations.ts index f657d161..d98572e7 100644 --- a/packages/core/src/normalization/normalizeDotRelations.ts +++ b/packages/core/src/normalization/normalizeDotRelations.ts @@ -1,7 +1,7 @@ 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, ModelRelationDotKey } from '@foscia/core/model/types'; +import { Model, ModelKey, ModelRelationDotKey } from '@foscia/core/model/types'; import normalizeKey from '@foscia/core/normalization/normalizeKey'; import { ModelsRegistry } from '@foscia/core/types'; import { isNone, Optional } from '@foscia/shared'; @@ -15,13 +15,13 @@ import { isNone, Optional } from '@foscia/shared'; * * @internal */ -const normalizeDotRelations = ( - model: Model, - relations: ModelRelationDotKey>[], +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 keyof typeof model['$schema']]; + 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.`, @@ -30,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 761d8c72..5eb65257 100644 --- a/packages/core/src/normalization/normalizeKey.ts +++ b/packages/core/src/normalization/normalizeKey.ts @@ -1,4 +1,4 @@ -import { Model, ModelKey, ModelValueProp } from '@foscia/core/model/types'; +import { Model, ModelKey } from '@foscia/core/model/types'; import { using } from '@foscia/shared'; /** @@ -9,9 +9,9 @@ import { using } from '@foscia/shared'; * * @internal */ -export default ( - model: Model, - key: ModelKey>, -) => using(model.$schema[key] as unknown as ModelValueProp, (def) => def.alias ?? ( - model.$config.guessAlias ? model.$config.guessAlias(def.key) : def.key +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/types.ts b/packages/core/src/registry/types.ts index eb20761d..3338aa1d 100644 --- a/packages/core/src/registry/types.ts +++ b/packages/core/src/registry/types.ts @@ -5,6 +5,8 @@ import { Optional, Transformer } from '@foscia/shared'; /** * Config for registry map implementation. * + * @interface + * * @internal */ export type MapRegistryConfig = { @@ -15,6 +17,8 @@ export type MapRegistryConfig = { /** * Registry implementation using mapped models by types. * + * @interface + * * @internal */ export type MapRegistry = ModelsRegistry; diff --git a/packages/core/src/transformers/isTransformer.ts b/packages/core/src/transformers/isTransformer.ts index af564b1b..cd7648af 100644 --- a/packages/core/src/transformers/isTransformer.ts +++ b/packages/core/src/transformers/isTransformer.ts @@ -2,6 +2,13 @@ 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/toBoolean.ts b/packages/core/src/transformers/toBoolean.ts index ea7cfeeb..e73b317d 100644 --- a/packages/core/src/transformers/toBoolean.ts +++ b/packages/core/src/transformers/toBoolean.ts @@ -1,11 +1,5 @@ import makeTransformer from '@foscia/core/transformers/makeTransformer'; -type ToBooleanOptions = { - trueValues?: unknown[]; -}; - -const DEFAULT_TRUE_VALUES = [true, 1, '1', 'true', 'yes']; - /** * Create a boolean transformer. * @@ -13,6 +7,8 @@ const DEFAULT_TRUE_VALUES = [true, 1, '1', 'true', 'yes']; * * @category Factories */ -export default (options?: ToBooleanOptions) => makeTransformer( - (value: unknown) => (options?.trueValues ?? DEFAULT_TRUE_VALUES).indexOf(value) !== -1, +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/types.ts b/packages/core/src/transformers/types.ts index 80ee73ab..30a57638 100644 --- a/packages/core/src/transformers/types.ts +++ b/packages/core/src/transformers/types.ts @@ -3,6 +3,8 @@ import { Awaitable, FosciaObject, Transformer } from '@foscia/shared'; /** * Bi-directional object transformer. + * + * @internal */ export type ObjectTransformer = & { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 17e77f36..6e03081a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -12,6 +12,8 @@ import type { Arrayable, Awaitable } from '@foscia/shared'; * * It will be used by other dependencies, like {@link Deserializer | `Deserializer`}, * to deserialize raw data source records in the correct models. + * + * @interface */ export type ModelsRegistry = { /** @@ -31,6 +33,8 @@ export type ModelsRegistry = { * 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 type InstancesCache = { /** @@ -95,6 +99,8 @@ export type AdapterResponse = { * @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 type Adapter = { /** @@ -122,6 +128,8 @@ export type DeserializedData = { * @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 = { /** @@ -140,6 +148,8 @@ export type Deserializer = { /** diff --git a/packages/core/tests/mocks/models/post.mock.ts b/packages/core/tests/mocks/models/post.mock.ts index 2cb6d05c..48f675c7 100644 --- a/packages/core/tests/mocks/models/post.mock.ts +++ b/packages/core/tests/mocks/models/post.mock.ts @@ -8,8 +8,8 @@ export default class PostMock extends makeModel('posts', { title: attr(), body: attr(), publishedAt: attr(toDateTime()).nullable().readOnly(), -}) { get published() { return !!this.publishedAt; - } + }, +}) { } diff --git a/packages/core/tests/typecheck/actions.test-d.ts b/packages/core/tests/typecheck/actions.test-d.ts index 95971761..59c32f99 100644 --- a/packages/core/tests/typecheck/actions.test-d.ts +++ b/packages/core/tests/typecheck/actions.test-d.ts @@ -1,6 +1,7 @@ import { Adapter, - all, appendActionMiddlewares, + all, + appendActionMiddlewares, associate, attach, cached, @@ -49,6 +50,11 @@ 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[]; @@ -56,6 +62,7 @@ test('Actions are type safe', async () => { expectTypeOf(postsUsingFunc).toEqualTypeOf(); expectTypeOf(postsUsingVariadic).toEqualTypeOf(); expectTypeOf(postsUsingRunVariadic).toEqualTypeOf(); + expectTypeOf(postsUsingFactoryVariadic).toEqualTypeOf(); expectTypeOf(manualPostsUsingRunVariadic).toEqualTypeOf(); const postNullUsingFunc = await action() diff --git a/packages/core/tests/typecheck/models-composition.test-d.ts b/packages/core/tests/typecheck/models-composition.test-d.ts index 297c695a..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, @@ -31,7 +45,8 @@ test('Models compositions are type safe', () => { // @ts-expect-error property does not exist expectTypeOf(instance.bar).toEqualTypeOf(); }); - onPropertyWrite(foo, 'foo', ({ instance }) => { + onPropertyWrite(foo, 'foo', ({ instance, def }) => { + expectTypeOf(def).toMatchTypeOf>(); expectTypeOf(instance.foo).toEqualTypeOf(); // @ts-expect-error property does not exist expectTypeOf(instance.bar).toEqualTypeOf(); @@ -48,7 +63,8 @@ test('Models compositions are type safe', () => { // @ts-expect-error property does not exist expectTypeOf(instance.unknown).toEqualTypeOf(); }); - onPropertyWrite(Model, 'foo', ({ instance }) => { + onPropertyWrite(Model, 'foo', ({ instance, def }) => { + expectTypeOf(def).toMatchTypeOf>(); expectTypeOf(instance.foo).toEqualTypeOf(); expectTypeOf(instance.baz).toEqualTypeOf(); // @ts-expect-error property does not exist @@ -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 acecebb4..57effbf3 100644 --- a/packages/core/tests/typecheck/models.test-d.ts +++ b/packages/core/tests/typecheck/models.test-d.ts @@ -39,6 +39,8 @@ test('Models are type safe', () => { // @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); @@ -66,6 +68,11 @@ test('Models are type safe', () => { // @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).toEqualTypeOf(); 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 fcd1ee40..762cd73a 100644 --- a/packages/core/tests/unit/model/snapshots.test.ts +++ b/packages/core/tests/unit/model/snapshots.test.ts @@ -105,38 +105,38 @@ describe.concurrent('unit: snapshots', () => { }); it('should use clone and compare model options', () => { - const cloneValue = vi.fn((v) => (Array.isArray(v) ? [...v] : v)); - const compareValues = 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, - compareValues, + cloneSnapshotValue, + compareSnapshotValues, }); - expect(cloneValue).not.toHaveBeenCalled(); - expect(compareValues).not.toHaveBeenCalled(); + expect(cloneSnapshotValue).not.toHaveBeenCalled(); + expect(compareSnapshotValues).not.toHaveBeenCalled(); const post = new ExtendedPostMock(); - expect(cloneValue).not.toHaveBeenCalled(); - expect(compareValues).not.toHaveBeenCalled(); + expect(cloneSnapshotValue).not.toHaveBeenCalled(); + expect(compareSnapshotValues).not.toHaveBeenCalled(); post.title = 'foo'; post.comments = []; const snapshot = takeSnapshot(post); - expect(cloneValue).toHaveBeenCalledTimes(3); - expect(compareValues).not.toHaveBeenCalled(); + expect(cloneSnapshotValue).toHaveBeenCalledTimes(3); + expect(compareSnapshotValues).not.toHaveBeenCalled(); expect(isSameSnapshot(snapshot, post.$original, 'title')).toStrictEqual(true); - expect(compareValues).toHaveBeenCalledTimes(1); + expect(compareSnapshotValues).toHaveBeenCalledTimes(1); expect(isSameSnapshot(snapshot, post.$original, 'comments')).toStrictEqual(true); - expect(compareValues).toHaveBeenCalledTimes(2); + expect(compareSnapshotValues).toHaveBeenCalledTimes(2); expect(isSameSnapshot(snapshot, post.$original, 'commentsCount')).toStrictEqual(true); - expect(compareValues).toHaveBeenCalledTimes(3); + expect(compareSnapshotValues).toHaveBeenCalledTimes(3); - expect(cloneValue).toHaveBeenCalledTimes(3); + expect(cloneSnapshotValue).toHaveBeenCalledTimes(3); }); it('should take deep and limited snapshots', () => { diff --git a/packages/http/src/errors/httpNotFoundError.ts b/packages/http/src/errors/httpNotFoundError.ts index 2113c9b8..0540c69b 100644 --- a/packages/http/src/errors/httpNotFoundError.ts +++ b/packages/http/src/errors/httpNotFoundError.ts @@ -1,11 +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'; /** * Error thrown on HTTP response status `404 Not Found`. * * @group Errors */ -export default class HttpNotFoundError extends HttpInvalidRequestError implements NotFoundErrorI { - public readonly $FOSCIA_ERROR_NOT_FOUND = true; +export default class HttpNotFoundError + extends HttpInvalidRequestError + implements FosciaFlaggedObject { + public readonly $FOSCIA_FLAGS = FLAG_ERROR_NOT_FOUND; } diff --git a/packages/http/src/makeHttpAdapter.ts b/packages/http/src/makeHttpAdapter.ts index 3d3df081..d7103d1f 100644 --- a/packages/http/src/makeHttpAdapter.ts +++ b/packages/http/src/makeHttpAdapter.ts @@ -132,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, @@ -143,7 +143,7 @@ export default (config: HttpAdapterConfig) => { referrerPolicy: contextConfig.referrerPolicy, signal: contextConfig.signal, window: contextConfig.window, - } as { [K in HttpRequestInitPickKey]: RequestInit[K]; }; + }; return { body, diff --git a/packages/http/src/types.ts b/packages/http/src/types.ts index 8d093e1c..c45d4493 100644 --- a/packages/http/src/types.ts +++ b/packages/http/src/types.ts @@ -38,6 +38,8 @@ export type HttpRequestInitPickKey = /** * Context value representing a HTTP request config. * + * @interface + * * @internal */ export type HttpRequestConfig = @@ -140,12 +142,16 @@ export type HttpResponseReader = (response: Response) => Promise>; /** * The configuration for the HTTP adapter implementation. * + * @interface + * * @internal */ export type HttpAdapterConfig = { @@ -222,6 +228,8 @@ export type HttpAdapterConfig = { /** * HTTP adapter. * + * @interface + * * @internal */ export type HttpAdapter = Adapter; @@ -233,27 +241,6 @@ export type HttpAdapter = Adapter; */ export type HttpParamsSerializer = (params: Dictionary) => string | undefined; -/** - * Transforms a {@link !Request | `Request`} object. - * - * @internal - */ -export type RequestTransformer = (request: Request) => Awaitable; - -/** - * Transforms a {@link !Response | `Response`} object. - * - * @internal - */ -export type ResponseTransformer = (response: Response) => Awaitable; - -/** - * Transforms an unknown error. - * - * @internal - */ -export type ErrorTransformer = (error: unknown) => Awaitable; - /** * Converts given body as a valid {@link !Request | `Request`} body. * diff --git a/packages/jsonapi/src/actions/context/enhancers/sortBy.ts b/packages/jsonapi/src/actions/context/enhancers/sortBy.ts index 1d55144e..29476c77 100644 --- a/packages/jsonapi/src/actions/context/enhancers/sortBy.ts +++ b/packages/jsonapi/src/actions/context/enhancers/sortBy.ts @@ -4,8 +4,10 @@ import { Arrayable, Dictionary, optionalJoin, uniqueValues, wrap } from '@foscia /** * Sort direction to apply. + * + * @internal */ -type SortDirection = 'asc' | 'desc'; +export type SortDirection = 'asc' | 'desc'; /** * Resolve the keys and directions arrays from given sort parameters. @@ -32,6 +34,7 @@ const serializeSort = ( key: string, direction: SortDirection, ) => `${direction === 'desc' ? '-' : ''}${key}`; + export default /* @__PURE__ */ makeEnhancer('sortBy', (( keys: Arrayable | Dictionary, directions: Arrayable = 'asc', diff --git a/packages/jsonapi/src/actions/context/runners/usingDocument.ts b/packages/jsonapi/src/actions/context/runners/usingDocument.ts index bc89ebbc..2035a8f2 100644 --- a/packages/jsonapi/src/actions/context/runners/usingDocument.ts +++ b/packages/jsonapi/src/actions/context/runners/usingDocument.ts @@ -3,7 +3,7 @@ import { JsonApiDeserializedData } from '@foscia/jsonapi/types'; /** * Append the {@link JsonApiDocument | JSON:API document object} to data object. - * Use it as the parameter of `allUsing` and `oneUsing` runners. + * Use it as the parameter of `all` or `one` runners. * * @param data * diff --git a/packages/jsonapi/src/types.ts b/packages/jsonapi/src/types.ts index 92020358..690f495f 100644 --- a/packages/jsonapi/src/types.ts +++ b/packages/jsonapi/src/types.ts @@ -5,16 +5,21 @@ 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) * @@ -164,6 +169,8 @@ export type JsonApiDeserializedData = /** * Configuration for JSON:API adapter. * + * @interface + * * @internal */ export type JsonApiAdapterConfig = RestAdapterConfig; @@ -171,6 +178,8 @@ export type JsonApiAdapterConfig = RestAdapterConfig; /** * Configuration for JSON:API deserializer. * + * @interface + * * @internal */ export type JsonApiDeserializerConfig< @@ -216,15 +225,17 @@ export type JsonApiDeserializerConfig< 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/rest/src/types.ts b/packages/rest/src/types.ts index 661b07c7..fbb97774 100644 --- a/packages/rest/src/types.ts +++ b/packages/rest/src/types.ts @@ -1,11 +1,11 @@ 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'; @@ -30,6 +30,8 @@ export type RestNewResource = RestAbstractResource & { /** * Configuration for REST adapter. * + * @interface + * * @internal */ export type RestAdapterConfig = HttpAdapterConfig & { @@ -44,6 +46,8 @@ export type RestAdapterConfig = HttpAdapterConfig & { /** * Configuration for REST deserializer. * + * @interface + * * @internal */ export type RestDeserializerConfig< @@ -89,11 +93,13 @@ export type RestDeserializerConfig< extract: Extract, ) => Awaitable | null | undefined>; } - & DeserializerConfig; + & RecordDeserializerConfig; /** * Configuration for REST serializer. * + * @interface + * * @internal */ export type RestSerializerConfig< @@ -108,4 +114,4 @@ export type RestSerializerConfig< */ serializeType?: boolean; } - & SerializerConfig; + & RecordSerializerConfig; diff --git a/packages/serialization/src/makeDeserializer.ts b/packages/serialization/src/makeDeserializer.ts index 8b9aaf9a..008a7e76 100644 --- a/packages/serialization/src/makeDeserializer.ts +++ b/packages/serialization/src/makeDeserializer.ts @@ -25,7 +25,7 @@ import { shouldSync, } from '@foscia/core'; import { - DeserializerConfig, + RecordDeserializerConfig, DeserializerContext, DeserializerExtract, DeserializerInstancesMap, @@ -59,7 +59,7 @@ export default < Data, Deserialized extends DeserializedData, Extract extends DeserializerExtract, ->(config: DeserializerConfig) => { +>(config: RecordDeserializerConfig) => { const NON_IDENTIFIED_LOCAL_ID = '__foscia_non_identified_local_id__'; const makeModelIdentifier = async ( @@ -212,7 +212,7 @@ You should either: setInstanceId('id'); setInstanceId('lid'); - await Promise.all([ + await Promise.all(Object.values({ ...mapAttributes(instance.$model, async (def) => { const deserializerContext = await makeDeserializerContext( instance, @@ -241,7 +241,7 @@ You should either: attachRelationInverse(instance, def, wrap(related)); } }), - ]); + })); const action = consumeAction(context, null); diff --git a/packages/serialization/src/makeSerializer.ts b/packages/serialization/src/makeSerializer.ts index dc9559f5..790cf05a 100644 --- a/packages/serialization/src/makeSerializer.ts +++ b/packages/serialization/src/makeSerializer.ts @@ -12,7 +12,7 @@ import SerializerCircularRelationError from '@foscia/serialization/errors/serializerCircularRelationError'; import { RecordSerializer, - SerializerConfig, + RecordSerializerConfig, SerializerContext, SerializerParents, } from '@foscia/serialization/types'; @@ -26,7 +26,7 @@ import { Arrayable, Awaitable, mapArrayable, using } from '@foscia/shared'; * @category Factories */ export default ( - config: SerializerConfig, + config: RecordSerializerConfig, ) => { const shouldSerialize = config.shouldSerialize ?? ((context) => ( @@ -87,7 +87,7 @@ export default ( ) => mapArrayable(snapshots, async (snapshot) => { const record = await config.createRecord(snapshot, context); - await Promise.all([ + await Promise.all(Object.values({ ...mapAttributes(snapshot.$instance.$model, async (def) => { const serializerContext = makeSerializerContext(snapshot, def, context); if (await shouldSerialize(serializerContext)) { @@ -129,7 +129,7 @@ export default ( } } }), - ]); + })); return record.retrieve(); }); diff --git a/packages/serialization/src/types.ts b/packages/serialization/src/types.ts index 836ee18c..d8243fa3 100644 --- a/packages/serialization/src/types.ts +++ b/packages/serialization/src/types.ts @@ -107,11 +107,13 @@ export type DeserializerRecordFactory< export type DeserializerInstancesMap = IdentifiersMap>; /** - * Configuration for deserializer. + * Configuration for generic record deserializer. + * + * @interface * * @internal */ -export type DeserializerConfig< +export type RecordDeserializerConfig< Record, Data, Deserialized extends DeserializedData, @@ -126,8 +128,6 @@ export type DeserializerConfig< extractData: (data: Data, context: {}) => Awaitable; /** * Create a deserializer record from. - * You should pass a {@link makeDeserializerRecordFactory | `makeDeserializerRecordFactory`} - * returned value instead of implementing the factory yourself. */ createRecord: DeserializerRecordFactory; /** @@ -188,6 +188,8 @@ export type DeserializerConfig< /** * Generic record deserializer. * + * @interface + * * @internal */ export type RecordDeserializer = @@ -276,15 +278,15 @@ export type SerializerParents = { model: Model; def: ModelRelation }[]; export type SerializerCircularRelationBehavior = 'throw' | 'skip' | 'keep'; /** - * Configuration for serializer. + * Configuration for generic record serializer. + * + * @interface * * @internal */ -export type SerializerConfig = { +export type RecordSerializerConfig = { /** * Create a serializer record object which can be hydrated and retrieved. - * You should pass a {@link makeSerializerRecordFactory | `makeSerializerRecordFactory`} - * returned value instead of implementing the factory yourself. */ createRecord: SerializerRecordFactory; /** @@ -377,6 +379,8 @@ export type SerializerConfig = { /** * Generic record serializer. * + * @interface + * * @internal */ export type RecordSerializer = 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/descriptors/makeDescriptorHolder.ts b/packages/shared/src/descriptors/makeDescriptorHolder.ts index a72567c2..231eaab1 100644 --- a/packages/shared/src/descriptors/makeDescriptorHolder.ts +++ b/packages/shared/src/descriptors/makeDescriptorHolder.ts @@ -14,7 +14,7 @@ export const SYMBOL_DESCRIPTOR_HOLDER = Symbol('foscia: descriptor holder'); * * @internal */ -export default (descriptor: PropertyDescriptor) => ({ +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 c99d9ec2..8466e6b4 100644 --- a/packages/shared/src/descriptors/types.ts +++ b/packages/shared/src/descriptors/types.ts @@ -1,12 +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/index.ts b/packages/shared/src/index.ts index e27bd98c..ef4a49c9 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -3,6 +3,7 @@ import mapWithKeys from '@foscia/shared/arrays/mapWithKeys'; 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'; @@ -10,7 +11,9 @@ import mergeConfig from '@foscia/shared/configs/mergeConfig'; import removeTimezoneOffset from '@foscia/shared/dates/removeTimezoneOffset'; import eachDescriptors from '@foscia/shared/descriptors/eachDescriptors'; import isDescriptorHolder from '@foscia/shared/descriptors/isDescriptorHolder'; -import makeDescriptorHolder from '@foscia/shared/descriptors/makeDescriptorHolder'; +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'; @@ -32,6 +35,7 @@ export * from '@foscia/shared/types'; export { IS_DEV, IS_TEST, + SYMBOL_DESCRIPTOR_HOLDER, mergeConfig, eachDescriptors, makeDescriptorHolder, @@ -40,6 +44,7 @@ export { mapWithKeys, isDescriptorHolder, isFosciaType, + isFosciaFlag, isNil, isNone, optionalJoin, diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index 348555da..8e5c0822 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -47,6 +47,11 @@ export type Arrayable = T[] | T; */ 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. * @@ -75,13 +80,6 @@ export type OnlyTruthy = T extends Falsy ? never : T; */ export type OnlyFalsy = T extends Falsy ? T : never; -/** - * Transformer function from a type to another. - * - * @internal - */ -export type Transformer = (value: T) => U; - /** * Exclude properties with types `never` from object type. * @@ -99,6 +97,34 @@ export type OmitNever = { 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. * @@ -140,3 +166,17 @@ export type FosciaObject = { */ 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/test/src/fosciaTestError.ts b/packages/test/src/fosciaTestError.ts index da4bf2fd..18bc051b 100644 --- a/packages/test/src/fosciaTestError.ts +++ b/packages/test/src/fosciaTestError.ts @@ -6,7 +6,6 @@ import { FosciaError } from '@foscia/core'; * @group Errors * * @internal - * @experimental */ export default class FosciaTestError extends FosciaError { } diff --git a/packages/test/src/makeActionFactoryMock.ts b/packages/test/src/makeActionFactoryMock.ts index 1761cd13..adca672c 100644 --- a/packages/test/src/makeActionFactoryMock.ts +++ b/packages/test/src/makeActionFactoryMock.ts @@ -25,8 +25,8 @@ import UnexpectedActionError from '@foscia/test/unexpectedActionError'; * * @internal */ -export default ( - factory: ActionFactory, +export default ( + factory: ActionFactory, ) => { const mocks = [] as ActionMock[]; const history = [] as ActionFactoryMockHistoryItem[]; @@ -115,15 +115,23 @@ export default ( } }; - const make = (...args: A) => new Proxy(factory(...args), { - get: (target, property) => ( - property === 'run' - ? ( - ...enhancers: (ContextEnhancer | ContextRunner)[] - ) => run(target, enhancers) - : target[property as keyof Action] - ), - }); + const make = ( + ...immediateEnhancers: (ContextEnhancer | ContextRunner)[] + ) => { + 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 mock = (result?: unknown) => { const newMock = makeActionMock().return(result); @@ -138,5 +146,5 @@ export default ( history.length = 0; }; - return { history, make, mock, reset } as ActionFactoryMock; + return { history, make, mock, reset } as ActionFactoryMock; }; diff --git a/packages/test/src/makeActionFactoryMockable.ts b/packages/test/src/makeActionFactoryMockable.ts index 503d51d3..21b7e4e9 100644 --- a/packages/test/src/makeActionFactoryMockable.ts +++ b/packages/test/src/makeActionFactoryMockable.ts @@ -1,4 +1,4 @@ -import type { ActionFactory } from '@foscia/core'; +import { ActionFactory, ContextEnhancer, ContextRunner } from '@foscia/core'; import type { ActionFactoryMock, ActionMockableFactory } from '@foscia/test/types'; /** @@ -9,16 +9,18 @@ import type { ActionFactoryMock, ActionMockableFactory } from '@foscia/test/type * @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.make(...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/mockAction.ts b/packages/test/src/mockAction.ts index 19e20525..b68b9624 100644 --- a/packages/test/src/mockAction.ts +++ b/packages/test/src/mockAction.ts @@ -9,8 +9,8 @@ import { ActionMockableFactory } from '@foscia/test/types'; * @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 02bcafc6..07e313a8 100644 --- a/packages/test/src/types.ts +++ b/packages/test/src/types.ts @@ -1,4 +1,4 @@ -import { Action, ActionCall, ActionFactory } from '@foscia/core'; +import { Action, ActionCall, ActionFactory, ContextEnhancer, ContextRunner } from '@foscia/core'; import { Dictionary } from '@foscia/shared'; /** @@ -241,7 +241,7 @@ export type ActionFactoryMockHistoryItem = { * * @experimental */ -export type ActionFactoryMock = { +export type ActionFactoryMock = { /** * History of action test context which were ran. * @@ -251,11 +251,9 @@ export type ActionFactoryMock = { /** * Make an action object. * - * @param args - * * @internal */ - make(...args: A): Action; + make(...immediateEnhancers: (ContextEnhancer | ContextRunner)[]): Action; /** * Make an action mock. * @@ -277,17 +275,17 @@ export type ActionFactoryMock = { * * @internal */ -export type ActionMockableFactory = { +export type ActionMockableFactory = { /** * Mock when mocking actions is activated. * * @internal */ - $mock: ActionFactoryMock | null; + $mock: ActionFactoryMock | null; /** * Real implementation of action factory. * * @internal */ - $real: ActionFactory; -} & ActionFactory; + $real: ActionFactory; +} & ActionFactory; diff --git a/packages/test/src/unmockAction.ts b/packages/test/src/unmockAction.ts index e2dbab28..6414e437 100644 --- a/packages/test/src/unmockAction.ts +++ b/packages/test/src/unmockAction.ts @@ -8,8 +8,8 @@ import { ActionMockableFactory } from '@foscia/test/types'; * @category Utilities * @experimental */ -export default ( - factory: ActionMockableFactory, +export default ( + factory: ActionMockableFactory, ) => { // eslint-disable-next-line no-param-reassign factory.$mock = null; diff --git a/scripts/generate-files.js b/scripts/generate-files.js index 6bb1ba0e..27946f91 100644 --- a/scripts/generate-files.js +++ b/scripts/generate-files.js @@ -16,33 +16,22 @@ 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}`), @@ -55,27 +44,22 @@ function renderActionVariadicUse(size) { 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\`. - -/** - * Variadic action use methods type. - * - * @internal - */ -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', @@ -87,22 +71,41 @@ function renderActionVariadicRun(size) { `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\`. /** - * Variadic action run methods type. + * 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) => renderMethod(index + 1)).join('\n')} +${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 1c1e59c0..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,16 +29,8 @@ 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(); @@ -40,7 +38,7 @@ async function check() { (await listFiles(path.resolve(rootDirname, 'packages'))).map(async (file) => { const [_, packageName, context] = file.match(/packages\/([a-z-]+)\/(src|tests)/) ?? []; if (packageName && (packageName !== 'cli' || context !== 'tests')) { - const errors = []; + const fileErrors = []; const fileContent = await readFile(file, { encoding: 'utf8' }); const matches = fileContent.matchAll(/[^\n]+(from '@foscia\/[a-z-]+(\/index|.))/g); [...matches].forEach((match) => { @@ -50,28 +48,34 @@ async function check() { } if (match[1].endsWith('/') && (context !== 'src' || packageName !== importPackageName)) { - console.log(packageName, context) - errors.push( + fileErrors.push( `import of ${c.red(`@foscia/${importPackageName}`)} must use root package export (instead of inner package export)`, ); } if (context === 'src' && packageName === importPackageName && (!match[1].endsWith('/') || match[1].endsWith('/index'))) { - errors.push( + 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/website/docs/core-concepts/actions.mdx b/website/docs/core-concepts/actions.mdx index 2a8e41d1..e94ac04f 100644 --- a/website/docs/core-concepts/actions.mdx +++ b/website/docs/core-concepts/actions.mdx @@ -53,7 +53,7 @@ the model instances. ```typescript import { query, all } from '@foscia/core'; -const posts = await action().run( +const posts = await action( query(Post), all(), ); @@ -72,12 +72,12 @@ 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), ); @@ -92,7 +92,7 @@ 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 @@ -119,7 +119,7 @@ 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(), @@ -127,7 +127,7 @@ const post = await action().run( post.title = 'Edited Hello World!'; -await action().run(save(post), none()); +await action(save(post), none()); ``` ### Deleting records @@ -142,7 +142,7 @@ 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 @@ -155,7 +155,7 @@ passing an already retrieved instance. ```typescript import { destroy, none } from '@foscia/core'; -await action().run(destroy(Post, '1'), none()); +await action(destroy(Post, '1'), none()); ``` ### Relationships @@ -178,7 +178,7 @@ To eager load a relation, you can use the ```typescript import { query, all, include } from '@foscia/core'; -const posts = await action().run( +const posts = await action( query(Post), include('author'), all(), @@ -194,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 @@ -219,14 +219,14 @@ to change the relation value. 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(), ); @@ -260,19 +260,19 @@ to change the relation value: 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(), ); @@ -302,7 +302,7 @@ This can be achieved using the import { create } from '@foscia/core'; // Create a comment through a post. -await action().run( +await action( create(newComment, post, 'comments'), oneOrCurrent(), ); @@ -319,8 +319,8 @@ using [`cached`](/docs/api/@foscia/core/functions/cached) and ```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 @@ -330,7 +330,7 @@ have been cached already. For this particular case, you can use ```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 @@ -353,7 +353,7 @@ sort's direction. This can be done easily using the import { query, when } from '@foscia/core'; import { sortByDesc } from '@foscia/jsonapi'; -action().run( +action( query(Post), when(displayLatestFirst, sortByDesc('createdAt')), ); @@ -384,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 */, @@ -399,22 +399,152 @@ 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()), +); +``` + +