diff --git a/api/docs/docs.go b/api/docs/docs.go
index eb72ac76d..04729b6e2 100644
--- a/api/docs/docs.go
+++ b/api/docs/docs.go
@@ -1166,6 +1166,13 @@ const docTemplate = `{
"description": "include matching contact details",
"name": "contacts",
"in": "query"
+ },
+ {
+ "type": "boolean",
+ "default": false,
+ "description": "only include threads with unread messages",
+ "name": "unread_only",
+ "in": "query"
}
],
"responses": {
@@ -1203,6 +1210,62 @@ const docTemplate = `{
}
},
"/message-threads/{messageThreadID}": {
+ "get": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "Returns a conversation belonging to the authenticated user, including matching contact details when available.",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "MessageThreads"
+ ],
+ "summary": "Get a message thread",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "ID of the message thread",
+ "name": "messageThreadID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/responses.MessageThreadResponse"
+ }
+ },
+ "401": {
+ "description": "Unauthorized",
+ "schema": {
+ "$ref": "#/definitions/responses.Unauthorized"
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "$ref": "#/definitions/responses.NotFound"
+ }
+ },
+ "422": {
+ "description": "Unprocessable Entity",
+ "schema": {
+ "$ref": "#/definitions/responses.UnprocessableEntity"
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "$ref": "#/definitions/responses.InternalServerError"
+ }
+ }
+ }
+ },
"put": {
"security": [
{
@@ -4204,6 +4267,7 @@ const docTemplate = `{
"entities.Phone": {
"type": "object",
"required": [
+ "archive_new_outgoing_message_threads",
"created_at",
"id",
"max_send_attempts",
@@ -4216,6 +4280,11 @@ const docTemplate = `{
"user_id"
],
"properties": {
+ "archive_new_outgoing_message_threads": {
+ "description": "ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.",
+ "type": "boolean",
+ "example": false
+ },
"created_at": {
"type": "string",
"example": "2022-06-05T14:26:02.302718+03:00"
@@ -4975,6 +5044,11 @@ const docTemplate = `{
"unarchive_thread"
],
"properties": {
+ "archive_new_outgoing_message_threads": {
+ "description": "ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.",
+ "type": "boolean",
+ "example": false
+ },
"fcm_token": {
"description": "FcmToken is either a Firebase registration token or a public HTTPS adapter callback URL.",
"type": "string",
diff --git a/api/docs/swagger.json b/api/docs/swagger.json
index 59c5637ba..a24b2c520 100644
--- a/api/docs/swagger.json
+++ b/api/docs/swagger.json
@@ -1163,6 +1163,13 @@
"description": "include matching contact details",
"name": "contacts",
"in": "query"
+ },
+ {
+ "type": "boolean",
+ "default": false,
+ "description": "only include threads with unread messages",
+ "name": "unread_only",
+ "in": "query"
}
],
"responses": {
@@ -1200,6 +1207,62 @@
}
},
"/message-threads/{messageThreadID}": {
+ "get": {
+ "security": [
+ {
+ "ApiKeyAuth": []
+ }
+ ],
+ "description": "Returns a conversation belonging to the authenticated user, including matching contact details when available.",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "MessageThreads"
+ ],
+ "summary": "Get a message thread",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "ID of the message thread",
+ "name": "messageThreadID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/responses.MessageThreadResponse"
+ }
+ },
+ "401": {
+ "description": "Unauthorized",
+ "schema": {
+ "$ref": "#/definitions/responses.Unauthorized"
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "$ref": "#/definitions/responses.NotFound"
+ }
+ },
+ "422": {
+ "description": "Unprocessable Entity",
+ "schema": {
+ "$ref": "#/definitions/responses.UnprocessableEntity"
+ }
+ },
+ "500": {
+ "description": "Internal Server Error",
+ "schema": {
+ "$ref": "#/definitions/responses.InternalServerError"
+ }
+ }
+ }
+ },
"put": {
"security": [
{
@@ -4201,6 +4264,7 @@
"entities.Phone": {
"type": "object",
"required": [
+ "archive_new_outgoing_message_threads",
"created_at",
"id",
"max_send_attempts",
@@ -4213,6 +4277,11 @@
"user_id"
],
"properties": {
+ "archive_new_outgoing_message_threads": {
+ "description": "ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.",
+ "type": "boolean",
+ "example": false
+ },
"created_at": {
"type": "string",
"example": "2022-06-05T14:26:02.302718+03:00"
@@ -4972,6 +5041,11 @@
"unarchive_thread"
],
"properties": {
+ "archive_new_outgoing_message_threads": {
+ "description": "ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.",
+ "type": "boolean",
+ "example": false
+ },
"fcm_token": {
"description": "FcmToken is either a Firebase registration token or a public HTTPS adapter callback URL.",
"type": "string",
diff --git a/api/docs/swagger.yaml b/api/docs/swagger.yaml
index 6dc240216..838e36738 100644
--- a/api/docs/swagger.yaml
+++ b/api/docs/swagger.yaml
@@ -408,6 +408,11 @@ definitions:
type: object
entities.Phone:
properties:
+ archive_new_outgoing_message_threads:
+ description: ArchiveNewOutgoingMessageThreads archives new conversations started
+ by an outgoing message on this phone.
+ example: false
+ type: boolean
created_at:
example: "2022-06-05T14:26:02.302718+03:00"
type: string
@@ -452,6 +457,7 @@ definitions:
example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC
type: string
required:
+ - archive_new_outgoing_message_threads
- created_at
- id
- max_send_attempts
@@ -990,6 +996,11 @@ definitions:
type: object
requests.PhoneUpsert:
properties:
+ archive_new_outgoing_message_threads:
+ description: ArchiveNewOutgoingMessageThreads archives new conversations started
+ by an outgoing message on this phone.
+ example: false
+ type: boolean
fcm_token:
description: FcmToken is either a Firebase registration token or a public
HTTPS adapter callback URL.
@@ -2510,6 +2521,11 @@ paths:
in: query
name: contacts
type: boolean
+ - default: false
+ description: only include threads with unread messages
+ in: query
+ name: unread_only
+ type: boolean
produces:
- application/json
responses:
@@ -2583,6 +2599,43 @@ paths:
summary: Delete a message thread from the database.
tags:
- MessageThreads
+ get:
+ description: Returns a conversation belonging to the authenticated user, including
+ matching contact details when available.
+ parameters:
+ - description: ID of the message thread
+ in: path
+ name: messageThreadID
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/responses.MessageThreadResponse'
+ "401":
+ description: Unauthorized
+ schema:
+ $ref: '#/definitions/responses.Unauthorized'
+ "404":
+ description: Not Found
+ schema:
+ $ref: '#/definitions/responses.NotFound'
+ "422":
+ description: Unprocessable Entity
+ schema:
+ $ref: '#/definitions/responses.UnprocessableEntity'
+ "500":
+ description: Internal Server Error
+ schema:
+ $ref: '#/definitions/responses.InternalServerError'
+ security:
+ - ApiKeyAuth: []
+ summary: Get a message thread
+ tags:
+ - MessageThreads
put:
consumes:
- application/json
diff --git a/api/pkg/entities/phone.go b/api/pkg/entities/phone.go
index 36be28aeb..ca5deac14 100644
--- a/api/pkg/entities/phone.go
+++ b/api/pkg/entities/phone.go
@@ -30,6 +30,9 @@ type Phone struct {
// UnarchiveThread moves an archived message thread back to the inbox when a new message is received on this phone.
UnarchiveThread bool `json:"unarchive_thread" gorm:"default:false" example:"false"`
+ // ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.
+ ArchiveNewOutgoingMessageThreads bool `json:"archive_new_outgoing_message_threads" gorm:"default:false" example:"false"`
+
CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"`
UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"`
}
diff --git a/api/pkg/handlers/message_thread_handler.go b/api/pkg/handlers/message_thread_handler.go
index 72e0b543d..0225ee894 100644
--- a/api/pkg/handlers/message_thread_handler.go
+++ b/api/pkg/handlers/message_thread_handler.go
@@ -42,6 +42,7 @@ func NewMessageThreadHandler(
// RegisterRoutes registers the routes for the MessageHandler
func (h *MessageThreadHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) {
h.register(router, fiber.MethodGet, "/v1/message-threads", middlewares, h.Index)
+ h.register(router, fiber.MethodGet, "/v1/message-threads/:messageThreadID", middlewares, h.Get)
h.register(router, fiber.MethodPut, "/v1/message-threads/:messageThreadID", middlewares, h.Update)
h.register(router, fiber.MethodDelete, "/v1/message-threads/:messageThreadID", middlewares, h.Delete)
}
@@ -58,6 +59,7 @@ func (h *MessageThreadHandler) RegisterRoutes(router fiber.Router, middlewares .
// @Param query query string false "filter message threads containing query"
// @Param limit query int false "number of messages to return" minimum(1) maximum(20)
// @Param contacts query bool false "include matching contact details"
+// @Param unread_only query bool false "only include threads with unread messages" default(false)
// @Success 200 {object} responses.MessageThreadsResponse
// @Failure 400 {object} responses.BadRequest
// @Failure 401 {object} responses.Unauthorized
@@ -92,6 +94,39 @@ func (h *MessageThreadHandler) Index(c fiber.Ctx) error {
return h.responseOK(c, fmt.Sprintf("fetched %d message %s", len(*threads), h.pluralize("thread", len(*threads))), threads)
}
+// Get returns a single entities.MessageThread independently of list filters and pagination.
+// @Summary Get a message thread
+// @Description Returns a conversation belonging to the authenticated user, including matching contact details when available.
+// @Security ApiKeyAuth
+// @Tags MessageThreads
+// @Produce json
+// @Param messageThreadID path string true "ID of the message thread"
+// @Success 200 {object} responses.MessageThreadResponse
+// @Failure 401 {object} responses.Unauthorized
+// @Failure 404 {object} responses.NotFound
+// @Failure 422 {object} responses.UnprocessableEntity
+// @Failure 500 {object} responses.InternalServerError
+// @Router /message-threads/{messageThreadID} [get]
+func (h *MessageThreadHandler) Get(c fiber.Ctx) error {
+ ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger)
+ defer span.End()
+
+ id := c.Params("messageThreadID")
+ if errors := h.validator.ValidateUUID(id, "messageThreadID"); len(errors) != 0 {
+ ctxLogger.Warn(stacktrace.NewErrorf("invalid message thread ID [%s]", id))
+ return h.responseUnprocessableEntity(c, errors, "validation errors while fetching a message thread")
+ }
+ thread, err := h.service.GetThread(ctx, h.userIDFomContext(c), uuid.MustParse(id), true)
+ if stacktrace.GetCode(err) == repositories.ErrCodeNotFound {
+ return h.responseNotFound(c, fmt.Sprintf("cannot find message thread with ID [%s]", id))
+ }
+ if err != nil {
+ ctxLogger.Error(stacktrace.Propagatef(err, "cannot fetch message thread [%s]", id))
+ return h.responseInternalServerError(c)
+ }
+ return h.responseOK(c, "message thread fetched successfully", thread)
+}
+
// Update an entities.MessageThread
// @Summary Update a message thread
// @Description Updates the details of a message thread
@@ -161,7 +196,7 @@ func (h *MessageThreadHandler) Delete(c fiber.Ctx) error {
return h.responseUnprocessableEntity(c, errors, "validation errors while deleting a thread thread")
}
- thread, err := h.service.GetThread(ctx, h.userIDFomContext(c), uuid.MustParse(messageThreadID))
+ thread, err := h.service.GetThread(ctx, h.userIDFomContext(c), uuid.MustParse(messageThreadID), false)
if stacktrace.GetCode(err) == repositories.ErrCodeNotFound {
return h.responseNotFound(c, fmt.Sprintf("cannot find thread thread with ID [%s]", messageThreadID))
}
diff --git a/api/pkg/handlers/message_thread_handler_contacts_test.go b/api/pkg/handlers/message_thread_handler_contacts_test.go
index f6681fbd7..d6c74770e 100644
--- a/api/pkg/handlers/message_thread_handler_contacts_test.go
+++ b/api/pkg/handlers/message_thread_handler_contacts_test.go
@@ -3,6 +3,7 @@ package handlers
import (
"context"
"encoding/json"
+ "fmt"
"net/http"
"net/http/httptest"
"testing"
@@ -22,12 +23,18 @@ import (
type messageThreadHandlerIndexRepositoryStub struct {
repositories.MessageThreadRepository
- threads []entities.MessageThread
+ threads []entities.MessageThread
+ unreadOnly bool
+ limit int
}
-func (stub *messageThreadHandlerIndexRepositoryStub) Index(context.Context, entities.UserID, string, bool, repositories.IndexParams) (*[]entities.MessageThread, error) {
+func (stub *messageThreadHandlerIndexRepositoryStub) Index(_ context.Context, _ entities.UserID, _ string, _ bool, unreadOnly bool, params repositories.IndexParams) (*[]entities.MessageThread, error) {
+ stub.unreadOnly = unreadOnly
+ stub.limit = params.Limit
threads := make([]entities.MessageThread, len(stub.threads))
copy(threads, stub.threads)
+ start := min(params.Skip, len(threads))
+ threads = threads[start:min(start+params.Limit, len(threads))]
return &threads, nil
}
@@ -41,7 +48,7 @@ func (stub *messageThreadHandlerContactProviderStub) GetContactMap(context.Conte
return stub.contacts, nil
}
-func TestMessageThreadHandlerIndex_ParsesContactsQuery(t *testing.T) {
+func TestMessageThreadHandlerIndex_ParsesContactsAndUnreadQuery(t *testing.T) {
contact := &entities.Contact{ID: uuid.New(), Name: "Alice", PhoneNumbers: []string{"+18005550100"}}
repository := &messageThreadHandlerIndexRepositoryStub{threads: []entities.MessageThread{{Contact: "+18005550100"}}}
provider := &messageThreadHandlerContactProviderStub{contacts: map[string]*entities.Contact{"+18005550100": contact}}
@@ -57,19 +64,83 @@ func TestMessageThreadHandlerIndex_ParsesContactsQuery(t *testing.T) {
})
handler.RegisterRoutes(app)
- req := httptest.NewRequest(http.MethodGet, "/v1/message-threads?owner=%2B18005550199&contacts=true", nil)
+ req := httptest.NewRequest(http.MethodGet, "/v1/message-threads?owner=%2B18005550199&contacts=true&unread_only=true", nil)
resp, err := app.Test(req, fiber.TestConfig{Timeout: time.Second})
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, 1, provider.calls)
+ assert.True(t, repository.unreadOnly)
var payload struct {
Data []entities.MessageThread `json:"data"`
}
+
require.NoError(t, json.NewDecoder(resp.Body).Decode(&payload))
require.Len(t, payload.Data, 1)
require.NotNil(t, payload.Data[0].ContactDetails)
assert.Equal(t, contact.ID, payload.Data[0].ContactDetails.ID)
assert.Equal(t, "Alice", payload.Data[0].ContactDetails.Name)
}
+
+func TestMessageThreadHandlerIndexRejectsInvalidUnreadFilter(t *testing.T) {
+ repository := &messageThreadHandlerIndexRepositoryStub{}
+ logger := &messageThreadHandlerNoopLogger{}
+ tracer := telemetry.NewOtelLogger("test", logger)
+ service := services.NewMessageThreadService(logger, tracer, repository, nil, nil, nil)
+ handler := NewMessageThreadHandler(logger, tracer, validators.NewMessageThreadHandlerValidator(logger, tracer), service)
+ app := fiber.New()
+ app.Use(func(c fiber.Ctx) error {
+ c.Locals(middlewares.ContextKeyAuthUserID, entities.AuthContext{ID: entities.UserID("user-id"), Email: "user@example.com"})
+ return c.Next()
+ })
+ handler.RegisterRoutes(app)
+
+ response, err := app.Test(httptest.NewRequest(http.MethodGet, "/v1/message-threads?owner=%2B18005550199&unread_only=invalid", nil))
+
+ require.NoError(t, err)
+ defer response.Body.Close()
+ assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
+}
+
+func TestMessageThreadIndexUsesRequestedLimitWithoutLookAhead(t *testing.T) {
+ for _, test := range []struct {
+ name string
+ count int
+ wantLen int
+ limit int
+ }{
+ {name: "empty", count: 0, wantLen: 0, limit: 20},
+ {name: "partial page", count: 19, wantLen: 19, limit: 20},
+ {name: "exactly one page", count: 20, wantLen: 20, limit: 20},
+ {name: "another page exists", count: 21, wantLen: 20, limit: 20},
+ {name: "custom limit", count: 4, wantLen: 3, limit: 3},
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ repository := &messageThreadHandlerIndexRepositoryStub{threads: make([]entities.MessageThread, test.count)}
+ logger := &messageThreadHandlerNoopLogger{}
+ tracer := telemetry.NewOtelLogger("test", logger)
+ service := services.NewMessageThreadService(logger, tracer, repository, nil, nil, nil)
+ handler := NewMessageThreadHandler(logger, tracer, validators.NewMessageThreadHandlerValidator(logger, tracer), service)
+ app := fiber.New()
+ app.Use(func(c fiber.Ctx) error {
+ c.Locals(middlewares.ContextKeyAuthUserID, entities.AuthContext{ID: entities.UserID("user-id"), Email: "user@example.com"})
+ return c.Next()
+ })
+ handler.RegisterRoutes(app)
+
+ response, err := app.Test(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/v1/message-threads?owner=%%2B18005550199&limit=%d", test.limit), nil))
+
+ require.NoError(t, err)
+ defer response.Body.Close()
+ require.Equal(t, http.StatusOK, response.StatusCode)
+ var body map[string]json.RawMessage
+ require.NoError(t, json.NewDecoder(response.Body).Decode(&body))
+ var threads []entities.MessageThread
+ require.NoError(t, json.Unmarshal(body["data"], &threads))
+ assert.Len(t, threads, test.wantLen)
+ assert.NotContains(t, body, "has_more")
+ assert.Equal(t, test.limit, repository.limit)
+ })
+ }
+}
diff --git a/api/pkg/handlers/message_thread_handler_test.go b/api/pkg/handlers/message_thread_handler_test.go
index a304f4ad3..e545adc31 100644
--- a/api/pkg/handlers/message_thread_handler_test.go
+++ b/api/pkg/handlers/message_thread_handler_test.go
@@ -23,7 +23,9 @@ import (
"gorm.io/gorm"
)
-type messageThreadHandlerRepositoryStub struct{}
+type messageThreadHandlerRepositoryStub struct {
+ load func(context.Context, entities.UserID, uuid.UUID) (*entities.MessageThread, error)
+}
func (stub *messageThreadHandlerRepositoryStub) Store(context.Context, *entities.MessageThread) error {
return nil
@@ -41,11 +43,14 @@ func (stub *messageThreadHandlerRepositoryStub) LoadByOwnerContact(context.Conte
return nil, nil
}
-func (stub *messageThreadHandlerRepositoryStub) Load(context.Context, entities.UserID, uuid.UUID) (*entities.MessageThread, error) {
+func (stub *messageThreadHandlerRepositoryStub) Load(ctx context.Context, userID entities.UserID, id uuid.UUID) (*entities.MessageThread, error) {
+ if stub.load != nil {
+ return stub.load(ctx, userID, id)
+ }
return nil, nil
}
-func (stub *messageThreadHandlerRepositoryStub) Index(context.Context, entities.UserID, string, bool, repositories.IndexParams) (*[]entities.MessageThread, error) {
+func (stub *messageThreadHandlerRepositoryStub) Index(context.Context, entities.UserID, string, bool, bool, repositories.IndexParams) (*[]entities.MessageThread, error) {
return nil, nil
}
@@ -90,6 +95,67 @@ func TestMessageThreadHandlerUpdate_ReturnsNotFoundWhenThreadIsMissing(t *testin
require.Equal(t, "cannot find message thread with ID ["+messageThreadID.String()+"]", payload.Message)
}
+func TestMessageThreadHandlerGetLoadsIndividualUserScopedThread(t *testing.T) {
+ for _, test := range []struct {
+ name string
+ status int
+ }{
+ {name: "existing", status: http.StatusOK},
+ {name: "missing", status: http.StatusNotFound},
+ {name: "another user", status: http.StatusNotFound},
+ {name: "invalid UUID", status: http.StatusUnprocessableEntity},
+ {name: "repository failure", status: http.StatusInternalServerError},
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ id := uuid.New()
+ contact := &entities.Contact{ID: uuid.New(), Name: "Alice", PhoneNumbers: []string{"+18005550100"}}
+ repository := &messageThreadHandlerRepositoryStub{
+ load: func(_ context.Context, userID entities.UserID, threadID uuid.UUID) (*entities.MessageThread, error) {
+ require.Equal(t, entities.UserID("user-id"), userID)
+ require.Equal(t, id, threadID)
+ if test.name == "missing" || test.name == "another user" {
+ return nil, stacktrace.PropagateWithCodef(gorm.ErrRecordNotFound, repositories.ErrCodeNotFound, "not found")
+ }
+ if test.name == "repository failure" {
+ return nil, stacktrace.NewError("database unavailable")
+ }
+ return &entities.MessageThread{ID: id, UserID: userID, Owner: "+18005550199", Contact: "+18005550100"}, nil
+ },
+ }
+ logger := &messageThreadHandlerNoopLogger{}
+ tracer := telemetry.NewOtelLogger("test", logger)
+ provider := &messageThreadHandlerContactProviderStub{contacts: map[string]*entities.Contact{"+18005550100": contact}}
+ service := services.NewMessageThreadService(logger, tracer, repository, nil, nil, provider)
+ handler := NewMessageThreadHandler(logger, tracer, validators.NewMessageThreadHandlerValidator(logger, tracer), service)
+ app := fiber.New()
+ app.Use(func(c fiber.Ctx) error {
+ c.Locals(middlewares.ContextKeyAuthUserID, entities.AuthContext{ID: entities.UserID("user-id"), Email: "user@example.com"})
+ return c.Next()
+ })
+ handler.RegisterRoutes(app)
+ pathID := id.String()
+ if test.name == "invalid UUID" {
+ pathID = "invalid"
+ }
+
+ response, err := app.Test(httptest.NewRequest(http.MethodGet, "/v1/message-threads/"+pathID, nil))
+
+ require.NoError(t, err)
+ defer response.Body.Close()
+ require.Equal(t, test.status, response.StatusCode)
+ if test.status == http.StatusOK {
+ var payload struct {
+ Data entities.MessageThread `json:"data"`
+ }
+ require.NoError(t, json.NewDecoder(response.Body).Decode(&payload))
+ require.Equal(t, id, payload.Data.ID)
+ require.NotNil(t, payload.Data.ContactDetails)
+ require.Equal(t, "Alice", payload.Data.ContactDetails.Name)
+ }
+ })
+ }
+}
+
type messageThreadHandlerNoopLogger struct{}
var _ telemetry.Logger = (*messageThreadHandlerNoopLogger)(nil)
diff --git a/api/pkg/listeners/read_receipts_test_helpers_test.go b/api/pkg/listeners/read_receipts_test_helpers_test.go
index 60cd27e65..3227685c5 100644
--- a/api/pkg/listeners/read_receipts_test_helpers_test.go
+++ b/api/pkg/listeners/read_receipts_test_helpers_test.go
@@ -52,7 +52,7 @@ func (repository *listenerMessageThreadRepository) Load(context.Context, entitie
return &entities.MessageThread{}, nil
}
-func (repository *listenerMessageThreadRepository) Index(context.Context, entities.UserID, string, bool, repositories.IndexParams) (*[]entities.MessageThread, error) {
+func (repository *listenerMessageThreadRepository) Index(context.Context, entities.UserID, string, bool, bool, repositories.IndexParams) (*[]entities.MessageThread, error) {
threads := []entities.MessageThread{}
return &threads, nil
}
diff --git a/api/pkg/repositories/gorm_message_repository.go b/api/pkg/repositories/gorm_message_repository.go
index a4bf309f5..b6fdf9586 100644
--- a/api/pkg/repositories/gorm_message_repository.go
+++ b/api/pkg/repositories/gorm_message_repository.go
@@ -234,13 +234,32 @@ func (repository *gormMessageRepository) Update(ctx context.Context, message *en
ctx, span := repository.tracer.Start(ctx)
defer span.End()
- if err := repository.db.WithContext(ctx).Save(message).Error; err != nil {
+ // Send attempts are owned by the atomic increment, not a loaded status snapshot.
+ if err := repository.db.WithContext(ctx).Omit("send_attempt_count").Save(message).Error; err != nil {
return repository.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot update message with ID [%s]", message.ID))
}
return nil
}
+func (repository *gormMessageRepository) IncreaseSendAttemptCount(ctx context.Context, userID entities.UserID, messageID uuid.UUID) error {
+ ctx, span := repository.tracer.Start(ctx)
+ defer span.End()
+
+ result := repository.db.WithContext(ctx).
+ Model(&entities.Message{}).
+ Where("user_id = ?", userID).
+ Where("id = ?", messageID).
+ Update("send_attempt_count", gorm.Expr("send_attempt_count + ?", 1))
+ if result.Error != nil {
+ return repository.tracer.WrapErrorSpan(span, stacktrace.Propagatef(result.Error, "cannot increment send attempts for message [%s] for user [%s]", messageID, userID))
+ }
+ if result.RowsAffected == 0 {
+ return repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCodef(gorm.ErrRecordNotFound, ErrCodeNotFound, "message [%s] for user [%s] does not exist", messageID, userID))
+ }
+ return nil
+}
+
// GetOutstanding fetches messages that still to be sent to the phone
func (repository *gormMessageRepository) GetOutstanding(ctx context.Context, userID entities.UserID, messageID uuid.UUID, phoneNumbers []string) (*entities.Message, error) {
ctx, span := repository.tracer.Start(ctx)
diff --git a/api/pkg/repositories/gorm_message_repository_test.go b/api/pkg/repositories/gorm_message_repository_test.go
new file mode 100644
index 000000000..11dc28b7e
--- /dev/null
+++ b/api/pkg/repositories/gorm_message_repository_test.go
@@ -0,0 +1,55 @@
+package repositories
+
+import (
+ "context"
+ "strings"
+ "testing"
+
+ "github.com/NdoleStudio/httpsms/pkg/entities"
+ "github.com/NdoleStudio/httpsms/pkg/telemetry"
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gorm.io/driver/postgres"
+ "gorm.io/gorm"
+)
+
+func TestMessageStatusUpdatesPreserveConcurrentSendAttemptCounts(t *testing.T) {
+ pool := &messageThreadTestConnPool{}
+ db, err := gorm.Open(postgres.New(postgres.Config{Conn: pool, WithoutReturning: true}), &gorm.Config{DisableAutomaticPing: true})
+ require.NoError(t, err)
+ logger := &messageThreadTestLogger{}
+ repository := NewGormMessageRepository(logger, telemetry.NewOtelLogger("test", logger), db)
+
+ err = repository.Update(context.Background(), &entities.Message{
+ ID: uuid.New(),
+ UserID: entities.UserID("user-id"),
+ Status: entities.MessageStatusScheduled,
+ SendAttemptCount: 0,
+ })
+
+ require.NoError(t, err)
+ require.NotEmpty(t, pool.statements)
+ update := pool.statements[len(pool.statements)-1]
+ assert.True(t, strings.HasPrefix(update.query, `UPDATE "messages"`))
+ assert.Contains(t, update.query, `"status"=`)
+ assert.NotContains(t, update.query, `"send_attempt_count"=`, "a stale status snapshot must not reset an atomic attempt increment")
+}
+
+func TestMessageSendAttemptIncrementOnlyUpdatesItsOwnedColumn(t *testing.T) {
+ pool := &messageThreadTestConnPool{}
+ db, err := gorm.Open(postgres.New(postgres.Config{Conn: pool, WithoutReturning: true}), &gorm.Config{DisableAutomaticPing: true})
+ require.NoError(t, err)
+ logger := &messageThreadTestLogger{}
+ repository := NewGormMessageRepository(logger, telemetry.NewOtelLogger("test", logger), db)
+
+ err = repository.IncreaseSendAttemptCount(context.Background(), entities.UserID("user-id"), uuid.New())
+
+ require.NoError(t, err)
+ require.Len(t, pool.statements, 1)
+ update := pool.statements[0]
+ assert.Contains(t, update.query, `"send_attempt_count"=send_attempt_count +`)
+ assert.NotContains(t, update.query, `"status"=`)
+ assert.Contains(t, update.query, "WHERE user_id =")
+ assert.Contains(t, update.query, "AND id =")
+}
diff --git a/api/pkg/repositories/gorm_message_thread_repository.go b/api/pkg/repositories/gorm_message_thread_repository.go
index c30091b9b..316f8ab26 100644
--- a/api/pkg/repositories/gorm_message_thread_repository.go
+++ b/api/pkg/repositories/gorm_message_thread_repository.go
@@ -234,7 +234,7 @@ func (repository *gormMessageThreadRepository) Load(ctx context.Context, userID
}
// Index message threads for an owner
-func (repository *gormMessageThreadRepository) Index(ctx context.Context, userID entities.UserID, owner string, isArchived bool, params IndexParams) (*[]entities.MessageThread, error) {
+func (repository *gormMessageThreadRepository) Index(ctx context.Context, userID entities.UserID, owner string, isArchived bool, unreadOnly bool, params IndexParams) (*[]entities.MessageThread, error) {
ctx, span := repository.tracer.Start(ctx)
defer span.End()
@@ -249,6 +249,10 @@ func (repository *gormMessageThreadRepository) Index(ctx context.Context, userID
query.Where(repository.db.Where("is_archived = ?", isArchived).Or("is_archived IS NULL"))
}
+ if unreadOnly {
+ query.Where("unread_count > ?", 0)
+ }
+
if len(params.Query) > 0 {
queryPattern := "%" + params.Query + "%"
query.Where(
@@ -259,7 +263,7 @@ func (repository *gormMessageThreadRepository) Index(ctx context.Context, userID
}
threads := new([]entities.MessageThread)
- if err := query.Order("order_timestamp DESC").Limit(params.Limit).Offset(params.Skip).Find(&threads).Error; err != nil {
+ if err := query.Order("order_timestamp DESC").Order("id DESC").Limit(params.Limit).Offset(params.Skip).Find(&threads).Error; err != nil {
return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot fetch message threads with owner [%s] and params [%+#v]", owner, params))
}
diff --git a/api/pkg/repositories/gorm_message_thread_repository_test.go b/api/pkg/repositories/gorm_message_thread_repository_test.go
index 5ca7d4a3f..6f2fde4a3 100644
--- a/api/pkg/repositories/gorm_message_thread_repository_test.go
+++ b/api/pkg/repositories/gorm_message_thread_repository_test.go
@@ -5,6 +5,7 @@ import (
"database/sql"
"database/sql/driver"
"errors"
+ "fmt"
"strings"
"testing"
"time"
@@ -24,6 +25,42 @@ type messageThreadTestStatement struct {
args []any
}
+func TestMessageThreadIndexFiltersUnreadBeforePagination(t *testing.T) {
+ for _, unreadOnly := range []bool{false, true} {
+ t.Run(fmt.Sprintf("unread_only=%t", unreadOnly), func(t *testing.T) {
+ db, err := gorm.Open(postgres.New(postgres.Config{Conn: &messageThreadTestConnPool{}}), &gorm.Config{
+ DisableAutomaticPing: true,
+ DryRun: true,
+ })
+ require.NoError(t, err)
+ var query string
+ var args []any
+ require.NoError(t, db.Callback().Query().After("gorm:query").Register("capture", func(db *gorm.DB) {
+ query = db.Statement.SQL.String()
+ args = db.Statement.Vars
+ }))
+ logger := &messageThreadTestLogger{}
+ repository := NewGormMessageThreadRepository(logger, telemetry.NewOtelLogger("test", logger), db)
+
+ _, err = repository.Index(context.Background(), entities.UserID("user-id"), "+18005550199", true, unreadOnly, IndexParams{Limit: 20, Skip: 5})
+
+ require.NoError(t, err)
+ assert.Contains(t, query, "user_id = $1")
+ assert.Contains(t, query, "owner = $2")
+ assert.Contains(t, query, "is_archived = $3")
+ assert.Equal(t, []any{entities.UserID("user-id"), "+18005550199", true}, args[:3])
+ if unreadOnly {
+ assert.Contains(t, query, "unread_count >")
+ assert.Less(t, strings.Index(query, "unread_count >"), strings.Index(query, "LIMIT"))
+ } else {
+ assert.NotContains(t, query, "unread_count >")
+ }
+ assert.Contains(t, query, "ORDER BY order_timestamp DESC,id DESC LIMIT")
+ assert.Contains(t, query, "OFFSET")
+ })
+ }
+}
+
type messageThreadTestConnPool struct {
statements []messageThreadTestStatement
}
diff --git a/api/pkg/repositories/message_repository.go b/api/pkg/repositories/message_repository.go
index c8f85fbb0..dd069d284 100644
--- a/api/pkg/repositories/message_repository.go
+++ b/api/pkg/repositories/message_repository.go
@@ -15,6 +15,9 @@ type MessageRepository interface {
// Update a new entities.Message
Update(ctx context.Context, message *entities.Message) error
+ // IncreaseSendAttemptCount atomically increments attempts without changing message status.
+ IncreaseSendAttemptCount(ctx context.Context, userID entities.UserID, messageID uuid.UUID) error
+
// Load an entities.Message by ID
Load(ctx context.Context, userID entities.UserID, messageID uuid.UUID) (*entities.Message, error)
diff --git a/api/pkg/repositories/message_thread_repository.go b/api/pkg/repositories/message_thread_repository.go
index 28efc11ee..88e7a896d 100644
--- a/api/pkg/repositories/message_thread_repository.go
+++ b/api/pkg/repositories/message_thread_repository.go
@@ -53,7 +53,7 @@ type MessageThreadRepository interface {
Load(ctx context.Context, userID entities.UserID, ID uuid.UUID) (*entities.MessageThread, error)
// Index message threads for an owner
- Index(ctx context.Context, userID entities.UserID, owner string, archived bool, params IndexParams) (*[]entities.MessageThread, error)
+ Index(ctx context.Context, userID entities.UserID, owner string, archived bool, unreadOnly bool, params IndexParams) (*[]entities.MessageThread, error)
// UpdateAfterDeletedMessage updates a thread after the original message has been deleted
UpdateAfterDeletedMessage(ctx context.Context, params MessageThreadDeletedUpdate) error
diff --git a/api/pkg/requests/message_thread_index_request.go b/api/pkg/requests/message_thread_index_request.go
index 053f90003..7cf20f693 100644
--- a/api/pkg/requests/message_thread_index_request.go
+++ b/api/pkg/requests/message_thread_index_request.go
@@ -14,6 +14,7 @@ import (
type MessageThreadIndex struct {
request
IsArchived string `json:"is_archived" query:"is_archived" example:"false"`
+ UnreadOnly string `json:"unread_only" query:"unread_only" example:"false"`
Skip string `json:"skip" query:"skip"`
Query string `json:"query" query:"query"`
Limit string `json:"limit" query:"limit"`
@@ -36,6 +37,10 @@ func (input *MessageThreadIndex) Sanitize() MessageThreadIndex {
}
input.IsArchived = input.sanitizeBool(input.IsArchived)
+ input.UnreadOnly = input.sanitizeBool(input.UnreadOnly)
+ if input.UnreadOnly == "" {
+ input.UnreadOnly = "false"
+ }
input.Contacts = input.sanitizeBool(input.Contacts)
if input.Contacts != "true" && input.Contacts != "false" {
input.Contacts = "false"
@@ -62,6 +67,7 @@ func (input *MessageThreadIndex) ToGetParams(userID entities.UserID) services.Me
},
UserID: userID,
IsArchived: input.getBool(input.IsArchived),
+ UnreadOnly: input.getBool(input.UnreadOnly),
WithContacts: input.getBool(input.Contacts),
Owner: input.Owner,
}
diff --git a/api/pkg/requests/phone_update_request.go b/api/pkg/requests/phone_update_request.go
index cedd51a0d..48f8dbff7 100644
--- a/api/pkg/requests/phone_update_request.go
+++ b/api/pkg/requests/phone_update_request.go
@@ -33,6 +33,9 @@ type PhoneUpsert struct {
// UnarchiveThread moves an archived thread back to the inbox when a new message is received on this phone.
UnarchiveThread bool `json:"unarchive_thread" example:"false"`
+ // ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.
+ ArchiveNewOutgoingMessageThreads bool `json:"archive_new_outgoing_message_threads" example:"false" validate:"optional"`
+
// SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot
SIM string `json:"sim" example:"SIM1"`
@@ -84,6 +87,11 @@ func (input *PhoneUpsert) ToUpsertParams(user entities.AuthContext, source strin
unarchiveThread = &input.UnarchiveThread
}
+ var archiveNewOutgoingMessageThreads *bool
+ if _, exists := fields["archive_new_outgoing_message_threads"]; exists {
+ archiveNewOutgoingMessageThreads = &input.ArchiveNewOutgoingMessageThreads
+ }
+
var scheduleID *uuid.UUID
if _, exists := fields["message_send_schedule_id"]; exists {
if parsed, err := uuid.Parse(strings.TrimSpace(input.MessageSendScheduleID)); err == nil {
@@ -92,16 +100,17 @@ func (input *PhoneUpsert) ToUpsertParams(user entities.AuthContext, source strin
}
return &services.PhoneUpsertParams{
- Source: source,
- PhoneNumber: phone,
- MessagesPerMinute: messagesPerMinute,
- MissedCallAutoReply: input.MissedCallAutoReply,
- MessageExpirationDuration: timeout,
- MaxSendAttempts: maxSendAttempts,
- FcmToken: fcmToken,
- UserID: user.ID,
- SIM: entities.SIM(input.SIM),
- UnarchiveThread: unarchiveThread,
- MessageSendScheduleID: scheduleID,
+ Source: source,
+ PhoneNumber: phone,
+ MessagesPerMinute: messagesPerMinute,
+ MissedCallAutoReply: input.MissedCallAutoReply,
+ MessageExpirationDuration: timeout,
+ MaxSendAttempts: maxSendAttempts,
+ FcmToken: fcmToken,
+ UserID: user.ID,
+ SIM: entities.SIM(input.SIM),
+ UnarchiveThread: unarchiveThread,
+ ArchiveNewOutgoingMessageThreads: archiveNewOutgoingMessageThreads,
+ MessageSendScheduleID: scheduleID,
}
}
diff --git a/api/pkg/requests/phone_update_request_test.go b/api/pkg/requests/phone_update_request_test.go
new file mode 100644
index 000000000..ff5a0e0ae
--- /dev/null
+++ b/api/pkg/requests/phone_update_request_test.go
@@ -0,0 +1,39 @@
+package requests
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/NdoleStudio/httpsms/pkg/entities"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestPhoneUpsertArchiveNewOutgoingMessageThreadsPresence(t *testing.T) {
+ tests := []struct {
+ name string
+ body string
+ present bool
+ want bool
+ }{
+ {name: "enabled", body: `{"phone_number":"+18005550199","archive_new_outgoing_message_threads":true}`, present: true, want: true},
+ {name: "disabled", body: `{"phone_number":"+18005550199","archive_new_outgoing_message_threads":false}`, present: true, want: false},
+ {name: "omitted by older client", body: `{"phone_number":"+18005550199"}`, present: false},
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ var input PhoneUpsert
+ require.NoError(t, json.Unmarshal([]byte(test.body), &input))
+
+ params := input.ToUpsertParams(entities.AuthContext{}, "test", []byte(test.body))
+
+ if !test.present {
+ assert.Nil(t, params.ArchiveNewOutgoingMessageThreads)
+ return
+ }
+ require.NotNil(t, params.ArchiveNewOutgoingMessageThreads)
+ assert.Equal(t, test.want, *params.ArchiveNewOutgoingMessageThreads)
+ })
+ }
+}
diff --git a/api/pkg/services/message_service.go b/api/pkg/services/message_service.go
index 89c216817..75267be77 100644
--- a/api/pkg/services/message_service.go
+++ b/api/pkg/services/message_service.go
@@ -800,16 +800,11 @@ func (service *MessageService) HandleMessageNotificationSent(ctx context.Context
ctxLogger := service.tracer.CtxLogger(service.logger, span)
- message, err := service.repository.Load(ctx, params.UserID, params.ID)
- if err != nil {
- return service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot find message with id [%s]", params.ID))
- }
-
- if err = service.repository.Update(ctx, message.AddSendAttemptCount()); err != nil {
- return service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot update message with id [%s] as expired", message.ID))
+ if err := service.repository.IncreaseSendAttemptCount(ctx, params.UserID, params.ID); err != nil {
+ return service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "cannot increment send attempts for message with id [%s]", params.ID))
}
- ctxLogger.Info(fmt.Sprintf("notification for message with id [%s] has been sent at [%s]", message.ID, params.Timestamp.String()))
+ ctxLogger.Info(fmt.Sprintf("notification for message with id [%s] has been sent at [%s]", params.ID, params.Timestamp.String()))
return nil
}
diff --git a/api/pkg/services/message_service_test.go b/api/pkg/services/message_service_test.go
index 263816f41..be17a3a14 100644
--- a/api/pkg/services/message_service_test.go
+++ b/api/pkg/services/message_service_test.go
@@ -1,16 +1,65 @@
package services
import (
+ "context"
"testing"
"time"
+ "github.com/NdoleStudio/httpsms/pkg/entities"
"github.com/NdoleStudio/httpsms/pkg/events"
+ "github.com/NdoleStudio/httpsms/pkg/repositories"
"github.com/NdoleStudio/httpsms/pkg/telemetry"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
)
+type notificationMessageRepository struct {
+ repositories.MessageRepository
+ stored entities.Message
+}
+
+func (repository *notificationMessageRepository) Load(context.Context, entities.UserID, uuid.UUID) (*entities.Message, error) {
+ stale := repository.stored
+ stale.Status = entities.MessageStatusPending
+ return &stale, nil
+}
+
+func (repository *notificationMessageRepository) Update(_ context.Context, message *entities.Message) error {
+ repository.stored = *message
+ return nil
+}
+
+func (repository *notificationMessageRepository) IncreaseSendAttemptCount(context.Context, entities.UserID, uuid.UUID) error {
+ repository.stored.SendAttemptCount++
+ return nil
+}
+
+func TestNotificationSentDoesNotRestoreStalePendingStatus(t *testing.T) {
+ repository := ¬ificationMessageRepository{stored: entities.Message{
+ ID: uuid.New(),
+ UserID: entities.UserID("user-id"),
+ Status: entities.MessageStatusScheduled,
+ }}
+ logger := &noopLogger{}
+ service := &MessageService{
+ logger: logger,
+ tracer: telemetry.NewOtelLogger("test", logger),
+ repository: repository,
+ }
+
+ err := service.HandleMessageNotificationSent(context.Background(), HandleMessageParams{
+ ID: repository.stored.ID,
+ UserID: repository.stored.UserID,
+ Timestamp: time.Now().UTC(),
+ })
+
+ require.NoError(t, err)
+ assert.Equal(t, entities.MessageStatus(entities.MessageStatusScheduled), repository.stored.Status)
+ assert.EqualValues(t, 1, repository.stored.SendAttemptCount)
+}
+
func TestGetSendDelay_WithSendAt_ReturnsTimeUntil(t *testing.T) {
service := &MessageService{}
logger := &noopLogger{}
diff --git a/api/pkg/services/message_thread_service.go b/api/pkg/services/message_thread_service.go
index efcfadd17..75488149a 100644
--- a/api/pkg/services/message_thread_service.go
+++ b/api/pkg/services/message_thread_service.go
@@ -209,6 +209,16 @@ func (service *MessageThreadService) createThread(ctx context.Context, params Me
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
+ isArchived := false
+ if params.Status != entities.MessageStatusReceived {
+ phone, err := service.phoneRepository.Load(ctx, params.UserID, params.Owner)
+ if err != nil {
+ ctxLogger.Warn(stacktrace.Propagatef(err, "cannot load phone [%s] for user [%s] to resolve ArchiveNewOutgoingMessageThreads; creating the thread unarchived", params.Owner, params.UserID))
+ } else {
+ isArchived = phone.ArchiveNewOutgoingMessageThreads
+ }
+ }
+
unreadCount := uint(0)
if params.MarkAsUnread {
unreadCount = 1
@@ -220,7 +230,7 @@ func (service *MessageThreadService) createThread(ctx context.Context, params Me
Owner: params.Owner,
Contact: params.Contact,
UserID: params.UserID,
- IsArchived: false,
+ IsArchived: isArchived,
UnreadCount: unreadCount,
Color: service.getColor(),
LastMessageContent: ¶ms.Content,
@@ -274,6 +284,7 @@ func (service *MessageThreadService) getColor() string {
type MessageThreadGetParams struct {
repositories.IndexParams
IsArchived bool
+ UnreadOnly bool
WithContacts bool
UserID entities.UserID
Owner string
@@ -284,7 +295,7 @@ func (service *MessageThreadService) GetThreads(ctx context.Context, params Mess
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
- threads, err := service.repository.Index(ctx, params.UserID, params.Owner, params.IsArchived, params.IndexParams)
+ threads, err := service.repository.Index(ctx, params.UserID, params.Owner, params.IsArchived, params.UnreadOnly, params.IndexParams)
if err != nil {
return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(err, "could not fetch messages threads for params [%+#v]", params))
}
@@ -312,8 +323,8 @@ func (service *MessageThreadService) GetThreads(ctx context.Context, params Mess
}
// GetThread fetches an entities.MessageThread message thread by the ID
-func (service *MessageThreadService) GetThread(ctx context.Context, userID entities.UserID, messageThreadID uuid.UUID) (*entities.MessageThread, error) {
- ctx, span := service.tracer.Start(ctx)
+func (service *MessageThreadService) GetThread(ctx context.Context, userID entities.UserID, messageThreadID uuid.UUID, withContacts bool) (*entities.MessageThread, error) {
+ ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
thread, err := service.repository.Load(ctx, userID, messageThreadID)
@@ -321,6 +332,14 @@ func (service *MessageThreadService) GetThread(ctx context.Context, userID entit
return nil, service.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCodef(err, stacktrace.GetCode(err), "could not fetch thread with ID [%s] for user [%s]", messageThreadID, userID))
}
+ if withContacts {
+ contacts, contactErr := service.contactService.GetContactMap(ctx, userID, []string{thread.Contact})
+ if contactErr != nil {
+ ctxLogger.Error(service.tracer.WrapErrorSpan(span, stacktrace.Propagatef(contactErr, "cannot resolve contact for thread [%s]", messageThreadID)))
+ } else {
+ thread.ContactDetails = contacts[thread.Contact]
+ }
+ }
return thread, nil
}
diff --git a/api/pkg/services/message_thread_service_contacts_test.go b/api/pkg/services/message_thread_service_contacts_test.go
index 2c021d2f7..a941a0f7b 100644
--- a/api/pkg/services/message_thread_service_contacts_test.go
+++ b/api/pkg/services/message_thread_service_contacts_test.go
@@ -22,7 +22,7 @@ type messageThreadContactRepositoryStub struct {
calls int
}
-func (stub *messageThreadContactRepositoryStub) Index(_ context.Context, _ entities.UserID, _ string, _ bool, _ repositories.IndexParams) (*[]entities.MessageThread, error) {
+func (stub *messageThreadContactRepositoryStub) Index(_ context.Context, _ entities.UserID, _ string, _ bool, _ bool, _ repositories.IndexParams) (*[]entities.MessageThread, error) {
stub.calls++
if stub.err != nil {
return nil, stub.err
diff --git a/api/pkg/services/message_thread_service_test.go b/api/pkg/services/message_thread_service_test.go
index 1ef9b4bde..eaa487296 100644
--- a/api/pkg/services/message_thread_service_test.go
+++ b/api/pkg/services/message_thread_service_test.go
@@ -15,6 +15,15 @@ import (
"gorm.io/gorm"
)
+type threadPhoneRepositoryStub struct {
+ repositories.PhoneRepository
+ load func(context.Context, entities.UserID, string) (*entities.Phone, error)
+}
+
+func (stub *threadPhoneRepositoryStub) Load(ctx context.Context, userID entities.UserID, owner string) (*entities.Phone, error) {
+ return stub.load(ctx, userID, owner)
+}
+
type messageThreadRepositoryStub struct {
loadByOwnerContact func(context.Context, entities.UserID, string, string) (*entities.MessageThread, error)
load func(context.Context, entities.UserID, uuid.UUID) (*entities.MessageThread, error)
@@ -56,7 +65,7 @@ func (stub *messageThreadRepositoryStub) Load(ctx context.Context, userID entiti
return stub.load(ctx, userID, id)
}
-func (stub *messageThreadRepositoryStub) Index(context.Context, entities.UserID, string, bool, repositories.IndexParams) (*[]entities.MessageThread, error) {
+func (stub *messageThreadRepositoryStub) Index(context.Context, entities.UserID, string, bool, bool, repositories.IndexParams) (*[]entities.MessageThread, error) {
threads := []entities.MessageThread{}
return &threads, nil
}
@@ -233,3 +242,144 @@ func TestShouldCheckUnarchive(t *testing.T) {
assert.False(t, service.shouldCheckUnarchive(notArchived, received), "already unarchived -> no check")
assert.False(t, service.shouldCheckUnarchive(notArchived, sent), "not archived + outbound -> no check")
}
+
+func TestCreateThreadArchivesOnlyNewOutgoingConversationsForEnabledPhone(t *testing.T) {
+ tests := []struct {
+ name string
+ status entities.MessageStatus
+ owner string
+ wantArchived bool
+ }{
+ {name: "outgoing pending", status: entities.MessageStatusPending, owner: "+18005550199", wantArchived: true},
+ {name: "outgoing sent", status: entities.MessageStatusSent, owner: "+18005550199", wantArchived: true},
+ {name: "outgoing scheduled", status: entities.MessageStatusScheduled, owner: "+18005550199", wantArchived: true},
+ {name: "incoming", status: entities.MessageStatusReceived, owner: "+18005550199", wantArchived: false},
+ {name: "other phone defaults off", status: entities.MessageStatusPending, owner: "+18005550198", wantArchived: false},
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ var stored *entities.MessageThread
+ repository := &messageThreadRepositoryStub{
+ loadByOwnerContact: func(context.Context, entities.UserID, string, string) (*entities.MessageThread, error) {
+ return nil, stacktrace.PropagateWithCodef(gorm.ErrRecordNotFound, repositories.ErrCodeNotFound, "not found")
+ },
+ store: func(_ context.Context, thread *entities.MessageThread) error {
+ stored = thread
+ return nil
+ },
+ }
+ service := newMessageThreadServiceForTest(repository)
+ service.phoneRepository = &threadPhoneRepositoryStub{
+ load: func(_ context.Context, userID entities.UserID, owner string) (*entities.Phone, error) {
+ assert.Equal(t, entities.UserID("user-id"), userID)
+ assert.Equal(t, test.owner, owner)
+ assert.NotEqual(t, entities.MessageStatusReceived, test.status, "incoming creation must not consult the setting")
+ phone := &entities.Phone{}
+ if owner == "+18005550199" {
+ phone.ArchiveNewOutgoingMessageThreads = true
+ }
+ return phone, nil
+ },
+ }
+
+ err := service.UpdateThread(context.Background(), MessageThreadUpdateParams{
+ UserID: entities.UserID("user-id"),
+ Owner: test.owner,
+ Contact: "+18005550100",
+ MessageID: uuid.New(),
+ Status: test.status,
+ Timestamp: time.Now().UTC(),
+ })
+
+ require.NoError(t, err)
+ require.NotNil(t, stored)
+ assert.Equal(t, test.wantArchived, stored.IsArchived)
+ })
+ }
+}
+
+func TestCreateOutgoingThreadDefaultsToUnarchivedWhenPhoneCannotBeLoaded(t *testing.T) {
+ var stored *entities.MessageThread
+ service := newMessageThreadServiceForTest(&messageThreadRepositoryStub{
+ loadByOwnerContact: func(context.Context, entities.UserID, string, string) (*entities.MessageThread, error) {
+ return nil, stacktrace.PropagateWithCodef(gorm.ErrRecordNotFound, repositories.ErrCodeNotFound, "not found")
+ },
+ store: func(_ context.Context, thread *entities.MessageThread) error {
+ stored = thread
+ return nil
+ },
+ })
+ logger := &phoneNotificationLogger{}
+ service.logger = logger
+ phoneErr := stacktrace.NewError("phone unavailable")
+ service.phoneRepository = &threadPhoneRepositoryStub{
+ load: func(context.Context, entities.UserID, string) (*entities.Phone, error) {
+ return nil, phoneErr
+ },
+ }
+
+ err := service.UpdateThread(context.Background(), MessageThreadUpdateParams{
+ UserID: entities.UserID("user-id"),
+ Owner: "+18005550199",
+ Status: entities.MessageStatusPending,
+ })
+
+ require.NoError(t, err)
+ require.NotNil(t, stored)
+ assert.False(t, stored.IsArchived)
+ require.Len(t, logger.warnings, 1)
+ assert.Contains(t, logger.warnings[0], "phone unavailable")
+}
+
+func TestArchiveNewOutgoingMessageThreadsPreservesExistingConversationBehavior(t *testing.T) {
+ tests := []struct {
+ name string
+ status entities.MessageStatus
+ archived bool
+ unarchive bool
+ wantUnarchive bool
+ }{
+ {name: "outgoing existing inbox", status: entities.MessageStatusPending},
+ {name: "outgoing existing archive", status: entities.MessageStatusPending, archived: true},
+ {name: "reply stays archived", status: entities.MessageStatusReceived, archived: true},
+ {name: "reply unarchives when enabled", status: entities.MessageStatusReceived, archived: true, unarchive: true, wantUnarchive: true},
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ var captured repositories.MessageThreadActivityUpdate
+ repository := &messageThreadRepositoryStub{
+ loadByOwnerContact: func(context.Context, entities.UserID, string, string) (*entities.MessageThread, error) {
+ return &entities.MessageThread{ID: uuid.New(), IsArchived: test.archived}, nil
+ },
+ updateActivity: func(_ context.Context, params repositories.MessageThreadActivityUpdate) error {
+ captured = params
+ return nil
+ },
+ store: func(context.Context, *entities.MessageThread) error {
+ t.Fatal("existing conversations must not be recreated")
+ return nil
+ },
+ }
+ service := newMessageThreadServiceForTest(repository)
+ service.phoneRepository = &threadPhoneRepositoryStub{
+ load: func(context.Context, entities.UserID, string) (*entities.Phone, error) {
+ return &entities.Phone{UnarchiveThread: test.unarchive, ArchiveNewOutgoingMessageThreads: true}, nil
+ },
+ }
+
+ err := service.UpdateThread(context.Background(), MessageThreadUpdateParams{
+ UserID: entities.UserID("user-id"),
+ Owner: "+18005550199",
+ Contact: "+18005550100",
+ MessageID: uuid.New(),
+ Status: test.status,
+ Timestamp: time.Now().UTC(),
+ })
+
+ require.NoError(t, err)
+ assert.Equal(t, test.wantUnarchive, captured.Unarchive)
+ })
+ }
+}
diff --git a/api/pkg/services/phone_service.go b/api/pkg/services/phone_service.go
index c7078375b..46c984c82 100644
--- a/api/pkg/services/phone_service.go
+++ b/api/pkg/services/phone_service.go
@@ -94,18 +94,19 @@ func (service *PhoneService) Load(ctx context.Context, userID entities.UserID, o
// PhoneUpsertParams are parameters for creating a new entities.Phone
type PhoneUpsertParams struct {
- PhoneNumber *phonenumbers.PhoneNumber
- FcmToken *string
- MessagesPerMinute *uint
- MaxSendAttempts *uint
- WebhookURL *string
- MessageExpirationDuration *time.Duration
- MissedCallAutoReply *string
- UnarchiveThread *bool
- SIM entities.SIM
- MessageSendScheduleID *uuid.UUID
- Source string
- UserID entities.UserID
+ PhoneNumber *phonenumbers.PhoneNumber
+ FcmToken *string
+ MessagesPerMinute *uint
+ MaxSendAttempts *uint
+ WebhookURL *string
+ MessageExpirationDuration *time.Duration
+ MissedCallAutoReply *string
+ UnarchiveThread *bool
+ ArchiveNewOutgoingMessageThreads *bool
+ SIM entities.SIM
+ MessageSendScheduleID *uuid.UUID
+ Source string
+ UserID entities.UserID
}
// Upsert a new entities.Phone
@@ -125,7 +126,7 @@ func (service *PhoneService) Upsert(ctx context.Context, params *PhoneUpsertPara
FcmToken: params.FcmToken,
SIM: params.SIM,
MessageSendScheduleID: params.MessageSendScheduleID,
- })
+ }, params.ArchiveNewOutgoingMessageThreads != nil && *params.ArchiveNewOutgoingMessageThreads)
}
if err != nil {
@@ -224,7 +225,7 @@ func (service *PhoneService) UpsertFCMToken(ctx context.Context, params *PhoneFC
phone, err := service.repository.Load(ctx, params.UserID, phonenumbers.Format(params.PhoneNumber, phonenumbers.E164))
if stacktrace.GetCode(err) == repositories.ErrCodeNotFound {
- return service.createPhone(ctx, params)
+ return service.createPhone(ctx, params, false)
}
if err != nil {
@@ -242,7 +243,7 @@ func (service *PhoneService) UpsertFCMToken(ctx context.Context, params *PhoneFC
return phone, service.dispatchPhoneUpdatedEvent(ctx, phone, params)
}
-func (service *PhoneService) createPhone(ctx context.Context, params *PhoneFCMTokenParams) (*entities.Phone, error) {
+func (service *PhoneService) createPhone(ctx context.Context, params *PhoneFCMTokenParams, archiveNewOutgoingMessageThreads bool) (*entities.Phone, error) {
ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger)
defer span.End()
@@ -252,15 +253,16 @@ func (service *PhoneService) createPhone(ctx context.Context, params *PhoneFCMTo
FcmToken: params.FcmToken,
// Android has a limit of 30 SMS messages per minute without user permission, to be safe let's use 10 messages per minute
// https://android.googlesource.com/platform/frameworks/opt/telephony/+/master/src/java/com/android/internal/telephony/SmsUsageMonitor.java#80
- MessagesPerMinute: 10,
- MessageExpirationSeconds: 10 * 60, // 10 minutes
- MaxSendAttempts: 2,
- SIM: params.SIM,
- MissedCallAutoReply: nil,
- MessageSendScheduleID: params.MessageSendScheduleID,
- PhoneNumber: phonenumbers.Format(params.PhoneNumber, phonenumbers.E164),
- CreatedAt: time.Now().UTC(),
- UpdatedAt: time.Now().UTC(),
+ MessagesPerMinute: 10,
+ MessageExpirationSeconds: 10 * 60, // 10 minutes
+ MaxSendAttempts: 2,
+ SIM: params.SIM,
+ MissedCallAutoReply: nil,
+ MessageSendScheduleID: params.MessageSendScheduleID,
+ ArchiveNewOutgoingMessageThreads: archiveNewOutgoingMessageThreads,
+ PhoneNumber: phonenumbers.Format(params.PhoneNumber, phonenumbers.E164),
+ CreatedAt: time.Now().UTC(),
+ UpdatedAt: time.Now().UTC(),
}
if err := service.repository.Save(ctx, phone); err != nil {
@@ -303,6 +305,10 @@ func (service *PhoneService) update(phone *entities.Phone, params *PhoneUpsertPa
phone.UnarchiveThread = *params.UnarchiveThread
}
+ if params.ArchiveNewOutgoingMessageThreads != nil {
+ phone.ArchiveNewOutgoingMessageThreads = *params.ArchiveNewOutgoingMessageThreads
+ }
+
phone.SIM = params.SIM
phone.MessageSendScheduleID = params.MessageSendScheduleID
diff --git a/api/pkg/services/phone_service_test.go b/api/pkg/services/phone_service_test.go
new file mode 100644
index 000000000..d99cc7853
--- /dev/null
+++ b/api/pkg/services/phone_service_test.go
@@ -0,0 +1,94 @@
+package services
+
+import (
+ "context"
+ "testing"
+
+ "github.com/NdoleStudio/httpsms/pkg/entities"
+ "github.com/NdoleStudio/httpsms/pkg/repositories"
+ "github.com/NdoleStudio/httpsms/pkg/telemetry"
+ "github.com/NdoleStudio/stacktrace"
+ "github.com/nyaruka/phonenumbers"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gorm.io/gorm"
+)
+
+type phoneCreationRepository struct {
+ repositories.PhoneRepository
+ saved entities.Phone
+}
+
+func (*phoneCreationRepository) Load(context.Context, entities.UserID, string) (*entities.Phone, error) {
+ return nil, stacktrace.PropagateWithCodef(gorm.ErrRecordNotFound, repositories.ErrCodeNotFound, "phone not found")
+}
+
+func (repository *phoneCreationRepository) Save(_ context.Context, phone *entities.Phone) error {
+ repository.saved = *phone
+ return nil
+}
+
+func TestPhoneUpsertAppliesArchiveSettingOnFirstRegistration(t *testing.T) {
+ enabled := true
+ disabled := false
+ for _, test := range []struct {
+ name string
+ setting *bool
+ want bool
+ }{
+ {name: "enabled", setting: &enabled, want: true},
+ {name: "disabled", setting: &disabled},
+ {name: "omitted defaults off"},
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ repository := &phoneCreationRepository{}
+ logger := &noopLogger{}
+ tracer := telemetry.NewOtelLogger("test", logger)
+ dispatcher := NewEventDispatcher(logger, tracer, nil, &phoneNotificationEventQueue{}, PushQueueConfig{})
+ service := NewPhoneService(logger, tracer, repository, dispatcher)
+ number, err := phonenumbers.Parse("+18005550199", phonenumbers.UNKNOWN_REGION)
+ require.NoError(t, err)
+
+ phone, err := service.Upsert(context.Background(), &PhoneUpsertParams{
+ Source: "https://httpsms.test",
+ UserID: entities.UserID("user-id"),
+ PhoneNumber: number,
+ SIM: entities.SIM1,
+ ArchiveNewOutgoingMessageThreads: test.setting,
+ })
+
+ require.NoError(t, err)
+ require.NotNil(t, phone)
+ assert.Equal(t, test.want, phone.ArchiveNewOutgoingMessageThreads)
+ assert.Equal(t, test.want, repository.saved.ArchiveNewOutgoingMessageThreads)
+ assert.Equal(t, "+18005550199", repository.saved.PhoneNumber)
+ })
+ }
+}
+
+func TestPhoneUpdateArchiveNewOutgoingMessageThreads(t *testing.T) {
+ enabled := true
+ disabled := false
+ tests := []struct {
+ name string
+ initial bool
+ setting *bool
+ want bool
+ }{
+ {name: "enable", setting: &enabled, want: true},
+ {name: "disable", initial: true, setting: &disabled, want: false},
+ {name: "omitted preserves enabled", initial: true, want: true},
+ {name: "omitted defaults off", want: false},
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ phone := &entities.Phone{ArchiveNewOutgoingMessageThreads: test.initial}
+ params := PhoneUpsertParams{ArchiveNewOutgoingMessageThreads: test.setting}
+
+ updated := (&PhoneService{}).update(phone, ¶ms)
+
+ assert.Equal(t, test.want, updated.ArchiveNewOutgoingMessageThreads)
+ })
+ }
+}
diff --git a/api/pkg/validators/message_thread_handler_validator.go b/api/pkg/validators/message_thread_handler_validator.go
index d4f08002f..3c43575b3 100644
--- a/api/pkg/validators/message_thread_handler_validator.go
+++ b/api/pkg/validators/message_thread_handler_validator.go
@@ -48,6 +48,10 @@ func (validator *MessageThreadHandlerValidator) ValidateMessageThreadIndex(_ con
"required",
"in:true,false",
},
+ "unread_only": []string{
+ "required",
+ "in:true,false",
+ },
"query": []string{
"max:100",
},
diff --git a/tests/archive_new_outgoing_message_threads_integration_test.go b/tests/archive_new_outgoing_message_threads_integration_test.go
new file mode 100644
index 000000000..850ee491e
--- /dev/null
+++ b/tests/archive_new_outgoing_message_threads_integration_test.go
@@ -0,0 +1,155 @@
+package tests
+
+import (
+ "context"
+ "net/http"
+ "testing"
+ "time"
+
+ httpsms "github.com/NdoleStudio/httpsms-go"
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+type phoneThreadSettings struct {
+ ArchiveNewOutgoingMessageThreads bool `json:"archive_new_outgoing_message_threads"`
+ UnarchiveThread bool `json:"unarchive_thread"`
+}
+
+func updatePhoneThreadSettings(ctx context.Context, t *testing.T, phone testPhone, settings map[string]bool) phoneThreadSettings {
+ t.Helper()
+
+ payload := map[string]any{
+ "phone_number": phone.PhoneNumber,
+ "fcm_token": phone.FcmToken,
+ "sim": "SIM1",
+ }
+ for key, value := range settings {
+ payload[key] = value
+ }
+ var response struct {
+ Data phoneThreadSettings `json:"data"`
+ }
+ requestJSON(ctx, t, http.MethodPut, "/v1/phones", userAPIKey, payload, http.StatusOK, &response)
+ return response.Data
+}
+
+func sendThreadMessage(ctx context.Context, t *testing.T, phone testPhone, contact, content string) {
+ t.Helper()
+
+ _, response, err := newAPIClient().Messages.Send(ctx, &httpsms.MessageSendParams{
+ From: phone.PhoneNumber,
+ To: contact,
+ Content: content,
+ })
+ require.NoError(t, err)
+ require.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
+}
+
+func TestArchiveNewOutgoingMessageThreadsOnFirstRegistration(t *testing.T) {
+ ctx := context.Background()
+ phone := testPhone{
+ PhoneNumber: randomPhoneNumber(),
+ FcmToken: "fcm-" + uuid.NewString(),
+ }
+ var response struct {
+ Data phoneThreadSettings `json:"data"`
+ }
+ requestJSON(ctx, t, http.MethodPut, "/v1/phones", userAPIKey, map[string]any{
+ "phone_number": phone.PhoneNumber,
+ "fcm_token": phone.FcmToken,
+ "sim": "SIM1",
+ "archive_new_outgoing_message_threads": true,
+ }, http.StatusOK, &response)
+ require.True(t, response.Data.ArchiveNewOutgoingMessageThreads)
+
+ contact := "+18005540100"
+ sendThreadMessage(ctx, t, phone, contact, "First registration archives outgoing")
+ require.NotNil(t, waitForThread(ctx, t, phone.PhoneNumber, contact, true, "First registration archives outgoing", 20*time.Second))
+ assert.Nil(t, findThreadByContact(fetchThreads(ctx, t, phone.PhoneNumber, false), contact))
+}
+
+func TestArchiveNewOutgoingMessageThreadsPerPhoneAndDirection(t *testing.T) {
+ ctx := context.Background()
+ enabledPhone := setupPhone(ctx, t, 60)
+ defaultPhone := setupPhone(ctx, t, 60)
+ require.NotEqual(t, enabledPhone.PhoneNumber, defaultPhone.PhoneNumber)
+
+ existingContact := randomPhoneNumber()
+ receiveInbound(ctx, t, enabledPhone.PhoneAPIKey, existingContact, enabledPhone.PhoneNumber, "Existing inbox conversation", time.Now())
+ require.NotNil(t, waitForThread(ctx, t, enabledPhone.PhoneNumber, existingContact, false, "Existing inbox conversation", 20*time.Second))
+
+ settings := updatePhoneThreadSettings(ctx, t, enabledPhone, map[string]bool{"archive_new_outgoing_message_threads": true})
+ require.True(t, settings.ArchiveNewOutgoingMessageThreads)
+ assert.False(t, updatePhoneThreadSettings(ctx, t, defaultPhone, nil).ArchiveNewOutgoingMessageThreads)
+
+ contact := randomPhoneNumber()
+ sendThreadMessage(ctx, t, enabledPhone, contact, "New outgoing archived")
+ archived := waitForThread(ctx, t, enabledPhone.PhoneNumber, contact, true, "New outgoing archived", 20*time.Second)
+ require.NotNil(t, archived)
+ assert.True(t, archived.IsArchived)
+ assert.Nil(t, findThreadByContact(fetchThreads(ctx, t, enabledPhone.PhoneNumber, false), contact))
+
+ sendThreadMessage(ctx, t, defaultPhone, contact, "Other phone stays in inbox")
+ require.NotNil(t, waitForThread(ctx, t, defaultPhone.PhoneNumber, contact, false, "Other phone stays in inbox", 20*time.Second))
+
+ sendThreadMessage(ctx, t, enabledPhone, existingContact, "Existing conversation stays in inbox")
+ require.NotNil(t, waitForThread(ctx, t, enabledPhone.PhoneNumber, existingContact, false, "Existing conversation stays in inbox", 20*time.Second))
+
+ inboundContact := randomPhoneNumber()
+ receiveInbound(ctx, t, enabledPhone.PhoneAPIKey, inboundContact, enabledPhone.PhoneNumber, "New incoming stays in inbox", time.Now())
+ require.NotNil(t, waitForThread(ctx, t, enabledPhone.PhoneNumber, inboundContact, false, "New incoming stays in inbox", 20*time.Second))
+}
+
+func TestArchiveNewOutgoingMessageThreadsToggleAndPartialUpdates(t *testing.T) {
+ ctx := context.Background()
+ phone := setupPhone(ctx, t, 60)
+
+ require.True(t, updatePhoneThreadSettings(ctx, t, phone, map[string]bool{"archive_new_outgoing_message_threads": true}).ArchiveNewOutgoingMessageThreads)
+ settings := updatePhoneThreadSettings(ctx, t, phone, map[string]bool{"unarchive_thread": true})
+ assert.True(t, settings.ArchiveNewOutgoingMessageThreads, "omitting the setting must preserve it")
+ assert.True(t, settings.UnarchiveThread)
+
+ archivedContact := randomPhoneNumber()
+ sendThreadMessage(ctx, t, phone, archivedContact, "Archived before disabling")
+ require.NotNil(t, waitForThread(ctx, t, phone.PhoneNumber, archivedContact, true, "Archived before disabling", 20*time.Second))
+
+ settings = updatePhoneThreadSettings(ctx, t, phone, map[string]bool{"archive_new_outgoing_message_threads": false})
+ require.False(t, settings.ArchiveNewOutgoingMessageThreads)
+ assert.True(t, settings.UnarchiveThread, "the two settings must be independent")
+
+ inboxContact := randomPhoneNumber()
+ sendThreadMessage(ctx, t, phone, inboxContact, "Inbox after disabling")
+ require.NotNil(t, waitForThread(ctx, t, phone.PhoneNumber, inboxContact, false, "Inbox after disabling", 20*time.Second))
+ assert.NotNil(t, findThreadByContact(fetchThreads(ctx, t, phone.PhoneNumber, true), archivedContact), "disabling must not change existing archived threads")
+}
+
+func TestArchiveNewOutgoingMessageThreadsReplyRespectsUnarchiveSetting(t *testing.T) {
+ for _, test := range []struct {
+ name string
+ unarchive bool
+ }{
+ {name: "reply remains archived", unarchive: false},
+ {name: "reply returns to inbox", unarchive: true},
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ ctx := context.Background()
+ phone := setupPhone(ctx, t, 60)
+ updatePhoneThreadSettings(ctx, t, phone, map[string]bool{
+ "archive_new_outgoing_message_threads": true,
+ "unarchive_thread": test.unarchive,
+ })
+ contact := randomPhoneNumber()
+ sendThreadMessage(ctx, t, phone, contact, "Outgoing starts archived")
+ require.NotNil(t, waitForThread(ctx, t, phone.PhoneNumber, contact, true, "Outgoing starts archived", 20*time.Second))
+
+ receiveInbound(ctx, t, phone.PhoneAPIKey, contact, phone.PhoneNumber, "Incoming reply", time.Now())
+
+ thread := waitForThread(ctx, t, phone.PhoneNumber, contact, !test.unarchive, "Incoming reply", 20*time.Second)
+ require.NotNil(t, thread)
+ assert.Equal(t, !test.unarchive, thread.IsArchived)
+ assert.Nil(t, findThreadByContact(fetchThreads(ctx, t, phone.PhoneNumber, test.unarchive), contact))
+ })
+ }
+}
diff --git a/tests/helpers_test.go b/tests/helpers_test.go
index 797b2f09c..6d8106a33 100644
--- a/tests/helpers_test.go
+++ b/tests/helpers_test.go
@@ -466,6 +466,7 @@ func fireEvent(ctx context.Context, t *testing.T, phoneAPIKey string, messageID
func pollMessageStatus(ctx context.Context, t *testing.T, messageID string, targetStatus string, timeout time.Duration) httpsms.Message {
t.Helper()
deadline := time.Now().Add(timeout)
+ lastStatus := "not observed"
for time.Now().Before(deadline) {
url := fmt.Sprintf("%s/v1/messages/%s", apiBaseURL, messageID)
@@ -480,8 +481,11 @@ func pollMessageStatus(ctx context.Context, t *testing.T, messageID string, targ
resp.Body.Close()
if readErr == nil && resp.StatusCode == http.StatusOK {
var result httpsms.MessageResponse
- if json.Unmarshal(body, &result) == nil && result.Data.Status == targetStatus {
- return result.Data
+ if json.Unmarshal(body, &result) == nil {
+ lastStatus = result.Data.Status
+ if result.Data.Status == targetStatus {
+ return result.Data
+ }
}
}
}
@@ -490,7 +494,7 @@ func pollMessageStatus(ctx context.Context, t *testing.T, messageID string, targ
time.Sleep(500 * time.Millisecond)
}
- t.Fatalf("message %s did not reach status %q within %v", messageID, targetStatus, timeout)
+ t.Fatalf("message %s did not reach status %q within %v (last observed status: %q)", messageID, targetStatus, timeout, lastStatus)
return httpsms.Message{}
}
diff --git a/tests/unarchive_thread_integration_test.go b/tests/unarchive_thread_integration_test.go
index 35ee8bf56..c7fb3f55c 100644
--- a/tests/unarchive_thread_integration_test.go
+++ b/tests/unarchive_thread_integration_test.go
@@ -21,6 +21,7 @@ type integrationThread struct {
Contact string `json:"contact"`
Owner string `json:"owner"`
IsArchived bool `json:"is_archived"`
+ UnreadCount uint `json:"unread_count"`
LastMessageContent *string `json:"last_message_content"`
}
diff --git a/tests/unread_filter_integration_test.go b/tests/unread_filter_integration_test.go
new file mode 100644
index 000000000..6366517b5
--- /dev/null
+++ b/tests/unread_filter_integration_test.go
@@ -0,0 +1,144 @@
+package tests
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func fetchFilteredThreads(ctx context.Context, t *testing.T, owner string, archived, unreadOnly bool, limit, skip int) []integrationThread {
+ t.Helper()
+
+ query := url.Values{
+ "owner": {owner},
+ "is_archived": {fmt.Sprint(archived)},
+ "unread_only": {fmt.Sprint(unreadOnly)},
+ "limit": {fmt.Sprint(limit)},
+ "skip": {fmt.Sprint(skip)},
+ }
+ var response struct {
+ Data []integrationThread `json:"data"`
+ }
+ requestJSON(ctx, t, http.MethodGet, "/v1/message-threads?"+query.Encode(), userAPIKey, nil, http.StatusOK, &response)
+ return response.Data
+}
+
+func TestMessageThreadUnreadFilter(t *testing.T) {
+ ctx := context.Background()
+ phone := setupPhone(ctx, t, 60)
+ otherPhone := setupPhone(ctx, t, 60)
+ require.NotEqual(t, phone.PhoneNumber, otherPhone.PhoneNumber)
+ unreadContact := randomPhoneNumber()
+ readContact := randomPhoneNumber()
+ require.NotEqual(t, unreadContact, readContact)
+
+ receiveInbound(ctx, t, phone.PhoneAPIKey, unreadContact, phone.PhoneNumber, "Older unread message", time.Now().Add(-2*time.Minute))
+ unread := waitForThread(ctx, t, phone.PhoneNumber, unreadContact, false, "Older unread message", 20*time.Second)
+ require.NotNil(t, unread)
+ require.Equal(t, uint(1), unread.UnreadCount)
+
+ receiveInbound(ctx, t, phone.PhoneAPIKey, readContact, phone.PhoneNumber, "Recent read message", time.Now().Add(-time.Minute))
+ read := waitForThread(ctx, t, phone.PhoneNumber, readContact, false, "Recent read message", 20*time.Second)
+ require.NotNil(t, read)
+ clearMessageThreadUnreadCount(ctx, t, read.ID)
+
+ receiveInbound(ctx, t, otherPhone.PhoneAPIKey, unreadContact, otherPhone.PhoneNumber, "Other phone unread", time.Now())
+ require.NotNil(t, waitForThread(ctx, t, otherPhone.PhoneNumber, unreadContact, false, "Other phone unread", 20*time.Second))
+
+ all := fetchThreads(ctx, t, phone.PhoneNumber, false)
+ require.Len(t, all, 2, "All defaults to including read and unread conversations for this phone")
+ assert.Equal(t, []string{read.ID, unread.ID}, []string{all[0].ID, all[1].ID})
+
+ filtered := fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, true, 1, 0)
+ require.Len(t, filtered, 1, "the unread condition must run before the result limit")
+ assert.Equal(t, unread.ID, filtered[0].ID)
+ assert.Positive(t, filtered[0].UnreadCount)
+ assert.Empty(t, fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, true, 1, 1), "offset must apply to the filtered result")
+
+ all = fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, false, 1, 0)
+ require.Len(t, all, 1)
+ assert.Equal(t, read.ID, all[0].ID, "All must restore the most recent read conversation")
+
+ archiveThread(ctx, t, unread.ID)
+ assert.Empty(t, fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, true, 20, 0), "archived conversations must not appear in the inbox unread filter")
+ filtered = fetchFilteredThreads(ctx, t, phone.PhoneNumber, true, true, 20, 0)
+ require.Len(t, filtered, 1)
+ assert.Equal(t, unread.ID, filtered[0].ID)
+ assert.True(t, filtered[0].IsArchived)
+
+ clearMessageThreadUnreadCount(ctx, t, unread.ID)
+ assert.Empty(t, fetchFilteredThreads(ctx, t, phone.PhoneNumber, true, true, 20, 0), "marking a conversation read removes it from Unread")
+ all = fetchFilteredThreads(ctx, t, phone.PhoneNumber, true, false, 20, 0)
+ require.Len(t, all, 1, "marking read must not delete or unarchive the conversation")
+ assert.Equal(t, unread.ID, all[0].ID)
+ assert.Zero(t, all[0].UnreadCount)
+ assert.True(t, all[0].IsArchived)
+}
+
+func TestMessageThreadUnreadFilterRejectsInvalidValue(t *testing.T) {
+ query := url.Values{
+ "owner": {randomPhoneNumber()},
+ "unread_only": {"invalid"},
+ }
+ requestJSON(context.Background(), t, http.MethodGet, "/v1/message-threads?"+query.Encode(), userAPIKey, nil, http.StatusUnprocessableEntity, nil)
+}
+
+func TestMessageThreadPaginationReturnsRequestedPages(t *testing.T) {
+ ctx := context.Background()
+ phone := setupPhone(ctx, t, 60)
+ timestamp := time.Now().Add(-time.Minute).Truncate(time.Second)
+ for index := 0; index < 20; index++ {
+ receiveInbound(ctx, t, phone.PhoneAPIKey, fmt.Sprintf("+1800554%04d", index), phone.PhoneNumber, "Pagination thread", timestamp)
+ }
+
+ var first []integrationThread
+ deadline := time.Now().Add(20 * time.Second)
+ for time.Now().Before(deadline) {
+ first = fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, false, 20, 0)
+ if len(first) == 20 {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+ require.Len(t, first, 20)
+ assert.Empty(t, fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, false, 20, 20))
+
+ receiveInbound(ctx, t, phone.PhoneAPIKey, "+18005540020", phone.PhoneNumber, "One extra thread", timestamp)
+ var second []integrationThread
+ deadline = time.Now().Add(20 * time.Second)
+ for time.Now().Before(deadline) {
+ second = fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, false, 20, 20)
+ if len(second) == 1 {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+ require.Len(t, second, 1)
+ first = fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, false, 20, 0)
+ require.Len(t, first, 20)
+ assert.Empty(t, fetchFilteredThreads(ctx, t, phone.PhoneNumber, false, false, 20, 40))
+ seen := map[string]bool{}
+ for _, thread := range first {
+ seen[thread.ID] = true
+ }
+ assert.False(t, seen[second[0].ID], "equal timestamps must not duplicate threads between pages")
+
+ var individual struct {
+ Data integrationThread `json:"data"`
+ }
+ path := "/v1/message-threads/" + second[0].ID
+ requestJSON(ctx, t, http.MethodGet, path, userAPIKey, nil, http.StatusOK, &individual)
+ assert.Equal(t, second[0].ID, individual.Data.ID, "an older conversation must load independently of the first page")
+ assert.Equal(t, phone.PhoneNumber, individual.Data.Owner)
+ archiveThread(ctx, t, individual.Data.ID)
+ requestJSON(ctx, t, http.MethodGet, path, userAPIKey, nil, http.StatusOK, &individual)
+ assert.True(t, individual.Data.IsArchived, "direct lookup must also resolve archived conversations")
+ requestJSON(ctx, t, http.MethodGet, path, systemAPIKey, nil, http.StatusNotFound, nil)
+ requestJSON(ctx, t, http.MethodGet, "/v1/message-threads/invalid", userAPIKey, nil, http.StatusUnprocessableEntity, nil)
+}
diff --git a/web/app/components/ContactDialog.vue b/web/app/components/ContactDialog.vue
index 2df329a93..65e559521 100644
--- a/web/app/components/ContactDialog.vue
+++ b/web/app/components/ContactDialog.vue
@@ -45,7 +45,7 @@ const props = withDefaults(
)
const emit = defineEmits<{
- saved: []
+ saved: [contacts: EntitiesContact[]]
'update:modelValue': [value: boolean]
}>()
@@ -183,17 +183,17 @@ async function submitForm() {
const payload = buildPayload()
saving.value = true
try {
- if (props.contact) {
- await contactsStore.updateContact(props.contact.id, payload, {
- refresh: props.refreshContacts,
- })
- } else {
- await contactsStore.saveContacts([payload], {
- refresh: props.refreshContacts,
- })
- }
+ const savedContacts = props.contact
+ ? [
+ await contactsStore.updateContact(props.contact.id, payload, {
+ refresh: props.refreshContacts,
+ }),
+ ]
+ : await contactsStore.saveContacts([payload], {
+ refresh: props.refreshContacts,
+ })
closeDialog()
- emit('saved')
+ emit('saved', savedContacts)
} catch (error: unknown) {
const bag = new ErrorMessages()
const messages = fieldErrorsFromApi(error)
diff --git a/web/app/components/FirebaseAuth.vue b/web/app/components/FirebaseAuth.vue
index 5f783e07c..659b426aa 100644
--- a/web/app/components/FirebaseAuth.vue
+++ b/web/app/components/FirebaseAuth.vue
@@ -300,7 +300,7 @@ function getGeneralErrorMessage(
block
color="white"
size="large"
- class="mb-3 position-relative"
+ class="mb-3 position-relative overflow-visible"
:loading="loading"
:disabled="loading"
@click="signInWithGoogle"
@@ -324,7 +324,7 @@ function getGeneralErrorMessage(
size="large"
variant="flat"
color="black"
- class="mb-3 position-relative"
+ class="mb-3 position-relative overflow-visible"
:loading="loading"
:disabled="loading"
@click="signInWithGithub"
@@ -349,7 +349,7 @@ function getGeneralErrorMessage(
size="large"
variant="flat"
color="red"
- class="mb-3 position-relative"
+ class="mb-3 position-relative overflow-visible"
:disabled="loading"
@click="showEmailForm = true"
>
diff --git a/web/app/components/MessageThread.vue b/web/app/components/MessageThread.vue
index 2a699408a..b0a01dd0c 100644
--- a/web/app/components/MessageThread.vue
+++ b/web/app/components/MessageThread.vue
@@ -49,6 +49,18 @@ function onInstallApp() {
})
}
+async function loadMoreThreads() {
+ try {
+ await threadsStore.loadMoreThreads()
+ } catch (error) {
+ notificationsStore.addNotification({
+ type: 'error',
+ message: 'More conversations could not be loaded',
+ })
+ console.error(error)
+ }
+}
+
function isWhatsappFormatted(thread: EntitiesMessageThread): boolean {
const phone = phonesStore.phones.find((x) => x.phone_number === thread.owner)
return isUrlNotificationToken(phone?.fcm_token)
@@ -94,7 +106,8 @@ function threadAvatarInitial(thread: EntitiesMessageThread): string {
+
+ No unread conversations
+
+
+
+ Load More
+
+
diff --git a/web/app/components/MessageThreadHeader.vue b/web/app/components/MessageThreadHeader.vue
index 12917b598..e1a8a318b 100644
--- a/web/app/components/MessageThreadHeader.vue
+++ b/web/app/components/MessageThreadHeader.vue
@@ -67,6 +67,24 @@ async function toggleArchive() {
await threadsStore.loadThreads()
}
+async function filterThreads(unreadOnly: boolean) {
+ if (threadsStore.unreadOnly === unreadOnly) return
+
+ const previous = threadsStore.unreadOnly
+ threadsStore.unreadOnly = unreadOnly
+ threadsStore.loadingThreads = true
+ try {
+ await threadsStore.loadThreads()
+ } catch (error) {
+ threadsStore.unreadOnly = previous
+ notificationsStore.addNotification({
+ type: 'error',
+ message: 'The message thread filter could not be updated',
+ })
+ console.error(error)
+ }
+}
+
async function logout() {
const auth = getAuth()
await signOut(auth)
@@ -83,134 +101,176 @@ async function logout() {
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {{ phoneCountry(phonesStore.owner) }}
+
+
+
+
+
+
+
+
+ Last Heartbeat
+ {{ humanizeTime(phonesStore.heartbeat.timestamp) }} ago
+
-
-
-
- {{ phoneCountry(phonesStore.owner) }}
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
- Last Heartbeat
- {{ humanizeTime(phonesStore.heartbeat.timestamp) }} ago
-
-
+
+
+
+ {{ threadsStore.archivedThreads ? 'Unarchived' : 'Archived' }}
+
+
+
+
+ New Message
+
+
+
+ Bulk Messages
+
+
+
+ Search Messages
+
+
+
+ Contacts
+
+
+
+ Settings
+
+
+
+ Phone API Keys
+
+
+
+ Download App
+
+
+
+ Usage & Billing
+
+
+
+ Logout
+
+
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
- {{ threadsStore.archivedThreads ? 'Unarchived' : 'Archived' }}
-
-
-
-
- New Message
-
-
-
- Bulk Messages
-
-
-
- Search Messages
-
-
-
- Contacts
-
-
-
- Settings
-
-
-
- Phone API Keys
-
-
-
- Download App
-
-
-
- Usage & Billing
-
-
-
- Logout
-
-
-
+ All
+
+
+ Unread
+
+
diff --git a/web/app/composables/useApi.ts b/web/app/composables/useApi.ts
index b69020c03..4c6e9a836 100644
--- a/web/app/composables/useApi.ts
+++ b/web/app/composables/useApi.ts
@@ -1,5 +1,3 @@
-import type { $Fetch } from 'ofetch'
-
let authToken: string | null = null
let apiKey: string | null = null
@@ -11,7 +9,7 @@ export function setApiKey(key: string | null) {
apiKey = key
}
-function createApiFetch(): $Fetch {
+function createApiFetch() {
const config = useRuntimeConfig()
const publicConfig = config.public as Record
const baseURL = publicConfig.apiBaseUrl
diff --git a/web/app/layouts/default.vue b/web/app/layouts/default.vue
index 47c901de3..12f3dfc52 100644
--- a/web/app/layouts/default.vue
+++ b/web/app/layouts/default.vue
@@ -68,10 +68,12 @@ function startPoller() {
-
-
-
-
+
@@ -96,8 +98,12 @@ function startPoller() {
padding-left: 400px;
}
}
+ .message-thread-sidebar {
+ height: 100dvh;
+ }
.v-navigation-drawer__message-thread {
- height: calc(100vh - 120px);
+ flex: 1;
+ min-height: 0;
&::-webkit-scrollbar {
width: 8px;
}
diff --git a/web/app/pages/bulk-messages/index.vue b/web/app/pages/bulk-messages/index.vue
index b25c64c07..cc3bf02dd 100644
--- a/web/app/pages/bulk-messages/index.vue
+++ b/web/app/pages/bulk-messages/index.vue
@@ -31,7 +31,7 @@ function parseErrors(error: unknown): ErrorMessages {
const bag = new ErrorMessages()
const data = toApiError(error).data?.data
if (data && typeof data === 'object') {
- Object.keys(data).forEach((key) => bag.addMany(key, data[key]))
+ Object.entries(data).forEach(([key, values]) => bag.addMany(key, values))
}
return bag
}
@@ -42,7 +42,7 @@ function cleanName(requestId: string): string {
if (requestId.startsWith('bulk-xls-'))
return requestId.replace(/^bulk-xls-/, '') + '.xlsx'
const newFormatMatch = requestId.match(/^bulk-[0-9A-Za-z]+-(.+)$/)
- if (newFormatMatch) return newFormatMatch[1]
+ if (newFormatMatch?.[1]) return newFormatMatch[1]
return requestId.replace(/^bulk-/, '')
}
diff --git a/web/app/pages/index.vue b/web/app/pages/index.vue
index be30493f7..b0077847a 100644
--- a/web/app/pages/index.vue
+++ b/web/app/pages/index.vue
@@ -53,9 +53,7 @@ const pricing = ref(0)
const pricingLabels = ['10K', '20K', '50K', '100K', '200K']
const pricingLabelsFull = ['10,000', '20,000', '50,000', '100,000', '200,000']
-const planMessages = computed(() =>
- pricingLabels[pricing.value].replace('K', ',000'),
-)
+const planMessages = computed(() => pricingLabelsFull[pricing.value])
const planMonthlyPrice = computed(() => [20, 35, 89, 175, 350][pricing.value])
const planYearlyPrice = computed(
() => [200, 350, 1068, 2100, 4200][pricing.value],
diff --git a/web/app/pages/phone-api-keys/index.vue b/web/app/pages/phone-api-keys/index.vue
index aded9453d..5b3e4779f 100644
--- a/web/app/pages/phone-api-keys/index.vue
+++ b/web/app/pages/phone-api-keys/index.vue
@@ -44,7 +44,7 @@ function parseErrors(error: unknown): ErrorMessages {
const bag = new ErrorMessages()
const data = toApiError(error).data?.data
if (data && typeof data === 'object') {
- Object.keys(data).forEach((key) => bag.addMany(key, data[key]))
+ Object.entries(data).forEach(([key, values]) => bag.addMany(key, values))
}
return bag
}
diff --git a/web/app/pages/search-messages/index.vue b/web/app/pages/search-messages/index.vue
index 324131b7b..c77d5b7ef 100644
--- a/web/app/pages/search-messages/index.vue
+++ b/web/app/pages/search-messages/index.vue
@@ -152,7 +152,7 @@ function parseErrors(error: unknown): ErrorMessages {
const bag = new ErrorMessages()
const data = toApiError(error).data?.data
if (data && typeof data === 'object') {
- Object.keys(data).forEach((key) => bag.addMany(key, data[key]))
+ Object.entries(data).forEach(([key, values]) => bag.addMany(key, values))
}
return bag
}
diff --git a/web/app/pages/settings/index.vue b/web/app/pages/settings/index.vue
index f4c312035..cd608ef1a 100644
--- a/web/app/pages/settings/index.vue
+++ b/web/app/pages/settings/index.vue
@@ -1698,6 +1698,15 @@ onMounted(async () => {
placeholder="We are currently closed at the moment, please send us a text message from 09:00 to 17:00"
hint="Here you can configure an automated SMS message which is sent to the caller when this phone has a missed call"
/>
+
{
const polling = ref(false)
const appData = computed(() => {
- const publicConfig = config.public as Record
+ const publicConfig = config.public
let url = publicConfig.appUrl || ''
if (url.endsWith('/')) {
url = url.substring(0, url.length - 1)
@@ -29,9 +29,7 @@ export const useAppStore = defineStore('app', () => {
}
})
- const isLocal = computed(
- () => (config.public as Record).appEnv === 'local',
- )
+ const isLocal = computed(() => config.public.appEnv === 'local')
function setPolling(value: boolean) {
polling.value = value
diff --git a/web/app/stores/contacts.ts b/web/app/stores/contacts.ts
index afa798f72..ce149410f 100644
--- a/web/app/stores/contacts.ts
+++ b/web/app/stores/contacts.ts
@@ -117,14 +117,15 @@ export const useContactsStore = defineStore('contacts', () => {
async function saveContacts(
items: ContactInput | ContactInput[],
options: ContactMutationOptions = {},
- ): Promise {
+ ): Promise {
const contactsToSave = Array.isArray(items) ? items : [items]
const { refresh = true } = options
loading.value = true
try {
+ let response: { data: EntitiesContact[] }
try {
- await apiFetch('/v1/contacts', {
+ response = await apiFetch<{ data: EntitiesContact[] }>('/v1/contacts', {
method: 'POST',
body: contactsToSave,
})
@@ -146,6 +147,7 @@ export const useContactsStore = defineStore('contacts', () => {
if (refresh) {
await loadContacts(true)
}
+ return response.data
} finally {
loading.value = false
}
@@ -155,15 +157,19 @@ export const useContactsStore = defineStore('contacts', () => {
id: string,
payload: ContactInput,
options: ContactMutationOptions = {},
- ): Promise {
+ ): Promise {
const { refresh = true } = options
loading.value = true
try {
+ let response: { data: EntitiesContact }
try {
- await apiFetch(`/v1/contacts/${id}`, {
- method: 'PUT',
- body: payload,
- })
+ response = await apiFetch<{ data: EntitiesContact }>(
+ `/v1/contacts/${id}`,
+ {
+ method: 'PUT',
+ body: payload,
+ },
+ )
} catch (error: unknown) {
notificationsStore.addNotification({
message: getApiErrorMessage(error, 'Error while updating contact'),
@@ -179,6 +185,7 @@ export const useContactsStore = defineStore('contacts', () => {
if (refresh) {
await loadContacts(true)
}
+ return response.data
} finally {
loading.value = false
}
diff --git a/web/app/stores/phones.ts b/web/app/stores/phones.ts
index 276fe2928..4c6a7a597 100644
--- a/web/app/stores/phones.ts
+++ b/web/app/stores/phones.ts
@@ -61,6 +61,8 @@ export const usePhonesStore = defineStore('phones', () => {
messages_per_minute: parseInt(phone.messages_per_minute.toString()),
message_send_schedule_id: phone.message_send_schedule_id ?? null,
unarchive_thread: phone.unarchive_thread ?? false,
+ archive_new_outgoing_message_threads:
+ phone.archive_new_outgoing_message_threads ?? false,
},
})
notificationsStore.addNotification({
diff --git a/web/app/stores/threads.test.ts b/web/app/stores/threads.test.ts
new file mode 100644
index 000000000..2580efd8f
--- /dev/null
+++ b/web/app/stores/threads.test.ts
@@ -0,0 +1,585 @@
+import assert from 'node:assert/strict'
+import { test } from 'node:test'
+import { createPinia, setActivePinia } from 'pinia'
+import { computed, ref } from 'vue'
+import type {
+ EntitiesContact,
+ EntitiesMessageThread,
+} from '../../shared/types/api'
+import { useThreadsStore } from './threads'
+
+function thread(id: string, unreadCount: number): EntitiesMessageThread {
+ return {
+ id,
+ unread_count: unreadCount,
+ owner: '+18005550199',
+ contact: '+18005550100',
+ color: 'indigo',
+ is_archived: false,
+ last_message_content: 'Hello',
+ last_message_id: 'message-id',
+ status: 'received',
+ created_at: '2026-10-27T10:00:00Z',
+ updated_at: '2026-10-27T10:00:00Z',
+ order_timestamp: '2026-10-27T10:00:00Z',
+ user_id: 'user-id',
+ }
+}
+
+function setupStore() {
+ const calls: { path: string; params?: Record }[] = []
+ let responseThreads = [thread('unread', 2), thread('read', 0)]
+ const individualThreads = new Map()
+ let nextLookup: Promise<{ data: EntitiesMessageThread }> | undefined
+ let nextResponse: Promise<{ data: EntitiesMessageThread[] }> | undefined
+ const phones = {
+ owner: '+18005550199',
+ phones: [],
+ getHeartbeat: async () => [],
+ }
+ Object.assign(globalThis, {
+ ref,
+ computed,
+ useApi: () => ({
+ apiFetch: async (
+ path: string,
+ options: {
+ params?: Record
+ method?: string
+ } = {},
+ ) => {
+ calls.push({ path, params: options.params })
+ if (options.method === 'PUT') {
+ responseThreads = responseThreads.map((item) =>
+ item.id === 'unread' ? { ...item, unread_count: 0 } : item,
+ )
+ return { data: thread('unread', 0) }
+ }
+ if (path === '/v1/messages') return { data: [] }
+ if (
+ (!options.method || options.method === 'GET') &&
+ path.startsWith('/v1/message-threads/')
+ ) {
+ if (nextLookup) {
+ const pending = nextLookup
+ nextLookup = undefined
+ return pending
+ }
+ const item = individualThreads.get(path.split('/').at(-1) ?? '')
+ if (!item)
+ throw Object.assign(new Error('Thread not found'), { status: 404 })
+ return { data: item }
+ }
+ const pending = nextResponse
+ nextResponse = undefined
+ if (pending) return pending
+ const skip = Number(options.params?.skip ?? 0)
+ const limit = Number(options.params?.limit ?? 20)
+ return {
+ data: responseThreads.slice(skip, skip + limit),
+ }
+ },
+ }),
+ usePhonesStore: () => phones,
+ useNotificationsStore: () => ({ addNotification: () => {} }),
+ })
+ setActivePinia(createPinia())
+ return {
+ store: useThreadsStore(),
+ phones,
+ calls,
+ respondWith: (threads: EntitiesMessageThread[]) => {
+ responseThreads = threads
+ },
+ respondToLookup: (item: EntitiesMessageThread) => {
+ individualThreads.set(item.id, item)
+ },
+ deferLookup: (response: Promise<{ data: EntitiesMessageThread }>) => {
+ nextLookup = response
+ },
+ deferNextLoad: (response: Promise<{ data: EntitiesMessageThread[] }>) => {
+ nextResponse = response
+ },
+ }
+}
+
+function contact(
+ name: string,
+ phoneNumbers = ['+18005550100'],
+): EntitiesContact {
+ return {
+ id: 'contact-id',
+ name,
+ phone_numbers: phoneNumbers,
+ emails: [],
+ properties: {},
+ user_id: 'user-id',
+ created_at: '2026-10-27T10:00:00Z',
+ updated_at: '2026-10-27T11:00:00Z',
+ }
+}
+
+async function setupReadConversationHistory() {
+ const fixture = setupStore()
+ const other = { ...thread('other', 1), contact: '+18005550101' }
+ fixture.respondWith([thread('unread', 1), other])
+ await fixture.store.loadThreads()
+ fixture.store.setThreadId('unread')
+ fixture.store.unreadOnly = true
+ await fixture.store.resetThreadUnreadCount('unread')
+ fixture.respondWith([other])
+ await fixture.store.loadThreads()
+ await fixture.store.loadThreadMessages('other')
+ return fixture
+}
+
+test('Unread requests server-side filtering within the API limit', async () => {
+ const { store, calls } = setupStore()
+ store.unreadOnly = true
+ store.archivedThreads = true
+
+ await store.loadThreads()
+
+ assert.equal(calls[0]?.params?.unread_only, true)
+ assert.equal(calls[0]?.params?.is_archived, true)
+ assert.equal(calls[0]?.params?.owner, '+18005550199')
+ assert.equal(calls[0]?.params?.limit, 20)
+})
+
+test('marking a thread read hides it from Unread without closing it', async () => {
+ const { store, calls, respondWith } = setupStore()
+ await store.loadThreads()
+ store.setThreadId('unread')
+ store.unreadOnly = true
+
+ await store.resetThreadUnreadCount('unread')
+
+ assert.ok(store.visibleThreads)
+ assert.deepEqual(store.visibleThreads, [])
+ assert.equal(store.currentThread?.id, 'unread')
+ assert.equal(store.currentThread.unread_count, 0)
+
+ respondWith([])
+ await store.loadThreads()
+ assert.equal(store.currentThread?.id, 'unread')
+ await store.loadThreadMessages('unread')
+ assert.equal(calls.at(-1)?.path, '/v1/messages')
+})
+
+test('switching back to All restores read conversations', async () => {
+ const { store } = setupStore()
+ await store.loadThreads()
+ store.unreadOnly = true
+
+ assert.ok(store.visibleThreads)
+ assert.deepEqual(
+ store.visibleThreads.map((item) => item.id),
+ ['unread'],
+ )
+
+ store.unreadOnly = false
+ await store.loadThreads()
+ assert.deepEqual(
+ store.visibleThreads.map((item) => item.id),
+ ['unread', 'read'],
+ )
+})
+
+test('resetting the store clears the filter and retained conversation', async () => {
+ const { store, respondWith } = setupStore()
+ await store.loadThreads()
+ store.setThreadId('unread')
+ store.unreadOnly = true
+ respondWith([])
+ await store.loadThreads()
+
+ store.resetState()
+
+ assert.equal(store.unreadOnly, false)
+ assert.equal(store.currentThread, null)
+ assert.deepEqual(store.threads, [])
+})
+
+test('an older polling response cannot overwrite a newer unread-filter load', async () => {
+ const { store, respondWith, deferNextLoad } = setupStore()
+ const pending = Promise.withResolvers<{ data: EntitiesMessageThread[] }>()
+ deferNextLoad(pending.promise)
+ const oldLoad = store.loadThreads()
+ store.unreadOnly = true
+ respondWith([thread('unread', 2)])
+ await store.loadThreads()
+
+ pending.resolve({ data: [thread('read', 0)] })
+ await oldLoad
+
+ assert.deepEqual(
+ store.threads.map((item) => item.id),
+ ['unread'],
+ )
+})
+
+test('All does not retain a conversation removed from the server list', async () => {
+ const { store, respondWith } = setupStore()
+ await store.loadThreads()
+ store.setThreadId('unread')
+ respondWith([])
+
+ await store.loadThreads()
+
+ assert.equal(store.currentThread, null)
+})
+
+for (const scenario of [
+ {
+ name: 'adding a contact refreshes a retained read conversation',
+ existing: undefined,
+ saved: contact('New contact'),
+ expectedName: 'New contact',
+ },
+ {
+ name: 'editing a contact refreshes a retained read conversation',
+ existing: contact('Old name'),
+ saved: contact('Updated name'),
+ expectedName: 'Updated name',
+ },
+ {
+ name: 'removing the conversation number clears its retained contact',
+ existing: contact('Old name'),
+ saved: contact('Updated name', ['+18005550101']),
+ expectedName: undefined,
+ },
+]) {
+ test(scenario.name, async () => {
+ const { store, respondWith } = setupStore()
+ respondWith([
+ { ...thread('unread', 1), contact_details: scenario.existing },
+ ])
+ await store.loadThreads()
+ store.setThreadId('unread')
+ store.unreadOnly = true
+ await store.resetThreadUnreadCount('unread')
+ respondWith([])
+ await store.loadThreads()
+
+ await store.loadThreads([scenario.saved])
+
+ assert.equal(store.currentThread?.id, 'unread')
+ assert.equal(
+ store.currentThread.contact_details?.name,
+ scenario.expectedName,
+ )
+ assert.deepEqual(store.visibleThreads, [])
+ })
+}
+
+test('a late poll cannot restore unread counts after mark-read succeeds', async () => {
+ const { store, deferNextLoad } = setupStore()
+ await store.loadThreads()
+ store.setThreadId('unread')
+ store.unreadOnly = true
+ const pending = Promise.withResolvers<{ data: EntitiesMessageThread[] }>()
+ deferNextLoad(pending.promise)
+ store.loadingThreads = true
+ const poll = store.loadThreads()
+
+ await store.resetThreadUnreadCount('unread')
+ pending.resolve({ data: [thread('unread', 2)] })
+ await poll
+
+ assert.equal(store.currentThread?.unread_count, 0)
+ assert.deepEqual(store.visibleThreads, [])
+ assert.equal(store.loadingThreads, false)
+})
+
+test('refreshing an overlapping read preserves messages received after the read', async () => {
+ const { store, deferNextLoad, respondWith } = setupStore()
+ await store.loadThreads()
+ store.setThreadId('unread')
+ store.unreadOnly = true
+ const pending = Promise.withResolvers<{ data: EntitiesMessageThread[] }>()
+ deferNextLoad(pending.promise)
+ const poll = store.loadThreads()
+
+ await store.resetThreadUnreadCount('unread')
+ respondWith([
+ {
+ ...thread('unread', 1),
+ last_message_id: 'new-inbound-message',
+ last_message_content: 'Received after mark-read',
+ },
+ ])
+ pending.resolve({ data: [thread('unread', 2)] })
+ await poll
+
+ assert.equal(store.currentThread?.unread_count, 1)
+ assert.equal(store.currentThread.last_message_id, 'new-inbound-message')
+ assert.equal(store.visibleThreads.length, 1)
+ assert.equal(store.loadingThreads, false)
+})
+
+test('Back navigation restores an earlier read conversation in Unread', async () => {
+ const { store, calls } = await setupReadConversationHistory()
+
+ store.setThreadId('unread')
+
+ assert.equal(store.currentThread?.id, 'unread')
+ assert.equal(store.currentThread.unread_count, 0)
+ await store.loadThreadMessages('unread')
+ assert.equal(calls.at(-1)?.params?.contact, '+18005550100')
+ assert.deepEqual(
+ store.visibleThreads.map((item) => item.id),
+ ['other'],
+ )
+})
+
+test('saved contacts refresh previously opened read conversations too', async () => {
+ const { store } = await setupReadConversationHistory()
+ await store.loadThreads([contact('Alice')])
+
+ store.setThreadId('unread')
+
+ assert.equal(store.currentThread?.contact_details?.name, 'Alice')
+})
+
+for (const action of ['reset', 'delete', 'archive', 'owner', 'view']) {
+ test(`navigation cache does not restore conversations after ${action}`, async () => {
+ const { store, phones, respondWith } = await setupReadConversationHistory()
+ if (action === 'reset') store.resetState()
+ if (action === 'delete') await store.deleteThread('unread')
+ if (action === 'archive') {
+ await store.updateThread({ threadId: 'unread', isArchived: true })
+ }
+ if (action === 'owner' || action === 'view') {
+ if (action === 'owner') phones.owner = '+18005550198'
+ if (action === 'view') store.archivedThreads = true
+ respondWith([])
+ await store.loadThreads()
+ }
+
+ store.setThreadId('unread')
+
+ assert.equal(store.currentThread, null)
+ assert.equal(store.hasThreadId('unread'), false)
+ })
+}
+
+function manyThreads(count: number) {
+ return Array.from({ length: count }, (_, index) => ({
+ ...thread(`page-${index}`, 1),
+ contact: `+1800555${String(index).padStart(4, '0')}`,
+ }))
+}
+
+for (const count of [0, 19, 20, 21]) {
+ test(`Load More is available when the last page is full (${count})`, async () => {
+ const { store, respondWith } = setupStore()
+ respondWith(manyThreads(count))
+
+ await store.loadThreads()
+
+ assert.equal(store.threads.length, Math.min(count, 20))
+ assert.equal(store.hasMoreThreads, count >= 20)
+ })
+}
+
+test('Load More expands the loaded list and disappears on the final page', async () => {
+ const { store, respondWith, calls } = setupStore()
+ respondWith(manyThreads(45))
+ await store.loadThreads()
+
+ await store.loadMoreThreads()
+ assert.equal(store.threads.length, 40)
+ assert.equal(store.hasMoreThreads, true)
+ await store.loadMoreThreads()
+ assert.equal(store.threads.length, 45)
+ assert.equal(new Set(store.threads.map((item) => item.id)).size, 45)
+ assert.equal(store.hasMoreThreads, false)
+ assert.equal(store.loadingMoreThreads, false)
+ const completedCalls = calls.length
+ await store.loadMoreThreads()
+ assert.equal(calls.length, completedCalls)
+})
+
+for (const count of [20, 40]) {
+ test(`an empty final page hides Load More without losing ${count} loaded threads`, async () => {
+ const { store, respondWith } = setupStore()
+ respondWith(manyThreads(count))
+ await store.loadThreads()
+ if (count === 40) await store.loadMoreThreads()
+ assert.equal(store.threads.length, count)
+ assert.equal(store.hasMoreThreads, true)
+
+ await store.loadMoreThreads()
+
+ assert.equal(store.threads.length, count)
+ assert.equal(store.hasMoreThreads, false)
+ assert.equal(store.loadingMoreThreads, false)
+ })
+}
+
+test('polling preserves loaded pages and changing filters resets them', async () => {
+ const { store, respondWith } = setupStore()
+ respondWith(manyThreads(45))
+ await store.loadThreads()
+ await store.loadMoreThreads()
+
+ await store.loadThreads()
+ assert.equal(store.threads.length, 40)
+
+ store.unreadOnly = true
+ await store.loadThreads()
+ assert.equal(store.threads.length, 20)
+ await store.loadMoreThreads()
+ store.archivedThreads = true
+ await store.loadThreads()
+ assert.equal(store.threads.length, 20)
+})
+
+test('switching phones resets loaded pages', async () => {
+ const { store, respondWith, phones } = setupStore()
+ respondWith(manyThreads(45))
+ await store.loadThreads()
+ await store.loadMoreThreads()
+
+ phones.owner = '+18005550198'
+ await store.loadThreads()
+
+ assert.equal(store.threads.length, 20)
+})
+
+test('Load More failure preserves rows and permits a retry', async () => {
+ const { store, respondWith, deferNextLoad } = setupStore()
+ respondWith(manyThreads(45))
+ await store.loadThreads()
+ const pending = Promise.withResolvers<{ data: EntitiesMessageThread[] }>()
+ deferNextLoad(pending.promise)
+
+ const loading = store.loadMoreThreads()
+ pending.reject(new Error('Network failed'))
+ await assert.rejects(loading, /Network failed/)
+
+ assert.equal(store.threads.length, 20)
+ assert.equal(store.hasMoreThreads, true)
+ assert.equal(store.loadingMoreThreads, false)
+ await store.loadMoreThreads()
+ assert.equal(store.threads.length, 40)
+})
+
+test('new activity does not close an open conversation pushed beyond the loaded page', async () => {
+ const { store, respondWith } = setupStore()
+ const existing = manyThreads(45)
+ respondWith(existing)
+ await store.loadThreads()
+ store.setThreadId('page-19')
+ respondWith([thread('newest', 1), ...existing])
+
+ await store.loadThreads()
+
+ assert.equal(store.currentThread?.id, 'page-19')
+ await store.loadThreadMessages('page-19')
+})
+
+test('a second Load More click is ignored while the first is running', async () => {
+ const { store, respondWith, deferNextLoad } = setupStore()
+ const existing = manyThreads(65)
+ respondWith(existing)
+ await store.loadThreads()
+ const pending = Promise.withResolvers<{ data: EntitiesMessageThread[] }>()
+ deferNextLoad(pending.promise)
+
+ const loading = store.loadMoreThreads()
+ assert.equal(store.loadingMoreThreads, true)
+ await store.loadMoreThreads()
+ pending.resolve({ data: existing.slice(0, 20) })
+ await loading
+
+ assert.equal(store.threads.length, 40)
+ assert.equal(store.loadingMoreThreads, false)
+})
+
+test('changing filters cancels an older Load More result', async () => {
+ const { store, respondWith, deferNextLoad } = setupStore()
+ const existing = manyThreads(45)
+ respondWith(existing)
+ await store.loadThreads()
+ const pending = Promise.withResolvers<{ data: EntitiesMessageThread[] }>()
+ deferNextLoad(pending.promise)
+
+ const loading = store.loadMoreThreads()
+ assert.equal(store.loadingMoreThreads, true)
+ store.unreadOnly = true
+ await store.loadThreads()
+ pending.resolve({ data: existing.slice(0, 20) })
+ await loading
+
+ assert.equal(store.threads.length, 20)
+ assert.equal(store.loadingMoreThreads, false)
+ await store.loadMoreThreads()
+ assert.equal(store.threads.length, 40)
+})
+
+test('a cold deep link resolves an older conversation independently of page one', async () => {
+ const { store, respondWith, respondToLookup, calls } = setupStore()
+ const rows = manyThreads(65)
+ const requested = {
+ ...thread('page-60', 0),
+ contact: '+18005550060',
+ contact_details: contact('Older contact', ['+18005550060']),
+ }
+ respondWith(rows)
+ respondToLookup(requested)
+ store.setThreadId(requested.id)
+
+ await store.fetchThread(requested.id)
+ await store.loadThreads()
+ store.setThreadId(requested.id)
+
+ assert.equal(store.threads.length, 20)
+ assert.equal(store.currentThread?.id, requested.id)
+ assert.equal(store.currentThread.contact_details?.name, 'Older contact')
+ await store.loadThreadMessages(requested.id)
+ assert.equal(calls.at(-1)?.params?.contact, requested.contact)
+ assert.equal(
+ calls.filter((call) => call.path === '/v1/message-threads').length,
+ 1,
+ )
+})
+
+test('individual lookup preserves a real 404 instead of searching every page', async () => {
+ const { store, calls } = setupStore()
+
+ await assert.rejects(store.fetchThread('missing'), { status: 404 })
+
+ assert.equal(calls.length, 1)
+ assert.equal(calls[0]?.path, '/v1/message-threads/missing')
+})
+
+test('a successfully fetched conversation remains selectable independently of list metadata', async () => {
+ const { store, respondWith, respondToLookup } = setupStore()
+ respondWith(manyThreads(19))
+ const requested = thread('directly-requested', 0)
+ respondToLookup(requested)
+ await store.loadThreads()
+ assert.equal(store.hasMoreThreads, false)
+
+ await store.fetchThread(requested.id)
+ store.setThreadId(requested.id)
+
+ assert.equal(store.currentThread?.id, requested.id)
+ assert.equal(store.threads.length, 19)
+})
+
+test('resetting the session cancels an older individual lookup', async () => {
+ const { store, deferLookup } = setupStore()
+ const pending = Promise.withResolvers<{ data: EntitiesMessageThread }>()
+ deferLookup(pending.promise)
+ const loading = store.fetchThread('old-session')
+
+ store.resetState()
+ pending.resolve({ data: thread('old-session', 0) })
+
+ await assert.rejects(loading, { name: 'AbortError' })
+ store.unreadOnly = true
+ store.setThreadId('old-session')
+ assert.equal(store.currentThread, null)
+})
diff --git a/web/app/stores/threads.ts b/web/app/stores/threads.ts
index 1abd68bea..77f2ccef0 100644
--- a/web/app/stores/threads.ts
+++ b/web/app/stores/threads.ts
@@ -1,76 +1,213 @@
import { defineStore } from 'pinia'
import type {
+ EntitiesContact,
EntitiesMessageThread,
EntitiesMessage,
} from '~~/shared/types/api'
+const PAGE_SIZE = 20
+
export const useThreadsStore = defineStore('threads', () => {
const threads = ref([])
const threadId = ref(null)
const loadingThreads = ref(true)
+ const loadingMoreThreads = ref(false)
+ const hasMoreThreads = ref(false)
+ let pageCount = 1
+ let pageContext = ''
+ let loadMoreGeneration = 0
const archivedThreads = ref(false)
+ const unreadOnly = ref(false)
+ const retainedThread = ref(null)
+ const openedThreads = ref(new Map())
+ const fetchedThreadIds = ref(new Set())
+ let threadRequestId = 0
+ let stateGeneration = 0
+ let readMutationGeneration = 0
const { apiFetch } = useApi()
const notificationsStore = useNotificationsStore()
const currentThread = computed(() => {
- return threads.value.find((x) => x.id === threadId.value) ?? null
+ return (
+ threads.value.find((x) => x.id === threadId.value) ??
+ ((unreadOnly.value ||
+ loadingThreads.value ||
+ hasMoreThreads.value ||
+ (threadId.value !== null &&
+ fetchedThreadIds.value.has(threadId.value))) &&
+ retainedThread.value?.id === threadId.value
+ ? retainedThread.value
+ : null)
+ )
})
+ const visibleThreads = computed(() =>
+ unreadOnly.value
+ ? threads.value.filter((thread) => thread.unread_count > 0)
+ : threads.value,
+ )
+
const hasThread = computed(
- () => threadId.value != null && !loadingThreads.value,
+ () => currentThread.value !== null && !loadingThreads.value,
)
function hasThreadId(id: string): boolean {
- return threads.value.find((x) => x.id === id) !== undefined
+ return (
+ threads.value.some((thread) => thread.id === id) ||
+ currentThread.value?.id === id
+ )
}
function replaceThread(updatedThread: EntitiesMessageThread) {
const index = threads.value.findIndex(
(thread) => thread.id === updatedThread.id,
)
+ const existingThread =
+ index !== -1 ? threads.value[index] : retainedThread.value
+ const replacement = {
+ ...updatedThread,
+ // Mark-read PUT responses are not contact-enriched.
+ contact_details:
+ updatedThread.contact_details ??
+ (updatedThread.contact === existingThread?.contact
+ ? existingThread.contact_details
+ : undefined),
+ }
if (index !== -1) {
- const existingThread = threads.value[index]!
- threads.value[index] = {
- ...updatedThread,
- // Mark-read PUT responses are not contact-enriched; keep the resolved
- // contact so display names do not disappear after the thread updates.
- contact_details:
- updatedThread.contact_details ??
- (updatedThread.contact === existingThread.contact
- ? existingThread.contact_details
- : undefined),
- }
+ threads.value[index] = replacement
+ }
+ if (updatedThread.id === threadId.value) {
+ retainedThread.value = replacement
+ }
+ if (
+ openedThreads.value.has(updatedThread.id) ||
+ updatedThread.id === threadId.value
+ ) {
+ openedThreads.value.set(updatedThread.id, replacement)
}
}
- async function loadThreads() {
+ async function loadThreads(savedContacts: EntitiesContact[] = []) {
+ const requestId = ++threadRequestId
+ const readGeneration = readMutationGeneration
+ const selectedThread = currentThread.value
+ if (selectedThread) {
+ openedThreads.value.set(selectedThread.id, selectedThread)
+ }
+ for (const openedThread of openedThreads.value.values()) {
+ for (const contact of savedContacts) {
+ const matchesNumber = contact.phone_numbers.includes(
+ openedThread.contact,
+ )
+ if (matchesNumber || openedThread.contact_details?.id === contact.id) {
+ openedThread.contact_details = matchesNumber ? contact : undefined
+ }
+ }
+ }
const phonesStore = usePhonesStore()
if (phonesStore.owner === null && phonesStore.phones.length === 0) {
loadingThreads.value = false
+ hasMoreThreads.value = false
return
}
- const response = await apiFetch<{ data: EntitiesMessageThread[] }>(
- '/v1/message-threads',
- {
- params: {
- owner: phonesStore.owner ?? phonesStore.phones[0]?.phone_number,
- limit: 100,
- is_archived: archivedThreads.value,
- contacts: true,
- },
- },
+ const owner = phonesStore.owner ?? phonesStore.phones[0]?.phone_number
+ const archived = archivedThreads.value
+ const unread = unreadOnly.value
+ const context = JSON.stringify([owner, archived, unread])
+ if (context !== pageContext) {
+ pageContext = context
+ pageCount = 1
+ hasMoreThreads.value = false
+ loadingMoreThreads.value = false
+ loadMoreGeneration++
+ }
+
+ try {
+ const responses = await Promise.all(
+ Array.from({ length: pageCount }, (_, page) =>
+ apiFetch<{ data: EntitiesMessageThread[] }>('/v1/message-threads', {
+ params: {
+ owner,
+ limit: PAGE_SIZE,
+ skip: page * PAGE_SIZE,
+ is_archived: archived,
+ unread_only: unread,
+ contacts: true,
+ },
+ }),
+ ),
+ )
+ if (requestId !== threadRequestId) return
+ if (readGeneration !== readMutationGeneration) {
+ await loadThreads()
+ return
+ }
+
+ phonesStore.getHeartbeat().catch(console.error)
+ retainedThread.value = currentThread.value
+ const combined = new Map()
+ let hasMore = false
+ for (const response of responses) {
+ hasMore = response.data.length === PAGE_SIZE
+ for (const thread of response.data) {
+ if (!combined.has(thread.id)) combined.set(thread.id, thread)
+ }
+ }
+ threads.value = [...combined.values()]
+ hasMoreThreads.value = hasMore
+ for (const thread of threads.value) {
+ if (openedThreads.value.has(thread.id)) {
+ openedThreads.value.set(thread.id, thread)
+ }
+ }
+ } finally {
+ if (requestId === threadRequestId) {
+ loadingThreads.value = false
+ }
+ }
+ }
+
+ async function loadMoreThreads() {
+ if (
+ loadingThreads.value ||
+ loadingMoreThreads.value ||
+ !hasMoreThreads.value
)
+ return
- phonesStore.getHeartbeat().catch(console.error)
- threads.value = [...response.data]
- loadingThreads.value = false
+ const generation = ++loadMoreGeneration
+ const previousPageCount = pageCount
+ pageCount++
+ loadingMoreThreads.value = true
+ try {
+ await loadThreads()
+ } catch (error) {
+ if (generation !== loadMoreGeneration) return
+ pageCount = previousPageCount
+ throw error
+ } finally {
+ if (generation === loadMoreGeneration) loadingMoreThreads.value = false
+ }
+ }
+
+ async function fetchThread(id: string): Promise {
+ const generation = stateGeneration
+ const response = await apiFetch<{ data: EntitiesMessageThread }>(
+ `/v1/message-threads/${id}`,
+ )
+ if (generation !== stateGeneration) {
+ throw new DOMException('thread fetch was canceled', 'AbortError')
+ }
+ openedThreads.value.set(response.data.id, response.data)
+ fetchedThreadIds.value.add(response.data.id)
+ return response.data
}
async function loadThreadMessages(
id: string | null,
): Promise {
- threadId.value = id
+ setThreadId(id)
const thread = currentThread.value
if (!thread) throw new Error(`Cannot find thread with id ${id}`)
@@ -88,6 +225,29 @@ export const useThreadsStore = defineStore('threads', () => {
}
function setThreadId(id: string | null) {
+ const previousThread = currentThread.value
+ if (previousThread) {
+ openedThreads.value.set(previousThread.id, previousThread)
+ }
+ const cached = id === null ? undefined : openedThreads.value.get(id)
+ const phonesStore = usePhonesStore()
+ const owner = phonesStore.owner ?? phonesStore.phones[0]?.phone_number
+ retainedThread.value =
+ id === null
+ ? null
+ : (threads.value.find((thread) => thread.id === id) ??
+ ((unreadOnly.value ||
+ loadingThreads.value ||
+ hasMoreThreads.value ||
+ fetchedThreadIds.value.has(id)) &&
+ cached &&
+ cached.owner === owner &&
+ cached.is_archived === archivedThreads.value
+ ? cached
+ : null))
+ if (retainedThread.value) {
+ openedThreads.value.set(retainedThread.value.id, retainedThread.value)
+ }
threadId.value = id
}
@@ -106,7 +266,9 @@ export const useThreadsStore = defineStore('threads', () => {
threads.value = threads.value.filter(
(thread) => thread.id !== payload.threadId,
)
- threadId.value = null
+ setThreadId(null)
+ openedThreads.value.delete(payload.threadId)
+ fetchedThreadIds.value.delete(payload.threadId)
notificationsStore.addNotification({
message: payload.isArchived ? 'Archived' : 'Unarchived',
type: 'success',
@@ -114,7 +276,9 @@ export const useThreadsStore = defineStore('threads', () => {
}
async function resetThreadUnreadCount(threadId: string, force = false) {
- const thread = threads.value.find((item) => item.id === threadId)
+ const thread =
+ threads.value.find((item) => item.id === threadId) ??
+ (retainedThread.value?.id === threadId ? retainedThread.value : null)
if (!thread) throw new Error(`Cannot find thread with id ${threadId}`)
if (!force && thread.unread_count === 0) return
@@ -126,6 +290,7 @@ export const useThreadsStore = defineStore('threads', () => {
body: { unread_count: 0 },
},
)
+ readMutationGeneration++
replaceThread(response.data)
} catch (error) {
notificationsStore.addNotification({
@@ -147,7 +312,9 @@ export const useThreadsStore = defineStore('threads', () => {
async function deleteThread(id: string) {
await apiFetch(`/v1/message-threads/${id}`, { method: 'DELETE' })
- threadId.value = null
+ setThreadId(null)
+ openedThreads.value.delete(id)
+ fetchedThreadIds.value.delete(id)
notificationsStore.addNotification({
message: 'The message thread has been deleted successfully',
type: 'success',
@@ -155,9 +322,21 @@ export const useThreadsStore = defineStore('threads', () => {
}
function resetState() {
+ threadRequestId++
+ stateGeneration++
+ readMutationGeneration = 0
+ loadMoreGeneration++
+ pageCount = 1
+ pageContext = ''
+ hasMoreThreads.value = false
+ loadingMoreThreads.value = false
threads.value = []
threadId.value = null
+ retainedThread.value = null
+ openedThreads.value.clear()
+ fetchedThreadIds.value.clear()
archivedThreads.value = false
+ unreadOnly.value = false
loadingThreads.value = true
}
@@ -165,11 +344,17 @@ export const useThreadsStore = defineStore('threads', () => {
threads,
threadId,
loadingThreads,
+ loadingMoreThreads,
+ hasMoreThreads,
archivedThreads,
+ unreadOnly,
+ visibleThreads,
currentThread,
hasThread,
hasThreadId,
loadThreads,
+ loadMoreThreads,
+ fetchThread,
loadThreadMessages,
setThreadId,
toggleArchive,
diff --git a/web/app/utils/bag.ts b/web/app/utils/bag.ts
index 73c46dd13..ce977fbe3 100644
--- a/web/app/utils/bag.ts
+++ b/web/app/utils/bag.ts
@@ -11,8 +11,8 @@ export default class Bag {
static fromObject(items: Record>): Bag {
const result = new Bag()
- Object.keys(items).forEach((key) => {
- result.addMany(key, items[key])
+ Object.entries(items).forEach(([key, values]) => {
+ result.addMany(key, values)
})
return result
}
diff --git a/web/app/utils/errors.ts b/web/app/utils/errors.ts
index b72bfbf38..38bc5f942 100644
--- a/web/app/utils/errors.ts
+++ b/web/app/utils/errors.ts
@@ -39,8 +39,8 @@ export const getErrorMessages = (error: AxiosLikeError): ErrorMessages => {
return errors
}
- Object.keys(error.response.data.data).forEach((key: string) => {
- errors.addMany(key, sanitize(key, error.response!.data!.data![key]))
+ Object.entries(error.response.data.data).forEach(([key, values]) => {
+ errors.addMany(key, sanitize(key, values))
})
return errors
diff --git a/web/nuxt.config.ts b/web/nuxt.config.ts
index c574ce308..87bcc2ace 100644
--- a/web/nuxt.config.ts
+++ b/web/nuxt.config.ts
@@ -20,7 +20,7 @@ export default defineNuxtConfig({
download: true,
},
- css: ['vuetify/styles'],
+ css: ['vuetify/styles', 'v-phone-input/styles'],
site: {
url: process.env.APP_URL || 'https://httpsms.com',
diff --git a/web/package.json b/web/package.json
index 9d88845c8..994deaa06 100644
--- a/web/package.json
+++ b/web/package.json
@@ -62,6 +62,7 @@
"stylelint-config-standard": "^40.0.0",
"tsx": "^4.23.15",
"typescript": "^6.0.3",
+ "vue-tsc": "^3.3.13",
"vuetify-nuxt-module": "^0.19.5"
}
}
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index 729f94881..8dac5d1fa 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -16,7 +16,7 @@ importers:
version: 3.2.0(magicast@0.5.4)
'@pinia/nuxt':
specifier: ^0.11.3
- version: 0.11.3(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ version: 0.11.3(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
chart.js:
specifier: ^4.5.1
version: 4.5.1
@@ -43,7 +43,7 @@ importers:
version: 2.31.0
nuxt:
specifier: ^4.5.1
- version: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0)
+ version: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0)
pinia:
specifier: ^3.0.4
version: 3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3))
@@ -70,7 +70,7 @@ importers:
version: 5.3.4(chart.js@4.5.1)(vue@3.5.41(typescript@6.0.3))
vue-router:
specifier: ^5.0.7
- version: 5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ version: 5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.25.12)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
vuetify:
specifier: ^4.0.7
version: 4.1.8(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.41(typescript@6.0.3))
@@ -83,19 +83,19 @@ importers:
version: 21.2.0
'@nuxt/eslint':
specifier: ^1.16.0
- version: 1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ version: 1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(srvx@0.11.22)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
'@nuxtjs/seo':
specifier: ^5.3.2
- version: 5.3.10(3b3462a6837e0c9bc3f7a989bd53696e)
+ version: 5.3.10(544d347ad5be336d527eb113013fd0f1)
'@types/qrcode':
specifier: ^1.5.6
version: 1.5.6
eslint:
specifier: ^10.5.0
- version: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ version: 10.8.0(jiti@2.7.0)
eslint-config-prettier:
specifier: ^10.1.8
- version: 10.1.8(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ version: 10.1.8(eslint@10.8.0(jiti@2.7.0))
husky:
specifier: ^9.1.7
version: 9.1.7
@@ -107,22 +107,25 @@ importers:
version: 1.8.1
stylelint:
specifier: ^17.15.0
- version: 17.15.0(supports-color@10.2.2)(typescript@6.0.3)
+ version: 17.15.0(typescript@6.0.3)
stylelint-config-recommended-vue:
specifier: ^1.6.1
- version: 1.6.1(postcss-html@1.8.1)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))
+ version: 1.6.1(postcss-html@1.8.1)(stylelint@17.15.0(typescript@6.0.3))
stylelint-config-standard:
specifier: ^40.0.0
- version: 40.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))
+ version: 40.0.0(stylelint@17.15.0(typescript@6.0.3))
tsx:
specifier: ^4.23.15
version: 4.23.15
typescript:
specifier: ^6.0.3
version: 6.0.3
+ vue-tsc:
+ specifier: ^3.3.13
+ version: 3.3.13(typescript@6.0.3)
vuetify-nuxt-module:
specifier: ^0.19.5
- version: 0.19.5(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(supports-color@10.2.2)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ version: 0.19.5(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
packages:
@@ -2384,6 +2387,15 @@ packages:
vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
vue: ^3.2.25
+ '@volar/language-core@2.4.28':
+ resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==}
+
+ '@volar/source-map@2.4.28':
+ resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==}
+
+ '@volar/typescript@2.4.28':
+ resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==}
+
'@vue-macros/common@3.1.4':
resolution: {integrity: sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==}
engines: {node: '>=20.19.0'}
@@ -2444,6 +2456,9 @@ packages:
'@vue/devtools-shared@8.2.1':
resolution: {integrity: sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==}
+ '@vue/language-core@3.3.13':
+ resolution: {integrity: sha512-axA0Xemn5LuUKuKWob0/kVq7xQ2mJJM6VqA9Exvt35nM9yRC31goWVve/G/Zb2ZCLsXC8FHYjjfXlnshknSCew==}
+
'@vue/reactivity@3.5.41':
resolution: {integrity: sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==}
@@ -2544,6 +2559,9 @@ packages:
ajv@8.20.0:
resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
+ alien-signals@3.2.1:
+ resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==}
+
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -4524,6 +4542,9 @@ packages:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -5708,6 +5729,9 @@ packages:
yaml:
optional: true
+ vscode-uri@3.2.0:
+ resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==}
+
vue-bundle-renderer@2.3.1:
resolution: {integrity: sha512-7F4LNMopUw5RgYWo4zCmVUHCc6aQRC6dCKHUYkM/n+fux4AUGdL1x6m5A515WWyFysRRN7cx3hBzVqoisfRfzw==}
@@ -5747,6 +5771,12 @@ packages:
vite:
optional: true
+ vue-tsc@3.3.13:
+ resolution: {integrity: sha512-jE4/bXdOMSHVxEGgQCqNllJy+9N3Gx6tfSAsg0yhFK/MAN6eXqISihupnDEKSCt12VVnjN2gI3AqLYcqoh5x/A==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
vue@3.5.41:
resolution: {integrity: sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==}
peerDependencies:
@@ -5962,20 +5992,20 @@ snapshots:
'@babel/compat-data@7.29.7': {}
- '@babel/core@7.29.7(supports-color@10.2.2)':
+ '@babel/core@7.29.7':
dependencies:
'@babel/code-frame': 7.29.7
'@babel/generator': 7.29.8
'@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)
+ '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
'@babel/helpers': 7.29.7
'@babel/parser': 7.29.8
'@babel/template': 7.29.7
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/traverse': 7.29.8
'@babel/types': 7.29.8
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -6011,41 +6041,41 @@ snapshots:
lru-cache: 5.1.1
semver: 6.3.1
- '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)':
+ '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)':
dependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
'@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2)
+ '@babel/helper-member-expression-to-functions': 7.29.7
'@babel/helper-optimise-call-expression': 7.29.7
- '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2)
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
+ '@babel/traverse': 7.29.8
semver: 6.3.1
transitivePeerDependencies:
- supports-color
'@babel/helper-globals@7.29.7': {}
- '@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.2.2)':
+ '@babel/helper-member-expression-to-functions@7.29.7':
dependencies:
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/traverse': 7.29.8
'@babel/types': 7.29.8
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)':
+ '@babel/helper-module-imports@7.29.7':
dependencies:
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/traverse': 7.29.8
'@babel/types': 7.29.8
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)':
+ '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
dependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
- '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
+ '@babel/helper-module-imports': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/traverse': 7.29.8
transitivePeerDependencies:
- supports-color
@@ -6055,18 +6085,18 @@ snapshots:
'@babel/helper-plugin-utils@7.29.7': {}
- '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)':
+ '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)':
dependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
- '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
+ '@babel/helper-member-expression-to-functions': 7.29.7
'@babel/helper-optimise-call-expression': 7.29.7
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/traverse': 7.29.8
transitivePeerDependencies:
- supports-color
- '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.2.2)':
+ '@babel/helper-skip-transparent-expression-wrappers@7.29.7':
dependencies:
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/traverse': 7.29.8
'@babel/types': 7.29.8
transitivePeerDependencies:
- supports-color
@@ -6094,24 +6124,24 @@ snapshots:
dependencies:
'@babel/types': 8.0.4
- '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))':
+ '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)':
dependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))':
+ '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)':
dependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)':
+ '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)':
dependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
'@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)
+ '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
'@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2)
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))
+ '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
+ '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
transitivePeerDependencies:
- supports-color
@@ -6121,7 +6151,7 @@ snapshots:
'@babel/parser': 7.29.8
'@babel/types': 7.29.8
- '@babel/traverse@7.29.8(supports-color@10.2.2)':
+ '@babel/traverse@7.29.8':
dependencies:
'@babel/code-frame': 7.29.7
'@babel/generator': 7.29.8
@@ -6129,7 +6159,7 @@ snapshots:
'@babel/parser': 7.29.8
'@babel/template': 7.29.7
'@babel/types': 7.29.8
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -6323,17 +6353,17 @@ snapshots:
dependencies:
postcss-selector-parser: 7.1.5
- '@dxup/nuxt@0.5.6(esbuild@0.28.1)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
+ '@dxup/nuxt@0.5.6(esbuild@0.25.12)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
dependencies:
'@dxup/unimport': 0.1.2
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
'@vue/compiler-dom': 3.5.41
chokidar: 5.0.0
knitwork: 1.3.0
magic-string: 1.1.0
pathe: 2.0.3
tinyglobby: 0.2.17
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
transitivePeerDependencies:
- '@farmfe/core'
- '@rspack/core'
@@ -6552,23 +6582,23 @@ snapshots:
'@esbuild/win32-x64@0.28.1':
optional: true
- '@eslint-community/eslint-utils@4.10.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@eslint-community/eslint-utils@4.10.1(eslint@10.8.0(jiti@2.7.0))':
dependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
- '@eslint/compat@2.1.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@eslint/compat@2.1.0(eslint@10.8.0(jiti@2.7.0))':
dependencies:
'@eslint/core': 1.2.1
optionalDependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
- '@eslint/config-array@0.23.5(supports-color@10.2.2)':
+ '@eslint/config-array@0.23.5':
dependencies:
'@eslint/object-schema': 3.0.5
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
minimatch: 10.2.6
transitivePeerDependencies:
- supports-color
@@ -6581,13 +6611,13 @@ snapshots:
dependencies:
'@eslint/core': 1.2.1
- '@eslint/config-inspector@3.2.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(srvx@0.11.22)':
+ '@eslint/config-inspector@3.2.0(eslint@10.8.0(jiti@2.7.0))(srvx@0.11.22)':
dependencies:
ansis: 4.3.1
cac: 7.0.0
chokidar: 5.0.0
devframe: 0.8.2(cac@7.0.0)(srvx@0.11.22)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
jiti: 2.7.0
tinyglobby: 0.2.17
transitivePeerDependencies:
@@ -6604,9 +6634,9 @@ snapshots:
mdn-data: 2.29.0
source-map-js: 1.2.1
- '@eslint/js@10.0.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@eslint/js@10.0.1(eslint@10.8.0(jiti@2.7.0))':
optionalDependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
'@eslint/object-schema@3.0.5': {}
@@ -7119,19 +7149,19 @@ snapshots:
'@kurkle/color@0.3.4': {}
- '@kwsites/file-exists@1.1.1(supports-color@10.2.2)':
+ '@kwsites/file-exists@1.1.1':
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
transitivePeerDependencies:
- supports-color
'@kwsites/promise-deferred@1.1.1': {}
- '@mapbox/node-pre-gyp@2.0.3(supports-color@10.2.2)':
+ '@mapbox/node-pre-gyp@2.0.3':
dependencies:
consola: 3.4.2
detect-libc: 2.1.2
- https-proxy-agent: 7.0.6(supports-color@10.2.2)
+ https-proxy-agent: 7.0.6
node-fetch: 2.7.0
nopt: 8.1.0
semver: 7.8.5
@@ -7173,7 +7203,7 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
- '@nuxt/cli@3.37.0(@nuxt/schema@4.5.2)(@parcel/watcher@2.6.0)(cac@7.0.0)(magicast@0.5.4)(supports-color@10.2.2)':
+ '@nuxt/cli@3.37.0(@nuxt/schema@4.5.2)(@parcel/watcher@2.6.0)(cac@7.0.0)(magicast@0.5.4)':
dependencies:
'@bomb.sh/tab': 0.0.19(cac@7.0.0)(citty@0.2.2)
'@clack/prompts': 1.7.0
@@ -7181,7 +7211,7 @@ snapshots:
citty: 0.2.2
confbox: 0.2.4
consola: 3.4.2
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
defu: 6.1.7
exsolve: 1.1.1
fuse.js: 7.5.0
@@ -7214,11 +7244,11 @@ snapshots:
'@nuxt/devalue@2.0.2': {}
- '@nuxt/devtools-kit@3.4.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
+ '@nuxt/devtools-kit@3.4.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
execa: 8.0.1
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
transitivePeerDependencies:
- magic-string
- magicast
@@ -7226,11 +7256,11 @@ snapshots:
- rolldown
- unplugin
- '@nuxt/devtools-kit@4.0.0-alpha.7(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
+ '@nuxt/devtools-kit@4.0.0-alpha.7(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
tinyexec: 1.3.0
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
transitivePeerDependencies:
- magic-string
- magicast
@@ -7249,11 +7279,11 @@ snapshots:
pkg-types: 2.3.1
semver: 7.8.5
- '@nuxt/devtools@3.4.1(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
+ '@nuxt/devtools@3.4.1(db0@0.3.4)(ioredis@5.11.1)(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
dependencies:
- '@nuxt/devtools-kit': 3.4.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ '@nuxt/devtools-kit': 3.4.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
'@nuxt/devtools-wizard': 3.4.1
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
'@vue/devtools-core': 8.2.1(vue@3.5.41(typescript@6.0.3))
'@vue/devtools-kit': 8.2.1
birpc: 4.0.0
@@ -7275,14 +7305,14 @@ snapshots:
perfect-debounce: 2.1.0
pkg-types: 2.3.1
semver: 7.8.5
- simple-git: 3.36.0(supports-color@10.2.2)
+ simple-git: 3.36.0
sirv: 3.0.2
structured-clone-es: 2.0.1
tinyglobby: 0.2.17
- unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
- vite-plugin-inspect: 11.4.1(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- vite-plugin-vue-tracer: 1.4.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite-plugin-inspect: 11.4.1(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ vite-plugin-vue-tracer: 1.4.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
which: 6.0.1
ws: 8.21.2
transitivePeerDependencies:
@@ -7314,30 +7344,30 @@ snapshots:
- utf-8-validate
- vue
- '@nuxt/eslint-config@1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@nuxt/eslint-config@1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@antfu/install-pkg': 2.0.1
'@clack/prompts': 1.7.0
- '@eslint/js': 10.0.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- '@nuxt/eslint-plugin': 1.17.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@stylistic/eslint-plugin': 5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
- eslint-config-flat-gitignore: 2.3.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint/js': 10.0.1(eslint@10.8.0(jiti@2.7.0))
+ '@nuxt/eslint-plugin': 1.17.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ '@stylistic/eslint-plugin': 5.10.0(eslint@10.8.0(jiti@2.7.0))
+ '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ eslint: 10.8.0(jiti@2.7.0)
+ eslint-config-flat-gitignore: 2.3.0(eslint@10.8.0(jiti@2.7.0))
eslint-flat-config-utils: 3.2.0
- eslint-merge-processors: 2.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-import-lite: 0.6.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
- eslint-plugin-jsdoc: 63.3.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
- eslint-plugin-regexp: 3.1.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-unicorn: 73.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-vue: 10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2))
- eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint-merge-processors: 2.0.0(eslint@10.8.0(jiti@2.7.0))
+ eslint-plugin-import-lite: 0.6.0(eslint@10.8.0(jiti@2.7.0))
+ eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0))
+ eslint-plugin-jsdoc: 63.3.3(eslint@10.8.0(jiti@2.7.0))
+ eslint-plugin-regexp: 3.1.1(eslint@10.8.0(jiti@2.7.0))
+ eslint-plugin-unicorn: 73.0.0(eslint@10.8.0(jiti@2.7.0))
+ eslint-plugin-vue: 10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.8.0(jiti@2.7.0)))(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.8.0(jiti@2.7.0)))
+ eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0))
globals: 17.9.0
local-pkg: 1.2.1
pathe: 2.0.3
- vue-eslint-parser: 10.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
+ vue-eslint-parser: 10.4.1(eslint@10.8.0(jiti@2.7.0))
transitivePeerDependencies:
- '@typescript-eslint/utils'
- '@vue/compiler-sfc'
@@ -7345,31 +7375,31 @@ snapshots:
- supports-color
- typescript
- '@nuxt/eslint-plugin@1.17.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@nuxt/eslint-plugin@1.17.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@typescript-eslint/types': 8.66.0
- '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ eslint: 10.8.0(jiti@2.7.0)
transitivePeerDependencies:
- supports-color
- typescript
- '@nuxt/eslint@1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
+ '@nuxt/eslint@1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(srvx@0.11.22)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
dependencies:
- '@eslint/config-inspector': 3.2.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(srvx@0.11.22)
- '@nuxt/devtools-kit': 3.4.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- '@nuxt/eslint-config': 1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@nuxt/eslint-plugin': 1.17.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@eslint/config-inspector': 3.2.0(eslint@10.8.0(jiti@2.7.0))(srvx@0.11.22)
+ '@nuxt/devtools-kit': 3.4.1(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ '@nuxt/eslint-config': 1.17.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ '@nuxt/eslint-plugin': 1.17.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
chokidar: 5.0.0
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
eslint-flat-config-utils: 3.2.0
- eslint-typegen: 2.3.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ eslint-typegen: 2.3.1(eslint@10.8.0(jiti@2.7.0))
find-up: 8.0.0
get-port-please: 3.2.0
mlly: 1.8.2
pathe: 2.0.3
- unimport: 6.4.0(esbuild@0.28.1)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unimport: 6.4.0(esbuild@0.25.12)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
transitivePeerDependencies:
- '@farmfe/core'
- '@modelcontextprotocol/client'
@@ -7420,7 +7450,7 @@ snapshots:
transitivePeerDependencies:
- magicast
- '@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))':
+ '@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))':
dependencies:
c12: 3.3.4(magicast@0.5.4)
consola: 3.4.2
@@ -7440,7 +7470,7 @@ snapshots:
scule: 1.3.0
tinyglobby: 0.2.17
ufo: 1.6.4
- unctx: 3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ unctx: 3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
untyped: 2.0.0
verkit: 0.3.2
transitivePeerDependencies:
@@ -7450,11 +7480,11 @@ snapshots:
- rolldown
- unplugin
- '@nuxt/nitro-server@4.5.2(fe9885ce02309805246d9295eb36b44b)':
+ '@nuxt/nitro-server@4.5.2(b9bd6ba5516dc543dbc917b33a0d10ad)':
dependencies:
'@nuxt/devalue': 2.0.2
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
- '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@vue/shared': 3.5.41
consola: 3.4.2
defu: 6.1.7
@@ -7464,25 +7494,25 @@ snapshots:
escape-string-regexp: 5.0.0
exsolve: 1.1.1
h3: 1.15.11
- impound: 1.1.6(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ impound: 1.1.6(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
klona: 2.0.6
mocked-exports: 0.1.1
- nitropack: 2.13.4(@parcel/watcher@2.6.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ nitropack: 2.13.4(@parcel/watcher@2.6.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(srvx@0.11.22)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
nostics: 1.2.0
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0)
nypm: 0.6.9
ohash: 2.0.11
pathe: 2.0.3
rou3: 0.9.1
std-env: 4.2.0
ufo: 1.6.4
- unctx: 3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
- unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unctx: 3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
vue: 3.5.41(typescript@6.0.3)
vue-bundle-renderer: 2.3.1
vue-devtools-stub: 0.1.0
optionalDependencies:
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))
+ '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -7545,20 +7575,20 @@ snapshots:
pkg-types: 2.3.1
std-env: 4.2.0
- '@nuxt/telemetry@2.8.0(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))':
+ '@nuxt/telemetry@2.8.0(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))':
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
citty: 0.2.2
consola: 3.4.2
ofetch: 2.0.0-alpha.3
rc9: 3.0.1
std-env: 4.2.0
- '@nuxt/vite-builder@4.5.2(bbb59840355872275ede4355b56b34bc)':
+ '@nuxt/vite-builder@4.5.2(cbd7f0b9e291f24836832fe380a0c379)':
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
- '@vitejs/plugin-vue': 6.0.8(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
- '@vitejs/plugin-vue-jsx': 5.1.6(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@vitejs/plugin-vue': 6.0.8(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@vitejs/plugin-vue-jsx': 5.1.6(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
autoprefixer: 10.5.4(postcss@8.5.26)
consola: 3.4.2
cssnano: 8.0.4(postcss@8.5.26)
@@ -7572,7 +7602,7 @@ snapshots:
knitwork: 1.3.0
mlly: 1.8.2
mocked-exports: 0.1.1
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0)
nypm: 0.6.9
pathe: 2.0.3
pkg-types: 2.3.1
@@ -7582,23 +7612,19 @@ snapshots:
std-env: 4.2.0
ufo: 1.6.4
unenv: 2.0.0-rc.24
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
- vite-node: 6.0.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
- vite-plugin-checker: 0.14.5(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(meow@14.1.0)(optionator@0.9.4)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite-node: 6.0.0(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite-plugin-checker: 0.14.5(eslint@10.8.0(jiti@2.7.0))(meow@14.1.0)(optionator@0.9.4)(stylelint@17.15.0(typescript@6.0.3))(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))
vue: 3.5.41(typescript@6.0.3)
vue-bundle-renderer: 2.3.1
optionalDependencies:
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))
+ '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
rolldown: 1.2.14
rollup-plugin-visualizer: 7.0.1(rolldown@1.2.14)(rollup@4.62.4)
transitivePeerDependencies:
- '@biomejs/biome'
- - '@farmfe/core'
- - '@rspack/core'
- '@types/node'
- '@vitejs/devtools'
- - bun-types-no-globals
- esbuild
- eslint
- less
@@ -7608,7 +7634,6 @@ snapshots:
- optionator
- oxc-parser
- oxlint
- - rollup
- sass
- sass-embedded
- stylelint
@@ -7618,9 +7643,8 @@ snapshots:
- terser
- tsx
- typescript
- - unloader
+ - unplugin
- vue-tsc
- - webpack
- yaml
'@nuxtjs/google-fonts@3.2.0(magicast@0.5.4)':
@@ -7631,15 +7655,15 @@ snapshots:
transitivePeerDependencies:
- magicast
- '@nuxtjs/robots@6.1.3(7d8087514e1614c3470bdbe0b86f0686)':
+ '@nuxtjs/robots@6.1.3(0655b6d95986fa09e450619a2471d7fb)':
dependencies:
'@fingerprintjs/botd': 2.0.0
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
h3: 1.15.11
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
pathe: 2.0.3
pkg-types: 2.3.1
ufo: 1.6.4
@@ -7654,18 +7678,18 @@ snapshots:
- vite
- vue
- '@nuxtjs/seo@5.3.10(3b3462a6837e0c9bc3f7a989bd53696e)':
- dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
- '@nuxtjs/robots': 6.1.3(7d8087514e1614c3470bdbe0b86f0686)
- '@nuxtjs/sitemap': 8.3.3(7d8087514e1614c3470bdbe0b86f0686)
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0)
- nuxt-link-checker: 5.1.3(2afc54efe4333e7b21be164e8edbf89c)
- nuxt-og-image: 6.7.6(c5b09079d96a60970e5bd704c57de0fb)
- nuxt-schema-org: 6.2.8(5a5c97359c4cfa887a5b2273eea7c903)
- nuxt-seo-utils: 8.3.3(58a2c7fe40baae257b6ed398e9cddc7f)
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ '@nuxtjs/seo@5.3.10(544d347ad5be336d527eb113013fd0f1)':
+ dependencies:
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxtjs/robots': 6.1.3(0655b6d95986fa09e450619a2471d7fb)
+ '@nuxtjs/sitemap': 8.3.3(0655b6d95986fa09e450619a2471d7fb)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0)
+ nuxt-link-checker: 5.1.3(d5df5b90bf7ae7ca065098da8ab00f11)
+ nuxt-og-image: 6.7.6(1260c5194fdf2814eb31b9771c8bd346)
+ nuxt-schema-org: 6.2.8(765524ba5b9822e3426864f203b74fb3)
+ nuxt-seo-utils: 8.3.3(128dc6963dfcd1b0c746c3148ce2ad1c)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -7724,13 +7748,13 @@ snapshots:
- webpack
- zod
- '@nuxtjs/sitemap@8.3.3(7d8087514e1614c3470bdbe0b86f0686)':
+ '@nuxtjs/sitemap@8.3.3(0655b6d95986fa09e450619a2471d7fb)':
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
ofetch: 1.5.1
pathe: 2.0.3
pkg-types: 2.3.1
@@ -7850,7 +7874,7 @@ snapshots:
'@parcel/watcher-wasm@2.6.0':
dependencies:
is-glob: 4.0.3
- picomatch: 4.0.5
+ picomatch: 4.0.7
'@parcel/watcher-win32-arm64@2.6.0':
optional: true
@@ -7879,9 +7903,9 @@ snapshots:
'@parcel/watcher-win32-x64': 2.6.0
optional: true
- '@pinia/nuxt@0.11.3(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))':
+ '@pinia/nuxt@0.11.3(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))':
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
pinia: 3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3))
transitivePeerDependencies:
- magic-string
@@ -7983,10 +8007,10 @@ snapshots:
'@rollup/pluginutils': 5.4.0(rollup@4.62.4)
commondir: 1.0.1
estree-walker: 2.0.2
- fdir: 6.5.0(picomatch@4.0.5)
+ fdir: 6.5.0(picomatch@4.0.7)
is-reference: 1.2.1
magic-string: 0.30.21
- picomatch: 4.0.5
+ picomatch: 4.0.7
optionalDependencies:
rollup: 4.62.4
@@ -8033,7 +8057,7 @@ snapshots:
dependencies:
'@types/estree': 1.0.9
estree-walker: 2.0.2
- picomatch: 4.0.5
+ picomatch: 4.0.7
optionalDependencies:
rollup: 4.62.4
@@ -8134,15 +8158,15 @@ snapshots:
'@standard-schema/spec@1.1.0': {}
- '@stylistic/eslint-plugin@5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))':
+ '@stylistic/eslint-plugin@5.10.0(eslint@10.8.0(jiti@2.7.0))':
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0))
'@typescript-eslint/types': 8.66.0
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
eslint-visitor-keys: 4.2.1
espree: 10.4.0
estraverse: 5.3.0
- picomatch: 4.0.5
+ picomatch: 4.0.7
'@tybys/wasm-util@0.10.3':
dependencies:
@@ -8169,15 +8193,15 @@ snapshots:
'@types/web-bluetooth@0.0.21': {}
- '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.66.0
- '@typescript-eslint/type-utils': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/type-utils': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.66.0
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
ignore: 7.0.6
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.3)
@@ -8185,23 +8209,23 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.66.0
'@typescript-eslint/types': 8.66.0
- '@typescript-eslint/typescript-estree': 8.66.0(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.66.0
- debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ debug: 4.4.3
+ eslint: 10.8.0(jiti@2.7.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.66.0(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/project-service@8.66.0(typescript@6.0.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.66.0(typescript@6.0.3)
'@typescript-eslint/types': 8.66.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -8215,13 +8239,13 @@ snapshots:
dependencies:
typescript: 6.0.3
- '@typescript-eslint/type-utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/type-utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
'@typescript-eslint/types': 8.66.0
- '@typescript-eslint/typescript-estree': 8.66.0(supports-color@10.2.2)(typescript@6.0.3)
- '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
- debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
+ debug: 4.4.3
+ eslint: 10.8.0(jiti@2.7.0)
ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3
transitivePeerDependencies:
@@ -8229,13 +8253,13 @@ snapshots:
'@typescript-eslint/types@8.66.0': {}
- '@typescript-eslint/typescript-estree@8.66.0(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/typescript-estree@8.66.0(typescript@6.0.3)':
dependencies:
- '@typescript-eslint/project-service': 8.66.0(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/project-service': 8.66.0(typescript@6.0.3)
'@typescript-eslint/tsconfig-utils': 8.66.0(typescript@6.0.3)
'@typescript-eslint/types': 8.66.0
'@typescript-eslint/visitor-keys': 8.66.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
minimatch: 10.2.6
semver: 7.8.5
tinyglobby: 0.2.17
@@ -8244,13 +8268,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
+ '@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0))
'@typescript-eslint/scope-manager': 8.66.0
'@typescript-eslint/types': 8.66.0
- '@typescript-eslint/typescript-estree': 8.66.0(supports-color@10.2.2)(typescript@6.0.3)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3)
+ eslint: 10.8.0(jiti@2.7.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
@@ -8260,18 +8284,18 @@ snapshots:
'@typescript-eslint/types': 8.66.0
eslint-visitor-keys: 5.0.1
- '@unhead/bundler@3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
+ '@unhead/bundler@3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(unhead@3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))':
dependencies:
magic-string: 1.1.0
oxc-walker: 1.1.1(@oxc-project/types@0.154.0)(oxc-parser@0.142.0)(rolldown@1.2.14)
- unhead: 3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unhead: 3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
optionalDependencies:
- esbuild: 0.28.1
+ esbuild: 0.25.12
lightningcss: 1.33.0
oxc-parser: 0.142.0
rolldown: 1.2.14
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
transitivePeerDependencies:
- '@farmfe/core'
- '@oxc-project/types'
@@ -8280,15 +8304,15 @@ snapshots:
- rollup
- unloader
- '@unhead/vue@3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
+ '@unhead/vue@3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
dependencies:
- '@unhead/bundler': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ '@unhead/bundler': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(unhead@3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
hookable: 6.1.1
- unhead: 3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unhead: 3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
vue: 3.5.41(typescript@6.0.3)
optionalDependencies:
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
transitivePeerDependencies:
- '@farmfe/core'
- '@oxc-project/types'
@@ -8373,9 +8397,9 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.12.2':
optional: true
- '@vercel/nft@1.10.2(rollup@4.62.4)(supports-color@10.2.2)':
+ '@vercel/nft@1.10.2(rollup@4.62.4)':
dependencies:
- '@mapbox/node-pre-gyp': 2.0.3(supports-color@10.2.2)
+ '@mapbox/node-pre-gyp': 2.0.3
'@rollup/pluginutils': 5.4.0(rollup@4.62.4)
acorn: 8.18.0
acorn-import-attributes: 1.9.5(acorn@8.18.0)
@@ -8385,31 +8409,43 @@ snapshots:
glob: 13.0.6
graceful-fs: 4.2.11
node-gyp-build: 4.8.4
- picomatch: 4.0.5
+ picomatch: 4.0.7
resolve-from: 5.0.0
transitivePeerDependencies:
- encoding
- rollup
- supports-color
- '@vitejs/plugin-vue-jsx@5.1.6(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
+ '@vitejs/plugin-vue-jsx@5.1.6(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
dependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))
- '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)
+ '@babel/core': 7.29.7
+ '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
'@rolldown/pluginutils': 1.0.1
- '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
vue: 3.5.41(typescript@6.0.3)
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-vue@6.0.8(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
+ '@vitejs/plugin-vue@6.0.8(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
vue: 3.5.41(typescript@6.0.3)
+ '@volar/language-core@2.4.28':
+ dependencies:
+ '@volar/source-map': 2.4.28
+
+ '@volar/source-map@2.4.28': {}
+
+ '@volar/typescript@2.4.28':
+ dependencies:
+ '@volar/language-core': 2.4.28
+ path-browserify: 1.0.1
+ vscode-uri: 3.2.0
+
'@vue-macros/common@3.1.4(vue@3.5.41(typescript@6.0.3))':
dependencies:
'@vue/compiler-sfc': 3.5.41
@@ -8422,27 +8458,27 @@ snapshots:
'@vue/babel-helper-vue-transform-on@2.0.1': {}
- '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)':
+ '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7)':
dependencies:
- '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2)
+ '@babel/helper-module-imports': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))
+ '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
'@babel/template': 7.29.7
- '@babel/traverse': 7.29.8(supports-color@10.2.2)
+ '@babel/traverse': 7.29.8
'@babel/types': 7.29.8
'@vue/babel-helper-vue-transform-on': 2.0.1
- '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)
+ '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.7)
'@vue/shared': 3.5.41
optionalDependencies:
- '@babel/core': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
transitivePeerDependencies:
- supports-color
- '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)':
+ '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.7)':
dependencies:
'@babel/code-frame': 7.29.7
- '@babel/core': 7.29.7(supports-color@10.2.2)
- '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2)
+ '@babel/core': 7.29.7
+ '@babel/helper-module-imports': 7.29.7
'@babel/helper-plugin-utils': 7.29.7
'@babel/parser': 7.29.8
'@vue/compiler-sfc': 3.5.41
@@ -8516,6 +8552,16 @@ snapshots:
'@vue/devtools-shared@8.2.1': {}
+ '@vue/language-core@3.3.13':
+ dependencies:
+ '@volar/language-core': 2.4.28
+ '@vue/compiler-dom': 3.5.41
+ '@vue/shared': 3.5.41
+ alien-signals: 3.2.1
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ picomatch: 4.0.7
+
'@vue/reactivity@3.5.41':
dependencies:
'@vue/shared': 3.5.41
@@ -8596,6 +8642,8 @@ snapshots:
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
+ alien-signals@3.2.1: {}
+
ansi-regex@5.0.1: {}
ansi-regex@6.2.2: {}
@@ -8817,12 +8865,12 @@ snapshots:
chownr@3.0.0: {}
- chrome-launcher@1.2.1(supports-color@10.2.2):
+ chrome-launcher@1.2.1:
dependencies:
'@types/node': 26.1.2
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
- lighthouse-logger: 2.0.2(supports-color@10.2.2)
+ lighthouse-logger: 2.0.2
transitivePeerDependencies:
- supports-color
@@ -9037,11 +9085,9 @@ snapshots:
db0@0.3.4: {}
- debug@4.4.3(supports-color@10.2.2):
+ debug@4.4.3:
dependencies:
ms: 2.1.3
- optionalDependencies:
- supports-color: 10.2.2
decamelize@1.2.0: {}
@@ -9222,14 +9268,14 @@ snapshots:
escape-string-regexp@5.0.0: {}
- eslint-config-flat-gitignore@2.3.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-config-flat-gitignore@2.3.0(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- '@eslint/compat': 2.1.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@eslint/compat': 2.1.0(eslint@10.8.0(jiti@2.7.0))
+ eslint: 10.8.0(jiti@2.7.0)
- eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
eslint-flat-config-utils@3.2.0:
dependencies:
@@ -9243,20 +9289,20 @@ snapshots:
optionalDependencies:
unrs-resolver: 1.12.2
- eslint-merge-processors@2.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-merge-processors@2.0.0(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
- eslint-plugin-import-lite@0.6.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-plugin-import-lite@0.6.0(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
- eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
+ eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)):
dependencies:
'@typescript-eslint/types': 8.66.0
comment-parser: 1.4.7
- debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ debug: 4.4.3
+ eslint: 10.8.0(jiti@2.7.0)
eslint-import-context: 0.1.9(unrs-resolver@1.12.2)
is-glob: 4.0.3
minimatch: 10.2.6
@@ -9264,19 +9310,19 @@ snapshots:
stable-hash-x: 0.2.0
unrs-resolver: 1.12.2
optionalDependencies:
- '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
transitivePeerDependencies:
- supports-color
- eslint-plugin-jsdoc@63.3.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
+ eslint-plugin-jsdoc@63.3.3(eslint@10.8.0(jiti@2.7.0)):
dependencies:
'@es-joy/jsdoccomment': 0.91.0
'@es-joy/resolve.exports': 1.2.0
are-docs-informative: 0.0.2
comment-parser: 1.4.7
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
escape-string-regexp: 4.0.0
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
espree: 11.2.0
esquery: 1.7.0
html-entities: 2.6.0
@@ -9288,20 +9334,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-plugin-regexp@3.1.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-plugin-regexp@3.1.1(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2
comment-parser: 1.4.7
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
jsdoc-type-pratt-parser: 7.3.0
refa: 0.12.1
regexp-ast-analysis: 0.7.1
scslre: 0.3.0
- eslint-plugin-unicorn@73.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-plugin-unicorn@73.0.0(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0))
'@eslint/css-tree': 4.0.5
browserslist: 4.28.7
change-case: 5.4.4
@@ -9309,7 +9355,7 @@ snapshots:
core-js-compat: 3.50.0
detect-indent: 7.0.2
entities: 4.5.0
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
find-up-simple: 1.0.1
globals: 17.9.0
indent-string: 5.0.0
@@ -9323,24 +9369,24 @@ snapshots:
strip-indent: 4.1.1
yaml: 2.9.0
- eslint-plugin-vue@10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)):
+ eslint-plugin-vue@10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.8.0(jiti@2.7.0)))(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.8.0(jiti@2.7.0))):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0))
+ eslint: 10.8.0(jiti@2.7.0)
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 7.1.5
semver: 7.8.5
- vue-eslint-parser: 10.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)
+ vue-eslint-parser: 10.4.1(eslint@10.8.0(jiti@2.7.0))
xml-name-validator: 5.0.0
optionalDependencies:
- '@stylistic/eslint-plugin': 5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
- '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
+ '@stylistic/eslint-plugin': 5.10.0(eslint@10.8.0(jiti@2.7.0))
+ '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.7.0))(typescript@6.0.3)
- eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)):
dependencies:
'@vue/compiler-sfc': 3.5.41
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
eslint-scope@9.1.2:
dependencies:
@@ -9349,9 +9395,9 @@ snapshots:
esrecurse: 4.3.0
estraverse: 5.3.0
- eslint-typegen@2.3.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)):
+ eslint-typegen@2.3.1(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
json-schema-to-typescript-lite: 15.0.0
ohash: 2.0.11
@@ -9361,11 +9407,11 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
- eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2):
+ eslint@10.8.0(jiti@2.7.0):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2
- '@eslint/config-array': 0.23.5(supports-color@10.2.2)
+ '@eslint/config-array': 0.23.5
'@eslint/config-helpers': 0.7.0
'@eslint/core': 1.2.1
'@eslint/plugin-kit': 0.7.2
@@ -9375,7 +9421,7 @@ snapshots:
'@types/estree': 1.0.9
ajv: 6.15.0
cross-spawn: 7.0.6
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
escape-string-regexp: 4.0.0
eslint-scope: 9.1.2
eslint-visitor-keys: 5.0.1
@@ -9510,9 +9556,9 @@ snapshots:
dependencies:
websocket-driver: 0.7.5
- fdir@6.5.0(picomatch@4.0.5):
+ fdir@6.5.0(picomatch@4.0.7):
optionalDependencies:
- picomatch: 4.0.5
+ picomatch: 4.0.7
file-entry-cache@11.1.5:
dependencies:
@@ -9765,10 +9811,10 @@ snapshots:
http-shutdown@1.2.2: {}
- https-proxy-agent@7.0.6(supports-color@10.2.2):
+ https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -9803,10 +9849,10 @@ snapshots:
import-meta-resolve@4.2.0: {}
- importx@0.5.2(supports-color@10.2.2):
+ importx@0.5.2:
dependencies:
bundle-require: 5.1.0(esbuild@0.25.12)
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
esbuild: 0.25.12
jiti: 2.7.0
pathe: 2.0.3
@@ -9814,12 +9860,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- impound@1.1.6(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ impound@1.1.6(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
es-module-lexer: 2.3.1
pathe: 2.0.3
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
unplugin-utils: 0.3.2
transitivePeerDependencies:
- '@farmfe/core'
@@ -9844,11 +9890,11 @@ snapshots:
ini@6.0.0: {}
- ioredis@5.11.1(supports-color@10.2.2):
+ ioredis@5.11.1:
dependencies:
'@ioredis/commands': 1.10.0
cluster-key-slot: 1.1.1
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
denque: 2.1.0
redis-errors: 1.2.0
redis-parser: 3.0.0
@@ -10005,9 +10051,9 @@ snapshots:
libphonenumber-js@1.13.10: {}
- lighthouse-logger@2.0.2(supports-color@10.2.2):
+ lighthouse-logger@2.0.2:
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
marky: 1.3.0
transitivePeerDependencies:
- supports-color
@@ -10236,7 +10282,7 @@ snapshots:
natural-compare@1.4.0: {}
- nitropack@2.13.4(@parcel/watcher@2.6.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ nitropack@2.13.4(@parcel/watcher@2.6.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(srvx@0.11.22)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.2
'@rollup/plugin-alias': 6.0.0(rollup@4.62.4)
@@ -10246,7 +10292,7 @@ snapshots:
'@rollup/plugin-node-resolve': 16.0.3(rollup@4.62.4)
'@rollup/plugin-replace': 6.0.3(rollup@4.62.4)
'@rollup/plugin-terser': 1.0.0(rollup@4.62.4)
- '@vercel/nft': 1.10.2(rollup@4.62.4)(supports-color@10.2.2)
+ '@vercel/nft': 1.10.2(rollup@4.62.4)
archiver: 7.0.1
c12: 3.3.4(magicast@0.5.4)
chokidar: 5.0.0
@@ -10270,7 +10316,7 @@ snapshots:
h3: 1.15.11
hookable: 5.5.3
httpxy: 0.5.5
- ioredis: 5.11.1(supports-color@10.2.2)
+ ioredis: 5.11.1
jiti: 2.7.0
klona: 2.0.6
knitwork: 1.3.0
@@ -10293,7 +10339,7 @@ snapshots:
scule: 1.3.0
semver: 7.8.5
serve-placeholder: 2.0.2
- serve-static: 2.2.1(supports-color@10.2.2)
+ serve-static: 2.2.1
source-map: 0.7.6
std-env: 4.2.0
ufo: 1.6.4
@@ -10301,9 +10347,9 @@ snapshots:
uncrypto: 0.1.3
unctx: 2.5.0
unenv: 2.0.0-rc.24
- unimport: 6.4.0(esbuild@0.28.1)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unimport: 6.4.0(esbuild@0.28.1)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
unplugin-utils: 0.3.2
- unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
untyped: 2.0.0
unwasm: 0.5.3
youch: 4.1.1
@@ -10386,18 +10432,18 @@ snapshots:
dependencies:
boolbase: 1.0.0
- nuxt-link-checker@5.1.3(2afc54efe4333e7b21be164e8edbf89c):
+ nuxt-link-checker@5.1.3(d5df5b90bf7ae7ca065098da8ab00f11):
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
'@vueuse/core': 14.4.0(vue@3.5.41(typescript@6.0.3))
consola: 3.4.2
diff: 9.0.0
fuse.js: 7.5.0
h3: 1.15.11
magic-string: 1.1.0
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0)
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
ofetch: 1.5.1
pathe: 2.0.3
pkg-types: 2.3.1
@@ -10405,7 +10451,7 @@ snapshots:
site-config-stack: 4.2.0(vue@3.5.41(typescript@6.0.3))
ufo: 1.6.4
ultrahtml: 1.7.0
- unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -10435,13 +10481,13 @@ snapshots:
- vue
- zod
- nuxt-og-image@6.7.6(c5b09079d96a60970e5bd704c57de0fb):
+ nuxt-og-image@6.7.6(1260c5194fdf2814eb31b9771c8bd346):
dependencies:
'@clack/prompts': 1.7.0
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
- '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@vue/compiler-sfc': 3.5.41
- chrome-launcher: 1.2.1(supports-color@10.2.2)
+ chrome-launcher: 1.2.1
consola: 3.4.2
defu: 6.1.7
devalue: 5.9.4
@@ -10451,8 +10497,8 @@ snapshots:
magic-string: 1.1.0
magicast: 0.5.4
mocked-exports: 0.1.1
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
nypm: 0.6.9
object-identity: 0.2.3
ofetch: 1.5.1
@@ -10467,10 +10513,10 @@ snapshots:
tinyexec: 1.3.0
ufo: 1.6.4
ultrahtml: 1.7.0
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
optionalDependencies:
- nitropack: 2.13.4(@parcel/watcher@2.6.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ nitropack: 2.13.4(@parcel/watcher@2.6.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(srvx@0.11.22)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
transitivePeerDependencies:
- '@farmfe/core'
- '@nuxt/schema'
@@ -10487,17 +10533,17 @@ snapshots:
- vue
- webpack
- nuxt-schema-org@6.2.8(5a5c97359c4cfa887a5b2273eea7c903):
+ nuxt-schema-org@6.2.8(765524ba5b9822e3426864f203b74fb3):
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
defu: 6.1.7
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
pkg-types: 2.3.1
ufo: 1.6.4
optionalDependencies:
- '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
- unhead: 3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ unhead: 3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
transitivePeerDependencies:
- '@nuxt/schema'
- magic-string
@@ -10509,31 +10555,31 @@ snapshots:
- vite
- vue
- nuxt-seo-utils@8.3.3(58a2c7fe40baae257b6ed398e9cddc7f):
+ nuxt-seo-utils@8.3.3(128dc6963dfcd1b0c746c3148ce2ad1c):
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
citty: 0.2.2
consola: 3.4.2
defu: 6.1.7
escape-string-regexp: 5.0.0
exsolve: 1.1.1
image-size: 2.0.2
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0)
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
pathe: 2.0.3
pkg-types: 2.3.1
scule: 1.3.0
tinyglobby: 0.2.17
ufo: 1.6.4
optionalDependencies:
- '@unhead/bundler': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
- esbuild: 0.28.1
+ '@unhead/bundler': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(unhead@3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ esbuild: 0.25.12
lightningcss: 1.33.0
rolldown: 1.2.14
sharp: 0.35.4(@types/node@26.1.2)
- unhead: 3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unhead: 3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
transitivePeerDependencies:
- '@nuxt/schema'
- '@types/node'
@@ -10545,9 +10591,9 @@ snapshots:
- vue
- zod
- nuxt-site-config-kit@4.2.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vue@3.5.41(typescript@6.0.3)):
+ nuxt-site-config-kit@4.2.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vue@3.5.41(typescript@6.0.3)):
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
site-config-stack: 4.2.0(vue@3.5.41(typescript@6.0.3))
std-env: 4.2.0
ufo: 1.6.4
@@ -10559,15 +10605,15 @@ snapshots:
- unplugin
- vue
- nuxt-site-config@4.2.0(7d8087514e1614c3470bdbe0b86f0686):
+ nuxt-site-config@4.2.0(0655b6d95986fa09e450619a2471d7fb):
dependencies:
'@nuxt/devalue': 2.0.2
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
consola: 3.4.2
defu: 6.1.7
h3: 1.15.11
- nuxt-site-config-kit: 4.2.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vue@3.5.41(typescript@6.0.3))
- nuxtseo-shared: 5.3.10(db742c74deaf2b1c7500561a6d1e46b8)
+ nuxt-site-config-kit: 4.2.0(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vue@3.5.41(typescript@6.0.3))
+ nuxtseo-shared: 5.3.10(9475ed0abf43a57ec2edeac587f1a640)
pathe: 2.0.3
pkg-types: 2.3.1
site-config-stack: 4.2.0(vue@3.5.41(typescript@6.0.3))
@@ -10584,17 +10630,17 @@ snapshots:
- vite
- zod
- nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0):
+ nuxt@4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0):
dependencies:
- '@dxup/nuxt': 0.5.6(esbuild@0.28.1)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- '@nuxt/cli': 3.37.0(@nuxt/schema@4.5.2)(@parcel/watcher@2.6.0)(cac@7.0.0)(magicast@0.5.4)(supports-color@10.2.2)
- '@nuxt/devtools': 3.4.1(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
- '@nuxt/nitro-server': 4.5.2(fe9885ce02309805246d9295eb36b44b)
+ '@dxup/nuxt': 0.5.6(esbuild@0.25.12)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ '@nuxt/cli': 3.37.0(@nuxt/schema@4.5.2)(@parcel/watcher@2.6.0)(cac@7.0.0)(magicast@0.5.4)
+ '@nuxt/devtools': 3.4.1(db0@0.3.4)(ioredis@5.11.1)(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/nitro-server': 4.5.2(b9bd6ba5516dc543dbc917b33a0d10ad)
'@nuxt/schema': 4.5.2
- '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))
- '@nuxt/vite-builder': 4.5.2(bbb59840355872275ede4355b56b34bc)
- '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.28.1)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))
+ '@nuxt/vite-builder': 4.5.2(cbd7f0b9e291f24836832fe380a0c379)
+ '@unhead/vue': 3.3.1(@oxc-project/types@0.154.0)(esbuild@0.25.12)(lightningcss@1.33.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
'@vue/shared': 3.5.41
chokidar: 5.0.0
compatx: 0.2.0
@@ -10608,7 +10654,7 @@ snapshots:
fnv1a-64: 0.1.2
hookable: 6.1.1
ignore: 7.0.6
- impound: 1.1.6(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ impound: 1.1.6(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
jiti: 2.7.0
klona: 2.0.6
knitwork: 1.3.0
@@ -10635,17 +10681,17 @@ snapshots:
ufo: 1.6.4
ultrahtml: 1.7.0
uncrypto: 0.1.3
- unctx: 3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ unctx: 3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
undici: 8.11.2
- unhead: 3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- unimport: 6.4.0(esbuild@0.28.1)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unhead: 3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unimport: 6.4.0(esbuild@0.25.12)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
unrouting: 0.2.2
untyped: 2.0.0
verkit: 0.3.2
vue: 3.5.41(typescript@6.0.3)
vue-component-type-helpers: 3.3.9
- vue-router: 5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
+ vue-router: 5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.25.12)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))
optionalDependencies:
'@parcel/watcher': 2.6.0
'@types/node': 26.1.2
@@ -10726,16 +10772,16 @@ snapshots:
- xml2js
- yaml
- nuxtseo-shared@5.3.10(db742c74deaf2b1c7500561a6d1e46b8):
+ nuxtseo-shared@5.3.10(9475ed0abf43a57ec2edeac587f1a640):
dependencies:
'@clack/prompts': 1.7.0
- '@nuxt/devtools-kit': 4.0.0-alpha.7(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/devtools-kit': 4.0.0-alpha.7(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
'@nuxt/schema': 4.5.2
birpc: 4.0.0
consola: 3.4.2
defu: 6.1.7
- nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(yaml@2.9.0)
+ nuxt: 4.5.2(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@oxc-project/types@0.154.0)(@parcel/watcher@2.6.0)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@7.0.0)(db0@0.3.4)(esbuild@0.25.12)(eslint@10.8.0(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.33.0)(magicast@0.5.4)(meow@14.1.0)(optionator@0.9.4)(oxc-parser@0.142.0)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4))(rollup@4.62.4)(sass@1.105.1)(srvx@0.11.22)(stylelint@17.15.0(typescript@6.0.3))(terser@5.49.2)(tsx@4.23.15)(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3))(yaml@2.9.0)
nypm: 0.6.9
ofetch: 1.5.1
pathe: 2.0.3
@@ -10746,7 +10792,7 @@ snapshots:
ufo: 1.6.4
vue: 3.5.41(typescript@6.0.3)
optionalDependencies:
- nuxt-site-config: 4.2.0(7d8087514e1614c3470bdbe0b86f0686)
+ nuxt-site-config: 4.2.0(0655b6d95986fa09e450619a2471d7fb)
transitivePeerDependencies:
- magic-string
- magicast
@@ -10891,6 +10937,8 @@ snapshots:
parseurl@1.3.3: {}
+ path-browserify@1.0.1: {}
+
path-exists@4.0.0: {}
path-expression-matcher@1.6.2: {}
@@ -11300,7 +11348,7 @@ snapshots:
rollup-plugin-visualizer@7.0.1(rolldown@1.2.14)(rollup@4.62.4):
dependencies:
open: 11.0.0
- picomatch: 4.0.5
+ picomatch: 4.0.7
source-map: 0.7.6
yargs: 18.1.0
optionalDependencies:
@@ -11373,9 +11421,9 @@ snapshots:
semver@7.8.5: {}
- send@1.2.1(supports-color@10.2.2):
+ send@1.2.1:
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
@@ -11397,12 +11445,12 @@ snapshots:
dependencies:
defu: 6.1.7
- serve-static@2.2.1(supports-color@10.2.2):
+ serve-static@2.2.1:
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
- send: 1.2.1(supports-color@10.2.2)
+ send: 1.2.1
transitivePeerDependencies:
- supports-color
@@ -11456,13 +11504,13 @@ snapshots:
signal-exit@4.1.0: {}
- simple-git@3.36.0(supports-color@10.2.2):
+ simple-git@3.36.0:
dependencies:
- '@kwsites/file-exists': 1.1.1(supports-color@10.2.2)
+ '@kwsites/file-exists': 1.1.1
'@kwsites/promise-deferred': 1.1.1
'@simple-git/args-pathspec': 1.0.3
'@simple-git/argv-parser': 1.1.1
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -11599,29 +11647,29 @@ snapshots:
postcss: 8.5.26
postcss-selector-parser: 7.1.5
- stylelint-config-html@2.0.0(postcss-html@1.8.1)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)):
+ stylelint-config-html@2.0.0(postcss-html@1.8.1)(stylelint@17.15.0(typescript@6.0.3)):
dependencies:
postcss-html: 1.8.1
- stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3)
+ stylelint: 17.15.0(typescript@6.0.3)
- stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.1)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)):
+ stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.1)(stylelint@17.15.0(typescript@6.0.3)):
dependencies:
postcss-html: 1.8.1
semver: 7.8.5
- stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3)
- stylelint-config-html: 2.0.0(postcss-html@1.8.1)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))
- stylelint-config-recommended: 18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))
+ stylelint: 17.15.0(typescript@6.0.3)
+ stylelint-config-html: 2.0.0(postcss-html@1.8.1)(stylelint@17.15.0(typescript@6.0.3))
+ stylelint-config-recommended: 18.0.0(stylelint@17.15.0(typescript@6.0.3))
- stylelint-config-recommended@18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)):
+ stylelint-config-recommended@18.0.0(stylelint@17.15.0(typescript@6.0.3)):
dependencies:
- stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3)
+ stylelint: 17.15.0(typescript@6.0.3)
- stylelint-config-standard@40.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3)):
+ stylelint-config-standard@40.0.0(stylelint@17.15.0(typescript@6.0.3)):
dependencies:
- stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3)
- stylelint-config-recommended: 18.0.0(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))
+ stylelint: 17.15.0(typescript@6.0.3)
+ stylelint-config-recommended: 18.0.0(stylelint@17.15.0(typescript@6.0.3))
- stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3):
+ stylelint@17.15.0(typescript@6.0.3):
dependencies:
'@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
@@ -11634,7 +11682,7 @@ snapshots:
cosmiconfig: 9.0.2(typescript@6.0.3)
css-functions-list: 3.3.3
css-tree: 3.2.1
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
fast-glob: 3.3.3
fastest-levenshtein: 1.0.16
file-entry-cache: 11.1.5
@@ -11754,8 +11802,8 @@ snapshots:
tinyglobby@0.2.17:
dependencies:
- fdir: 6.5.0(picomatch@4.0.5)
- picomatch: 4.0.5
+ fdir: 6.5.0(picomatch@4.0.7)
+ picomatch: 4.0.7
to-regex-range@5.0.1:
dependencies:
@@ -11808,11 +11856,11 @@ snapshots:
ultrahtml@1.7.0: {}
- unconfig@0.6.1(supports-color@10.2.2):
+ unconfig@0.6.1:
dependencies:
'@antfu/utils': 8.1.1
defu: 6.1.7
- importx: 0.5.2(supports-color@10.2.2)
+ importx: 0.5.2
transitivePeerDependencies:
- supports-color
@@ -11825,12 +11873,12 @@ snapshots:
magic-string: 0.30.21
unplugin: 2.3.11
- unctx@3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))):
+ unctx@3.0.0(magic-string@1.1.0)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))):
optionalDependencies:
magic-string: 1.1.0
oxc-parser: 0.142.0
rolldown: 1.2.14
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
undici-types@8.3.0: {}
@@ -11840,12 +11888,12 @@ snapshots:
dependencies:
pathe: 2.0.3
- unhead@3.3.1(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ unhead@3.3.1(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
hookable: 6.1.1
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
optionalDependencies:
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
transitivePeerDependencies:
- '@farmfe/core'
- '@rspack/core'
@@ -11860,7 +11908,7 @@ snapshots:
unicorn-magic@0.4.0: {}
- unimport@6.4.0(esbuild@0.28.1)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ unimport@6.4.0(esbuild@0.25.12)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
acorn: 8.18.0
escape-string-regexp: 5.0.0
@@ -11874,7 +11922,36 @@ snapshots:
scule: 1.3.0
strip-literal: 4.0.0
tinyglobby: 0.2.17
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin-utils: 0.3.2
+ optionalDependencies:
+ oxc-parser: 0.142.0
+ rolldown: 1.2.14
+ transitivePeerDependencies:
+ - '@farmfe/core'
+ - '@rspack/core'
+ - bun-types-no-globals
+ - esbuild
+ - rollup
+ - unloader
+ - vite
+ - webpack
+
+ unimport@6.4.0(esbuild@0.28.1)(oxc-parser@0.142.0)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ dependencies:
+ acorn: 8.18.0
+ escape-string-regexp: 5.0.0
+ estree-walker: 3.0.3
+ local-pkg: 1.2.1
+ magic-string: 1.1.0
+ mlly: 1.8.2
+ pathe: 2.0.3
+ picomatch: 4.0.5
+ pkg-types: 2.3.1
+ scule: 1.3.0
+ strip-literal: 4.0.0
+ tinyglobby: 0.2.17
+ unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
unplugin-utils: 0.3.2
optionalDependencies:
oxc-parser: 0.142.0
@@ -11898,19 +11975,30 @@ snapshots:
dependencies:
'@jridgewell/remapping': 2.3.5
acorn: 8.18.0
- picomatch: 4.0.5
+ picomatch: 4.0.7
webpack-virtual-modules: 0.6.2
- unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
'@jridgewell/remapping': 2.3.5
- picomatch: 4.0.5
+ picomatch: 4.0.7
+ webpack-virtual-modules: 0.6.2
+ optionalDependencies:
+ esbuild: 0.25.12
+ rolldown: 1.2.14
+ rollup: 4.62.4
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+
+ unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ picomatch: 4.0.7
webpack-virtual-modules: 0.6.2
optionalDependencies:
esbuild: 0.28.1
rolldown: 1.2.14
rollup: 4.62.4
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
unrouting@0.2.2:
dependencies:
@@ -11944,7 +12032,7 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.12.2
'@unrs/resolver-binding-win32-x64-msvc': 1.12.2
- unstorage@1.17.5(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2)):
+ unstorage@1.17.5(db0@0.3.4)(ioredis@5.11.1):
dependencies:
anymatch: 3.1.3
chokidar: 5.0.0
@@ -11956,7 +12044,7 @@ snapshots:
ufo: 1.6.4
optionalDependencies:
db0: 0.3.4
- ioredis: 5.11.1(supports-color@10.2.2)
+ ioredis: 5.11.1
untun@0.2.2: {}
@@ -12018,23 +12106,23 @@ snapshots:
verkit@0.3.2: {}
- vite-dev-rpc@2.0.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ vite-dev-rpc@2.0.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
birpc: 4.0.0
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
- vite-hot-client: 2.2.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite-hot-client: 2.2.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
- vite-hot-client@2.2.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ vite-hot-client@2.2.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
- vite-node@6.0.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0):
+ vite-node@6.0.0(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0):
dependencies:
cac: 7.0.0
es-module-lexer: 2.3.1
obug: 2.1.4
pathe: 2.0.3
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
transitivePeerDependencies:
- '@types/node'
- '@vitejs/devtools'
@@ -12049,24 +12137,25 @@ snapshots:
- tsx
- yaml
- vite-plugin-checker@0.14.5(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(meow@14.1.0)(optionator@0.9.4)(stylelint@17.15.0(supports-color@10.2.2)(typescript@6.0.3))(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ vite-plugin-checker@0.14.5(eslint@10.8.0(jiti@2.7.0))(meow@14.1.0)(optionator@0.9.4)(stylelint@17.15.0(typescript@6.0.3))(typescript@6.0.3)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue-tsc@3.3.13(typescript@6.0.3)):
dependencies:
'@babel/code-frame': 7.29.7
chokidar: 5.0.0
npm-run-path: 6.0.0
picocolors: 1.1.1
- picomatch: 4.0.5
+ picomatch: 4.0.7
proper-lockfile: 4.1.2
tiny-invariant: 1.3.3
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
optionalDependencies:
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ eslint: 10.8.0(jiti@2.7.0)
meow: 14.1.0
optionator: 0.9.4
- stylelint: 17.15.0(supports-color@10.2.2)(typescript@6.0.3)
+ stylelint: 17.15.0(typescript@6.0.3)
typescript: 6.0.3
+ vue-tsc: 3.3.13(typescript@6.0.3)
- vite-plugin-inspect@11.4.1(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
+ vite-plugin-inspect@11.4.1(@nuxt/kit@4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)):
dependencies:
ansis: 4.3.1
error-stack-parser-es: 1.0.5
@@ -12076,33 +12165,33 @@ snapshots:
perfect-debounce: 2.1.0
sirv: 3.0.2
unplugin-utils: 0.3.2
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
- vite-dev-rpc: 2.0.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite-dev-rpc: 2.0.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
optionalDependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
- vite-plugin-vue-tracer@1.4.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)):
+ vite-plugin-vue-tracer@1.4.0(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)):
dependencies:
estree-walker: 3.0.3
exsolve: 1.1.1
magic-string: 0.30.21
pathe: 2.0.3
source-map-js: 1.2.1
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
vue: 3.5.41(typescript@6.0.3)
- vite-plugin-vuetify@2.1.3(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8):
+ vite-plugin-vuetify@2.1.3(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8):
dependencies:
'@vuetify/loader-shared': 2.1.2(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8)
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3
upath: 2.0.1
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
vue: 3.5.41(typescript@6.0.3)
vuetify: 4.1.8(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.41(typescript@6.0.3))
transitivePeerDependencies:
- supports-color
- vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0):
+ vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0):
dependencies:
lightningcss: 1.33.0
picomatch: 4.0.7
@@ -12111,7 +12200,7 @@ snapshots:
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 26.1.2
- esbuild: 0.28.1
+ esbuild: 0.25.12
fsevents: 2.3.3
jiti: 2.7.0
sass: 1.105.1
@@ -12119,6 +12208,8 @@ snapshots:
tsx: 4.23.15
yaml: 2.9.0
+ vscode-uri@3.2.0: {}
+
vue-bundle-renderer@2.3.1:
dependencies:
ufo: 1.6.4
@@ -12132,10 +12223,10 @@ snapshots:
vue-devtools-stub@0.1.0: {}
- vue-eslint-parser@10.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2):
+ vue-eslint-parser@10.4.1(eslint@10.8.0(jiti@2.7.0)):
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
- eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2)
+ debug: 4.4.3
+ eslint: 10.8.0(jiti@2.7.0)
eslint-scope: 9.1.2
eslint-visitor-keys: 5.0.1
espree: 11.2.0
@@ -12144,7 +12235,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- vue-router@5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)):
+ vue-router@5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.25.12)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3)))(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)):
dependencies:
'@babel/generator': 8.0.0
'@vue-macros/common': 3.1.4(vue@3.5.41(typescript@6.0.3))
@@ -12161,14 +12252,14 @@ snapshots:
picomatch: 4.0.5
scule: 1.3.0
tinyglobby: 0.2.17
- unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
+ unplugin: 3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))
unplugin-utils: 0.3.2
vue: 3.5.41(typescript@6.0.3)
yaml: 2.9.0
optionalDependencies:
'@vue/compiler-sfc': 3.5.41
pinia: 3.0.4(typescript@6.0.3)(vue@3.5.41(typescript@6.0.3))
- vite: 8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
+ vite: 8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)
transitivePeerDependencies:
- '@farmfe/core'
- '@rspack/core'
@@ -12179,6 +12270,12 @@ snapshots:
- unloader
- webpack
+ vue-tsc@3.3.13(typescript@6.0.3):
+ dependencies:
+ '@volar/typescript': 2.4.28
+ '@vue/language-core': 3.3.13
+ typescript: 6.0.3
+
vue@3.5.41(typescript@6.0.3):
dependencies:
'@vue/compiler-dom': 3.5.41
@@ -12189,9 +12286,9 @@ snapshots:
optionalDependencies:
typescript: 6.0.3
- vuetify-nuxt-module@0.19.5(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(supports-color@10.2.2)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)):
+ vuetify-nuxt-module@0.19.5(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(typescript@6.0.3)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)):
dependencies:
- '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
+ '@nuxt/kit': 4.5.2(magic-string@1.1.0)(magicast@0.5.4)(oxc-parser@0.142.0)(rolldown@1.2.14)(unplugin@3.3.0(esbuild@0.25.12)(rolldown@1.2.14)(rollup@4.62.4)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0)))
defu: 6.1.7
destr: 2.0.5
local-pkg: 1.2.1
@@ -12199,9 +12296,9 @@ snapshots:
perfect-debounce: 1.0.0
semver: 7.8.5
ufo: 1.6.4
- unconfig: 0.6.1(supports-color@10.2.2)
+ unconfig: 0.6.1
upath: 2.0.1
- vite-plugin-vuetify: 2.1.3(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8)
+ vite-plugin-vuetify: 2.1.3(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8)
vuetify: 4.1.8(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.41(typescript@6.0.3))
transitivePeerDependencies:
- magic-string
@@ -12220,7 +12317,7 @@ snapshots:
vue: 3.5.41(typescript@6.0.3)
optionalDependencies:
typescript: 6.0.3
- vite-plugin-vuetify: 2.1.3(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8)
+ vite-plugin-vuetify: 2.1.3(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8)
vuetify@4.2.2(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.41(typescript@6.0.3)):
dependencies:
@@ -12228,7 +12325,7 @@ snapshots:
vue: 3.5.41(typescript@6.0.3)
optionalDependencies:
typescript: 6.0.3
- vite-plugin-vuetify: 2.1.3(supports-color@10.2.2)(vite@8.3.4(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8)
+ vite-plugin-vuetify: 2.1.3(vite@8.3.4(@types/node@26.1.2)(esbuild@0.25.12)(jiti@2.7.0)(sass@1.105.1)(terser@5.49.2)(tsx@4.23.15)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3))(vuetify@4.1.8)
transitivePeerDependencies:
- '@adobe/leonardo-contrast-colors'
- '@ant-design/colors'
diff --git a/web/shared/types/api.ts b/web/shared/types/api.ts
index becb20636..9ecb82efb 100644
--- a/web/shared/types/api.ts
+++ b/web/shared/types/api.ts
@@ -244,6 +244,11 @@ export interface EntitiesMessageThread {
}
export interface EntitiesPhone {
+ /**
+ * ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.
+ * @example false
+ */
+ archive_new_outgoing_message_threads: boolean;
/** @example "2022-06-05T14:26:02.302718+03:00" */
created_at: string;
/** @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." */
@@ -537,7 +542,10 @@ export interface RequestsPhoneAPIKeyStoreRequest {
}
export interface RequestsPhoneFCMToken {
- /** @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." */
+ /**
+ * FcmToken is either a Firebase registration token or a public HTTPS adapter callback URL.
+ * @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....."
+ */
fcm_token: string;
/** @example "[+18005550199]" */
phone_number: string;
@@ -549,7 +557,15 @@ export interface RequestsPhoneFCMToken {
}
export interface RequestsPhoneUpsert {
- /** @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." */
+ /**
+ * ArchiveNewOutgoingMessageThreads archives new conversations started by an outgoing message on this phone.
+ * @example false
+ */
+ archive_new_outgoing_message_threads?: boolean;
+ /**
+ * FcmToken is either a Firebase registration token or a public HTTPS adapter callback URL.
+ * @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....."
+ */
fcm_token: string;
/**
* MaxSendAttempts is the number of attempts when sending an SMS message to handle the case where the phone is offline.
diff --git a/web/tsconfig.json b/web/tsconfig.json
index 875ee5af5..307b2134b 100644
--- a/web/tsconfig.json
+++ b/web/tsconfig.json
@@ -14,17 +14,5 @@
{
"path": "./.nuxt/tsconfig.node.json"
}
- ],
- "compilerOptions": {
- "strict": true,
- "moduleResolution": "bundler",
- "module": "ESNext",
- "target": "ESNext",
- "paths": {
- "~/*": ["./app/*"],
- "~~/*": ["./*"],
- "#imports": ["./app/types/nuxt-shims"]
- }
- },
- "include": ["app/**/*.ts", "app/**/*.d.ts", "shared/**/*.ts"]
+ ]
}