diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 000000000..6089fc807 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,7 @@ +# Shiori Test Commands + +# Run the entire test suite +make unittest + +# Run SQLite database tests only +go test -timeout 10s -count=1 -tags test_sqlite_only ./internal/database diff --git a/.gitignore b/.gitignore index 9c1ec21b5..ab0466650 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ e2e-report.html # Dist files -dist/ +/dist # macOS trash files .DS_Store diff --git a/Makefile b/Makefile index 28f2ed753..18a0481a9 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,12 @@ clean: ## Runs server for local development .PHONY: run-server run-server: generate - GIN_MODE=$(GIN_MODE) SHIORI_DEVELOPMENT=$(SHIORI_DEVELOPMENT) SHIORI_DIR=$(SHIORI_DIR) SHIORI_HTTP_SECRET_KEY=shiori SHIORI_HTTP_SERVE_SWAGGER=true go run main.go server --log-level debug + GIN_MODE=$(GIN_MODE) SHIORI_DEVELOPMENT=$(SHIORI_DEVELOPMENT) go run main.go server --log-level debug + +## Runs server for local development with v2 web UI +.PHONY: run-server-v2 +run-server-v2: generate + GIN_MODE=$(GIN_MODE) SHIORI_DEVELOPMENT=$(SHIORI_DEVELOPMENT) SHIORI_HTTP_SERVE_WEB_UI_V2=true go run main.go server --log-level debug ## Generate swagger docs .PHONY: swagger diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index b62baad67..10539de62 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -384,6 +384,119 @@ const docTemplate = `{ } } }, + "/api/v1/bookmarks/{id}/tags": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Get tags for a bookmark.", + "parameters": [ + { + "type": "integer", + "description": "Bookmark ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.TagDTO" + } + } + }, + "403": { + "description": "Token not provided/invalid" + }, + "404": { + "description": "Bookmark not found" + } + } + }, + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Add a tag to a bookmark.", + "parameters": [ + { + "type": "integer", + "description": "Bookmark ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Add Tag Payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api_v1.bookmarkTagPayload" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Token not provided/invalid" + }, + "404": { + "description": "Bookmark or tag not found" + } + } + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Remove a tag from a bookmark.", + "parameters": [ + { + "type": "integer", + "description": "Bookmark ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Remove Tag Payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api_v1.bookmarkTagPayload" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Token not provided/invalid" + }, + "404": { + "description": "Bookmark not found" + } + } + } + }, "/api/v1/system/info": { "get": { "description": "Get general system information like Shiori version, database, and OS", @@ -429,6 +542,12 @@ const docTemplate = `{ "description": "Filter tags by bookmark ID", "name": "bookmark_id", "in": "query" + }, + { + "type": "string", + "description": "Search tags by name", + "name": "search", + "in": "query" } ], "responses": { @@ -612,6 +731,17 @@ const docTemplate = `{ } }, "definitions": { + "api_v1.bookmarkTagPayload": { + "type": "object", + "required": [ + "tag_id" + ], + "properties": { + "tag_id": { + "type": "integer" + } + } + }, "api_v1.bulkUpdateBookmarkTagsPayload": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 1d8d36fbd..632622e3c 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -373,6 +373,119 @@ } } }, + "/api/v1/bookmarks/{id}/tags": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Get tags for a bookmark.", + "parameters": [ + { + "type": "integer", + "description": "Bookmark ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.TagDTO" + } + } + }, + "403": { + "description": "Token not provided/invalid" + }, + "404": { + "description": "Bookmark not found" + } + } + }, + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Add a tag to a bookmark.", + "parameters": [ + { + "type": "integer", + "description": "Bookmark ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Add Tag Payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api_v1.bookmarkTagPayload" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Token not provided/invalid" + }, + "404": { + "description": "Bookmark or tag not found" + } + } + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Remove a tag from a bookmark.", + "parameters": [ + { + "type": "integer", + "description": "Bookmark ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Remove Tag Payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api_v1.bookmarkTagPayload" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "403": { + "description": "Token not provided/invalid" + }, + "404": { + "description": "Bookmark not found" + } + } + } + }, "/api/v1/system/info": { "get": { "description": "Get general system information like Shiori version, database, and OS", @@ -418,6 +531,12 @@ "description": "Filter tags by bookmark ID", "name": "bookmark_id", "in": "query" + }, + { + "type": "string", + "description": "Search tags by name", + "name": "search", + "in": "query" } ], "responses": { @@ -601,6 +720,17 @@ } }, "definitions": { + "api_v1.bookmarkTagPayload": { + "type": "object", + "required": [ + "tag_id" + ], + "properties": { + "tag_id": { + "type": "integer" + } + } + }, "api_v1.bulkUpdateBookmarkTagsPayload": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 4a578ab58..1d732deac 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1,4 +1,11 @@ definitions: + api_v1.bookmarkTagPayload: + properties: + tag_id: + type: integer + required: + - tag_id + type: object api_v1.bulkUpdateBookmarkTagsPayload: properties: bookmark_ids: @@ -360,6 +367,80 @@ paths: summary: Refresh a token for an account tags: - Auth + /api/v1/bookmarks/{id}/tags: + delete: + parameters: + - description: Bookmark ID + in: path + name: id + required: true + type: integer + - description: Remove Tag Payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/api_v1.bookmarkTagPayload' + produces: + - application/json + responses: + "200": + description: OK + "403": + description: Token not provided/invalid + "404": + description: Bookmark not found + summary: Remove a tag from a bookmark. + tags: + - Auth + get: + parameters: + - description: Bookmark ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/model.TagDTO' + type: array + "403": + description: Token not provided/invalid + "404": + description: Bookmark not found + summary: Get tags for a bookmark. + tags: + - Auth + post: + parameters: + - description: Bookmark ID + in: path + name: id + required: true + type: integer + - description: Add Tag Payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/api_v1.bookmarkTagPayload' + produces: + - application/json + responses: + "200": + description: OK + "403": + description: Token not provided/invalid + "404": + description: Bookmark or tag not found + summary: Add a tag to a bookmark. + tags: + - Auth /api/v1/bookmarks/bulk/tags: put: parameters: @@ -450,6 +531,10 @@ paths: in: query name: bookmark_id type: integer + - description: Search tags by name + in: query + name: search + type: string produces: - application/json responses: diff --git a/internal/cmd/server.go b/internal/cmd/server.go index 48f42b703..852f9241c 100644 --- a/internal/cmd/server.go +++ b/internal/cmd/server.go @@ -24,6 +24,7 @@ func newServerCommand() *cobra.Command { cmd.Flags().StringP("webroot", "r", "/", "Root path that used by server") cmd.Flags().Bool("access-log", false, "Print out a non-standard access log") cmd.Flags().Bool("serve-web-ui", true, "Serve static files from the webroot path") + cmd.Flags().Bool("experimental-serve-web-ui-v2", false, "Serve static files from the webapp path") cmd.Flags().String("secret-key", "", "Secret key used for encrypting session data") return cmd @@ -45,6 +46,7 @@ func newServerCommandHandler() func(cmd *cobra.Command, args []string) { rootPath, _ := cmd.Flags().GetString("webroot") accessLog, _ := cmd.Flags().GetBool("access-log") serveWebUI, _ := cmd.Flags().GetBool("serve-web-ui") + serveWebUIV2, _ := cmd.Flags().GetBool("experimental-serve-web-ui-v2") secretKey, _ := cmd.Flags().GetBytesHex("secret-key") cfg, dependencies := initShiori(ctx, cmd) @@ -81,6 +83,9 @@ func newServerCommandHandler() func(cmd *cobra.Command, args []string) { setIfFlagChanged("secret-key", cmd.Flags(), cfg, func(cfg *config.Config) { cfg.Http.SecretKey = secretKey }) + setIfFlagChanged("experimental-serve-web-ui-v2", cmd.Flags(), cfg, func(cfg *config.Config) { + cfg.Http.ServeWebUIV2 = serveWebUIV2 + }) dependencies.Logger().Infof("Starting Shiori v%s", model.BuildVersion) diff --git a/internal/config/config.go b/internal/config/config.go index e70f00284..bc260ce48 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -55,6 +55,7 @@ type HttpConfig struct { RootPath string `env:"HTTP_ROOT_PATH,default=/"` AccessLog bool `env:"HTTP_ACCESS_LOG,default=True"` ServeWebUI bool `env:"HTTP_SERVE_WEB_UI,default=True"` + ServeWebUIV2 bool `env:"HTTP_SERVE_WEB_UI_V2,default=False"` ServeSwagger bool `env:"HTTP_SERVE_SWAGGER,default=False"` SecretKey []byte `env:"HTTP_SECRET_KEY"` // Fiber Specific @@ -84,6 +85,10 @@ func (c *HttpConfig) IsValid() error { return fmt.Errorf("root path should end with a slash") } + if c.ServeWebUIV2 && !c.ServeWebUI { + return fmt.Errorf("You need to enable serving the Web UI to use the experimental Web UI v2") + } + return nil } @@ -139,6 +144,7 @@ func (c *Config) DebugConfiguration(logger *logrus.Logger) { logger.Debugf(" SHIORI_HTTP_ROOT_PATH: %s", c.Http.RootPath) logger.Debugf(" SHIORI_HTTP_ACCESS_LOG: %t", c.Http.AccessLog) logger.Debugf(" SHIORI_HTTP_SERVE_WEB_UI: %t", c.Http.ServeWebUI) + logger.Debugf(" SHIORI_HTTP_SERVE_WEB_UI_V2: %t", c.Http.ServeWebUIV2) logger.Debugf(" SHIORI_HTTP_SECRET_KEY: %d characters", len(c.Http.SecretKey)) logger.Debugf(" SHIORI_HTTP_BODY_LIMIT: %d", c.Http.BodyLimit) logger.Debugf(" SHIORI_HTTP_READ_TIMEOUT: %s", c.Http.ReadTimeout) diff --git a/internal/database/database_tags.go b/internal/database/database_tags.go index e1afd3ab2..1d97088dd 100644 --- a/internal/database/database_tags.go +++ b/internal/database/database_tags.go @@ -4,10 +4,10 @@ import ( "context" "database/sql" "fmt" - "log/slog" "github.com/go-shiori/shiori/internal/model" "github.com/huandu/go-sqlbuilder" + "github.com/jmoiron/sqlx" ) // GetTags returns a list of tags from the database. @@ -50,6 +50,12 @@ func (db *dbbase) GetTags(ctx context.Context, opts model.DBListTagsOptions) ([] sb.Where(sb.IsNotNull("t.id")) } + // Add search condition if search term is provided + if opts.Search != "" { + // Note: Search and BookmarkID filtering are mutually exclusive as per requirements + sb.Where(sb.Like("t.name", "%"+opts.Search+"%")) + } + if opts.OrderBy == model.DBTagOrderByTagName { sb.OrderBy("t.name") } @@ -57,8 +63,6 @@ func (db *dbbase) GetTags(ctx context.Context, opts model.DBListTagsOptions) ([] query, args := sb.Build() query = db.ReaderDB().Rebind(query) - slog.Info("GetTags query", "query", query, "args", args) - tags := []model.TagDTO{} err := db.ReaderDB().SelectContext(ctx, &tags, query, args...) if err != nil && err != sql.ErrNoRows { @@ -67,3 +71,126 @@ func (db *dbbase) GetTags(ctx context.Context, opts model.DBListTagsOptions) ([] return tags, nil } + +// AddTagToBookmark adds a tag to a bookmark +func (db *dbbase) AddTagToBookmark(ctx context.Context, bookmarkID int, tagID int) error { + // Insert the bookmark-tag association + insertSb := db.Flavor().NewInsertBuilder() + insertSb.InsertInto("bookmark_tag") + insertSb.Cols("bookmark_id", "tag_id") + insertSb.Values(bookmarkID, tagID) + + insertQuery, insertArgs := insertSb.Build() + insertQuery = db.WriterDB().Rebind(insertQuery) + + if err := db.withTx(ctx, func(tx *sqlx.Tx) error { + // First check if the association already exists using sqlbuilder + selectSb := db.Flavor().NewSelectBuilder() + selectSb.Select("1") + selectSb.From("bookmark_tag") + selectSb.Where( + selectSb.And( + selectSb.Equal("bookmark_id", bookmarkID), + selectSb.Equal("tag_id", tagID), + ), + ) + + selectQuery, selectArgs := selectSb.Build() + selectQuery = db.ReaderDB().Rebind(selectQuery) + + var exists int + err := tx.QueryRowContext(ctx, selectQuery, selectArgs...).Scan(&exists) + + if err != nil && err != sql.ErrNoRows { + return fmt.Errorf("failed to check if tag is already associated: %w", err) + } + + // If it doesn't exist, insert it + if err == sql.ErrNoRows { + _, err = tx.ExecContext(ctx, insertQuery, insertArgs...) + if err != nil { + return fmt.Errorf("failed to add tag to bookmark: %w", err) + } + } + + return nil + }); err != nil { + return err + } + + return nil +} + +// RemoveTagFromBookmark removes a tag from a bookmark +func (db *dbbase) RemoveTagFromBookmark(ctx context.Context, bookmarkID int, tagID int) error { + // Delete the bookmark-tag association + deleteSb := db.Flavor().NewDeleteBuilder() + deleteSb.DeleteFrom("bookmark_tag") + deleteSb.Where( + deleteSb.And( + deleteSb.Equal("bookmark_id", bookmarkID), + deleteSb.Equal("tag_id", tagID), + ), + ) + + query, args := deleteSb.Build() + query = db.WriterDB().Rebind(query) + + if err := db.withTx(ctx, func(tx *sqlx.Tx) error { + _, err := tx.ExecContext(ctx, query, args...) + if err != nil { + return fmt.Errorf("failed to remove tag from bookmark: %w", err) + } + return nil + }); err != nil { + return err + } + + return nil +} + +// TagExists checks if a tag with the given ID exists in the database +func (db *dbbase) TagExists(ctx context.Context, tagID int) (bool, error) { + sb := db.Flavor().NewSelectBuilder() + sb.Select("1") + sb.From("tag") + sb.Where(sb.Equal("id", tagID)) + sb.Limit(1) + + query, args := sb.Build() + query = db.ReaderDB().Rebind(query) + + var exists int + err := db.ReaderDB().QueryRowContext(ctx, query, args...).Scan(&exists) + if err != nil { + if err == sql.ErrNoRows { + return false, nil + } + return false, fmt.Errorf("failed to check if tag exists: %w", err) + } + + return true, nil +} + +// BookmarkExists checks if a bookmark with the given ID exists in the database +func (db *dbbase) BookmarkExists(ctx context.Context, bookmarkID int) (bool, error) { + sb := db.Flavor().NewSelectBuilder() + sb.Select("1") + sb.From("bookmark") + sb.Where(sb.Equal("id", bookmarkID)) + sb.Limit(1) + + query, args := sb.Build() + query = db.ReaderDB().Rebind(query) + + var exists int + err := db.ReaderDB().QueryRowContext(ctx, query, args...).Scan(&exists) + if err != nil { + if err == sql.ErrNoRows { + return false, nil + } + return false, fmt.Errorf("failed to check if bookmark exists: %w", err) + } + + return true, nil +} diff --git a/internal/database/database_tags_test.go b/internal/database/database_tags_test.go index d368fd46f..990d8dd1f 100644 --- a/internal/database/database_tags_test.go +++ b/internal/database/database_tags_test.go @@ -184,7 +184,69 @@ func testGetTagsFunction(t *testing.T, db model.DB) { assert.Equal(t, "web", fetchedTags[3].Name) }) - // Test 6: Get tags for a non-existent bookmark + // Test 6: Get tags with search term + t.Run("GetTagsWithSearch", func(t *testing.T) { + // Search for tags containing "go" + fetchedTags, err := db.GetTags(ctx, model.DBListTagsOptions{ + Search: "go", + }) + require.NoError(t, err) + + // Should return only the golang tag + assert.Len(t, fetchedTags, 1) + assert.Equal(t, "golang", fetchedTags[0].Name) + + // Search for tags containing "a" + fetchedTags, err = db.GetTags(ctx, model.DBListTagsOptions{ + Search: "a", + }) + require.NoError(t, err) + + // Should return database and possibly other tags containing "a" + assert.GreaterOrEqual(t, len(fetchedTags), 1) + + // Create a map of tag names for easier checking + tagNames := make(map[string]bool) + for _, tag := range fetchedTags { + tagNames[tag.Name] = true + } + + // Verify database is in the results + assert.True(t, tagNames["database"], "Tag 'database' should be present") + + // Search for non-existent tag + fetchedTags, err = db.GetTags(ctx, model.DBListTagsOptions{ + Search: "nonexistent", + }) + require.NoError(t, err) + assert.Len(t, fetchedTags, 0) + }) + + // Test 7: Search and bookmark ID are mutually exclusive + t.Run("SearchAndBookmarkIDMutuallyExclusive", func(t *testing.T) { + // This test is just to document the behavior, as the validation happens at the model level + // The database layer will prioritize the bookmark ID filter if both are provided + fetchedTags, err := db.GetTags(ctx, model.DBListTagsOptions{ + Search: "go", + BookmarkID: savedBookmarks[0].ID, + }) + require.NoError(t, err) + + // Should return tags for the bookmark, not the search + // The number of tags may vary depending on the database implementation + assert.NotEmpty(t, fetchedTags, "Should return at least one tag for the bookmark") + + // Create a map of tag names for easier checking + tagNames := make(map[string]bool) + for _, tag := range fetchedTags { + tagNames[tag.Name] = true + } + + // Verify golang is in the results (it's associated with the first bookmark) + assert.True(t, tagNames["golang"], "Tag 'golang' should be present") + }) + + // Test 8: Get tags for a non-existent bookmark t.Run("GetTagsForNonExistentBookmark", func(t *testing.T) { fetchedTags, err := db.GetTags(ctx, model.DBListTagsOptions{ BookmarkID: 9999, // Non-existent ID @@ -195,7 +257,7 @@ func testGetTagsFunction(t *testing.T, db model.DB) { assert.Empty(t, fetchedTags) }) - // Test 7: Get tags for a bookmark with no tags + // Test 9: Get tags for a bookmark with no tags t.Run("GetTagsForBookmarkWithNoTags", func(t *testing.T) { // Create a bookmark with no tags bookmarkWithNoTags := model.BookmarkDTO{ @@ -217,7 +279,7 @@ func testGetTagsFunction(t *testing.T, db model.DB) { assert.Empty(t, fetchedTags) }) - // Test 8: Get tags with combined options (order + count) + // Test 10: Get tags with combined options (order + count) t.Run("GetTagsWithCombinedOptions", func(t *testing.T) { fetchedTags, err := db.GetTags(ctx, model.DBListTagsOptions{ WithBookmarkCount: true, @@ -242,3 +304,329 @@ func testGetTagsFunction(t *testing.T, db model.DB) { assert.Equal(t, int64(1), fetchedTags[3].BookmarkCount) }) } + +// testTagBookmarkOperations tests the tag-bookmark relationship operations +func testTagBookmarkOperations(t *testing.T, db model.DB) { + ctx := context.TODO() + + // Create test data + // 1. Create a test bookmark + bookmark := model.BookmarkDTO{ + URL: "https://example.com/tag-operations-test", + Title: "Tag Operations Test", + } + savedBookmarks, err := db.SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + require.Len(t, savedBookmarks, 1) + bookmarkID := savedBookmarks[0].ID + + // 2. Create a test tag + tag := model.Tag{ + Name: "tag-operations-test", + } + createdTags, err := db.CreateTags(ctx, tag) + require.NoError(t, err) + require.Len(t, createdTags, 1) + tagID := createdTags[0].ID + + // Test BookmarkExists function + t.Run("BookmarkExists", func(t *testing.T) { + // Test with existing bookmark + exists, err := db.BookmarkExists(ctx, bookmarkID) + require.NoError(t, err) + assert.True(t, exists, "Bookmark should exist") + + // Test with non-existent bookmark + exists, err = db.BookmarkExists(ctx, 9999) + require.NoError(t, err) + assert.False(t, exists, "Non-existent bookmark should return false") + }) + + // Test TagExists function + t.Run("TagExists", func(t *testing.T) { + // Test with existing tag + exists, err := db.TagExists(ctx, tagID) + require.NoError(t, err) + assert.True(t, exists, "Tag should exist") + + // Test with non-existent tag + exists, err = db.TagExists(ctx, 9999) + require.NoError(t, err) + assert.False(t, exists, "Non-existent tag should return false") + }) + + // Test AddTagToBookmark function + t.Run("AddTagToBookmark", func(t *testing.T) { + // Add tag to bookmark + err := db.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify tag was added by fetching tags for the bookmark + tags, err := db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1) + assert.Equal(t, tagID, tags[0].ID) + assert.Equal(t, "tag-operations-test", tags[0].Name) + + // Test adding the same tag again (should not error) + err = db.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify no duplicate was created + tags, err = db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1) + }) + + // Test RemoveTagFromBookmark function + t.Run("RemoveTagFromBookmark", func(t *testing.T) { + // First ensure the tag is associated with the bookmark + tags, err := db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1, "Tag should be associated with bookmark before removal test") + + // Remove tag from bookmark + err = db.RemoveTagFromBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify tag was removed + tags, err = db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + assert.Len(t, tags, 0, "Tag should be removed from bookmark") + + // Test removing a tag that's not associated (should not error) + err = db.RemoveTagFromBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Test removing a tag from a non-existent bookmark (should not error) + err = db.RemoveTagFromBookmark(ctx, 9999, tagID) + require.NoError(t, err) + + // Test removing a non-existent tag from a bookmark (should not error) + err = db.RemoveTagFromBookmark(ctx, bookmarkID, 9999) + require.NoError(t, err) + }) + + // Test edge cases + t.Run("EdgeCases", func(t *testing.T) { + // Test adding a tag to a non-existent bookmark + // This should not error at the database layer since we're not checking existence there + err := db.AddTagToBookmark(ctx, 9999, tagID) + // The test might fail depending on foreign key constraints in the database + // If it fails, that's acceptable behavior, but we're not explicitly testing for it + if err != nil { + t.Logf("Adding tag to non-existent bookmark failed as expected: %v", err) + } + + // Test adding a non-existent tag to a bookmark + // This should not error at the database layer since we're not checking existence there + err = db.AddTagToBookmark(ctx, bookmarkID, 9999) + // The test might fail depending on foreign key constraints in the database + // If it fails, that's acceptable behavior, but we're not explicitly testing for it + if err != nil { + t.Logf("Adding non-existent tag to bookmark failed as expected: %v", err) + } + }) +} + +// testTagExists tests the TagExists function +func testTagExists(t *testing.T, db model.DB) { + ctx := context.TODO() + + // Create a test tag + tag := model.Tag{ + Name: "tag-exists-test", + } + createdTags, err := db.CreateTags(ctx, tag) + require.NoError(t, err) + require.Len(t, createdTags, 1) + tagID := createdTags[0].ID + + // Test with existing tag + exists, err := db.TagExists(ctx, tagID) + require.NoError(t, err) + assert.True(t, exists, "Tag should exist") + + // Test with non-existent tag + exists, err = db.TagExists(ctx, 9999) + require.NoError(t, err) + assert.False(t, exists, "Non-existent tag should return false") +} + +// testBookmarkExists tests the BookmarkExists function +func testBookmarkExists(t *testing.T, db model.DB) { + ctx := context.TODO() + + // Create a test bookmark + bookmark := model.BookmarkDTO{ + URL: "https://example.com/bookmark-exists-test", + Title: "Bookmark Exists Test", + } + savedBookmarks, err := db.SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + require.Len(t, savedBookmarks, 1) + bookmarkID := savedBookmarks[0].ID + + // Test with existing bookmark + exists, err := db.BookmarkExists(ctx, bookmarkID) + require.NoError(t, err) + assert.True(t, exists, "Bookmark should exist") + + // Test with non-existent bookmark + exists, err = db.BookmarkExists(ctx, 9999) + require.NoError(t, err) + assert.False(t, exists, "Non-existent bookmark should return false") +} + +// testAddTagToBookmark tests the AddTagToBookmark function +func testAddTagToBookmark(t *testing.T, db model.DB) { + ctx := context.TODO() + + // Create test data + bookmark := model.BookmarkDTO{ + URL: "https://example.com/add-tag-test", + Title: "Add Tag Test", + } + savedBookmarks, err := db.SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + require.Len(t, savedBookmarks, 1) + bookmarkID := savedBookmarks[0].ID + + tag := model.Tag{ + Name: "add-tag-test", + } + createdTags, err := db.CreateTags(ctx, tag) + require.NoError(t, err) + require.Len(t, createdTags, 1) + tagID := createdTags[0].ID + + // Add tag to bookmark + err = db.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify tag was added by fetching tags for the bookmark + tags, err := db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1) + assert.Equal(t, tagID, tags[0].ID) + assert.Equal(t, "add-tag-test", tags[0].Name) + + // Test adding the same tag again (should not error) + err = db.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify no duplicate was created + tags, err = db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1) +} + +// testRemoveTagFromBookmark tests the RemoveTagFromBookmark function +func testRemoveTagFromBookmark(t *testing.T, db model.DB) { + ctx := context.TODO() + + // Create test data + bookmark := model.BookmarkDTO{ + URL: "https://example.com/remove-tag-test", + Title: "Remove Tag Test", + } + savedBookmarks, err := db.SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + require.Len(t, savedBookmarks, 1) + bookmarkID := savedBookmarks[0].ID + + tag := model.Tag{ + Name: "remove-tag-test", + } + createdTags, err := db.CreateTags(ctx, tag) + require.NoError(t, err) + require.Len(t, createdTags, 1) + tagID := createdTags[0].ID + + // Add tag to bookmark first + err = db.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify tag was added + tags, err := db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1, "Tag should be associated with bookmark before removal test") + + // Remove tag from bookmark + err = db.RemoveTagFromBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify tag was removed + tags, err = db.GetTags(ctx, model.DBListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + assert.Len(t, tags, 0, "Tag should be removed from bookmark") + + // Test removing a tag that's not associated (should not error) + err = db.RemoveTagFromBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Test removing a tag from a non-existent bookmark (should not error) + err = db.RemoveTagFromBookmark(ctx, 9999, tagID) + require.NoError(t, err) + + // Test removing a non-existent tag from a bookmark (should not error) + err = db.RemoveTagFromBookmark(ctx, bookmarkID, 9999) + require.NoError(t, err) +} + +// testTagBookmarkEdgeCases tests edge cases for tag-bookmark operations +func testTagBookmarkEdgeCases(t *testing.T, db model.DB) { + ctx := context.TODO() + + // Create test data + bookmark := model.BookmarkDTO{ + URL: "https://example.com/edge-cases-test", + Title: "Edge Cases Test", + } + savedBookmarks, err := db.SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + require.Len(t, savedBookmarks, 1) + bookmarkID := savedBookmarks[0].ID + + tag := model.Tag{ + Name: "edge-cases-test", + } + createdTags, err := db.CreateTags(ctx, tag) + require.NoError(t, err) + require.Len(t, createdTags, 1) + tagID := createdTags[0].ID + + // Test adding a tag to a non-existent bookmark + // This should not error at the database layer since we're not checking existence there + err = db.AddTagToBookmark(ctx, 9999, tagID) + // The test might fail depending on foreign key constraints in the database + // If it fails, that's acceptable behavior, but we're not explicitly testing for it + if err != nil { + t.Logf("Adding tag to non-existent bookmark failed as expected: %v", err) + } + + // Test adding a non-existent tag to a bookmark + // This should not error at the database layer since we're not checking existence there + err = db.AddTagToBookmark(ctx, bookmarkID, 9999) + // The test might fail depending on foreign key constraints in the database + // If it fails, that's acceptable behavior, but we're not explicitly testing for it + if err != nil { + t.Logf("Adding non-existent tag to bookmark failed as expected: %v", err) + } +} diff --git a/internal/database/database_test.go b/internal/database/database_test.go index eb32d087b..263c86653 100644 --- a/internal/database/database_test.go +++ b/internal/database/database_test.go @@ -34,18 +34,23 @@ func testDatabase(t *testing.T, dbFactory testDatabaseFactory) { "testGetBookmarksCount": testGetBookmarksCount, "testSaveBookmark": testSaveBookmark, "testBulkUpdateBookmarkTags": testBulkUpdateBookmarkTags, + "testBookmarkExists": testBookmarkExists, // Tags - "testCreateTag": testCreateTag, - "testCreateTags": testCreateTags, - "testGetTags": testGetTags, - "testGetTagsFunction": testGetTagsFunction, - // "testGetTagsBookmarkCount": testGetTagsBookmarkCount, - "testGetTag": testGetTag, - "testGetTagNotExistent": testGetTagNotExistent, - "testUpdateTag": testUpdateTag, - "testRenameTag": testRenameTag, - "testDeleteTag": testDeleteTag, - "testDeleteTagNotExistent": testDeleteTagNotExistent, + "testCreateTag": testCreateTag, + "testCreateTags": testCreateTags, + "testTagExists": testTagExists, + "testGetTags": testGetTags, + "testGetTagsFunction": testGetTagsFunction, + "testGetTag": testGetTag, + "testGetTagNotExistent": testGetTagNotExistent, + "testUpdateTag": testUpdateTag, + "testRenameTag": testRenameTag, + "testDeleteTag": testDeleteTag, + "testDeleteTagNotExistent": testDeleteTagNotExistent, + "testAddTagToBookmark": testAddTagToBookmark, + "testRemoveTagFromBookmark": testRemoveTagFromBookmark, + "testTagBookmarkEdgeCases": testTagBookmarkEdgeCases, + "testTagBookmarkOperations": testTagBookmarkOperations, // Accounts "testCreateAccount": testCreateAccount, "testCreateDuplicateAccount": testCreateDuplicateAccount, diff --git a/internal/domains/bookmark_tags_test.go b/internal/domains/bookmark_tags_test.go new file mode 100644 index 000000000..bce6f1c8c --- /dev/null +++ b/internal/domains/bookmark_tags_test.go @@ -0,0 +1,127 @@ +package domains_test + +import ( + "context" + "testing" + + "github.com/go-shiori/shiori/internal/model" + "github.com/go-shiori/shiori/internal/testutil" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBookmarkTagOperations(t *testing.T) { + ctx := context.Background() + logger := logrus.New() + + // Setup using the test configuration and dependencies + _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) + bookmarksDomain := deps.Domains().Bookmarks() + tagsDomain := deps.Domains().Tags() + db := deps.Database() + + // Create a test bookmark + bookmark := model.BookmarkDTO{ + URL: "https://example.com/bookmark-tags-test", + Title: "Bookmark Tags Test", + } + savedBookmarks, err := db.SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + require.Len(t, savedBookmarks, 1) + bookmarkID := savedBookmarks[0].ID + + // Create a test tag + tagDTO := model.TagDTO{ + Tag: model.Tag{ + Name: "test-tag", + }, + } + createdTag, err := tagsDomain.CreateTag(ctx, tagDTO) + require.NoError(t, err) + tagID := createdTag.ID + + // Test BookmarkExists + t.Run("BookmarkExists", func(t *testing.T) { + // Test with existing bookmark + exists, err := bookmarksDomain.BookmarkExists(ctx, bookmarkID) + require.NoError(t, err) + assert.True(t, exists, "Bookmark should exist") + + // Test with non-existent bookmark + exists, err = bookmarksDomain.BookmarkExists(ctx, 9999) + require.NoError(t, err) + assert.False(t, exists, "Non-existent bookmark should not exist") + }) + + // Test TagExists + t.Run("TagExists", func(t *testing.T) { + // Test with existing tag + exists, err := tagsDomain.TagExists(ctx, tagID) + require.NoError(t, err) + assert.True(t, exists, "Tag should exist") + + // Test with non-existent tag + exists, err = tagsDomain.TagExists(ctx, 9999) + require.NoError(t, err) + assert.False(t, exists, "Non-existent tag should not exist") + }) + + // Test AddTagToBookmark + t.Run("AddTagToBookmark", func(t *testing.T) { + // Add tag to bookmark + err := bookmarksDomain.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify tag was added by listing tags for the bookmark + tags, err := tagsDomain.ListTags(ctx, model.ListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1, "Should have exactly one tag") + assert.Equal(t, tagID, tags[0].ID, "Tag ID should match") + assert.Equal(t, "test-tag", tags[0].Name, "Tag name should match") + + // Test adding the same tag again (should not error) + err = bookmarksDomain.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err, "Adding the same tag again should not error") + + // Test adding tag to non-existent bookmark + err = bookmarksDomain.AddTagToBookmark(ctx, 9999, tagID) + require.Error(t, err) + assert.ErrorIs(t, err, model.ErrBookmarkNotFound, "Should return bookmark not found error") + + // Test adding non-existent tag to bookmark + err = bookmarksDomain.AddTagToBookmark(ctx, bookmarkID, 9999) + require.Error(t, err) + assert.ErrorIs(t, err, model.ErrTagNotFound, "Should return tag not found error") + }) + + // Test RemoveTagFromBookmark + t.Run("RemoveTagFromBookmark", func(t *testing.T) { + // Remove tag from bookmark + err := bookmarksDomain.RemoveTagFromBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Verify tag was removed by listing tags for the bookmark + tags, err := tagsDomain.ListTags(ctx, model.ListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 0, "Should have no tags after removal") + + // Test removing a tag that's not associated with the bookmark (should not error) + err = bookmarksDomain.RemoveTagFromBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err, "Removing a tag that's not associated should not error") + + // Test removing tag from non-existent bookmark + err = bookmarksDomain.RemoveTagFromBookmark(ctx, 9999, tagID) + require.Error(t, err) + assert.ErrorIs(t, err, model.ErrBookmarkNotFound, "Should return bookmark not found error") + + // Test removing non-existent tag from bookmark + err = bookmarksDomain.RemoveTagFromBookmark(ctx, bookmarkID, 9999) + require.Error(t, err) + assert.ErrorIs(t, err, model.ErrTagNotFound, "Should return tag not found error") + }) +} diff --git a/internal/domains/bookmarks.go b/internal/domains/bookmarks.go index 37a269098..de6367bb9 100644 --- a/internal/domains/bookmarks.go +++ b/internal/domains/bookmarks.go @@ -113,6 +113,59 @@ func (d *BookmarksDomain) BulkUpdateBookmarkTags(ctx context.Context, bookmarkID return nil } +// AddTagToBookmark adds a tag to a bookmark +func (d *BookmarksDomain) AddTagToBookmark(ctx context.Context, bookmarkID int, tagID int) error { + // Check if bookmark exists + exists, err := d.BookmarkExists(ctx, bookmarkID) + if err != nil { + return err + } + if !exists { + return model.ErrBookmarkNotFound + } + + // Check if tag exists + exists, err = d.deps.Domains().Tags().TagExists(ctx, tagID) + if err != nil { + return err + } + if !exists { + return model.ErrTagNotFound + } + + // Add tag to bookmark + return d.deps.Database().AddTagToBookmark(ctx, bookmarkID, tagID) +} + +// RemoveTagFromBookmark removes a tag from a bookmark +func (d *BookmarksDomain) RemoveTagFromBookmark(ctx context.Context, bookmarkID int, tagID int) error { + // Check if bookmark exists + exists, err := d.BookmarkExists(ctx, bookmarkID) + if err != nil { + return err + } + if !exists { + return model.ErrBookmarkNotFound + } + + // Check if tag exists + exists, err = d.deps.Domains().Tags().TagExists(ctx, tagID) + if err != nil { + return err + } + if !exists { + return model.ErrTagNotFound + } + + // Remove tag from bookmark + return d.deps.Database().RemoveTagFromBookmark(ctx, bookmarkID, tagID) +} + +// BookmarkExists checks if a bookmark with the given ID exists +func (d *BookmarksDomain) BookmarkExists(ctx context.Context, id int) (bool, error) { + return d.deps.Database().BookmarkExists(ctx, id) +} + func NewBookmarksDomain(deps model.Dependencies) *BookmarksDomain { return &BookmarksDomain{ deps: deps, diff --git a/internal/domains/tags.go b/internal/domains/tags.go index 5e9dd15ea..2c595bcc8 100644 --- a/internal/domains/tags.go +++ b/internal/domains/tags.go @@ -75,3 +75,8 @@ func (d *tagsDomain) DeleteTag(ctx context.Context, id int) error { return nil } + +// TagExists checks if a tag with the given ID exists +func (d *tagsDomain) TagExists(ctx context.Context, id int) (bool, error) { + return d.deps.Database().TagExists(ctx, id) +} diff --git a/internal/http/handlers/api/v1/accounts.go b/internal/http/handlers/api/v1/accounts.go index 50456f155..4e9ed2c55 100644 --- a/internal/http/handlers/api/v1/accounts.go +++ b/internal/http/handlers/api/v1/accounts.go @@ -44,7 +44,7 @@ func HandleListAccounts(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusOK, accounts) + response.SendJSON(c, http.StatusOK, accounts) } // @Summary Create an account @@ -63,18 +63,18 @@ func HandleCreateAccount(deps model.Dependencies, c model.WebContext) { var payload createAccountPayload if err := json.NewDecoder(c.Request().Body).Decode(&payload); err != nil { - response.SendError(c, http.StatusBadRequest, "invalid json", nil) + response.SendError(c, http.StatusBadRequest, "invalid json") return } account, err := deps.Domains().Accounts().CreateAccount(c.Request().Context(), payload.ToAccountDTO()) if err, isValidationErr := err.(model.ValidationError); isValidationErr { - response.SendError(c, http.StatusBadRequest, err.Error(), nil) + response.SendError(c, http.StatusBadRequest, err.Error()) return } if errors.Is(err, model.ErrAlreadyExists) { - response.SendError(c, http.StatusConflict, "account already exists", nil) + response.SendError(c, http.StatusConflict, "account already exists") return } @@ -84,7 +84,7 @@ func HandleCreateAccount(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusCreated, account) + response.SendJSON(c, http.StatusCreated, account) } // @Summary Delete an account @@ -103,13 +103,13 @@ func HandleDeleteAccount(deps model.Dependencies, c model.WebContext) { id, err := strconv.Atoi(c.Request().PathValue("id")) if err != nil { - response.SendError(c, http.StatusBadRequest, "invalid id", nil) + response.SendError(c, http.StatusBadRequest, "invalid id") return } err = deps.Domains().Accounts().DeleteAccount(c.Request().Context(), id) if errors.Is(err, model.ErrNotFound) { - response.SendError(c, http.StatusNotFound, "account not found", nil) + response.SendError(c, http.StatusNotFound, "account not found") return } @@ -119,7 +119,7 @@ func HandleDeleteAccount(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusNoContent, nil) + response.SendJSON(c, http.StatusNoContent, nil) } // @Summary Update an account @@ -141,13 +141,13 @@ func HandleUpdateAccount(deps model.Dependencies, c model.WebContext) { accountID, err := strconv.Atoi(c.Request().PathValue("id")) if err != nil { - response.SendError(c, http.StatusBadRequest, "invalid id", nil) + response.SendError(c, http.StatusBadRequest, "invalid id") return } var payload updateAccountPayload if err := json.NewDecoder(c.Request().Body).Decode(&payload); err != nil { - response.SendError(c, http.StatusBadRequest, "invalid json", nil) + response.SendError(c, http.StatusBadRequest, "invalid json") return } @@ -156,15 +156,15 @@ func HandleUpdateAccount(deps model.Dependencies, c model.WebContext) { account, err := deps.Domains().Accounts().UpdateAccount(c.Request().Context(), updatedAccount) if errors.Is(err, model.ErrNotFound) { - response.SendError(c, http.StatusNotFound, "account not found", nil) + response.SendError(c, http.StatusNotFound, "account not found") return } if errors.Is(err, model.ErrAlreadyExists) { - response.SendError(c, http.StatusConflict, "account already exists", nil) + response.SendError(c, http.StatusConflict, "account already exists") return } if err, isValidationErr := err.(model.ValidationError); isValidationErr { - response.SendError(c, http.StatusBadRequest, err.Error(), nil) + response.SendError(c, http.StatusBadRequest, err.Error()) return } @@ -174,5 +174,5 @@ func HandleUpdateAccount(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusOK, account) + response.SendJSON(c, http.StatusOK, account) } diff --git a/internal/http/handlers/api/v1/accounts_test.go b/internal/http/handlers/api/v1/accounts_test.go index 081b9ff92..2f8c04f06 100644 --- a/internal/http/handlers/api/v1/accounts_test.go +++ b/internal/http/handlers/api/v1/accounts_test.go @@ -58,10 +58,9 @@ func TestHandleListAccounts(t *testing.T) { HandleListAccounts(deps, c) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - require.Len(t, response.Response.Message, 1) // Admin + created account + response.AssertMessageIsListLength(t, 1) // Admin + created account }) } @@ -146,11 +145,11 @@ func TestHandleCreateAccount(t *testing.T) { }, "POST", "/api/v1/accounts", testutil.WithBody(body)) require.Equal(t, http.StatusCreated, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - response.AssertMessageContains(t, "id") - require.NotZero(t, response.Response.Message.(map[string]interface{})["id"]) + response.AssertMessageJSONKeyValue(t, "id", func(t *testing.T, value any) { + require.NotZero(t, value) + }) }) } @@ -279,11 +278,11 @@ func TestHandleUpdateAccount(t *testing.T) { }, "PATCH", "/api/v1/accounts/"+strconv.Itoa(int(account.ID)), testutil.WithBody(body)) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - response.AssertMessageContains(t, "owner") - require.True(t, response.Response.Message.(map[string]any)["owner"].(bool)) + response.AssertMessageJSONKeyValue(t, "owner", func(t *testing.T, value any) { + require.True(t, value.(bool)) + }) }) t.Run("update with empty payload", func(t *testing.T) { @@ -310,8 +309,7 @@ func TestHandleUpdateAccount(t *testing.T) { require.Equal(t, http.StatusBadRequest, w.Code) // Verify no changes were made - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertNotOk(t) }) @@ -332,10 +330,11 @@ func TestHandleUpdateAccount(t *testing.T) { }, "PATCH", "/api/v1/accounts/"+strconv.Itoa(int(account.ID)), testutil.WithBody(body)) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - require.Equal(t, "newname", response.Response.Message.(map[string]any)["username"]) + response.AssertMessageJSONKeyValue(t, "username", func(t *testing.T, value any) { + require.Equal(t, "newname", value) + }) }) t.Run("update password only", func(t *testing.T) { @@ -412,20 +411,20 @@ func TestHandleUpdateAccount(t *testing.T) { }, "PATCH", "/api/v1/accounts/"+strconv.Itoa(int(account.ID)), testutil.WithBody(body)) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - - config := response.Response.Message.(map[string]any)["config"].(map[string]any) - require.True(t, config["ShowId"].(bool)) - require.True(t, config["ListMode"].(bool)) - require.True(t, config["HideThumbnail"].(bool)) - require.True(t, config["HideExcerpt"].(bool)) - require.Equal(t, "dark", config["Theme"]) - require.True(t, config["KeepMetadata"].(bool)) - require.True(t, config["UseArchive"].(bool)) - require.True(t, config["CreateEbook"].(bool)) - require.True(t, config["MakePublic"].(bool)) + response.AssertMessageJSONKeyValue(t, "config", func(t *testing.T, value any) { + config := value.(map[string]any) + require.True(t, config["ShowId"].(bool)) + require.True(t, config["ListMode"].(bool)) + require.True(t, config["HideThumbnail"].(bool)) + require.True(t, config["HideExcerpt"].(bool)) + require.Equal(t, "dark", config["Theme"]) + require.True(t, config["KeepMetadata"].(bool)) + require.True(t, config["UseArchive"].(bool)) + require.True(t, config["CreateEbook"].(bool)) + require.True(t, config["MakePublic"].(bool)) + }) }) t.Run("update all fields", func(t *testing.T) { @@ -461,20 +460,22 @@ func TestHandleUpdateAccount(t *testing.T) { }, "PATCH", "/api/v1/accounts/"+strconv.Itoa(int(account.ID)), testutil.WithBody(body)) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - - msg := response.Response.Message.(map[string]any) - require.Equal(t, "updated", msg["username"]) - require.True(t, msg["owner"].(bool)) - - config := msg["config"].(map[string]any) - require.True(t, config["ShowId"].(bool)) - require.True(t, config["ListMode"].(bool)) - require.True(t, config["HideThumbnail"].(bool)) - require.True(t, config["HideExcerpt"].(bool)) - require.Equal(t, "dark", config["Theme"]) + response.AssertMessageJSONKeyValue(t, "username", func(t *testing.T, value any) { + require.Equal(t, "updated", value) + }) + response.AssertMessageJSONKeyValue(t, "owner", func(t *testing.T, value any) { + require.True(t, value.(bool)) + }) + response.AssertMessageJSONKeyValue(t, "config", func(t *testing.T, value any) { + config := value.(map[string]any) + require.True(t, config["ShowId"].(bool)) + require.True(t, config["ListMode"].(bool)) + require.True(t, config["HideThumbnail"].(bool)) + require.True(t, config["HideExcerpt"].(bool)) + require.Equal(t, "dark", config["Theme"]) + }) // Verify password change loginBody := `{"username": "updated", "password": "newpass"}` diff --git a/internal/http/handlers/api/v1/auth.go b/internal/http/handlers/api/v1/auth.go index eab98960f..6c6ae0ffe 100644 --- a/internal/http/handlers/api/v1/auth.go +++ b/internal/http/handlers/api/v1/auth.go @@ -43,18 +43,18 @@ type loginResponseMessage struct { func HandleLogin(deps model.Dependencies, c model.WebContext) { var payload loginRequestPayload if err := json.NewDecoder(c.Request().Body).Decode(&payload); err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid JSON payload", nil) + response.SendError(c, http.StatusBadRequest, "Invalid JSON payload") return } if err := payload.IsValid(); err != nil { - response.SendError(c, http.StatusBadRequest, err.Error(), nil) + response.SendError(c, http.StatusBadRequest, err.Error()) return } account, err := deps.Domains().Auth().GetAccountFromCredentials(c.Request().Context(), payload.Username, payload.Password) if err != nil { - response.SendError(c, http.StatusBadRequest, err.Error(), nil) + response.SendError(c, http.StatusBadRequest, err.Error()) return } @@ -71,7 +71,7 @@ func HandleLogin(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusOK, loginResponseMessage{ + response.SendJSON(c, http.StatusOK, loginResponseMessage{ Token: token, Expiration: expirationTime.Unix(), }) @@ -89,7 +89,7 @@ func HandleRefreshToken(deps model.Dependencies, c model.WebContext) { return } - expiration := time.Now().Add(time.Hour * 72) + expiration := time.Now().UTC().Add(time.Hour * 24 * 30) account := c.GetAccount() token, err := deps.Domains().Auth().CreateTokenForAccount(account, expiration) if err != nil { @@ -97,8 +97,9 @@ func HandleRefreshToken(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusAccepted, loginResponseMessage{ - Token: token, + response.SendJSON(c, http.StatusAccepted, loginResponseMessage{ + Token: token, + Expiration: expiration.Unix(), }) } @@ -113,7 +114,7 @@ func HandleGetMe(deps model.Dependencies, c model.WebContext) { if err := middleware.RequireLoggedInUser(deps, c); err != nil { return } - response.Send(c, http.StatusOK, c.GetAccount()) + response.SendJSON(c, http.StatusOK, c.GetAccount()) } type updateAccountPayload struct { @@ -132,7 +133,9 @@ func (p *updateAccountPayload) IsValid() error { } func (p *updateAccountPayload) ToAccountDTO() model.AccountDTO { - account := model.AccountDTO{} + account := model.AccountDTO{ + Config: p.Config, + } if p.NewPassword != "" { account.Password = p.NewPassword } @@ -168,7 +171,7 @@ func HandleUpdateLoggedAccount(deps model.Dependencies, c model.WebContext) { } if err := payload.IsValid(); err != nil { - response.SendError(c, http.StatusBadRequest, err.Error(), nil) + response.SendError(c, http.StatusBadRequest, err.Error()) return } @@ -177,11 +180,14 @@ func HandleUpdateLoggedAccount(deps model.Dependencies, c model.WebContext) { if payload.NewPassword != "" { _, err := deps.Domains().Auth().GetAccountFromCredentials(c.Request().Context(), account.Username, payload.OldPassword) if err != nil { - response.SendError(c, http.StatusBadRequest, "Old password is incorrect", nil) + response.SendError(c, http.StatusBadRequest, "Old password is incorrect") return } } + // TODO: Use a method in the AccountDTO to apply the updates directly: + // account := domains.Accounts().GetAccount(...) + // account.ApplyUpdates(payload) updatedAccount := payload.ToAccountDTO() updatedAccount.ID = account.ID @@ -192,7 +198,7 @@ func HandleUpdateLoggedAccount(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusOK, account) + response.SendJSON(c, http.StatusOK, account) } // @Summary Logout from the current session @@ -213,5 +219,5 @@ func HandleLogout(deps model.Dependencies, c model.WebContext) { Value: "", }) - response.Send(c, http.StatusOK, nil) + response.SendJSON(c, http.StatusOK, nil) } diff --git a/internal/http/handlers/api/v1/auth_test.go b/internal/http/handlers/api/v1/auth_test.go index 59aa48972..afe16d274 100644 --- a/internal/http/handlers/api/v1/auth_test.go +++ b/internal/http/handlers/api/v1/auth_test.go @@ -64,11 +64,14 @@ func TestHandleLogin(t *testing.T) { w := testutil.PerformRequest(deps, HandleLogin, "POST", "/login", testutil.WithBody(body)) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - response.AssertMessageContains(t, "token") - response.AssertMessageContains(t, "expires") + response.AssertMessageJSONKeyValue(t, "token", func(t *testing.T, value any) { + require.NotEmpty(t, value) + }) + response.AssertMessageJSONKeyValue(t, "expires", func(t *testing.T, value any) { + require.NotEmpty(t, value) + }) }) } @@ -90,10 +93,14 @@ func TestHandleRefreshToken(t *testing.T) { w := testutil.PerformRequest(deps, HandleRefreshToken, "POST", "/refresh", testutil.WithAccount(&account)) require.Equal(t, http.StatusAccepted, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - response.AssertMessageContains(t, "token") + response.AssertMessageJSONKeyValue(t, "token", func(t *testing.T, value any) { + require.NotEmpty(t, value) + }) + response.AssertMessageJSONKeyValue(t, "expires", func(t *testing.T, value any) { + require.NotZero(t, value) + }) }) } @@ -113,10 +120,30 @@ func TestHandleGetMe(t *testing.T) { HandleGetMe(deps, c) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - response.AssertMessageContains(t, "username") + response.AssertMessageJSONKeyValue(t, "username", func(t *testing.T, value any) { + require.Equal(t, "user", value) + }) + response.AssertMessageJSONKeyValue(t, "owner", func(t *testing.T, value any) { + require.False(t, value.(bool)) + }) + }) + + t.Run("returns admin info", func(t *testing.T) { + c, w := testutil.NewTestWebContext() + testutil.SetFakeAdmin(c) + HandleGetMe(deps, c) + require.Equal(t, http.StatusOK, w.Code) + + response := testutil.NewTestResponseFromRecorder(w) + response.AssertOk(t) + response.AssertMessageJSONKeyValue(t, "username", func(t *testing.T, value any) { + require.Equal(t, "user", value) + }) + response.AssertMessageJSONKeyValue(t, "owner", func(t *testing.T, value any) { + require.True(t, value.(bool)) + }) }) } @@ -173,17 +200,23 @@ func TestHandleUpdateLoggedAccount(t *testing.T) { "old_password": "gopher", "new_password": "newpass", "config": { - "show_id": true, - "list_mode": true + "ShowId": true, + "ListMode": true } }` w := testutil.PerformRequest(deps, HandleUpdateLoggedAccount, "PATCH", "/account", testutil.WithBody(body), testutil.WithAccount(account)) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - response.AssertMessageContains(t, "config") + response.AssertMessageJSONKeyValue(t, "username", func(t *testing.T, value any) { + require.Equal(t, "shiori", value) + }) + response.AssertMessageJSONKeyValue(t, "config", func(t *testing.T, value any) { + config := value.(map[string]any) + require.True(t, config["ShowId"].(bool)) + require.True(t, config["ListMode"].(bool)) + }) }) } diff --git a/internal/http/handlers/api/v1/bookmark_tags_test.go b/internal/http/handlers/api/v1/bookmark_tags_test.go new file mode 100644 index 000000000..1a71035d8 --- /dev/null +++ b/internal/http/handlers/api/v1/bookmark_tags_test.go @@ -0,0 +1,728 @@ +package api_v1_test + +import ( + "context" + "encoding/json" + "net/http" + "strconv" + "testing" + + api_v1 "github.com/go-shiori/shiori/internal/http/handlers/api/v1" + "github.com/go-shiori/shiori/internal/model" + "github.com/go-shiori/shiori/internal/testutil" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Define the BookmarkTagPayload struct to match the one in the API +type bookmarkTagPayload struct { + TagID int `json:"tag_id"` +} + +func TestBookmarkTagsAPI(t *testing.T) { + ctx := context.Background() + logger := logrus.New() + + // Setup using the test configuration and dependencies + _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) + db := deps.Database() + + // Create a test bookmark + bookmark := model.BookmarkDTO{ + URL: "https://example.com/api-tags-test", + Title: "API Tags Test", + } + savedBookmarks, err := db.SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + require.Len(t, savedBookmarks, 1) + bookmarkID := savedBookmarks[0].ID + + // Create a test tag + tag := model.Tag{ + Name: "api-test-tag", + } + createdTags, err := db.CreateTags(ctx, tag) + require.NoError(t, err) + require.Len(t, createdTags, 1) + tagID := createdTags[0].ID + + // Test authentication requirements + t.Run("AuthenticationRequirements", func(t *testing.T) { + // Test unauthenticated user for GetBookmarkTags + t.Run("UnauthenticatedUserGetTags", func(t *testing.T) { + rec := testutil.PerformRequest( + deps, + api_v1.HandleGetBookmarkTags, + http.MethodGet, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + ) + + require.Equal(t, http.StatusUnauthorized, rec.Code) + }) + + // Test unauthenticated user for AddTagToBookmark + t.Run("UnauthenticatedUserAddTag", func(t *testing.T) { + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + require.Equal(t, http.StatusUnauthorized, rec.Code) + }) + + // Test non-admin user for AddTagToBookmark (which requires admin) + t.Run("NonAdminUserAddTag", func(t *testing.T) { + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeUser(), // Regular user, not admin + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Just check the status code since the response might vary + require.Equal(t, http.StatusForbidden, rec.Code) + }) + + // Test unauthenticated user for RemoveTagFromBookmark + t.Run("UnauthenticatedUserRemoveTag", func(t *testing.T) { + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleRemoveTagFromBookmark, + http.MethodDelete, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + require.Equal(t, http.StatusUnauthorized, rec.Code) + }) + }) + + // Test BulkUpdateBookmarkTags + t.Run("BulkUpdateBookmarkTags", func(t *testing.T) { + // Define the payload struct + type bulkUpdatePayload struct { + BookmarkIDs []int `json:"bookmark_ids"` + TagIDs []int `json:"tag_ids"` + } + + // Test successful bulk update + t.Run("SuccessfulBulkUpdate", func(t *testing.T) { + payload := bulkUpdatePayload{ + BookmarkIDs: []int{bookmarkID}, + TagIDs: []int{tagID}, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleBulkUpdateBookmarkTags, + http.MethodPut, + "/api/v1/bookmarks/bulk/tags", + testutil.WithFakeAdmin(), + testutil.WithBody(string(payloadBytes)), + ) + + require.Equal(t, http.StatusOK, rec.Code) + + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertOk(t) + }) + + // Test unauthenticated user + t.Run("UnauthenticatedUser", func(t *testing.T) { + payload := bulkUpdatePayload{ + BookmarkIDs: []int{bookmarkID}, + TagIDs: []int{tagID}, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleBulkUpdateBookmarkTags, + http.MethodPut, + "/api/v1/bookmarks/bulk/tags", + testutil.WithBody(string(payloadBytes)), + ) + + require.Equal(t, http.StatusUnauthorized, rec.Code) + }) + + // Test invalid request payload + t.Run("InvalidRequestPayload", func(t *testing.T) { + invalidPayload := []byte(`{"bookmark_ids": "invalid", "tag_ids": [1]}`) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleBulkUpdateBookmarkTags, + http.MethodPut, + "/api/v1/bookmarks/bulk/tags", + testutil.WithFakeAdmin(), + testutil.WithBody(string(invalidPayload)), + ) + + require.Equal(t, http.StatusBadRequest, rec.Code) + + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Invalid request payload", value) + }) + }) + + // Test empty bookmark IDs + t.Run("EmptyBookmarkIDs", func(t *testing.T) { + payload := bulkUpdatePayload{ + BookmarkIDs: []int{}, + TagIDs: []int{tagID}, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleBulkUpdateBookmarkTags, + http.MethodPut, + "/api/v1/bookmarks/bulk/tags", + testutil.WithFakeAdmin(), + testutil.WithBody(string(payloadBytes)), + ) + + require.Equal(t, http.StatusBadRequest, rec.Code) + + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "bookmark_ids should not be empty", value) + }) + }) + + // Test empty tag IDs + t.Run("EmptyTagIDs", func(t *testing.T) { + payload := bulkUpdatePayload{ + BookmarkIDs: []int{bookmarkID}, + TagIDs: []int{}, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleBulkUpdateBookmarkTags, + http.MethodPut, + "/api/v1/bookmarks/bulk/tags", + testutil.WithFakeAdmin(), + testutil.WithBody(string(payloadBytes)), + ) + + require.Equal(t, http.StatusBadRequest, rec.Code) + + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "tag_ids should not be empty", value) + }) + }) + + // Test bookmark not found + t.Run("BookmarkNotFound", func(t *testing.T) { + payload := bulkUpdatePayload{ + BookmarkIDs: []int{9999}, // Non-existent bookmark ID + TagIDs: []int{tagID}, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleBulkUpdateBookmarkTags, + http.MethodPut, + "/api/v1/bookmarks/bulk/tags", + testutil.WithFakeAdmin(), + testutil.WithBody(string(payloadBytes)), + ) + + require.Equal(t, http.StatusInternalServerError, rec.Code) + + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Failed to update bookmarks", value) + }) + }) + }) + + // Test GetBookmarkTags + t.Run("GetBookmarkTags", func(t *testing.T) { + // Add a tag to the bookmark first + err := db.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Create a request to get the tags + rec := testutil.PerformRequest( + deps, + api_v1.HandleGetBookmarkTags, + http.MethodGet, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + ) + + // Check the response + require.Equal(t, http.StatusOK, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertOk(t) + + testResp.AssertMessageIsNotEmptyList(t) + + testResp.ForEach(t, func(item map[string]any) { + require.NotZero(t, item["id"]) + require.NotEmpty(t, item["name"]) + }) + }) + + // Test AddTagToBookmark + t.Run("AddTagToBookmark", func(t *testing.T) { + // Remove the tag first to ensure a clean state + err := db.RemoveTagFromBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Create a request to add the tag + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusCreated, rec.Code) + + // Verify the tag was added + tags, err := deps.Domains().Tags().ListTags(ctx, model.ListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 1) + assert.Equal(t, tagID, tags[0].ID) + }) + + // Test RemoveTagFromBookmark + t.Run("RemoveTagFromBookmark", func(t *testing.T) { + // Add the tag first to ensure it exists + err := db.AddTagToBookmark(ctx, bookmarkID, tagID) + require.NoError(t, err) + + // Create a request to remove the tag + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleRemoveTagFromBookmark, + http.MethodDelete, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusOK, rec.Code) + + // Verify the tag was removed + tags, err := deps.Domains().Tags().ListTags(ctx, model.ListTagsOptions{ + BookmarkID: bookmarkID, + }) + require.NoError(t, err) + require.Len(t, tags, 0) + }) + + // Test error cases + t.Run("ErrorCases", func(t *testing.T) { + // Test non-existent bookmark + t.Run("NonExistentBookmark", func(t *testing.T) { + // Create a request to get tags for a non-existent bookmark + rec := testutil.PerformRequest( + deps, + api_v1.HandleGetBookmarkTags, + http.MethodGet, + "/api/v1/bookmarks/9999/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", "9999"), + ) + + // Check the response + require.Equal(t, http.StatusNotFound, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Bookmark not found", value) + }) + }) + + // Test non-existent tag + t.Run("NonExistentTag", func(t *testing.T) { + // Create a request to add a non-existent tag + payload := bookmarkTagPayload{ + TagID: 9999, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusNotFound, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Tag not found", value) + }) + }) + + // Test non-existent bookmark for AddTagToBookmark + t.Run("NonExistentBookmarkForAddTag", func(t *testing.T) { + // Create a request to add a tag to a non-existent bookmark + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/9999/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", "9999"), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusNotFound, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Bookmark not found", value) + }) + }) + + // Test non-existent bookmark for RemoveTagFromBookmark + t.Run("NonExistentBookmarkForRemoveTag", func(t *testing.T) { + // Create a request to remove a tag from a non-existent bookmark + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleRemoveTagFromBookmark, + http.MethodDelete, + "/api/v1/bookmarks/9999/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", "9999"), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusNotFound, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Bookmark not found", value) + }) + }) + + // Test non-existent tag for RemoveTagFromBookmark + t.Run("NonExistentTagForRemoveTag", func(t *testing.T) { + // Create a request to remove a non-existent tag + payload := bookmarkTagPayload{ + TagID: 9999, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleRemoveTagFromBookmark, + http.MethodDelete, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusNotFound, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Tag not found", value) + }) + }) + + // Test invalid bookmark ID + t.Run("InvalidBookmarkID", func(t *testing.T) { + // Create a request with an invalid bookmark ID + rec := testutil.PerformRequest( + deps, + api_v1.HandleGetBookmarkTags, + http.MethodGet, + "/api/v1/bookmarks/invalid/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", "invalid"), + ) + + // Check the response + require.Equal(t, http.StatusBadRequest, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Invalid bookmark ID", value) + }) + }) + + // Test invalid bookmark ID for AddTagToBookmark + t.Run("InvalidBookmarkIDForAddTag", func(t *testing.T) { + // Create a request with an invalid bookmark ID + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/invalid/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", "invalid"), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusBadRequest, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Invalid bookmark ID", value) + }) + }) + + // Test invalid bookmark ID for RemoveTagFromBookmark + t.Run("InvalidBookmarkIDForRemoveTag", func(t *testing.T) { + // Create a request with an invalid bookmark ID + payload := bookmarkTagPayload{ + TagID: tagID, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleRemoveTagFromBookmark, + http.MethodDelete, + "/api/v1/bookmarks/invalid/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", "invalid"), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusBadRequest, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Invalid bookmark ID", value) + }) + }) + + // Test invalid payload + t.Run("InvalidPayload", func(t *testing.T) { + // Create a request with an invalid payload + invalidPayload := []byte(`{"tag_id": "invalid"}`) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(invalidPayload)), + ) + + // Check the response + require.Equal(t, http.StatusBadRequest, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "Invalid request payload", value) + }) + }) + + // Test zero tag ID + t.Run("ZeroTagID", func(t *testing.T) { + // Create a request with a zero tag ID + payload := bookmarkTagPayload{ + TagID: 0, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusBadRequest, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "tag_id should be a positive integer", value) + }) + }) + + // Test negative tag ID + t.Run("NegativeTagID", func(t *testing.T) { + // Create a request with a negative tag ID + payload := bookmarkTagPayload{ + TagID: -1, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleAddTagToBookmark, + http.MethodPost, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusBadRequest, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "tag_id should be a positive integer", value) + }) + }) + + // Test validation for RemoveTagFromBookmark + t.Run("RemoveTagValidation", func(t *testing.T) { + // Create a request with a zero tag ID + payload := bookmarkTagPayload{ + TagID: 0, + } + payloadBytes, err := json.Marshal(payload) + require.NoError(t, err) + + rec := testutil.PerformRequest( + deps, + api_v1.HandleRemoveTagFromBookmark, + http.MethodDelete, + "/api/v1/bookmarks/"+strconv.Itoa(bookmarkID)+"/tags", + testutil.WithFakeAdmin(), + testutil.WithRequestPathValue("id", strconv.Itoa(bookmarkID)), + testutil.WithBody(string(payloadBytes)), + ) + + // Check the response + require.Equal(t, http.StatusBadRequest, rec.Code) + + // Parse the response + testResp := testutil.NewTestResponseFromRecorder(rec) + testResp.AssertNotOk(t) + testResp.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "tag_id should be a positive integer", value) + }) + }) + }) +} diff --git a/internal/http/handlers/api/v1/bookmarks.go b/internal/http/handlers/api/v1/bookmarks.go index 68d24eb00..b1165b91c 100644 --- a/internal/http/handlers/api/v1/bookmarks.go +++ b/internal/http/handlers/api/v1/bookmarks.go @@ -2,6 +2,7 @@ package api_v1 import ( "encoding/json" + "errors" "fmt" "net/http" "strconv" @@ -48,23 +49,23 @@ type readableResponseMessage struct { // @Router /api/v1/bookmarks/id/readable [get] func HandleBookmarkReadable(deps model.Dependencies, c model.WebContext) { if err := middleware.RequireLoggedInUser(deps, c); err != nil { - response.SendError(c, http.StatusForbidden, err.Error(), nil) + response.SendError(c, http.StatusForbidden, err.Error()) return } bookmarkID, err := strconv.Atoi(c.Request().PathValue("id")) if err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid bookmark ID", nil) + response.SendError(c, http.StatusBadRequest, "Invalid bookmark ID") return } bookmark, err := deps.Domains().Bookmarks().GetBookmark(c.Request().Context(), model.DBID(bookmarkID)) if err != nil { - response.SendError(c, http.StatusNotFound, "Bookmark not found", nil) + response.SendError(c, http.StatusNotFound, "Bookmark not found") return } - response.Send(c, http.StatusOK, readableResponseMessage{ + response.SendJSON(c, http.StatusOK, readableResponseMessage{ Content: bookmark.Content, HTML: bookmark.HTML, }) @@ -82,31 +83,31 @@ func HandleBookmarkReadable(deps model.Dependencies, c model.WebContext) { // @Router /api/v1/bookmarks/cache [put] func HandleUpdateCache(deps model.Dependencies, c model.WebContext) { if err := middleware.RequireLoggedInAdmin(deps, c); err != nil { - response.SendError(c, http.StatusForbidden, err.Error(), nil) + response.SendError(c, http.StatusForbidden, err.Error()) return } // Parse request payload var payload updateCachePayload if err := json.NewDecoder(c.Request().Body).Decode(&payload); err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid request payload", nil) + response.SendError(c, http.StatusBadRequest, "Invalid request payload") return } if err := payload.IsValid(); err != nil { - response.SendError(c, http.StatusBadRequest, err.Error(), nil) + response.SendError(c, http.StatusBadRequest, err.Error()) return } // Get bookmarks from database bookmarks, err := deps.Domains().Bookmarks().GetBookmarks(c.Request().Context(), payload.Ids) if err != nil { - response.SendError(c, http.StatusInternalServerError, "Failed to get bookmarks", nil) + response.SendError(c, http.StatusInternalServerError, "Failed to get bookmarks") return } if len(bookmarks) == 0 { - response.SendError(c, http.StatusNotFound, "No bookmarks found", nil) + response.SendError(c, http.StatusNotFound, "No bookmarks found") return } @@ -158,7 +159,7 @@ func HandleUpdateCache(deps model.Dependencies, c model.WebContext) { wg.Wait() close(chDone) - response.Send(c, http.StatusOK, bookmarks) + response.SendJSON(c, http.StatusOK, bookmarks) } type bulkUpdateBookmarkTagsPayload struct { @@ -176,6 +177,172 @@ func (p *bulkUpdateBookmarkTagsPayload) IsValid() error { return nil } +// HandleGetBookmarkTags gets the tags for a bookmark +// +// @Summary Get tags for a bookmark. +// @Tags Auth +// @securityDefinitions.apikey ApiKeyAuth +// @Produce json +// @Param id path int true "Bookmark ID" +// @Success 200 {array} model.TagDTO +// @Failure 403 {object} nil "Token not provided/invalid" +// @Failure 404 {object} nil "Bookmark not found" +// @Router /api/v1/bookmarks/{id}/tags [get] +func HandleGetBookmarkTags(deps model.Dependencies, c model.WebContext) { + if err := middleware.RequireLoggedInUser(deps, c); err != nil { + response.SendError(c, http.StatusForbidden, err.Error()) + return + } + + bookmarkID, err := strconv.Atoi(c.Request().PathValue("id")) + if err != nil { + response.SendError(c, http.StatusBadRequest, "Invalid bookmark ID") + return + } + + // Check if bookmark exists + exists, err := deps.Domains().Bookmarks().BookmarkExists(c.Request().Context(), bookmarkID) + if err != nil { + response.SendError(c, http.StatusInternalServerError, "Failed to check if bookmark exists") + return + } + if !exists { + response.SendError(c, http.StatusNotFound, "Bookmark not found") + return + } + + // Get bookmark to retrieve its tags + tags, err := deps.Domains().Tags().ListTags(c.Request().Context(), model.ListTagsOptions{ + BookmarkID: bookmarkID, + }) + if err != nil { + response.SendError(c, http.StatusInternalServerError, "Failed to get bookmark tags") + return + } + + response.SendJSON(c, http.StatusOK, tags) +} + +// bookmarkTagPayload is used for both adding and removing tags from bookmarks +type bookmarkTagPayload struct { + TagID int `json:"tag_id" validate:"required"` +} + +func (p *bookmarkTagPayload) IsValid() error { + if p.TagID <= 0 { + return fmt.Errorf("tag_id should be a positive integer") + } + return nil +} + +// HandleAddTagToBookmark adds a tag to a bookmark +// +// @Summary Add a tag to a bookmark. +// @Tags Auth +// @securityDefinitions.apikey ApiKeyAuth +// @Param id path int true "Bookmark ID" +// @Param payload body bookmarkTagPayload true "Add Tag Payload" +// @Produce json +// @Success 200 {object} nil +// @Failure 403 {object} nil "Token not provided/invalid" +// @Failure 404 {object} nil "Bookmark or tag not found" +// @Router /api/v1/bookmarks/{id}/tags [post] +func HandleAddTagToBookmark(deps model.Dependencies, c model.WebContext) { + if err := middleware.RequireLoggedInAdmin(deps, c); err != nil { + response.SendError(c, http.StatusForbidden, err.Error()) + return + } + + bookmarkID, err := strconv.Atoi(c.Request().PathValue("id")) + if err != nil { + response.SendError(c, http.StatusBadRequest, "Invalid bookmark ID") + return + } + + // Parse request payload + var payload bookmarkTagPayload + if err := json.NewDecoder(c.Request().Body).Decode(&payload); err != nil { + response.SendError(c, http.StatusBadRequest, "Invalid request payload") + return + } + + if err := payload.IsValid(); err != nil { + response.SendError(c, http.StatusBadRequest, err.Error()) + return + } + + // Add tag to bookmark + err = deps.Domains().Bookmarks().AddTagToBookmark(c.Request().Context(), bookmarkID, payload.TagID) + if err != nil { + if errors.Is(err, model.ErrBookmarkNotFound) { + response.SendError(c, http.StatusNotFound, "Bookmark not found") + return + } + if errors.Is(err, model.ErrTagNotFound) { + response.SendError(c, http.StatusNotFound, "Tag not found") + return + } + response.SendError(c, http.StatusInternalServerError, "Failed to add tag to bookmark") + return + } + + response.SendJSON(c, http.StatusCreated, nil) +} + +// HandleRemoveTagFromBookmark removes a tag from a bookmark +// +// @Summary Remove a tag from a bookmark. +// @Tags Auth +// @securityDefinitions.apikey ApiKeyAuth +// @Param id path int true "Bookmark ID" +// @Param payload body bookmarkTagPayload true "Remove Tag Payload" +// @Produce json +// @Success 200 {object} nil +// @Failure 403 {object} nil "Token not provided/invalid" +// @Failure 404 {object} nil "Bookmark not found" +// @Router /api/v1/bookmarks/{id}/tags [delete] +func HandleRemoveTagFromBookmark(deps model.Dependencies, c model.WebContext) { + if err := middleware.RequireLoggedInUser(deps, c); err != nil { + response.SendError(c, http.StatusForbidden, err.Error()) + return + } + + bookmarkID, err := strconv.Atoi(c.Request().PathValue("id")) + if err != nil { + response.SendError(c, http.StatusBadRequest, "Invalid bookmark ID") + return + } + + // Parse request payload + var payload bookmarkTagPayload + if err := json.NewDecoder(c.Request().Body).Decode(&payload); err != nil { + response.SendError(c, http.StatusBadRequest, "Invalid request payload") + return + } + + if err := payload.IsValid(); err != nil { + response.SendError(c, http.StatusBadRequest, err.Error()) + return + } + + // Remove tag from bookmark + err = deps.Domains().Bookmarks().RemoveTagFromBookmark(c.Request().Context(), bookmarkID, payload.TagID) + if err != nil { + if errors.Is(err, model.ErrBookmarkNotFound) { + response.SendError(c, http.StatusNotFound, "Bookmark not found") + return + } + if errors.Is(err, model.ErrTagNotFound) { + response.SendError(c, http.StatusNotFound, "Tag not found") + return + } + response.SendError(c, http.StatusInternalServerError, "Failed to remove tag from bookmark") + return + } + + response.SendJSON(c, http.StatusOK, nil) +} + // HandleBulkUpdateBookmarkTags updates the tags for multiple bookmarks // // @Summary Bulk update tags for multiple bookmarks. @@ -190,32 +357,32 @@ func (p *bulkUpdateBookmarkTagsPayload) IsValid() error { // @Router /api/v1/bookmarks/bulk/tags [put] func HandleBulkUpdateBookmarkTags(deps model.Dependencies, c model.WebContext) { if err := middleware.RequireLoggedInUser(deps, c); err != nil { - response.SendError(c, http.StatusForbidden, err.Error(), nil) + response.SendError(c, http.StatusForbidden, err.Error()) return } // Parse request payload var payload bulkUpdateBookmarkTagsPayload if err := json.NewDecoder(c.Request().Body).Decode(&payload); err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid request payload", nil) + response.SendError(c, http.StatusBadRequest, "Invalid request payload") return } if err := payload.IsValid(); err != nil { - response.SendError(c, http.StatusBadRequest, err.Error(), nil) + response.SendError(c, http.StatusBadRequest, err.Error()) return } // Use the domain method to update bookmark tags err := deps.Domains().Bookmarks().BulkUpdateBookmarkTags(c.Request().Context(), payload.BookmarkIDs, payload.TagIDs) if err != nil { - if err == model.ErrBookmarkNotFound { - response.SendError(c, http.StatusNotFound, "No bookmarks found", nil) + if errors.Is(err, model.ErrBookmarkNotFound) { + response.SendError(c, http.StatusNotFound, "No bookmarks found") return } - response.SendError(c, http.StatusInternalServerError, "Failed to update bookmarks", nil) + response.SendError(c, http.StatusInternalServerError, "Failed to update bookmarks") return } - response.Send(c, http.StatusOK, nil) + response.SendJSON(c, http.StatusOK, nil) } diff --git a/internal/http/handlers/api/v1/bookmarks_test.go b/internal/http/handlers/api/v1/bookmarks_test.go index 7e78e49d2..61cff1754 100644 --- a/internal/http/handlers/api/v1/bookmarks_test.go +++ b/internal/http/handlers/api/v1/bookmarks_test.go @@ -78,11 +78,14 @@ func TestHandleBookmarkReadable(t *testing.T) { ) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromBytes(w.Body.Bytes()) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - require.Equal(t, bookmark.Content, response.Response.Message.(map[string]interface{})["content"]) - require.Equal(t, bookmark.HTML, response.Response.Message.(map[string]interface{})["html"]) + response.AssertMessageJSONKeyValue(t, "content", func(t *testing.T, value any) { + require.Equal(t, bookmark.Content, value) + }) + response.AssertMessageJSONKeyValue(t, "html", func(t *testing.T, value any) { + require.Equal(t, bookmark.HTML, value) + }) }) } @@ -180,8 +183,7 @@ func TestHandleUpdateCache(t *testing.T) { ) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromBytes(w.Body.Bytes()) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) // TODO: remove this sleep after refactoring into a job system @@ -311,8 +313,7 @@ func TestHandleUpdateBookmarkTags(t *testing.T) { require.Equal(t, http.StatusOK, w.Code) // Verify the response - response, err := testutil.NewTestResponseFromBytes(w.Body.Bytes()) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) }) } diff --git a/internal/http/handlers/api/v1/system.go b/internal/http/handlers/api/v1/system.go index 5924b8dbc..382543adf 100644 --- a/internal/http/handlers/api/v1/system.go +++ b/internal/http/handlers/api/v1/system.go @@ -32,7 +32,7 @@ func HandleSystemInfo(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusOK, infoResponse{ + response.SendJSON(c, http.StatusOK, infoResponse{ Version: struct { Tag string `json:"tag"` Commit string `json:"commit"` diff --git a/internal/http/handlers/api/v1/system_test.go b/internal/http/handlers/api/v1/system_test.go index 7547e10c2..c2136adb6 100644 --- a/internal/http/handlers/api/v1/system_test.go +++ b/internal/http/handlers/api/v1/system_test.go @@ -35,12 +35,17 @@ func TestHandleSystemInfo(t *testing.T) { require.Equal(t, http.StatusOK, w.Code) require.Equal(t, "application/json", w.Header().Get("Content-Type")) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - response.AssertMessageContains(t, "version") - response.AssertMessageContains(t, "database") - response.AssertMessageContains(t, "os") + response.AssertMessageJSONKeyValue(t, "version", func(t *testing.T, value any) { + require.NotEmpty(t, value) + }) + response.AssertMessageJSONKeyValue(t, "database", func(t *testing.T, value any) { + require.NotEmpty(t, value) + }) + response.AssertMessageJSONKeyValue(t, "os", func(t *testing.T, value any) { + require.NotEmpty(t, value) + }) }) } diff --git a/internal/http/handlers/api/v1/tags.go b/internal/http/handlers/api/v1/tags.go index 06d654792..04c20eda1 100644 --- a/internal/http/handlers/api/v1/tags.go +++ b/internal/http/handlers/api/v1/tags.go @@ -17,6 +17,7 @@ import ( // @Produce json // @Param with_bookmark_count query boolean false "Include bookmark count for each tag" // @Param bookmark_id query integer false "Filter tags by bookmark ID" +// @Param search query string false "Search tags by name" // @Success 200 {array} model.TagDTO // @Failure 403 {object} nil "Authentication required" // @Failure 500 {object} nil "Internal server error" @@ -28,29 +29,39 @@ func HandleListTags(deps model.Dependencies, c model.WebContext) { // Parse query parameters withBookmarkCount := c.Request().URL.Query().Get("with_bookmark_count") == "true" + search := c.Request().URL.Query().Get("search") var bookmarkID int if bookmarkIDStr := c.Request().URL.Query().Get("bookmark_id"); bookmarkIDStr != "" { var err error bookmarkID, err = strconv.Atoi(bookmarkIDStr) if err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid bookmark ID", nil) + response.SendError(c, http.StatusBadRequest, "Invalid bookmark ID") return } } - tags, err := deps.Domains().Tags().ListTags(c.Request().Context(), model.ListTagsOptions{ + // Create options and validate + opts := model.ListTagsOptions{ WithBookmarkCount: withBookmarkCount, BookmarkID: bookmarkID, OrderBy: model.DBTagOrderByTagName, - }) + Search: search, + } + + if err := opts.IsValid(); err != nil { + response.SendError(c, http.StatusBadRequest, err.Error()) + return + } + + tags, err := deps.Domains().Tags().ListTags(c.Request().Context(), opts) if err != nil { deps.Logger().WithError(err).Error("failed to get tags") response.SendInternalServerError(c) return } - response.Send(c, http.StatusOK, tags) + response.SendJSON(c, http.StatusOK, tags) } // @Summary Get tag @@ -72,7 +83,7 @@ func HandleGetTag(deps model.Dependencies, c model.WebContext) { idParam := c.Request().PathValue("id") id, err := strconv.Atoi(idParam) if err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid tag ID", nil) + response.SendError(c, http.StatusBadRequest, "Invalid tag ID") return } @@ -87,7 +98,7 @@ func HandleGetTag(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusOK, tag) + response.SendJSON(c, http.StatusOK, tag) } // @Summary Create tag @@ -110,12 +121,12 @@ func HandleCreateTag(deps model.Dependencies, c model.WebContext) { var tag model.TagDTO err := json.NewDecoder(c.Request().Body).Decode(&tag) if err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid request body", nil) + response.SendError(c, http.StatusBadRequest, "Invalid request body") return } if tag.Name == "" { - response.SendError(c, http.StatusBadRequest, "Tag name is required", nil) + response.SendError(c, http.StatusBadRequest, "Tag name is required") return } @@ -126,7 +137,7 @@ func HandleCreateTag(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusCreated, createdTag) + response.SendJSON(c, http.StatusCreated, createdTag) } // @Summary Update tag @@ -151,19 +162,19 @@ func HandleUpdateTag(deps model.Dependencies, c model.WebContext) { idParam := c.Request().PathValue("id") id, err := strconv.Atoi(idParam) if err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid tag ID", nil) + response.SendError(c, http.StatusBadRequest, "Invalid tag ID") return } var tag model.TagDTO err = json.NewDecoder(c.Request().Body).Decode(&tag) if err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid request body", nil) + response.SendError(c, http.StatusBadRequest, "Invalid request body") return } if tag.Name == "" { - response.SendError(c, http.StatusBadRequest, "Tag name is required", nil) + response.SendError(c, http.StatusBadRequest, "Tag name is required") return } @@ -181,7 +192,7 @@ func HandleUpdateTag(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusOK, updatedTag) + response.SendJSON(c, http.StatusOK, updatedTag) } // @Summary Delete tag @@ -202,7 +213,7 @@ func HandleDeleteTag(deps model.Dependencies, c model.WebContext) { idParam := c.Request().PathValue("id") id, err := strconv.Atoi(idParam) if err != nil { - response.SendError(c, http.StatusBadRequest, "Invalid tag ID", nil) + response.SendError(c, http.StatusBadRequest, "Invalid tag ID") return } @@ -217,5 +228,5 @@ func HandleDeleteTag(deps model.Dependencies, c model.WebContext) { return } - response.Send(c, http.StatusNoContent, nil) + response.SendJSON(c, http.StatusNoContent, nil) } diff --git a/internal/http/handlers/api/v1/tags_test.go b/internal/http/handlers/api/v1/tags_test.go index f55e37732..a2cad2d11 100644 --- a/internal/http/handlers/api/v1/tags_test.go +++ b/internal/http/handlers/api/v1/tags_test.go @@ -2,9 +2,9 @@ package api_v1 import ( "context" - "encoding/json" "net/http" "strconv" + "strings" "testing" "github.com/go-shiori/shiori/internal/model" @@ -35,8 +35,7 @@ func TestHandleListTags(t *testing.T) { w := testutil.PerformRequest(deps, HandleListTags, "GET", "/api/v1/tags", testutil.WithFakeUser()) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) response.AssertMessageIsNotEmptyList(t) }) @@ -50,6 +49,15 @@ func TestHandleListTags(t *testing.T) { require.NoError(t, err) require.Len(t, createdTags, 1) + // Create a bookmark with this tag + bookmark := model.BookmarkDTO{ + URL: "https://example.com/test", + Title: "Test Bookmark", + Tags: []model.TagDTO{{Tag: model.Tag{Name: tag.Name}}}, + } + _, err = deps.Database().SaveBookmarks(ctx, true, bookmark) + require.NoError(t, err) + w := testutil.PerformRequest( deps, HandleListTags, @@ -60,27 +68,19 @@ func TestHandleListTags(t *testing.T) { ) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) - // Verify the response contains tags with bookmark_count field - var tags []model.TagDTO - responseData, err := json.Marshal(response.Response.GetMessage()) - require.NoError(t, err) - err = json.Unmarshal(responseData, &tags) - require.NoError(t, err) - require.NotEmpty(t, tags) - - // The bookmark_count field should be present in the response - // Even if it's 0, it should be included when the parameter is set - for _, tag := range tags { - if tag.Name == "test-tag-with-count" { - // We're just checking that the field exists and is accessible - _ = tag.BookmarkCount - break + response.AssertMessageIsNotEmptyList(t) + + response.ForEach(t, func(item map[string]any) { + t.Logf("item: %+v", item) + if tag, ok := item["name"].(string); ok { + if tag == "test-tag-with-count" { + require.NotZero(t, item["bookmark_count"]) + } } - } + }) }) t.Run("invalid bookmark_id parameter", func(t *testing.T) { @@ -127,27 +127,116 @@ func TestHandleListTags(t *testing.T) { ) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) // Verify the response contains the tag associated with the bookmark - var tags []model.TagDTO - responseData, err := json.Marshal(response.Response.GetMessage()) - require.NoError(t, err) - err = json.Unmarshal(responseData, &tags) + found := false + response.ForEach(t, func(item map[string]any) { + if tag, ok := item["name"].(string); ok { + if tag == "test-tag-for-bookmark" { + found = true + } + } + }) + require.True(t, found, "The tag associated with the bookmark should be in the response") + }) + + t.Run("search parameter", func(t *testing.T) { + _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) + + // Create test tags with different names + tags := []model.Tag{ + {Name: "golang"}, + {Name: "python"}, + {Name: "javascript"}, + } + createdTags, err := deps.Database().CreateTags(ctx, tags...) require.NoError(t, err) + require.Len(t, createdTags, 3) + + // Test searching for "go" + w := testutil.PerformRequest( + deps, + HandleListTags, + "GET", + "/api/v1/tags", + testutil.WithFakeUser(), + testutil.WithRequestQueryParam("search", "go"), + ) + require.Equal(t, http.StatusOK, w.Code) + + response := testutil.NewTestResponseFromRecorder(w) + response.AssertOk(t) + + response.AssertMessageIsNotEmptyList(t) - // Check that we have at least one tag and it's the one we created - require.NotEmpty(t, tags) found := false - for _, t := range tags { - if t.Name == "test-tag-for-bookmark" { - found = true - break + response.ForEach(t, func(item map[string]any) { + if tag, ok := item["name"].(string); ok { + if tag == "golang" { + found = true + } } - } - require.True(t, found, "The tag associated with the bookmark should be in the response") + }) + require.True(t, found, "Tag 'golang' should be present") + + // Test searching for "on" + w = testutil.PerformRequest( + deps, + HandleListTags, + "GET", + "/api/v1/tags", + testutil.WithFakeUser(), + testutil.WithRequestQueryParam("search", "on"), + ) + require.Equal(t, http.StatusOK, w.Code) + + response = testutil.NewTestResponseFromRecorder(w) + response.AssertOk(t) + + response.AssertMessageIsNotEmptyList(t) + + found = false + response.ForEach(t, func(item map[string]any) { + if tag, ok := item["name"].(string); ok { + if strings.Contains(tag, "python") { + found = true + } + } + }) + require.True(t, found, "Tag 'python' should be present") + }) + + t.Run("search and bookmark_id parameters together", func(t *testing.T) { + _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) + + // Create a test bookmark + bookmark := testutil.GetValidBookmark() + bookmarks, err := deps.Database().SaveBookmarks(ctx, true, *bookmark) + require.NoError(t, err) + require.Len(t, bookmarks, 1) + bookmarkID := bookmarks[0].ID + + // Test using both search and bookmark_id parameters + w := testutil.PerformRequest( + deps, + HandleListTags, + "GET", + "/api/v1/tags", + testutil.WithFakeUser(), + testutil.WithRequestQueryParam("search", "go"), + testutil.WithRequestQueryParam("bookmark_id", strconv.Itoa(bookmarkID)), + ) + require.Equal(t, http.StatusBadRequest, w.Code) + + response := testutil.NewTestResponseFromRecorder(w) + response.AssertNotOk(t) + + // Verify the error message + response.AssertMessageJSONKeyValue(t, "error", func(t *testing.T, value any) { + require.Equal(t, "search and bookmark ID filtering cannot be used together", value) + }) }) } @@ -213,18 +302,16 @@ func TestHandleGetTag(t *testing.T) { ) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) // Verify the tag data - var tagDTO model.TagDTO - responseData, err := json.Marshal(response.Response.GetMessage()) - require.NoError(t, err) - err = json.Unmarshal(responseData, &tagDTO) - require.NoError(t, err) - require.Equal(t, tagID, tagDTO.ID) - require.Equal(t, "test-tag", tagDTO.Name) + response.AssertMessageJSONKeyValue(t, "id", func(t *testing.T, value any) { + require.Equal(t, tagID, int(value.(float64))) // TODO: Float64?? + }) + response.AssertMessageJSONKeyValue(t, "name", func(t *testing.T, value any) { + require.Equal(t, "test-tag", value) + }) }) } @@ -276,18 +363,16 @@ func TestHandleCreateTag(t *testing.T) { ) require.Equal(t, http.StatusCreated, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) // Verify the created tag - var tagDTO model.TagDTO - responseData, err := json.Marshal(response.Response.GetMessage()) - require.NoError(t, err) - err = json.Unmarshal(responseData, &tagDTO) - require.NoError(t, err) - require.Greater(t, tagDTO.ID, 0) - require.Equal(t, "new-test-tag", tagDTO.Name) + response.AssertMessageJSONKeyValue(t, "name", func(t *testing.T, value any) { + require.Equal(t, "new-test-tag", value) + }) + response.AssertMessageJSONKeyValue(t, "id", func(t *testing.T, value any) { + require.Greater(t, value.(float64), float64(0)) // TODO: Float64?? + }) }) } @@ -383,18 +468,19 @@ func TestHandleUpdateTag(t *testing.T) { ) require.Equal(t, http.StatusOK, w.Code) - response, err := testutil.NewTestResponseFromReader(w.Body) - require.NoError(t, err) + response := testutil.NewTestResponseFromRecorder(w) response.AssertOk(t) // Verify the updated tag - var tagDTO model.TagDTO - responseData, err := json.Marshal(response.Response.GetMessage()) - require.NoError(t, err) - err = json.Unmarshal(responseData, &tagDTO) + response.AssertMessageJSONKeyValue(t, "name", func(t *testing.T, value any) { + require.Equal(t, "updated-test-tag", value) + }) + + // Ensure database was updated + updatedTag, exists, err := deps.Database().GetTag(ctx, tagID) require.NoError(t, err) - require.Equal(t, tagID, tagDTO.ID) - require.Equal(t, "updated-test-tag", tagDTO.Name) + require.True(t, exists) + require.Equal(t, "updated-test-tag", updatedTag.Name) }) } diff --git a/internal/http/handlers/bookmark.go b/internal/http/handlers/bookmark.go index 4bca19b38..33ea33804 100644 --- a/internal/http/handlers/bookmark.go +++ b/internal/http/handlers/bookmark.go @@ -15,17 +15,17 @@ import ( func getBookmark(deps model.Dependencies, c model.WebContext) (*model.BookmarkDTO, error) { bookmarkID, err := strconv.Atoi(c.Request().PathValue("id")) if err != nil { - return nil, response.SendError(c, http.StatusNotFound, "Invalid bookmark ID", nil) + return nil, response.SendError(c, http.StatusNotFound, "Invalid bookmark ID") } if bookmarkID == 0 { - return nil, response.SendError(c, http.StatusNotFound, "Bookmark not found", nil) + return nil, response.SendError(c, http.StatusNotFound, "Bookmark not found") } // Get bookmark from database bookmark, err := deps.Domains().Bookmarks().GetBookmark(c.Request().Context(), model.DBID(bookmarkID)) if err != nil { - return nil, response.SendError(c, http.StatusNotFound, "Bookmark not found", nil) + return nil, response.SendError(c, http.StatusNotFound, "Bookmark not found") } // Check access permissions @@ -165,7 +165,7 @@ func HandleBookmarkEbook(deps model.Dependencies, c model.WebContext) { ebookPath := model.GetEbookPath(bookmark) if !deps.Domains().Storage().FileExists(ebookPath) { - response.SendError(c, http.StatusNotFound, "Ebook not found", nil) + response.SendError(c, http.StatusNotFound, "Ebook not found") return } diff --git a/internal/http/handlers/bookmark_test.go b/internal/http/handlers/bookmark_test.go index 462753e0e..f301ece73 100644 --- a/internal/http/handlers/bookmark_test.go +++ b/internal/http/handlers/bookmark_test.go @@ -17,7 +17,7 @@ func TestGetBookmark(t *testing.T) { logger := logrus.New() _, deps := testutil.GetTestConfigurationAndDependencies(t, context.TODO(), logger) - err := templates.SetupTemplates() + err := templates.SetupTemplates(deps.Config()) require.NoError(t, err) // Create a private and a public bookmark to use in tests @@ -75,7 +75,7 @@ func TestBookmarkContentHandler(t *testing.T) { logger := logrus.New() _, deps := testutil.GetTestConfigurationAndDependencies(t, context.Background(), logger) - err := templates.SetupTemplates() + err := templates.SetupTemplates(deps.Config()) require.NoError(t, err) bookmark := testutil.GetValidBookmark() @@ -105,7 +105,7 @@ func TestBookmarkFileHandlers(t *testing.T) { logger := logrus.New() _, deps := testutil.GetTestConfigurationAndDependencies(t, context.Background(), logger) - err := templates.SetupTemplates() + err := templates.SetupTemplates(deps.Config()) require.NoError(t, err) bookmark := testutil.GetValidBookmark() diff --git a/internal/http/handlers/frontend.go b/internal/http/handlers/frontend.go index 217d50afe..280d3f513 100644 --- a/internal/http/handlers/frontend.go +++ b/internal/http/handlers/frontend.go @@ -8,19 +8,27 @@ import ( "github.com/go-shiori/shiori/internal/http/response" "github.com/go-shiori/shiori/internal/model" views "github.com/go-shiori/shiori/internal/view" + webapp "github.com/go-shiori/shiori/webapp" ) type assetsFS struct { http.FileSystem + serveWebUIV2 bool } func (fs assetsFS) Open(name string) (http.File, error) { - return fs.FileSystem.Open(path.Join("assets", name)) + pathJoin := "assets" + if fs.serveWebUIV2 { + pathJoin = "dist/assets" + } + + return fs.FileSystem.Open(path.Join(pathJoin, name)) } -func newAssetsFS(fs embed.FS) http.FileSystem { +func newAssetsFS(fs embed.FS, serveWebUIV2 bool) http.FileSystem { return assetsFS{ - FileSystem: http.FS(fs), + FileSystem: http.FS(fs), + serveWebUIV2: serveWebUIV2, } } @@ -38,6 +46,9 @@ func HandleFrontend(deps model.Dependencies, c model.WebContext) { // HandleAssets serves static assets func HandleAssets(deps model.Dependencies, c model.WebContext) { - fs := newAssetsFS(views.Assets) - http.StripPrefix("/assets/", http.FileServer(fs)).ServeHTTP(c.ResponseWriter(), c.Request()) + fs := views.Assets + if deps.Config().Http.ServeWebUIV2 { + fs = webapp.Assets + } + http.StripPrefix("/assets/", http.FileServer(newAssetsFS(fs, deps.Config().Http.ServeWebUIV2))).ServeHTTP(c.ResponseWriter(), c.Request()) } diff --git a/internal/http/handlers/frontend_test.go b/internal/http/handlers/frontend_test.go index 9ae7d0cbf..eadac6936 100644 --- a/internal/http/handlers/frontend_test.go +++ b/internal/http/handlers/frontend_test.go @@ -15,7 +15,7 @@ func TestHandleFrontend(t *testing.T) { logger := logrus.New() _, deps := testutil.GetTestConfigurationAndDependencies(t, context.Background(), logger) - err := templates.SetupTemplates() + err := templates.SetupTemplates(deps.Config()) require.NoError(t, err) t.Run("serves index page", func(t *testing.T) { diff --git a/internal/http/handlers/system.go b/internal/http/handlers/system.go index 9048e6604..f1873425f 100644 --- a/internal/http/handlers/system.go +++ b/internal/http/handlers/system.go @@ -9,7 +9,7 @@ import ( // HandleLiveness handles the liveness check endpoint func HandleLiveness(deps model.Dependencies, c model.WebContext) { - response.Send(c, http.StatusOK, struct { + response.SendJSON(c, http.StatusOK, struct { Version string `json:"version"` Commit string `json:"commit"` Date string `json:"date"` diff --git a/internal/http/handlers/system_test.go b/internal/http/handlers/system_test.go index 21a666bba..d83b8ae28 100644 --- a/internal/http/handlers/system_test.go +++ b/internal/http/handlers/system_test.go @@ -2,14 +2,10 @@ package handlers import ( "context" - "encoding/json" - "net/http" "testing" - "github.com/go-shiori/shiori/internal/model" "github.com/go-shiori/shiori/internal/testutil" "github.com/sirupsen/logrus" - "github.com/stretchr/testify/require" ) func TestHandleLiveness(t *testing.T) { @@ -20,49 +16,17 @@ func TestHandleLiveness(t *testing.T) { c, w := testutil.NewTestWebContext() HandleLiveness(deps, c) - require.Equal(t, http.StatusOK, w.Code) - require.Equal(t, "application/json", w.Header().Get("Content-Type")) - - var response struct { - Message struct { - Version string `json:"version"` - Commit string `json:"commit"` - Date string `json:"date"` - } `json:"message"` - } - - err := json.NewDecoder(w.Body).Decode(&response) - require.NoError(t, err) - - // Check build info is populated - require.Equal(t, model.BuildVersion, response.Message.Version) - require.Equal(t, model.BuildCommit, response.Message.Commit) - require.Equal(t, model.BuildDate, response.Message.Date) + response := testutil.NewTestResponseFromRecorder(w) + response.AssertOk(t) + response.AssertMessageJSONContains(t, `{"version":"dev","commit":"none","date":"unknown"}`) }) t.Run("handles without auth", func(t *testing.T) { // Test that liveness check works without authentication c, w := testutil.NewTestWebContext() HandleLiveness(deps, c) - require.Equal(t, http.StatusOK, w.Code) - }) - - t.Run("returns valid JSON", func(t *testing.T) { - c, w := testutil.NewTestWebContext() - HandleLiveness(deps, c) - - var response struct { - Message struct { - Version string `json:"version"` - Commit string `json:"commit"` - Date string `json:"date"` - } `json:"message"` - } - err := json.NewDecoder(w.Body).Decode(&response) - require.NoError(t, err) - require.Equal(t, model.BuildVersion, response.Message.Version) - require.Equal(t, model.BuildCommit, response.Message.Commit) - require.Equal(t, model.BuildDate, response.Message.Date) + response := testutil.NewTestResponseFromRecorder(w) + response.AssertOk(t) }) } diff --git a/internal/http/middleware/auth.go b/internal/http/middleware/auth.go index 8307a7722..a45b5be44 100644 --- a/internal/http/middleware/auth.go +++ b/internal/http/middleware/auth.go @@ -53,7 +53,7 @@ func (m *AuthMiddleware) OnResponse(deps model.Dependencies, c model.WebContext) // RequireLoggedInUser ensures a user is authenticated func RequireLoggedInUser(deps model.Dependencies, c model.WebContext) error { if !c.UserIsLogged() { - response.SendError(c, http.StatusUnauthorized, "Authentication required", nil) + response.SendError(c, http.StatusUnauthorized, "Authentication required") return fmt.Errorf("authentication required") } return nil @@ -67,7 +67,7 @@ func RequireLoggedInAdmin(deps model.Dependencies, c model.WebContext) error { } if !account.IsOwner() { - response.SendError(c, http.StatusForbidden, "Admin access required", nil) + response.SendError(c, http.StatusForbidden, "Admin access required") return fmt.Errorf("admin access required") } diff --git a/internal/http/middleware/cors.go b/internal/http/middleware/cors.go new file mode 100644 index 000000000..43de5ba41 --- /dev/null +++ b/internal/http/middleware/cors.go @@ -0,0 +1,29 @@ +package middleware + +import ( + "strings" + + "github.com/go-shiori/shiori/internal/model" +) + +type CORSMiddleware struct { + allowedOrigins []string +} + +func (m *CORSMiddleware) OnRequest(deps model.Dependencies, c model.WebContext) error { + c.ResponseWriter().Header().Set("Access-Control-Allow-Origin", strings.Join(m.allowedOrigins, ", ")) + c.ResponseWriter().Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + return nil +} + +func (m *CORSMiddleware) OnResponse(deps model.Dependencies, c model.WebContext) error { + c.ResponseWriter().Header().Set("Access-Control-Allow-Origin", strings.Join(m.allowedOrigins, ", ")) + c.ResponseWriter().Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + return nil +} + +func NewCORSMiddleware(allowedOrigins []string) *CORSMiddleware { + return &CORSMiddleware{allowedOrigins: allowedOrigins} +} diff --git a/internal/http/response/file.go b/internal/http/response/file.go index bd1fbb41c..18cfb391a 100644 --- a/internal/http/response/file.go +++ b/internal/http/response/file.go @@ -18,7 +18,7 @@ type SendFileOptions struct { // SendFile sends a file from storage to the response writer func SendFile(c model.WebContext, storage model.StorageDomain, path string, options *SendFileOptions) error { if !storage.FileExists(path) { - return SendError(c, http.StatusNotFound, "File not found", nil) + return SendError(c, http.StatusNotFound, "File not found") } file, err := storage.FS().Open(path) diff --git a/internal/http/response/response.go b/internal/http/response/response.go index 4f85450f2..ee47c0754 100644 --- a/internal/http/response/response.go +++ b/internal/http/response/response.go @@ -2,43 +2,48 @@ package response import ( "encoding/json" + "net/http" "github.com/go-shiori/shiori/internal/model" ) type Response struct { - // Ok if the response was successful or not - Ok bool `json:"ok"` - - // Message the payload of the response, depending on the endpoint/response status - Message any `json:"message"` - - // ErrorParams parameters defined if the response is not successful to help client's debugging - ErrorParams map[string]string `json:"error_params,omitempty"` + // Data the payload of the response, depending on the endpoint/response status + Data any `json:"message"` // statusCode used for the http response status code statusCode int } +// GetData returns the data of the response +func (r *Response) GetData() any { + return r.Data +} + +// IsError returns true if the response is an error func (r *Response) IsError() bool { - return !r.Ok + return r.statusCode >= http.StatusBadRequest } -func (r *Response) GetMessage() any { - return r.Message +// Send sends the response to the client +func (r *Response) Send(c model.WebContext, contentType string) error { + c.ResponseWriter().Header().Set("Content-Type", contentType) + c.ResponseWriter().WriteHeader(r.statusCode) + _, err := c.ResponseWriter().Write([]byte(r.GetData().(string))) + return err } -func (r *Response) Send(c model.WebContext) error { +// SendJSON sends the response to the client +func (r *Response) SendJSON(c model.WebContext) error { c.ResponseWriter().Header().Set("Content-Type", "application/json") c.ResponseWriter().WriteHeader(r.statusCode) - return json.NewEncoder(c.ResponseWriter()).Encode(r) + return json.NewEncoder(c.ResponseWriter()).Encode(r.GetData()) } -func NewResponse(ok bool, message any, errorParams map[string]string, statusCode int) *Response { +// NewResponse creates a new response +func NewResponse(message any, statusCode int) *Response { return &Response{ - Ok: ok, - Message: message, - ErrorParams: errorParams, - statusCode: statusCode, + Data: message, + statusCode: statusCode, } } diff --git a/internal/http/response/response_test.go b/internal/http/response/response_test.go index 73f664411..b602ec277 100644 --- a/internal/http/response/response_test.go +++ b/internal/http/response/response_test.go @@ -36,10 +36,8 @@ func TestNewResponse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - resp := NewResponse(tt.ok, tt.message, tt.errParams, tt.statusCode) - assert.Equal(t, tt.ok, resp.Ok) - assert.Equal(t, tt.message, resp.Message) - assert.Equal(t, tt.errParams, resp.ErrorParams) + resp := NewResponse(tt.message, tt.statusCode) + assert.Equal(t, tt.message, resp.GetData()) assert.Equal(t, tt.statusCode, resp.statusCode) }) } @@ -53,12 +51,12 @@ func TestResponse_IsError(t *testing.T) { }{ { name: "successful response", - response: NewResponse(true, "success", nil, http.StatusOK), + response: NewResponse("success", http.StatusOK), want: false, }, { name: "error response", - response: NewResponse(false, "error", nil, http.StatusBadRequest), + response: NewResponse("error", http.StatusBadRequest), want: true, }, } @@ -78,19 +76,19 @@ func TestResponse_GetMessage(t *testing.T) { }{ { name: "string message", - response: NewResponse(true, "test message", nil, http.StatusOK), + response: NewResponse("test message", http.StatusOK), want: "test message", }, { name: "struct message", - response: NewResponse(true, struct{ Data string }{Data: "test"}, nil, http.StatusOK), + response: NewResponse(struct{ Data string }{Data: "test"}, http.StatusOK), want: struct{ Data string }{Data: "test"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, tt.response.GetMessage()) + assert.Equal(t, tt.want, tt.response.GetData()) }) } } @@ -100,25 +98,22 @@ func TestResponse_Send(t *testing.T) { name string response *Response expectedStatus int - expectedBody map[string]any + expectedBody any }{ { - name: "successful response", - response: NewResponse(true, "success", nil, http.StatusOK), + name: "plain response", + response: NewResponse("success", http.StatusOK), expectedStatus: http.StatusOK, - expectedBody: map[string]any{ - "ok": true, - "message": "success", - }, + expectedBody: "success", }, { - name: "error response with params", - response: NewResponse(false, "error", map[string]string{"field": "invalid"}, http.StatusBadRequest), - expectedStatus: http.StatusBadRequest, + name: "json response", + response: NewResponse(map[string]any{ + "message": "success", + }, http.StatusOK), + expectedStatus: http.StatusOK, expectedBody: map[string]any{ - "ok": false, - "message": "error", - "error_params": map[string]any{"field": "invalid"}, + "message": "success", }, }, } @@ -129,13 +124,13 @@ func TestResponse_Send(t *testing.T) { r := httptest.NewRequest(http.MethodGet, "/", nil) ctx := webcontext.NewWebContext(w, r) - err := tt.response.Send(ctx) + err := tt.response.SendJSON(ctx) assert.NoError(t, err) assert.Equal(t, tt.expectedStatus, w.Code) assert.Equal(t, "application/json", w.Header().Get("Content-Type")) - var responseBody map[string]any + var responseBody any err = json.NewDecoder(w.Body).Decode(&responseBody) assert.NoError(t, err) assert.Equal(t, tt.expectedBody, responseBody) diff --git a/internal/http/response/shortcuts.go b/internal/http/response/shortcuts.go index 636faf74a..7c0c75da7 100644 --- a/internal/http/response/shortcuts.go +++ b/internal/http/response/shortcuts.go @@ -1,7 +1,6 @@ package response import ( - "encoding/json" "net/http" "net/url" @@ -12,30 +11,31 @@ import ( const internalServerErrorMessage = "Internal server error, please contact an administrator" // New provides a shortcut to a successful response object -func New(ok bool, statusCode int, data any) *Response { - return NewResponse(ok, data, nil, statusCode) +func New(statusCode int, data any) *Response { + return NewResponse(data, statusCode) } // Send provides a shortcut to send a (potentially) successful response -func Send(c model.WebContext, statusCode int, message any) error { - resp := NewResponse(statusCode < 400, message, nil, statusCode) - return resp.Send(c) +func Send(c model.WebContext, statusCode int, message any, contentType string) error { + return NewResponse(message, statusCode).Send(c, contentType) } // SendError provides a shortcut to send an unsuccessful response -func SendError(c model.WebContext, statusCode int, message any, errorParams map[string]string) error { - resp := NewResponse(false, message, errorParams, statusCode) - return resp.Send(c) +func SendError(c model.WebContext, statusCode int, message any) error { + resp := NewResponse(struct { + Error string `json:"error"` + }{Error: message.(string)}, statusCode) + return resp.SendJSON(c) } // SendErrorWithParams the same as above but for errors that require error parameters func SendErrorWithParams(c model.WebContext, statusCode int, data any, errorParams map[string]string) error { - return NewResponse(false, data, errorParams, statusCode).Send(c) + return NewResponse(data, statusCode).SendJSON(c) } // SendInternalServerError directly sends an internal server error response func SendInternalServerError(c model.WebContext) error { - return SendError(c, http.StatusInternalServerError, internalServerErrorMessage, nil) + return SendError(c, http.StatusInternalServerError, internalServerErrorMessage) } // RedirectToLogin redirects to the login page with an optional destination @@ -56,14 +56,8 @@ func NotFound(c model.WebContext) { // SendJSON is a helper function to send JSON responses func SendJSON(c model.WebContext, statusCode int, data any) error { - c.ResponseWriter().Header().Set("Content-Type", "application/json") - c.ResponseWriter().WriteHeader(statusCode) - return json.NewEncoder(c.ResponseWriter()).Encode(data) -} - -// SendErrorJSON is a helper function to send error JSON responses -func SendErrorJSON(c model.WebContext, statusCode int, message string) error { - return SendError(c, statusCode, message, nil) + response := NewResponse(data, statusCode) + return response.SendJSON(c) } // SendTemplate renders and sends an HTML template diff --git a/internal/http/response/shortcuts_test.go b/internal/http/response/shortcuts_test.go index 83b3b6244..3176d0b52 100644 --- a/internal/http/response/shortcuts_test.go +++ b/internal/http/response/shortcuts_test.go @@ -13,107 +13,77 @@ import ( func TestNew(t *testing.T) { t.Run("creates successful response", func(t *testing.T) { - resp := response.New(true, http.StatusOK, "test data") - assert.True(t, resp.Ok) - assert.Equal(t, "test data", resp.Message) - assert.Nil(t, resp.ErrorParams) + resp := response.New(http.StatusOK, "test data") + assert.False(t, resp.IsError()) + assert.Equal(t, "test data", resp.GetData()) }) t.Run("creates error response", func(t *testing.T) { - errorParams := map[string]string{"field": "error message"} - resp := response.NewResponse(false, "error data", errorParams, http.StatusBadRequest) - assert.False(t, resp.Ok) - assert.Equal(t, "error data", resp.Message) - assert.Equal(t, errorParams, resp.ErrorParams) + resp := response.New(http.StatusBadRequest, "error data") + assert.True(t, resp.IsError()) + assert.Equal(t, "error data", resp.GetData()) }) } func TestSend(t *testing.T) { t.Run("sends successful response", func(t *testing.T) { c, w := testutil.NewTestWebContext() - err := response.Send(c, http.StatusOK, "success message") + err := response.Send(c, http.StatusOK, "success message", "text/plain") require.NoError(t, err) assert.Equal(t, http.StatusOK, w.Code) - var resp response.Response - err = json.Unmarshal(w.Body.Bytes(), &resp) - require.NoError(t, err) - - assert.True(t, resp.Ok) - assert.Equal(t, "success message", resp.Message) - assert.Nil(t, resp.ErrorParams) + response := testutil.NewTestResponseFromRecorder(w) + response.AssertOk(t) + response.AssertMessageIsBytes(t, []byte("success message")) }) t.Run("sends error response for status >= 400", func(t *testing.T) { + message := "error message" c, w := testutil.NewTestWebContext() - err := response.Send(c, http.StatusBadRequest, "error message") + err := response.Send(c, http.StatusBadRequest, message, "text/plain") require.NoError(t, err) - assert.Equal(t, http.StatusBadRequest, w.Code) - var resp response.Response - err = json.Unmarshal(w.Body.Bytes(), &resp) - require.NoError(t, err) - - assert.False(t, resp.Ok) - assert.Equal(t, "error message", resp.Message) - assert.Nil(t, resp.ErrorParams) + response := response.NewResponse(message, http.StatusBadRequest) + assert.True(t, response.IsError()) + assert.Equal(t, message, response.GetData()) }) } func TestSendError(t *testing.T) { t.Run("sends error response without params", func(t *testing.T) { c, w := testutil.NewTestWebContext() - err := response.SendError(c, http.StatusBadRequest, "error message", nil) + err := response.SendError(c, http.StatusBadRequest, "error message") require.NoError(t, err) - assert.Equal(t, http.StatusBadRequest, w.Code) - var resp response.Response - err = json.Unmarshal(w.Body.Bytes(), &resp) - require.NoError(t, err) + responseBody := struct { + Error string `json:"error"` + }{Error: "error message"} + response := response.NewResponse(responseBody, http.StatusBadRequest) - assert.False(t, resp.Ok) - assert.Equal(t, "error message", resp.Message) - assert.Nil(t, resp.ErrorParams) + assert.True(t, response.IsError()) + assert.Equal(t, responseBody, response.GetData()) }) t.Run("sends error response with params", func(t *testing.T) { c, w := testutil.NewTestWebContext() - errorParams := map[string]string{"field": "validation error"} - err := response.SendError(c, http.StatusBadRequest, "error message", errorParams) + err := response.SendError(c, http.StatusBadRequest, "error message") require.NoError(t, err) assert.Equal(t, http.StatusBadRequest, w.Code) - var resp response.Response - err = json.Unmarshal(w.Body.Bytes(), &resp) - require.NoError(t, err) + responseBody := struct { + Error string `json:"error"` + }{Error: "error message"} + response := response.NewResponse(responseBody, http.StatusBadRequest) - assert.False(t, resp.Ok) - assert.Equal(t, "error message", resp.Message) - assert.Equal(t, errorParams, resp.ErrorParams) + assert.True(t, response.IsError()) + assert.Equal(t, responseBody, response.GetData()) }) } -func TestSendErrorWithParams(t *testing.T) { - c, w := testutil.NewTestWebContext() - errorParams := map[string]string{"field": "validation error"} - err := response.SendErrorWithParams(c, http.StatusBadRequest, "error message", errorParams) - require.NoError(t, err) - - assert.Equal(t, http.StatusBadRequest, w.Code) - - var resp response.Response - err = json.Unmarshal(w.Body.Bytes(), &resp) - require.NoError(t, err) - - assert.False(t, resp.Ok) - assert.Equal(t, "error message", resp.Message) - assert.Equal(t, errorParams, resp.ErrorParams) -} - func TestSendInternalServerError(t *testing.T) { c, w := testutil.NewTestWebContext() err := response.SendInternalServerError(c) @@ -121,13 +91,13 @@ func TestSendInternalServerError(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, w.Code) - var resp response.Response - err = json.Unmarshal(w.Body.Bytes(), &resp) - require.NoError(t, err) + responseBody := struct { + Error string `json:"error"` + }{Error: "Internal server error, please contact an administrator"} + response := response.NewResponse(responseBody, http.StatusInternalServerError) - assert.False(t, resp.Ok) - assert.Equal(t, "Internal server error, please contact an administrator", resp.Message) - assert.Nil(t, resp.ErrorParams) + assert.True(t, response.IsError()) + assert.Equal(t, responseBody, response.GetData()) } func TestRedirectToLogin(t *testing.T) { @@ -175,24 +145,8 @@ func TestSendJSON(t *testing.T) { t.Run("handles encoding error", func(t *testing.T) { c, _ := testutil.NewTestWebContext() // Create a value that can't be marshaled to JSON - data := map[string]interface{}{"fn": func() {}} + data := map[string]any{"fn": func() {}} err := response.SendJSON(c, http.StatusOK, data) assert.Error(t, err) }) } - -func TestSendErrorJSON(t *testing.T) { - c, w := testutil.NewTestWebContext() - err := response.SendErrorJSON(c, http.StatusBadRequest, "error message") - require.NoError(t, err) - - assert.Equal(t, http.StatusBadRequest, w.Code) - - var resp response.Response - err = json.Unmarshal(w.Body.Bytes(), &resp) - require.NoError(t, err) - - assert.False(t, resp.Ok) - assert.Equal(t, "error message", resp.Message) - assert.Nil(t, resp.ErrorParams) -} diff --git a/internal/http/server.go b/internal/http/server.go index 0c97fe2e6..21f95b0a4 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -27,13 +27,14 @@ type HttpServer struct { func (s *HttpServer) Setup(cfg *config.Config, deps *dependencies.Dependencies) (*HttpServer, error) { s.mux = http.NewServeMux() - if err := templates.SetupTemplates(); err != nil { + if err := templates.SetupTemplates(cfg); err != nil { return nil, fmt.Errorf("failed to setup templates: %w", err) } globalMiddleware := []model.HttpMiddleware{ middleware.NewAuthMiddleware(deps), middleware.NewRequestIDMiddleware(deps), + middleware.NewCORSMiddleware([]string{"*"}), } if cfg.Http.AccessLog { @@ -167,6 +168,19 @@ func (s *HttpServer) Setup(cfg *config.Config, deps *dependencies.Dependencies) api_v1.HandleBulkUpdateBookmarkTags, globalMiddleware..., )) + // Bookmark tags endpoints + s.mux.HandleFunc("GET /api/v1/bookmarks/{id}/tags", ToHTTPHandler(deps, + api_v1.HandleGetBookmarkTags, + globalMiddleware..., + )) + s.mux.HandleFunc("POST /api/v1/bookmarks/{id}/tags", ToHTTPHandler(deps, + api_v1.HandleAddTagToBookmark, + globalMiddleware..., + )) + s.mux.HandleFunc("DELETE /api/v1/bookmarks/{id}/tags", ToHTTPHandler(deps, + api_v1.HandleRemoveTagFromBookmark, + globalMiddleware..., + )) s.server = &http.Server{ Addr: fmt.Sprintf("%s%d", cfg.Http.Address, cfg.Http.Port), diff --git a/internal/http/templates/templates.go b/internal/http/templates/templates.go index f4b75fa18..bd92767ad 100644 --- a/internal/http/templates/templates.go +++ b/internal/http/templates/templates.go @@ -5,7 +5,9 @@ import ( "html/template" "io" + "github.com/go-shiori/shiori/internal/config" views "github.com/go-shiori/shiori/internal/view" + webapp "github.com/go-shiori/shiori/webapp" ) const ( @@ -16,11 +18,21 @@ const ( var templates *template.Template // SetupTemplates initializes the templates for the webserver -func SetupTemplates() error { +func SetupTemplates(config *config.Config) error { var err error + fs := views.Templates + + globs := []string{"*.html"} + + if config.Http.ServeWebUIV2 { + fs = webapp.Templates + globs = []string{"**/*.html"} + } + templates, err = template.New("html"). Delims(leftTemplateDelim, rightTemplateDelim). - ParseFS(views.Templates, "*.html") + ParseFS(fs, globs...) + if err != nil { return fmt.Errorf("failed to parse templates: %w", err) } @@ -30,9 +42,7 @@ func SetupTemplates() error { // RenderTemplate renders a template with the given data func RenderTemplate(w io.Writer, name string, data any) error { if templates == nil { - if err := SetupTemplates(); err != nil { - return fmt.Errorf("failed to setup templates: %w", err) - } + return fmt.Errorf("templates not initialized") } return templates.ExecuteTemplate(w, name, data) } diff --git a/internal/model/database.go b/internal/model/database.go index 81d9d52b0..068b00b63 100644 --- a/internal/model/database.go +++ b/internal/model/database.go @@ -89,6 +89,18 @@ type DB interface { // BulkUpdateBookmarkTags updates tags for multiple bookmarks. // It ensures that all bookmarks and tags exist before proceeding. BulkUpdateBookmarkTags(ctx context.Context, bookmarkIDs []int, tagIDs []int) error + + // AddTagToBookmark adds a tag to a bookmark + AddTagToBookmark(ctx context.Context, bookmarkID int, tagID int) error + + // RemoveTagFromBookmark removes a tag from a bookmark + RemoveTagFromBookmark(ctx context.Context, bookmarkID int, tagID int) error + + // TagExists checks if a tag with the given ID exists in the database + TagExists(ctx context.Context, tagID int) (bool, error) + + // BookmarkExists checks if a bookmark with the given ID exists in the database + BookmarkExists(ctx context.Context, bookmarkID int) (bool, error) } // DBOrderMethod is the order method for getting bookmarks @@ -138,4 +150,5 @@ type DBListTagsOptions struct { BookmarkID int WithBookmarkCount bool OrderBy DBTagOrderBy + Search string } diff --git a/internal/model/domains.go b/internal/model/domains.go index 519869210..fe8ee91b1 100644 --- a/internal/model/domains.go +++ b/internal/model/domains.go @@ -18,6 +18,9 @@ type BookmarksDomain interface { GetBookmarks(ctx context.Context, ids []int) ([]BookmarkDTO, error) UpdateBookmarkCache(ctx context.Context, bookmark BookmarkDTO, keepMetadata bool, skipExist bool) (*BookmarkDTO, error) BulkUpdateBookmarkTags(ctx context.Context, bookmarkIDs []int, tagIDs []int) error + AddTagToBookmark(ctx context.Context, bookmarkID int, tagID int) error + RemoveTagFromBookmark(ctx context.Context, bookmarkID int, tagID int) error + BookmarkExists(ctx context.Context, id int) (bool, error) } type AuthDomain interface { @@ -53,4 +56,5 @@ type TagsDomain interface { GetTag(ctx context.Context, id int) (TagDTO, error) UpdateTag(ctx context.Context, tag TagDTO) (TagDTO, error) DeleteTag(ctx context.Context, id int) error + TagExists(ctx context.Context, id int) (bool, error) } diff --git a/internal/model/errors.go b/internal/model/errors.go index 72ab34fbd..167942cd2 100644 --- a/internal/model/errors.go +++ b/internal/model/errors.go @@ -5,7 +5,9 @@ import "errors" var ( ErrBookmarkNotFound = errors.New("bookmark not found") ErrBookmarkInvalidID = errors.New("invalid bookmark ID") - ErrUnauthorized = errors.New("unauthorized user") - ErrNotFound = errors.New("not found") - ErrAlreadyExists = errors.New("already exists") + ErrTagNotFound = errors.New("tag not found") + + ErrUnauthorized = errors.New("unauthorized user") + ErrNotFound = errors.New("not found") + ErrAlreadyExists = errors.New("already exists") ) diff --git a/internal/model/tag.go b/internal/model/tag.go index c6f84f9c6..eebc8fb35 100644 --- a/internal/model/tag.go +++ b/internal/model/tag.go @@ -1,5 +1,9 @@ package model +import ( + "errors" +) + // BookmarkTag is the relationship between a bookmark and a tag. type BookmarkTag struct { BookmarkID int `db:"bookmark_id"` @@ -40,4 +44,15 @@ type ListTagsOptions struct { BookmarkID int WithBookmarkCount bool OrderBy DBTagOrderBy + Search string +} + +// IsValid validates the ListTagsOptions. +// Returns an error if the options are invalid, nil otherwise. +// Currently, it checks that Search and BookmarkID are not used together. +func (o ListTagsOptions) IsValid() error { + if o.Search != "" && o.BookmarkID > 0 { + return errors.New("search and bookmark ID filtering cannot be used together") + } + return nil } diff --git a/internal/model/tag_test.go b/internal/model/tag_test.go new file mode 100644 index 000000000..55785e490 --- /dev/null +++ b/internal/model/tag_test.go @@ -0,0 +1,64 @@ +package model + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListTagsOptions_IsValid(t *testing.T) { + tests := []struct { + name string + options ListTagsOptions + wantErr bool + }{ + { + name: "valid options with search", + options: ListTagsOptions{ + Search: "test", + WithBookmarkCount: true, + OrderBy: DBTagOrderByTagName, + }, + wantErr: false, + }, + { + name: "valid options with bookmark ID", + options: ListTagsOptions{ + BookmarkID: 123, + WithBookmarkCount: true, + OrderBy: DBTagOrderByTagName, + }, + wantErr: false, + }, + { + name: "invalid options with both search and bookmark ID", + options: ListTagsOptions{ + Search: "test", + BookmarkID: 123, + WithBookmarkCount: true, + OrderBy: DBTagOrderByTagName, + }, + wantErr: true, + }, + { + name: "valid options with neither search nor bookmark ID", + options: ListTagsOptions{ + WithBookmarkCount: true, + OrderBy: DBTagOrderByTagName, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.options.IsValid() + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), "search and bookmark ID filtering cannot be used together") + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/testutil/response.go b/internal/testutil/response.go index d7b324775..a63cb9e87 100644 --- a/internal/testutil/response.go +++ b/internal/testutil/response.go @@ -2,12 +2,10 @@ package testutil import ( "encoding/json" - "io" "net/http/httptest" "testing" "github.com/go-shiori/shiori/internal/http/response" - "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -16,58 +14,84 @@ type testResponse struct { } func (r *testResponse) AssertMessageIsEmptyList(t *testing.T) { - require.Equal(t, []any{}, r.Response.GetMessage()) + var jsonData []any + err := json.Unmarshal(r.Response.GetData().([]byte), &jsonData) + require.NoError(t, err) + require.Equal(t, []any{}, jsonData) } func (r *testResponse) AssertMessageIsNotEmptyList(t *testing.T) { - require.Greater(t, len(r.Response.GetMessage().([]any)), 0) + var jsonData []any + err := json.Unmarshal(r.Response.GetData().([]byte), &jsonData) + require.NoError(t, err) + require.Greater(t, len(jsonData), 0) +} + +func (r *testResponse) AssertMessageIsListLength(t *testing.T, length int) { + var jsonData []any + err := json.Unmarshal(r.Response.GetData().([]byte), &jsonData) + require.NoError(t, err) + require.Len(t, jsonData, length) +} + +// ForEach iterates over the items in the response and calls the provided function +// with each item. +func (r *testResponse) ForEach(t *testing.T, fn func(item map[string]any)) { + var jsonData []any + err := json.Unmarshal(r.Response.GetData().([]byte), &jsonData) + require.NoError(t, err) + for _, item := range jsonData { + fn(item.(map[string]any)) + } } func (r *testResponse) AssertNilMessage(t *testing.T) { - require.Equal(t, nil, r.Response.GetMessage()) + require.Equal(t, nil, r.Response.GetData()) } func (r testResponse) AssertMessageEquals(t *testing.T, expected any) { - require.Equal(t, expected, r.Response.GetMessage()) + require.Equal(t, expected, r.Response.GetData()) } -func (r *testResponse) AssertMessageIsListLength(t *testing.T, length int) { - require.Len(t, r.Response.GetMessage(), length) +func (r testResponse) AssertMessageJSONContains(t *testing.T, expected string) { + require.JSONEq(t, expected, string(r.Response.GetData().([]byte))) } -func (r *testResponse) AssertMessageContains(t *testing.T, expected string) { - require.Contains(t, r.Response.GetMessage(), expected) +// AssertMessageJSONContainsKey asserts that the response message contains a key +// and returns the value of the key to be used in other comparisons depending on the +// value type. +func (r testResponse) AssertMessageJSONContainsKey(t *testing.T, key string) any { + var jsonData map[string]any + err := json.Unmarshal(r.Response.GetData().([]byte), &jsonData) + require.NoError(t, err) + require.Contains(t, jsonData, key) + return jsonData[key] } -func (r *testResponse) AssertOk(t *testing.T) { - require.False(t, r.Response.IsError()) +// AssertMessageJSONKeyValue asserts that the response message contains a key +// and calls the provided function with the value of the key to be used in other +// comparisons depending on the value type. +func (r *testResponse) AssertMessageJSONKeyValue(t *testing.T, key string, valueAssertFunc func(t *testing.T, value any)) { + value := r.AssertMessageJSONContainsKey(t, key) + valueAssertFunc(t, value) } -func (r *testResponse) AssertNotOk(t *testing.T) { - require.True(t, r.Response.IsError()) +func (r *testResponse) AssertMessageContains(t *testing.T, expected string) { + require.Contains(t, r.Response.GetData(), expected) } -func (r *testResponse) Assert(t *testing.T, fn func(t *testing.T, r *testResponse)) { - fn(t, r) +func (r *testResponse) AssertMessageIsBytes(t *testing.T, expected []byte) { + require.Equal(t, expected, r.Response.GetData().([]byte)) } -func NewTestResponseFromRecorder(w *httptest.ResponseRecorder) (*testResponse, error) { - return NewTestResponseFromBytes(w.Body.Bytes()) +func (r *testResponse) AssertOk(t *testing.T) { + require.False(t, r.Response.IsError()) } -func NewTestResponseFromBytes(b []byte) (*testResponse, error) { - tr := testResponse{} - if err := json.Unmarshal(b, &tr.Response); err != nil { - return nil, errors.Wrap(err, "error parsing response") - } - return &tr, nil +func (r *testResponse) AssertNotOk(t *testing.T) { + require.True(t, r.Response.IsError()) } -func NewTestResponseFromReader(r io.Reader) (*testResponse, error) { - tr := testResponse{} - decoder := json.NewDecoder(r) - if err := decoder.Decode(&tr.Response); err != nil { - return nil, errors.Wrap(err, "error parsing response") - } - return &tr, nil +func NewTestResponseFromRecorder(w *httptest.ResponseRecorder) *testResponse { + return &testResponse{Response: *response.NewResponse(w.Body.Bytes(), w.Code)} } diff --git a/internal/view/assets/js/component/login.js b/internal/view/assets/js/component/login.js index f354759f9..d3d702d73 100644 --- a/internal/view/assets/js/component/login.js +++ b/internal/view/assets/js/component/login.js @@ -71,14 +71,14 @@ export default { async getErrorMessage(err) { switch (err.constructor) { case Error: - return err.message; + return err.error; case Response: var text = await err.text(); // Handle new error messages if (text[0] == "{") { var json = JSON.parse(text); - return json.message; + return json.error; } return `${text} (${err.status})`; default: @@ -128,15 +128,15 @@ export default { }) .then((json) => { // Save session id - document.cookie = `token=${json.message.token}; Path=${ + document.cookie = `token=${json.token}; Path=${ new URL(document.baseURI).pathname - }; Expires=${new Date(json.message.expires * 1000).toUTCString()}`; + }; Expires=${new Date(json.expires * 1000).toUTCString()}`; // Save account data - localStorage.setItem("shiori-token", json.message.token); + localStorage.setItem("shiori-token", json.token); localStorage.setItem( "shiori-account", - JSON.stringify(this.parseJWT(json.message.token).account), + JSON.stringify(this.parseJWT(json.token).account), ); this.visible = false; diff --git a/internal/view/assets/js/page/base.js b/internal/view/assets/js/page/base.js index 1a2345d7d..166247994 100644 --- a/internal/view/assets/js/page/base.js +++ b/internal/view/assets/js/page/base.js @@ -78,7 +78,7 @@ export default { // Handle new error messages if (text[0] == "{") { var json = JSON.parse(text); - return json.message; + return json.error; } return `${text} (${err.status})`; diff --git a/internal/view/assets/js/page/home.js b/internal/view/assets/js/page/home.js index cf2d8c88e..cba9dd9bb 100644 --- a/internal/view/assets/js/page/home.js +++ b/internal/view/assets/js/page/home.js @@ -277,7 +277,7 @@ export default { return response.json(); }) .then((json) => { - this.tags = json.message; + this.tags = json; this.loading = false; }) .catch((err) => { @@ -697,7 +697,7 @@ export default { .then((json) => { this.selection = []; this.editMode = false; - json.message.forEach((book) => { + json.forEach((book) => { // download ebooks const id = book.id; if (book.hasEbook) { @@ -799,7 +799,7 @@ export default { let faildedUpdateArchives = []; let faildedCreateEbook = []; - json.message.forEach((book) => { + json.forEach((book) => { var item = items.find((el) => el.id === book.id); this.bookmarks.splice(item.index, 1, book); diff --git a/internal/view/assets/js/page/setting.js b/internal/view/assets/js/page/setting.js index 7a56e9dab..52bac8e74 100644 --- a/internal/view/assets/js/page/setting.js +++ b/internal/view/assets/js/page/setting.js @@ -151,7 +151,7 @@ export default { return response.json(); }) .then((responseData) => { - const responseString = JSON.stringify(responseData.message); + const responseString = JSON.stringify(responseData); localStorage.setItem("shiori-account", responseString); }) .catch((err) => { @@ -176,7 +176,7 @@ export default { }) .then((json) => { this.loading = false; - this.accounts = json.message; + this.accounts = json; }) .catch((err) => { this.loading = false; @@ -199,7 +199,7 @@ export default { return response.json(); }) .then((json) => { - this.system = json.message; + this.system = json; }) .catch((err) => { this.getErrorMessage(err).then((msg) => { @@ -277,7 +277,7 @@ export default { this.dialog.loading = false; this.dialog.visible = false; - this.accounts.push(json.message); + this.accounts.push(json); this.accounts.sort((a, b) => { var nameA = a.username.toLowerCase(), nameB = b.username.toLowerCase(); diff --git a/webapp/.editorconfig b/webapp/.editorconfig new file mode 100644 index 000000000..5a5809dbe --- /dev/null +++ b/webapp/.editorconfig @@ -0,0 +1,9 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +end_of_line = lf +max_line_length = 100 diff --git a/webapp/.gitattributes b/webapp/.gitattributes new file mode 100644 index 000000000..6313b56c5 --- /dev/null +++ b/webapp/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/webapp/.gitignore b/webapp/.gitignore new file mode 100644 index 000000000..53bd2e4e2 --- /dev/null +++ b/webapp/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +#dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo diff --git a/webapp/.prettierrc.json b/webapp/.prettierrc.json new file mode 100644 index 000000000..29a2402ef --- /dev/null +++ b/webapp/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100 +} diff --git a/webapp/README.md b/webapp/README.md new file mode 100644 index 000000000..fde3c9b0d --- /dev/null +++ b/webapp/README.md @@ -0,0 +1,45 @@ +# . + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Type Support for `.vue` Imports in TS + +TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +bun install +``` + +### Compile and Hot-Reload for Development + +```sh +bun dev +``` + +### Type-Check, Compile and Minify for Production + +```sh +bun run build +``` + +### Run Unit Tests with [Vitest](https://vitest.dev/) + +```sh +bun test:unit +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +bun lint +``` diff --git a/webapp/bun.lock b/webapp/bun.lock new file mode 100644 index 000000000..fdd00a031 --- /dev/null +++ b/webapp/bun.lock @@ -0,0 +1,1072 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "shiori", + "dependencies": { + "@tailwindcss/vite": "^4.0.14", + "@vueuse/core": "^13.0.0", + "pinia": "^3.0.1", + "vue": "^3.5.13", + "vue-router": "4", + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.0", + "@types/jsdom": "^21.1.7", + "@types/node": "^22.13.9", + "@types/vue-router": "^2.0.0", + "@vitejs/plugin-vue": "^5.2.1", + "@vitest/eslint-plugin": "^1.1.36", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.5.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.7.0", + "eslint": "^9.21.0", + "eslint-plugin-vue": "~10.0.0", + "jiti": "^2.4.2", + "jsdom": "^26.0.0", + "npm-run-all2": "^7.0.2", + "prettier": "3.5.3", + "tailwindcss": "^4.0.14", + "typescript": "~5.8.0", + "vite": "^6.2.1", + "vite-plugin-vue-devtools": "^7.7.2", + "vitest": "^3.0.8", + "vue-tsc": "^2.2.8", + }, + }, + }, + "packages": { + "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + + "@antfu/utils": ["@antfu/utils@0.7.10", "", {}, "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.1.1", "", { "dependencies": { "@csstools/css-calc": "^2.1.2", "@csstools/css-color-parser": "^3.0.8", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA=="], + + "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + + "@babel/compat-data": ["@babel/compat-data@7.26.8", "", {}, "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ=="], + + "@babel/core": ["@babel/core@7.26.10", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.10", "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", "@babel/traverse": "^7.26.10", "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ=="], + + "@babel/generator": ["@babel/generator@7.26.10", "", { "dependencies": { "@babel/parser": "^7.26.10", "@babel/types": "^7.26.10", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang=="], + + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.26.5", "", { "dependencies": { "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA=="], + + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.26.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg=="], + + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.26.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="], + + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="], + + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.26.5", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/traverse": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg=="], + + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.25.9", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="], + + "@babel/helpers": ["@babel/helpers@7.26.10", "", { "dependencies": { "@babel/template": "^7.26.9", "@babel/types": "^7.26.10" } }, "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g=="], + + "@babel/parser": ["@babel/parser@7.26.10", "", { "dependencies": { "@babel/types": "^7.26.10" }, "bin": "./bin/babel-parser.js" }, "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA=="], + + "@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-syntax-decorators": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g=="], + + "@babel/plugin-syntax-decorators": ["@babel/plugin-syntax-decorators@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg=="], + + "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A=="], + + "@babel/plugin-syntax-import-meta": ["@babel/plugin-syntax-import-meta@7.10.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g=="], + + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA=="], + + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ=="], + + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.26.8", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw=="], + + "@babel/template": ["@babel/template@7.26.9", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.26.9", "@babel/types": "^7.26.9" } }, "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA=="], + + "@babel/traverse": ["@babel/traverse@7.26.10", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.10", "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", "@babel/types": "^7.26.10", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A=="], + + "@babel/types": ["@babel/types@7.26.10", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@5.0.2", "", {}, "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="], + + "@csstools/css-calc": ["@csstools/css-calc@2.1.2", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.0.8", "", { "dependencies": { "@csstools/color-helpers": "^5.0.2", "@csstools/css-calc": "^2.1.2" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.4", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.1", "", { "os": "none", "cpu": "arm64" }, "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.5.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/config-array": ["@eslint/config-array@0.19.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.1.0", "", {}, "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA=="], + + "@eslint/core": ["@eslint/core@0.12.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ=="], + + "@eslint/js": ["@eslint/js@9.22.0", "", {}, "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.7", "", { "dependencies": { "@eslint/core": "^0.12.0", "levn": "^0.4.1" } }, "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "@pkgr/core": ["@pkgr/core@0.1.1", "", {}, "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA=="], + + "@polka/url": ["@polka/url@1.0.0-next.28", "", {}, "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.35.0", "", { "os": "android", "cpu": "arm" }, "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.35.0", "", { "os": "android", "cpu": "arm64" }, "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.35.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.35.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.35.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.35.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg=="], + + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.35.0", "", { "os": "linux", "cpu": "none" }, "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g=="], + + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.35.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.35.0", "", { "os": "linux", "cpu": "none" }, "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.35.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.35.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.35.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.35.0", "", { "os": "win32", "cpu": "x64" }, "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw=="], + + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.0.14", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "tailwindcss": "4.0.14" } }, "sha512-Ux9NbFkKWYE4rfUFz6M5JFLs/GEYP6ysxT8uSyPn6aTbh2K3xDE1zz++eVK4Vwx799fzMF8CID9sdHn4j/Ab8w=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.0.14", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.0.14", "@tailwindcss/oxide-darwin-arm64": "4.0.14", "@tailwindcss/oxide-darwin-x64": "4.0.14", "@tailwindcss/oxide-freebsd-x64": "4.0.14", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.14", "@tailwindcss/oxide-linux-arm64-gnu": "4.0.14", "@tailwindcss/oxide-linux-arm64-musl": "4.0.14", "@tailwindcss/oxide-linux-x64-gnu": "4.0.14", "@tailwindcss/oxide-linux-x64-musl": "4.0.14", "@tailwindcss/oxide-win32-arm64-msvc": "4.0.14", "@tailwindcss/oxide-win32-x64-msvc": "4.0.14" } }, "sha512-M8VCNyO/NBi5vJ2cRcI9u8w7Si+i76a7o1vveoGtbbjpEYJZYiyc7f2VGps/DqawO56l3tImIbq2OT/533jcrA=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.0.14", "", { "os": "android", "cpu": "arm64" }, "sha512-VBFKC2rFyfJ5J8lRwjy6ub3rgpY186kAcYgiUr8ArR8BAZzMruyeKJ6mlsD22Zp5ZLcPW/FXMasJiJBx0WsdQg=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.0.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U3XOwLrefGr2YQZ9DXasDSNWGPZBCh8F62+AExBEDMLDfvLLgI/HDzY8Oq8p/JtqkAY38sWPOaNnRwEGKU5Zmg=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.0.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-V5AjFuc3ndWGnOi1d379UsODb0TzAS2DYIP/lwEbfvafUaD2aNZIcbwJtYu2DQqO2+s/XBvDVA+w4yUyaewRwg=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.0.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tXvtxbaZfcPfqBwW3f53lTcyH6EDT+1eT7yabwcfcxTs+8yTPqxsDUhrqe9MrnEzpNkd+R/QAjJapfd4tjWdLg=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.0.14", "", { "os": "linux", "cpu": "arm" }, "sha512-cSeLNWWqIWeSTmBntQvyY2/2gcLX8rkPFfDDTQVF8qbRcRMVPLxBvFVJyfSAYRNch6ZyVH2GI6dtgALOBDpdNA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.0.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-bwDWLBalXFMDItcSXzFk6y7QKvj6oFlaY9vM+agTlwFL1n1OhDHYLZkSjaYsh6KCeG0VB0r7H8PUJVOM1LRZyg=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.0.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-gVkJdnR/L6iIcGYXx64HGJRmlme2FGr/aZH0W6u4A3RgPMAb+6ELRLi+UBiH83RXBm9vwCfkIC/q8T51h8vUJQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.0.14", "", { "os": "linux", "cpu": "x64" }, "sha512-EE+EQ+c6tTpzsg+LGO1uuusjXxYx0Q00JE5ubcIGfsogSKth8n8i2BcS2wYTQe4jXGs+BQs35l78BIPzgwLddw=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.0.14", "", { "os": "linux", "cpu": "x64" }, "sha512-KCCOzo+L6XPT0oUp2Jwh233ETRQ/F6cwUnMnR0FvMUCbkDAzHbcyOgpfuAtRa5HD0WbTbH4pVD+S0pn1EhNfbw=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.0.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-AHObFiFL9lNYcm3tZSPqa/cHGpM5wOrNmM2uOMoKppp+0Hom5uuyRh0QkOp7jftsHZdrZUpmoz0Mp6vhh2XtUg=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.0.14", "", { "os": "win32", "cpu": "x64" }, "sha512-rNXXMDJfCJLw/ZaFTOLOHoGULxyXfh2iXTGiChFiYTSgKBKQHIGEpV0yn5N25WGzJJ+VBnRjHzlmDqRV+d//oQ=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.0.14", "", { "dependencies": { "@tailwindcss/node": "4.0.14", "@tailwindcss/oxide": "4.0.14", "lightningcss": "1.29.2", "tailwindcss": "4.0.14" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-y69ztPTRFy+13EPS/7dEFVl7q2Goh1pQueVO8IfGeyqSpcx/joNJXFk0lLhMgUbF0VFJotwRSb9ZY7Xoq3r26Q=="], + + "@tsconfig/node22": ["@tsconfig/node22@22.0.0", "", {}, "sha512-twLQ77zevtxobBOD4ToAtVmuYrpeYUh3qh+TEp+08IWhpsrIflVHqQ1F1CiPxQGL7doCdBIOOCF+1Tm833faNg=="], + + "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], + + "@types/jsdom": ["@types/jsdom@21.1.7", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@22.13.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="], + + "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], + + "@types/vue-router": ["@types/vue-router@2.0.0", "", { "dependencies": { "vue-router": "*" } }, "sha512-E454lQ6tp9ftVWdZ8VGZpRcIV4YeqVAcx/uifl3P1GGwscYsxOFdYfgIuKasKO0Fm6Np2JM/L378D3bcRQE9hg=="], + + "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.26.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.26.1", "@typescript-eslint/type-utils": "8.26.1", "@typescript-eslint/utils": "8.26.1", "@typescript-eslint/visitor-keys": "8.26.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.26.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.26.1", "@typescript-eslint/types": "8.26.1", "@typescript-eslint/typescript-estree": "8.26.1", "@typescript-eslint/visitor-keys": "8.26.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.26.1", "", { "dependencies": { "@typescript-eslint/types": "8.26.1", "@typescript-eslint/visitor-keys": "8.26.1" } }, "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.26.1", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.26.1", "@typescript-eslint/utils": "8.26.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.26.1", "", {}, "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.26.1", "", { "dependencies": { "@typescript-eslint/types": "8.26.1", "@typescript-eslint/visitor-keys": "8.26.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.26.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.26.1", "@typescript-eslint/types": "8.26.1", "@typescript-eslint/typescript-estree": "8.26.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.26.1", "", { "dependencies": { "@typescript-eslint/types": "8.26.1", "eslint-visitor-keys": "^4.2.0" } }, "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg=="], + + "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.1", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ=="], + + "@vitest/eslint-plugin": ["@vitest/eslint-plugin@1.1.37", "", { "peerDependencies": { "@typescript-eslint/utils": "^8.24.0", "eslint": ">= 8.57.0", "typescript": ">= 5.0.0", "vitest": "*" }, "optionalPeers": ["typescript", "vitest"] }, "sha512-cnlBV8zr0oaBu1Vk6ruqWzpPzFCtwY0yuwUQpNIeFOUl3UhXVwNUoOYfWkZzeToGuNBaXvIsr6/RgHrXiHXqXA=="], + + "@vitest/expect": ["@vitest/expect@3.0.8", "", { "dependencies": { "@vitest/spy": "3.0.8", "@vitest/utils": "3.0.8", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ=="], + + "@vitest/mocker": ["@vitest/mocker@3.0.8", "", { "dependencies": { "@vitest/spy": "3.0.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.0.8", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg=="], + + "@vitest/runner": ["@vitest/runner@3.0.8", "", { "dependencies": { "@vitest/utils": "3.0.8", "pathe": "^2.0.3" } }, "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w=="], + + "@vitest/snapshot": ["@vitest/snapshot@3.0.8", "", { "dependencies": { "@vitest/pretty-format": "3.0.8", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A=="], + + "@vitest/spy": ["@vitest/spy@3.0.8", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q=="], + + "@vitest/utils": ["@vitest/utils@3.0.8", "", { "dependencies": { "@vitest/pretty-format": "3.0.8", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" } }, "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q=="], + + "@volar/language-core": ["@volar/language-core@2.4.12", "", { "dependencies": { "@volar/source-map": "2.4.12" } }, "sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA=="], + + "@volar/source-map": ["@volar/source-map@2.4.12", "", {}, "sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw=="], + + "@volar/typescript": ["@volar/typescript@2.4.12", "", { "dependencies": { "@volar/language-core": "2.4.12", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g=="], + + "@vue/babel-helper-vue-transform-on": ["@vue/babel-helper-vue-transform-on@1.4.0", "", {}, "sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw=="], + + "@vue/babel-plugin-jsx": ["@vue/babel-plugin-jsx@1.4.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/template": "^7.26.9", "@babel/traverse": "^7.26.9", "@babel/types": "^7.26.9", "@vue/babel-helper-vue-transform-on": "1.4.0", "@vue/babel-plugin-resolve-type": "1.4.0", "@vue/shared": "^3.5.13" }, "peerDependencies": { "@babel/core": "^7.0.0-0" }, "optionalPeers": ["@babel/core"] }, "sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA=="], + + "@vue/babel-plugin-resolve-type": ["@vue/babel-plugin-resolve-type@1.4.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "@babel/parser": "^7.26.9", "@vue/compiler-sfc": "^3.5.13" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ=="], + + "@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.13", "", { "dependencies": { "@vue/compiler-core": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="], + + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/compiler-core": "3.5.13", "@vue/compiler-dom": "3.5.13", "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ=="], + + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], + + "@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="], + + "@vue/devtools-api": ["@vue/devtools-api@7.7.2", "", { "dependencies": { "@vue/devtools-kit": "^7.7.2" } }, "sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA=="], + + "@vue/devtools-core": ["@vue/devtools-core@7.7.2", "", { "dependencies": { "@vue/devtools-kit": "^7.7.2", "@vue/devtools-shared": "^7.7.2", "mitt": "^3.0.1", "nanoid": "^5.0.9", "pathe": "^2.0.2", "vite-hot-client": "^0.2.4" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-lexREWj1lKi91Tblr38ntSsy6CvI8ba7u+jmwh2yruib/ltLUcsIzEjCnrkh1yYGGIKXbAuYV2tOG10fGDB9OQ=="], + + "@vue/devtools-kit": ["@vue/devtools-kit@7.7.2", "", { "dependencies": { "@vue/devtools-shared": "^7.7.2", "birpc": "^0.2.19", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.1" } }, "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ=="], + + "@vue/devtools-shared": ["@vue/devtools-shared@7.7.2", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA=="], + + "@vue/eslint-config-prettier": ["@vue/eslint-config-prettier@10.2.0", "", { "dependencies": { "eslint-config-prettier": "^10.0.1", "eslint-plugin-prettier": "^5.2.2" }, "peerDependencies": { "eslint": ">= 8.21.0", "prettier": ">= 3.0.0" } }, "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw=="], + + "@vue/eslint-config-typescript": ["@vue/eslint-config-typescript@14.5.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.26.0", "fast-glob": "^3.3.3", "typescript-eslint": "^8.26.0", "vue-eslint-parser": "^10.1.1" }, "peerDependencies": { "eslint": "^9.10.0", "eslint-plugin-vue": "^9.28.0 || ^10.0.0", "typescript": ">=4.8.4" }, "optionalPeers": ["typescript"] }, "sha512-5oPOyuwkw++AP5gHDh5YFmST50dPfWOcm3/W7Nbh42IK5O3H74ytWAw0TrCRTaBoD/02khnWXuZf1Bz1xflavQ=="], + + "@vue/language-core": ["@vue/language-core@2.2.8", "", { "dependencies": { "@volar/language-core": "~2.4.11", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^1.0.3", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-rrzB0wPGBvcwaSNRriVWdNAbHQWSf0NlGqgKHK5mEkXpefjUlVRP62u03KvwZpvKVjRnBIQ/Lwre+Mx9N6juUQ=="], + + "@vue/reactivity": ["@vue/reactivity@3.5.13", "", { "dependencies": { "@vue/shared": "3.5.13" } }, "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg=="], + + "@vue/runtime-core": ["@vue/runtime-core@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw=="], + + "@vue/runtime-dom": ["@vue/runtime-dom@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/runtime-core": "3.5.13", "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog=="], + + "@vue/server-renderer": ["@vue/server-renderer@3.5.13", "", { "dependencies": { "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "vue": "3.5.13" } }, "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA=="], + + "@vue/shared": ["@vue/shared@3.5.13", "", {}, "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="], + + "@vue/test-utils": ["@vue/test-utils@2.4.6", "", { "dependencies": { "js-beautify": "^1.14.9", "vue-component-type-helpers": "^2.0.0" } }, "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow=="], + + "@vue/tsconfig": ["@vue/tsconfig@0.7.0", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg=="], + + "@vueuse/core": ["@vueuse/core@13.0.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "13.0.0", "@vueuse/shared": "13.0.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-rkgb4a8/0b234lMGCT29WkCjPfsX0oxrIRR7FDndRoW3FsaC9NBzefXg/9TLhAgwM11f49XnutshM4LzJBrQ5g=="], + + "@vueuse/metadata": ["@vueuse/metadata@13.0.0", "", {}, "sha512-TRNksqmvtvqsuHf7bbgH9OSXEV2b6+M3BSN4LR5oxWKykOFT9gV78+C2/0++Pq9KCp9KQ1OQDPvGlWNQpOb2Mw=="], + + "@vueuse/shared": ["@vueuse/shared@13.0.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-9MiHhAPw+sqCF/RLo8V6HsjRqEdNEWVpDLm2WBRW2G/kSQjb8X901sozXpSCaeLG0f7TEfMrT4XNaA5m1ez7Dg=="], + + "abbrev": ["abbrev@2.0.0", "", {}, "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ=="], + + "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "alien-signals": ["alien-signals@1.0.4", "", {}, "sha512-DJqqQD3XcsaQcQ1s+iE2jDUZmmQpXwHiR6fCAim/w87luaW+vmLY8fMlrdkmRwzaFXhkxf3rqPCR59tKVv1MDw=="], + + "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "birpc": ["birpc@0.2.19", "", {}, "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ=="], + + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + + "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="], + + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001704", "", {}, "sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew=="], + + "chai": ["chai@5.2.0", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "config-chain": ["config-chain@1.1.13", "", { "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "copy-anything": ["copy-anything@3.0.5", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "cssstyle": ["cssstyle@4.3.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.1.1", "rrweb-cssom": "^0.8.0" } }, "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], + + "de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="], + + "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="], + + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "default-browser": ["default-browser@5.2.1", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg=="], + + "default-browser-id": ["default-browser-id@5.0.0", "", {}, "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA=="], + + "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "editorconfig": ["editorconfig@1.0.4", "", { "dependencies": { "@one-ini/wasm": "0.1.1", "commander": "^10.0.0", "minimatch": "9.0.1", "semver": "^7.5.3" }, "bin": { "editorconfig": "bin/editorconfig" } }, "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.118", "", {}, "sha512-yNDUus0iultYyVoEFLnQeei7LOQkL8wg8GQpkPCRrOlJXlcCwa6eGKZkxQ9ciHsqZyYbj8Jd94X1CTPzGm+uIA=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "error-stack-parser-es": ["error-stack-parser-es@0.1.5", "", {}, "sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@1.6.0", "", {}, "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.22.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/config-helpers": "^0.1.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.0", "@eslint/js": "9.22.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ=="], + + "eslint-config-prettier": ["eslint-config-prettier@10.1.1", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw=="], + + "eslint-plugin-prettier": ["eslint-plugin-prettier@5.2.3", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.9.1" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "optionalPeers": ["@types/eslint", "eslint-config-prettier"] }, "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw=="], + + "eslint-plugin-vue": ["eslint-plugin-vue@10.0.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" } }, "sha512-XKckedtajqwmaX6u1VnECmZ6xJt+YvlmMzBPZd+/sI3ub2lpYZyFnsyWo7c3nMOQKJQudeyk1lw/JxdgeKT64w=="], + + "eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="], + + "espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "execa": ["execa@9.5.2", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.0", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.0.0" } }, "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q=="], + + "expect-type": ["expect-type@1.2.0", "", {}, "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="], + + "fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], + + "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "human-signals": ["human-signals@8.0.0", "", {}, "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], + + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + + "is-what": ["is-what@4.1.16", "", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="], + + "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], + + "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], + + "js-beautify": ["js-beautify@1.15.4", "", { "dependencies": { "config-chain": "^1.1.13", "editorconfig": "^1.0.4", "glob": "^10.4.2", "js-cookie": "^3.0.5", "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", "html-beautify": "js/bin/html-beautify.js", "js-beautify": "js/bin/js-beautify.js" } }, "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA=="], + + "js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "jsdom": ["jsdom@26.0.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", "form-data": "^4.0.1", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@4.0.0", "", {}, "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.29.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.2", "lightningcss-darwin-x64": "1.29.2", "lightningcss-freebsd-x64": "1.29.2", "lightningcss-linux-arm-gnueabihf": "1.29.2", "lightningcss-linux-arm64-gnu": "1.29.2", "lightningcss-linux-arm64-musl": "1.29.2", "lightningcss-linux-x64-gnu": "1.29.2", "lightningcss-linux-x64-musl": "1.29.2", "lightningcss-win32-arm64-msvc": "1.29.2", "lightningcss-win32-x64-msvc": "1.29.2" } }, "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.29.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.29.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.29.2", "", { "os": "linux", "cpu": "arm" }, "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.29.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "loupe": ["loupe@3.1.3", "", {}, "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug=="], + + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "memorystream": ["memorystream@0.3.1", "", {}, "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], + + "nanoid": ["nanoid@3.3.9", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], + + "nopt": ["nopt@7.2.1", "", { "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w=="], + + "npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="], + + "npm-run-all2": ["npm-run-all2@7.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "minimatch": "^9.0.0", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-7tXR+r9hzRNOPNTvXegM+QzCuMjzUIIq66VDunL6j60O4RrExx32XUhlrS7UK4VcdGw5/Wxzb3kfNcFix9JKDA=="], + + "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], + + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + + "nwsapi": ["nwsapi@2.2.18", "", {}, "sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA=="], + + "open": ["open@10.1.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + + "parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="], + + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "pathval": ["pathval@2.0.0", "", {}, "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA=="], + + "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + + "pinia": ["pinia@3.0.1", "", { "dependencies": { "@vue/devtools-api": "^7.7.2" }, "peerDependencies": { "typescript": ">=4.4.4", "vue": "^2.7.0 || ^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg=="], + + "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], + + "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], + + "prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="], + + "pretty-ms": ["pretty-ms@9.2.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg=="], + + "proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "read-package-json-fast": ["read-package-json-fast@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rollup": ["rollup@4.35.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.35.0", "@rollup/rollup-android-arm64": "4.35.0", "@rollup/rollup-darwin-arm64": "4.35.0", "@rollup/rollup-darwin-x64": "4.35.0", "@rollup/rollup-freebsd-arm64": "4.35.0", "@rollup/rollup-freebsd-x64": "4.35.0", "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", "@rollup/rollup-linux-arm-musleabihf": "4.35.0", "@rollup/rollup-linux-arm64-gnu": "4.35.0", "@rollup/rollup-linux-arm64-musl": "4.35.0", "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", "@rollup/rollup-linux-riscv64-gnu": "4.35.0", "@rollup/rollup-linux-s390x-gnu": "4.35.0", "@rollup/rollup-linux-x64-gnu": "4.35.0", "@rollup/rollup-linux-x64-musl": "4.35.0", "@rollup/rollup-win32-arm64-msvc": "4.35.0", "@rollup/rollup-win32-ia32-msvc": "4.35.0", "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg=="], + + "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], + + "run-applescript": ["run-applescript@7.0.0", "", {}, "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + + "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shell-quote": ["shell-quote@1.8.2", "", {}, "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "speakingurl": ["speakingurl@14.0.1", "", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@3.8.1", "", {}, "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA=="], + + "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "superjson": ["superjson@2.2.2", "", { "dependencies": { "copy-anything": "^3.0.2" } }, "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "synckit": ["synckit@0.9.2", "", { "dependencies": { "@pkgr/core": "^0.1.0", "tslib": "^2.6.2" } }, "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw=="], + + "tailwindcss": ["tailwindcss@4.0.14", "", {}, "sha512-92YT2dpt671tFiHH/e1ok9D987N9fHD5VWoly1CdPD/Cd1HMglvZwP3nx2yTj2lbXDAHt8QssZkxTLCCTNL+xw=="], + + "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinypool": ["tinypool@1.0.2", "", {}, "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA=="], + + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="], + + "tldts": ["tldts@6.1.84", "", { "dependencies": { "tldts-core": "^6.1.84" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg=="], + + "tldts-core": ["tldts-core@6.1.84", "", {}, "sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + + "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], + + "tr46": ["tr46@5.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g=="], + + "ts-api-utils": ["ts-api-utils@2.0.1", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], + + "typescript-eslint": ["typescript-eslint@8.26.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.26.1", "@typescript-eslint/parser": "8.26.1", "@typescript-eslint/utils": "8.26.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-t/oIs9mYyrwZGRpDv3g+3K6nZ5uhKEMt2oNmAPwaY4/ye0+EH4nXIPYNtkYFS6QHm+1DFg34DbglYBz5P9Xysg=="], + + "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], + + "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + + "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "vite": ["vite@6.2.2", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ=="], + + "vite-hot-client": ["vite-hot-client@0.2.4", "", { "peerDependencies": { "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0" } }, "sha512-a1nzURqO7DDmnXqabFOliz908FRmIppkBKsJthS8rbe8hBEXwEwe4C3Pp33Z1JoFCYfVL4kTOMLKk0ZZxREIeA=="], + + "vite-node": ["vite-node@3.0.8", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg=="], + + "vite-plugin-inspect": ["vite-plugin-inspect@0.8.9", "", { "dependencies": { "@antfu/utils": "^0.7.10", "@rollup/pluginutils": "^5.1.3", "debug": "^4.3.7", "error-stack-parser-es": "^0.1.5", "fs-extra": "^11.2.0", "open": "^10.1.0", "perfect-debounce": "^1.0.0", "picocolors": "^1.1.1", "sirv": "^3.0.0" }, "peerDependencies": { "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1" } }, "sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A=="], + + "vite-plugin-vue-devtools": ["vite-plugin-vue-devtools@7.7.2", "", { "dependencies": { "@vue/devtools-core": "^7.7.2", "@vue/devtools-kit": "^7.7.2", "@vue/devtools-shared": "^7.7.2", "execa": "^9.5.1", "sirv": "^3.0.0", "vite-plugin-inspect": "0.8.9", "vite-plugin-vue-inspector": "^5.3.1" }, "peerDependencies": { "vite": "^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0" } }, "sha512-5V0UijQWiSBj32blkyPEqIbzc6HO9c1bwnBhx+ay2dzU0FakH+qMdNUT8nF9BvDE+i6I1U8CqCuJiO20vKEdQw=="], + + "vite-plugin-vue-inspector": ["vite-plugin-vue-inspector@5.3.1", "", { "dependencies": { "@babel/core": "^7.23.0", "@babel/plugin-proposal-decorators": "^7.23.0", "@babel/plugin-syntax-import-attributes": "^7.22.5", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-typescript": "^7.22.15", "@vue/babel-plugin-jsx": "^1.1.5", "@vue/compiler-dom": "^3.3.4", "kolorist": "^1.8.0", "magic-string": "^0.30.4" }, "peerDependencies": { "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0" } }, "sha512-cBk172kZKTdvGpJuzCCLg8lJ909wopwsu3Ve9FsL1XsnLBiRT9U3MePcqrgGHgCX2ZgkqZmAGR8taxw+TV6s7A=="], + + "vitest": ["vitest@3.0.8", "", { "dependencies": { "@vitest/expect": "3.0.8", "@vitest/mocker": "3.0.8", "@vitest/pretty-format": "^3.0.8", "@vitest/runner": "3.0.8", "@vitest/snapshot": "3.0.8", "@vitest/spy": "3.0.8", "@vitest/utils": "3.0.8", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", "vite-node": "3.0.8", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.0.8", "@vitest/ui": "3.0.8", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA=="], + + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + + "vue": ["vue@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", "@vue/runtime-dom": "3.5.13", "@vue/server-renderer": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="], + + "vue-component-type-helpers": ["vue-component-type-helpers@2.2.8", "", {}, "sha512-4bjIsC284coDO9om4HPA62M7wfsTvcmZyzdfR0aUlFXqq4tXxM1APyXpNVxPC8QazKw9OhmZNHBVDA6ODaZsrA=="], + + "vue-eslint-parser": ["vue-eslint-parser@10.1.1", "", { "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.6.0", "lodash": "^4.17.21", "semver": "^7.6.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-bh2Z/Au5slro9QJ3neFYLanZtb1jH+W2bKqGHXAoYD4vZgNG3KeotL7JpPv5xzY4UXUXJl7TrIsnzECH63kd3Q=="], + + "vue-router": ["vue-router@4.5.0", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.2.0" } }, "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w=="], + + "vue-tsc": ["vue-tsc@2.2.8", "", { "dependencies": { "@volar/typescript": "~2.4.11", "@vue/language-core": "2.2.8" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-jBYKBNFADTN+L+MdesNX/TB3XuDSyaWynKMDgR+yCSln0GQ9Tfb7JS2lr46s2LiFUT1WsmfWsSvIElyxzOPqcQ=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + + "whatwg-url": ["whatwg-url@14.1.1", "", { "dependencies": { "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" } }, "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ=="], + + "which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="], + + "xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "yoctocolors": ["yoctocolors@2.1.1", "", {}, "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + + "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "@vue/devtools-core/nanoid": ["nanoid@5.1.3", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ=="], + + "@vue/language-core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "jsdom/xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "npm-run-all2/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "vue-router/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="], + + "w3c-xmlserializer/xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "@vue/language-core/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "editorconfig/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "npm-run-all2/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/webapp/dist/assets/ArchiveView-DU4ueUTk.js b/webapp/dist/assets/ArchiveView-DU4ueUTk.js new file mode 100644 index 000000000..e68ba6cdf --- /dev/null +++ b/webapp/dist/assets/ArchiveView-DU4ueUTk.js @@ -0,0 +1 @@ +import{b as n,e as o,w as t,_ as a,f as i,i as s}from"./index-B6yYn_6W.js";const f=n({__name:"ArchiveView",setup(r){return(l,e)=>(i(),o(a,null,{header:t(()=>e[0]||(e[0]=[s("div",{class:"flex justify-between items-center"},[s("h1",{class:"text-xl font-bold"},"Archive")],-1)])),default:t(()=>[e[1]||(e[1]=s("div",{class:"bg-white p-6 rounded-md shadow-sm"},[s("p",null,"Archive view - Coming soon")],-1))]),_:1}))}});export{f as default}; diff --git a/webapp/dist/assets/FoldersView-MT8bk8jx.js b/webapp/dist/assets/FoldersView-MT8bk8jx.js new file mode 100644 index 000000000..40fa4e5f9 --- /dev/null +++ b/webapp/dist/assets/FoldersView-MT8bk8jx.js @@ -0,0 +1 @@ +import{b as d,r as i,e as c,w as l,_ as m,f as s,i as e,g as o,F as u,m as g,t as n,n as h}from"./index-B6yYn_6W.js";const x={class:"mt-6"},v={key:0,class:"text-center py-8 text-gray-500"},w={key:1,class:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"},p={class:"flex items-center"},f={class:"flex-1"},k={class:"font-medium text-lg"},_={class:"text-sm text-gray-500"},b=d({__name:"FoldersView",setup(y){const r=i([{id:1,name:"Work",bookmarkCount:12},{id:2,name:"Personal",bookmarkCount:8},{id:3,name:"Research",bookmarkCount:15},{id:4,name:"Reading List",bookmarkCount:23}]);return(C,t)=>(s(),c(m,null,{header:l(()=>t[0]||(t[0]=[e("div",{class:"flex justify-between items-center"},[e("h1",{class:"text-xl font-bold"},"Folders"),e("div",{class:"flex space-x-2"},[e("button",{class:"bg-red-500 text-white px-3 py-1 rounded-md hover:bg-red-600"}," New Folder ")])],-1)])),default:l(()=>[e("div",x,[r.value.length===0?(s(),o("div",v," No folders yet. Create your first folder to organize your bookmarks. ")):(s(),o("ul",w,[(s(!0),o(u,null,g(r.value,a=>(s(),o("li",{key:a.id,class:"bg-white p-4 rounded-md shadow-sm hover:shadow-md transition-shadow border border-gray-200"},[e("div",p,[t[1]||(t[1]=e("div",{class:"mr-3 text-gray-400"},[e("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-8 w-8",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"})])],-1)),e("div",f,[e("h3",k,n(a.name),1),e("p",_,n(a.bookmarkCount)+" bookmarks",1)]),t[2]||(t[2]=e("div",{class:"flex space-x-1"},[e("button",{class:"text-gray-400 hover:text-gray-600 p-1",title:"Edit"},[e("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[e("path",{d:"M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"})])]),e("button",{class:"text-gray-400 hover:text-red-500 p-1",title:"Delete"},[e("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[e("path",{"fill-rule":"evenodd",d:"M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z","clip-rule":"evenodd"})])])],-1))])]))),128))]))])]),_:1}))}}),z=h(b,[["__scopeId","data-v-f30fbcbb"]]);export{z as default}; diff --git a/webapp/dist/assets/FoldersView-tn0RQdqM.css b/webapp/dist/assets/FoldersView-tn0RQdqM.css new file mode 100644 index 000000000..e69de29bb diff --git a/webapp/dist/assets/SettingsView-BN1uxgdS.js b/webapp/dist/assets/SettingsView-BN1uxgdS.js new file mode 100644 index 000000000..6fcd003e0 --- /dev/null +++ b/webapp/dist/assets/SettingsView-BN1uxgdS.js @@ -0,0 +1 @@ +import{b as h,p as u,r as v,e as f,w as c,_ as p,f as a,i as e,t as d,l,g as i,m as b,q as w,h as x,F as k,s as y}from"./index-B6yYn_6W.js";const C={class:"flex justify-between items-center"},B={class:"text-xl font-bold"},L={class:"bg-white dark:bg-gray-800 p-6 rounded-md shadow-sm"},z={class:"mb-6"},E={class:"text-lg font-semibold mb-4"},F={class:"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"},V=["onClick"],j={class:"flex items-center"},D={class:"flex-1"},N={class:"font-medium"},S={key:0,class:"text-red-500"},M=h({__name:"SettingsView",setup($){const{t:r,locale:m}=u(),_=[{code:"en",name:"English"},{code:"es",name:"Español"},{code:"fr",name:"Français"},{code:"de",name:"Deutsch"},{code:"ja",name:"日本語"}],t=v(m.value),g=o=>{t.value=o,y(o)};return(o,n)=>(a(),f(p,null,{header:c(()=>[e("div",C,[e("h1",B,d(l(r)("settings.title")),1)])]),default:c(()=>[e("div",L,[e("div",z,[e("h2",E,d(l(r)("settings.language")),1),e("div",F,[(a(),i(k,null,b(_,s=>e("div",{key:s.code,onClick:q=>g(s.code),class:w(["border rounded-md p-4 cursor-pointer transition-colors",t.value===s.code?"border-red-500 bg-red-50 dark:bg-red-900/20":"border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700"])},[e("div",j,[e("div",D,[e("div",N,d(s.name),1)]),t.value===s.code?(a(),i("div",S,n[0]||(n[0]=[e("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[e("path",{"fill-rule":"evenodd",d:"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z","clip-rule":"evenodd"})],-1)]))):x("",!0)])],10,V)),64))])])])]),_:1}))}});export{M as default}; diff --git a/webapp/dist/assets/TagsView-C_xWTaWR.js b/webapp/dist/assets/TagsView-C_xWTaWR.js new file mode 100644 index 000000000..642a05ee8 --- /dev/null +++ b/webapp/dist/assets/TagsView-C_xWTaWR.js @@ -0,0 +1 @@ +import{B as U,J as P,M as E,R as k,V as O,a as S,d as J,r as f,u as F,C as H,b as K,o as Q,c as W,e as X,w as B,_ as Y,f as g,g as v,h as C,i as t,j as M,v as N,t as V,k as Z,l as b,F as ee,m as te}from"./index-B6yYn_6W.js";class ae extends U{async apiV1TagsGetRaw(e,s){const o={};e.withBookmarkCount!=null&&(o.with_bookmark_count=e.withBookmarkCount),e.bookmarkId!=null&&(o.bookmark_id=e.bookmarkId),e.search!=null&&(o.search=e.search);const r={},u=await this.request({path:"/api/v1/tags",method:"GET",headers:r,query:o},s);return new P(u,w=>w.map(E))}async apiV1TagsGet(e={},s){return await(await this.apiV1TagsGetRaw(e,s)).value()}async apiV1TagsIdDeleteRaw(e,s){if(e.id==null)throw new k("id",'Required parameter "id" was null or undefined when calling apiV1TagsIdDelete().');const o={},r={},u=await this.request({path:"/api/v1/tags/{id}".replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",headers:r,query:o},s);return new O(u)}async apiV1TagsIdDelete(e,s){await this.apiV1TagsIdDeleteRaw(e,s)}async apiV1TagsIdGetRaw(e,s){if(e.id==null)throw new k("id",'Required parameter "id" was null or undefined when calling apiV1TagsIdGet().');const o={},r={},u=await this.request({path:"/api/v1/tags/{id}".replace("{id}",encodeURIComponent(String(e.id))),method:"GET",headers:r,query:o},s);return new P(u,w=>E(w))}async apiV1TagsIdGet(e,s){return await(await this.apiV1TagsIdGetRaw(e,s)).value()}async apiV1TagsIdPutRaw(e,s){if(e.id==null)throw new k("id",'Required parameter "id" was null or undefined when calling apiV1TagsIdPut().');if(e.tag==null)throw new k("tag",'Required parameter "tag" was null or undefined when calling apiV1TagsIdPut().');const o={},r={};r["Content-Type"]="application/json";const u=await this.request({path:"/api/v1/tags/{id}".replace("{id}",encodeURIComponent(String(e.id))),method:"PUT",headers:r,query:o,body:S(e.tag)},s);return new P(u,w=>E(w))}async apiV1TagsIdPut(e,s){return await(await this.apiV1TagsIdPutRaw(e,s)).value()}async apiV1TagsPostRaw(e,s){if(e.tag==null)throw new k("tag",'Required parameter "tag" was null or undefined when calling apiV1TagsPost().');const o={},r={};r["Content-Type"]="application/json";const u=await this.request({path:"/api/v1/tags",method:"POST",headers:r,query:o,body:S(e.tag)},s);return new P(u,w=>E(w))}async apiV1TagsPost(e,s){return await(await this.apiV1TagsPostRaw(e,s)).value()}}const se=J("tags",()=>{const h=f([]),e=f(!1),s=f(null),o=()=>{const n=F().token,l=new H({basePath:"http://localhost:8080",accessToken:n||void 0,headers:n?{Authorization:`Bearer ${n}`}:void 0});return new ae(l)};return{tags:h,isLoading:e,error:s,fetchTags:async(c=!0)=>{e.value=!0,s.value=null;try{const l=await o().apiV1TagsGet({withBookmarkCount:c});return Array.isArray(l)?h.value=l:(console.error("Expected array response but got:",typeof l),h.value=[]),h.value}catch(n){throw console.error("Failed to fetch tags:",n),n instanceof Error&&n.message.includes("401")?s.value="Authentication error. Please log in again.":s.value="Failed to load tags. Please try again.",n}finally{e.value=!1}},createTag:async c=>{e.value=!0,s.value=null;try{const l=await o().apiV1TagsPost({tag:{name:c}});return h.value.push(l),l}catch(n){throw console.error("Failed to create tag:",n),n instanceof Error&&n.message.includes("401")?s.value="Authentication error. Please log in again.":s.value="Failed to create tag. Please try again.",n}finally{e.value=!1}},updateTag:async(c,n)=>{e.value=!0,s.value=null;try{const m=await o().apiV1TagsIdPut({id:c,tag:{id:c,name:n}}),y=h.value.findIndex(p=>p.id===c);return y!==-1&&(h.value[y]=m),m}catch(l){throw console.error("Failed to update tag:",l),l instanceof Error&&l.message.includes("401")?s.value="Authentication error. Please log in again.":s.value="Failed to update tag. Please try again.",l}finally{e.value=!1}},deleteTag:async c=>{e.value=!0,s.value=null;try{await o().apiV1TagsIdDelete({id:c}),h.value=h.value.filter(l=>l.id!==c)}catch(n){throw console.error("Failed to delete tag:",n),n instanceof Error&&n.message.includes("401")?s.value="Authentication error. Please log in again.":s.value="Failed to delete tag. Please try again.",n}finally{e.value=!1}}}}),oe={class:"flex justify-between items-center"},ne={class:"flex space-x-2"},le={key:0,class:"bg-white p-4 rounded-md shadow-sm mb-6"},re=["disabled"],ie={key:0,class:"mt-1 text-sm text-red-600"},de={class:"flex justify-end space-x-2"},ue=["disabled"],ce=["disabled"],ge={key:1,class:"bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md mb-6"},pe={key:2,class:"bg-white p-6 rounded-md shadow-sm flex justify-center"},ve={key:3,class:"bg-white p-6 rounded-md shadow-sm text-center"},he={key:4,class:"mt-6"},we={class:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"},me={key:0,class:"flex items-center"},fe=["disabled"],ye={class:"flex ml-2 space-x-1"},be=["onClick","disabled"],xe=["disabled"],Te={key:1,class:"flex items-center"},ke={class:"flex-1"},Ce={class:"font-medium text-lg"},Ve={class:"text-sm text-gray-500"},_e={class:"flex space-x-1"},Re=["onClick"],Ie=["onClick"],Pe={key:5,class:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"},Ee={class:"bg-white rounded-lg p-6 max-w-md w-full"},De={class:"flex justify-end space-x-3"},Se=K({__name:"TagsView",setup(h){const e=se(),s=F(),o=W(),{tags:r,isLoading:u,error:w,fetchTags:D,createTag:c,updateTag:n,deleteTag:l}=e,m=f(!1),y=f(""),p=f(!1),_=f(null),R=f(null),x=f("");Q(async()=>{try{s.isAuthenticated?await D():await s.validateToken()?(await D(),console.log("tags",r)):(s.setRedirectDestination("/tags"),o.push("/login"))}catch(i){i instanceof Error&&i.message.includes("401")&&(s.setRedirectDestination("/tags"),o.push("/login"))}});const A=i=>{i instanceof Error&&i.message.includes("401")&&(s.setRedirectDestination("/tags"),o.push("/login"))},L=async()=>{if(!y.value.trim()){_.value="Tag name cannot be empty";return}_.value=null,p.value=!0;try{await c(y.value.trim()),y.value="",m.value=!1}catch(i){A(i)}finally{p.value=!1}},$=(i,a)=>{R.value=i,x.value=a},z=()=>{R.value=null,x.value=""},q=async i=>{if(x.value.trim()){p.value=!0;try{await n(i,x.value.trim()),R.value=null}catch(a){A(a)}finally{p.value=!1}}},T=f(null),G=i=>{T.value=i},j=async()=>{if(T.value!==null)try{await l(T.value),T.value=null}catch(i){A(i)}};return(i,a)=>(g(),X(Y,null,{header:B(()=>[t("div",oe,[a[6]||(a[6]=t("h1",{class:"text-xl font-bold"},"Tags",-1)),t("div",ne,[t("button",{onClick:a[0]||(a[0]=d=>m.value=!m.value),class:"bg-blue-500 text-white px-3 py-1 rounded-md hover:bg-blue-600 transition"},V(m.value?"Cancel":"New Tag"),1)])])]),default:B(()=>[m.value?(g(),v("div",le,[a[8]||(a[8]=t("h2",{class:"text-lg font-medium mb-3"},"Create New Tag",-1)),t("form",{onSubmit:Z(L,["prevent"]),class:"flex flex-col space-y-3"},[t("div",null,[a[7]||(a[7]=t("label",{for:"tagName",class:"block text-sm font-medium text-gray-700 mb-1"},"Tag Name",-1)),M(t("input",{id:"tagName","onUpdate:modelValue":a[1]||(a[1]=d=>y.value=d),type:"text",class:"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",placeholder:"Enter tag name",disabled:p.value},null,8,re),[[N,y.value]]),_.value?(g(),v("p",ie,V(_.value),1)):C("",!0)]),t("div",de,[t("button",{type:"button",onClick:a[2]||(a[2]=d=>m.value=!1),class:"px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50",disabled:p.value}," Cancel ",8,ue),t("button",{type:"submit",class:"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:opacity-50",disabled:p.value}," Create ",8,ce)])],32)])):C("",!0),b(w)?(g(),v("div",ge,V(b(w)),1)):C("",!0),b(u)&&!b(r).length?(g(),v("div",pe,a[9]||(a[9]=[t("div",{class:"animate-pulse text-gray-500"},"Loading tags...",-1)]))):!b(u)&&!b(r).length?(g(),v("div",ve,[a[10]||(a[10]=t("p",{class:"text-gray-500 mb-4"},"No tags found. Create your first tag to organize your bookmarks.",-1)),m.value?C("",!0):(g(),v("button",{key:0,onClick:a[3]||(a[3]=d=>m.value=!0),class:"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"}," Create Tag "))])):(g(),v("div",he,[t("ul",we,[(g(!0),v(ee,null,te(b(r),d=>(g(),v("li",{key:d.id,class:"bg-white p-4 rounded-md shadow-sm hover:shadow-md transition-shadow border border-gray-200"},[R.value===d.id?(g(),v("div",me,[M(t("input",{"onUpdate:modelValue":a[4]||(a[4]=I=>x.value=I),type:"text",class:"flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",disabled:p.value},null,8,fe),[[N,x.value]]),t("div",ye,[t("button",{onClick:I=>q(d.id),class:"text-blue-500 hover:text-blue-700 p-1",disabled:p.value,title:"Save"},a[11]||(a[11]=[t("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[t("path",{"fill-rule":"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z","clip-rule":"evenodd"})],-1)]),8,be),t("button",{onClick:z,class:"text-gray-500 hover:text-gray-700 p-1",disabled:p.value,title:"Cancel"},a[12]||(a[12]=[t("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[t("path",{"fill-rule":"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z","clip-rule":"evenodd"})],-1)]),8,xe)])])):(g(),v("div",Te,[a[15]||(a[15]=t("div",{class:"mr-3 text-blue-400"},[t("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-6 w-6",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"})])],-1)),t("div",ke,[t("h3",Ce,V(d.name),1),t("p",Ve,V(d.bookmarkCount||0)+" bookmarks",1)]),t("div",_e,[t("button",{onClick:I=>$(d.id,d.name),class:"text-gray-400 hover:text-gray-600 p-1",title:"Edit"},a[13]||(a[13]=[t("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[t("path",{d:"M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"})],-1)]),8,Re),t("button",{onClick:I=>G(d.id),class:"text-gray-400 hover:text-red-500 p-1",title:"Delete"},a[14]||(a[14]=[t("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[t("path",{"fill-rule":"evenodd",d:"M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z","clip-rule":"evenodd"})],-1)]),8,Ie)])]))]))),128))])])),T.value!==null?(g(),v("div",Pe,[t("div",Ee,[a[16]||(a[16]=t("h3",{class:"text-lg font-medium mb-4"},"Confirm Delete",-1)),a[17]||(a[17]=t("p",{class:"mb-6"},"Are you sure you want to delete this tag? This action cannot be undone.",-1)),t("div",De,[t("button",{onClick:a[5]||(a[5]=d=>T.value=null),class:"px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"}," Cancel "),t("button",{onClick:j,class:"px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600"}," Delete ")])])])):C("",!0)]),_:1}))}});export{Se as default}; diff --git a/webapp/dist/assets/index-B6yYn_6W.js b/webapp/dist/assets/index-B6yYn_6W.js new file mode 100644 index 000000000..b247e7403 --- /dev/null +++ b/webapp/dist/assets/index-B6yYn_6W.js @@ -0,0 +1,58 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/FoldersView-MT8bk8jx.js","assets/FoldersView-tn0RQdqM.css"])))=>i.map(i=>d[i]); +var mc=Object.defineProperty;var gc=(e,t,n)=>t in e?mc(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Qt=(e,t,n)=>gc(e,typeof t!="symbol"?t+"":t,n);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))r(s);new MutationObserver(s=>{for(const o of s)if(o.type==="childList")for(const a of o.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&r(a)}).observe(document,{childList:!0,subtree:!0});function n(s){const o={};return s.integrity&&(o.integrity=s.integrity),s.referrerPolicy&&(o.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?o.credentials="include":s.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(s){if(s.ep)return;s.ep=!0;const o=n(s);fetch(s.href,o)}})();/** +* @vue/shared v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**//*! #__NO_SIDE_EFFECTS__ */function Ys(e){const t=Object.create(null);for(const n of e.split(","))t[n]=1;return n=>n in t}const _e={},bn=[],vt=()=>{},pc=()=>!1,xr=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Js=e=>e.startsWith("onUpdate:"),We=Object.assign,Xs=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},_c=Object.prototype.hasOwnProperty,he=(e,t)=>_c.call(e,t),J=Array.isArray,yn=e=>or(e)==="[object Map]",Mr=e=>or(e)==="[object Set]",Lo=e=>or(e)==="[object Date]",te=e=>typeof e=="function",Re=e=>typeof e=="string",dt=e=>typeof e=="symbol",ve=e=>e!==null&&typeof e=="object",ci=e=>(ve(e)||te(e))&&te(e.then)&&te(e.catch),ui=Object.prototype.toString,or=e=>ui.call(e),bc=e=>or(e).slice(8,-1),fi=e=>or(e)==="[object Object]",zs=e=>Re(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,$n=Ys(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Dr=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},yc=/-(\w)/g,Wt=Dr(e=>e.replace(yc,(t,n)=>n?n.toUpperCase():"")),vc=/\B([A-Z])/g,un=Dr(e=>e.replace(vc,"-$1").toLowerCase()),di=Dr(e=>e.charAt(0).toUpperCase()+e.slice(1)),rs=Dr(e=>e?`on${di(e)}`:""),$t=(e,t)=>!Object.is(e,t),_r=(e,...t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:n})},ks=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let Oo;const Fr=()=>Oo||(Oo=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Qs(e){if(J(e)){const t={};for(let n=0;n{if(n){const r=n.split(kc);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function $r(e){let t="";if(Re(e))t=e;else if(J(e))for(let n=0;nUr(n,t))}const pi=e=>!!(e&&e.__v_isRef===!0),Ie=e=>Re(e)?e:e==null?"":J(e)||ve(e)&&(e.toString===ui||!te(e.toString))?pi(e)?Ie(e.value):JSON.stringify(e,_i,2):String(e),_i=(e,t)=>pi(t)?_i(e,t.value):yn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,s],o)=>(n[ss(r,o)+" =>"]=s,n),{})}:Mr(t)?{[`Set(${t.size})`]:[...t.values()].map(n=>ss(n))}:dt(t)?ss(t):ve(t)&&!J(t)&&!fi(t)?String(t):t,ss=(e,t="")=>{var n;return dt(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};/** +* @vue/reactivity v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let qe;class bi{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=qe,!t&&qe&&(this.index=(qe.scopes||(qe.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,n;if(this.scopes)for(t=0,n=this.scopes.length;t0)return;if(Vn){let t=Vn;for(Vn=void 0;t;){const n=t.next;t.next=void 0,t.flags&=-9,t=n}}let e;for(;Un;){let t=Un;for(Un=void 0;t;){const n=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(r){e||(e=r)}t=n}}if(e)throw e}function Ti(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function wi(e){let t,n=e.depsTail,r=n;for(;r;){const s=r.prevDep;r.version===-1?(r===n&&(n=s),no(r),Ac(r)):t=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=s}e.deps=t,e.depsTail=n}function Ts(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Si(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Si(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===jn))return;e.globalVersion=jn;const t=e.dep;if(e.flags|=2,t.version>0&&!e.isSSR&&e.deps&&!Ts(e)){e.flags&=-3;return}const n=be,r=ft;be=e,ft=!0;try{Ti(e);const s=e.fn(e._value);(t.version===0||$t(s,e._value))&&(e._value=s,t.version++)}catch(s){throw t.version++,s}finally{be=n,ft=r,wi(e),e.flags&=-3}}function no(e,t=!1){const{dep:n,prevSub:r,nextSub:s}=e;if(r&&(r.nextSub=s,e.prevSub=void 0),s&&(s.prevSub=r,e.nextSub=void 0),n.subs===e&&(n.subs=r,!r&&n.computed)){n.computed.flags&=-5;for(let o=n.computed.deps;o;o=o.nextDep)no(o,!0)}!t&&!--n.sc&&n.map&&n.map.delete(n.key)}function Ac(e){const{prevDep:t,nextDep:n}=e;t&&(t.nextDep=n,e.prevDep=void 0),n&&(n.prevDep=t,e.nextDep=void 0)}let ft=!0;const Li=[];function Gt(){Li.push(ft),ft=!1}function jt(){const e=Li.pop();ft=e===void 0?!0:e}function Io(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const n=be;be=void 0;try{t()}finally{be=n}}}let jn=0;class Pc{constructor(t,n){this.sub=t,this.dep=n,this.version=n.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class ro{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0}track(t){if(!be||!ft||be===this.computed)return;let n=this.activeLink;if(n===void 0||n.sub!==be)n=this.activeLink=new Pc(be,this),be.deps?(n.prevDep=be.depsTail,be.depsTail.nextDep=n,be.depsTail=n):be.deps=be.depsTail=n,Oi(n);else if(n.version===-1&&(n.version=this.version,n.nextDep)){const r=n.nextDep;r.prevDep=n.prevDep,n.prevDep&&(n.prevDep.nextDep=r),n.prevDep=be.depsTail,n.nextDep=void 0,be.depsTail.nextDep=n,be.depsTail=n,be.deps===n&&(be.deps=r)}return n}trigger(t){this.version++,jn++,this.notify(t)}notify(t){eo();try{for(let n=this.subs;n;n=n.prevSub)n.sub.notify()&&n.sub.dep.notify()}finally{to()}}}function Oi(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let r=t.deps;r;r=r.nextDep)Oi(r)}const n=e.dep.subs;n!==e&&(e.prevSub=n,n&&(n.nextSub=e)),e.dep.subs=e}}const Tr=new WeakMap,an=Symbol(""),ws=Symbol(""),qn=Symbol("");function Ve(e,t,n){if(ft&&be){let r=Tr.get(e);r||Tr.set(e,r=new Map);let s=r.get(n);s||(r.set(n,s=new ro),s.map=r,s.key=n),s.track()}}function Ot(e,t,n,r,s,o){const a=Tr.get(e);if(!a){jn++;return}const i=l=>{l&&l.trigger()};if(eo(),t==="clear")a.forEach(i);else{const l=J(e),c=l&&zs(n);if(l&&n==="length"){const u=Number(r);a.forEach((f,m)=>{(m==="length"||m===qn||!dt(m)&&m>=u)&&i(f)})}else switch((n!==void 0||a.has(void 0))&&i(a.get(n)),c&&i(a.get(qn)),t){case"add":l?c&&i(a.get("length")):(i(a.get(an)),yn(e)&&i(a.get(ws)));break;case"delete":l||(i(a.get(an)),yn(e)&&i(a.get(ws)));break;case"set":yn(e)&&i(a.get(an));break}}to()}function Rc(e,t){const n=Tr.get(e);return n&&n.get(t)}function dn(e){const t=ie(e);return t===e?t:(Ve(t,"iterate",qn),at(e)?t:t.map(He))}function Vr(e){return Ve(e=ie(e),"iterate",qn),e}const Cc={__proto__:null,[Symbol.iterator](){return as(this,Symbol.iterator,He)},concat(...e){return dn(this).concat(...e.map(t=>J(t)?dn(t):t))},entries(){return as(this,"entries",e=>(e[1]=He(e[1]),e))},every(e,t){return kt(this,"every",e,t,void 0,arguments)},filter(e,t){return kt(this,"filter",e,t,n=>n.map(He),arguments)},find(e,t){return kt(this,"find",e,t,He,arguments)},findIndex(e,t){return kt(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return kt(this,"findLast",e,t,He,arguments)},findLastIndex(e,t){return kt(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return kt(this,"forEach",e,t,void 0,arguments)},includes(...e){return is(this,"includes",e)},indexOf(...e){return is(this,"indexOf",e)},join(e){return dn(this).join(e)},lastIndexOf(...e){return is(this,"lastIndexOf",e)},map(e,t){return kt(this,"map",e,t,void 0,arguments)},pop(){return Nn(this,"pop")},push(...e){return Nn(this,"push",e)},reduce(e,...t){return Ao(this,"reduce",e,t)},reduceRight(e,...t){return Ao(this,"reduceRight",e,t)},shift(){return Nn(this,"shift")},some(e,t){return kt(this,"some",e,t,void 0,arguments)},splice(...e){return Nn(this,"splice",e)},toReversed(){return dn(this).toReversed()},toSorted(e){return dn(this).toSorted(e)},toSpliced(...e){return dn(this).toSpliced(...e)},unshift(...e){return Nn(this,"unshift",e)},values(){return as(this,"values",He)}};function as(e,t,n){const r=Vr(e),s=r[t]();return r!==e&&!at(e)&&(s._next=s.next,s.next=()=>{const o=s._next();return o.value&&(o.value=n(o.value)),o}),s}const Nc=Array.prototype;function kt(e,t,n,r,s,o){const a=Vr(e),i=a!==e&&!at(e),l=a[t];if(l!==Nc[t]){const f=l.apply(e,o);return i?He(f):f}let c=n;a!==e&&(i?c=function(f,m){return n.call(this,He(f),m,e)}:n.length>2&&(c=function(f,m){return n.call(this,f,m,e)}));const u=l.call(a,c,r);return i&&s?s(u):u}function Ao(e,t,n,r){const s=Vr(e);let o=n;return s!==e&&(at(e)?n.length>3&&(o=function(a,i,l){return n.call(this,a,i,l,e)}):o=function(a,i,l){return n.call(this,a,He(i),l,e)}),s[t](o,...r)}function is(e,t,n){const r=ie(e);Ve(r,"iterate",qn);const s=r[t](...n);return(s===-1||s===!1)&&ao(n[0])?(n[0]=ie(n[0]),r[t](...n)):s}function Nn(e,t,n=[]){Gt(),eo();const r=ie(e)[t].apply(e,n);return to(),jt(),r}const xc=Ys("__proto__,__v_isRef,__isVue"),Ii=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(dt));function Mc(e){dt(e)||(e=String(e));const t=ie(this);return Ve(t,"has",e),t.hasOwnProperty(e)}class Ai{constructor(t=!1,n=!1){this._isReadonly=t,this._isShallow=n}get(t,n,r){if(n==="__v_skip")return t.__v_skip;const s=this._isReadonly,o=this._isShallow;if(n==="__v_isReactive")return!s;if(n==="__v_isReadonly")return s;if(n==="__v_isShallow")return o;if(n==="__v_raw")return r===(s?o?Gc:Ni:o?Ci:Ri).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const a=J(t);if(!s){let l;if(a&&(l=Cc[n]))return l;if(n==="hasOwnProperty")return Mc}const i=Reflect.get(t,n,Oe(t)?t:r);return(dt(n)?Ii.has(n):xc(n))||(s||Ve(t,"get",n),o)?i:Oe(i)?a&&zs(n)?i:i.value:ve(i)?s?Mi(i):ar(i):i}}class Pi extends Ai{constructor(t=!1){super(!1,t)}set(t,n,r,s){let o=t[n];if(!this._isShallow){const l=cn(o);if(!at(r)&&!cn(r)&&(o=ie(o),r=ie(r)),!J(t)&&Oe(o)&&!Oe(r))return l?!1:(o.value=r,!0)}const a=J(t)&&zs(n)?Number(n)e,fr=e=>Reflect.getPrototypeOf(e);function Vc(e,t,n){return function(...r){const s=this.__v_raw,o=ie(s),a=yn(o),i=e==="entries"||e===Symbol.iterator&&a,l=e==="keys"&&a,c=s[e](...r),u=n?Ss:t?Ls:He;return!t&&Ve(o,"iterate",l?ws:an),{next(){const{value:f,done:m}=c.next();return m?{value:f,done:m}:{value:i?[u(f[0]),u(f[1])]:u(f),done:m}},[Symbol.iterator](){return this}}}}function dr(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Hc(e,t){const n={get(s){const o=this.__v_raw,a=ie(o),i=ie(s);e||($t(s,i)&&Ve(a,"get",s),Ve(a,"get",i));const{has:l}=fr(a),c=t?Ss:e?Ls:He;if(l.call(a,s))return c(o.get(s));if(l.call(a,i))return c(o.get(i));o!==a&&o.get(s)},get size(){const s=this.__v_raw;return!e&&Ve(ie(s),"iterate",an),Reflect.get(s,"size",s)},has(s){const o=this.__v_raw,a=ie(o),i=ie(s);return e||($t(s,i)&&Ve(a,"has",s),Ve(a,"has",i)),s===i?o.has(s):o.has(s)||o.has(i)},forEach(s,o){const a=this,i=a.__v_raw,l=ie(i),c=t?Ss:e?Ls:He;return!e&&Ve(l,"iterate",an),i.forEach((u,f)=>s.call(o,c(u),c(f),a))}};return We(n,e?{add:dr("add"),set:dr("set"),delete:dr("delete"),clear:dr("clear")}:{add(s){!t&&!at(s)&&!cn(s)&&(s=ie(s));const o=ie(this);return fr(o).has.call(o,s)||(o.add(s),Ot(o,"add",s,s)),this},set(s,o){!t&&!at(o)&&!cn(o)&&(o=ie(o));const a=ie(this),{has:i,get:l}=fr(a);let c=i.call(a,s);c||(s=ie(s),c=i.call(a,s));const u=l.call(a,s);return a.set(s,o),c?$t(o,u)&&Ot(a,"set",s,o):Ot(a,"add",s,o),this},delete(s){const o=ie(this),{has:a,get:i}=fr(o);let l=a.call(o,s);l||(s=ie(s),l=a.call(o,s)),i&&i.call(o,s);const c=o.delete(s);return l&&Ot(o,"delete",s,void 0),c},clear(){const s=ie(this),o=s.size!==0,a=s.clear();return o&&Ot(s,"clear",void 0,void 0),a}}),["keys","values","entries",Symbol.iterator].forEach(s=>{n[s]=Vc(s,e,t)}),n}function so(e,t){const n=Hc(e,t);return(r,s,o)=>s==="__v_isReactive"?!e:s==="__v_isReadonly"?e:s==="__v_raw"?r:Reflect.get(he(n,s)&&s in r?n:r,s,o)}const Wc={get:so(!1,!1)},Bc={get:so(!1,!0)},Kc={get:so(!0,!1)};const Ri=new WeakMap,Ci=new WeakMap,Ni=new WeakMap,Gc=new WeakMap;function jc(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function qc(e){return e.__v_skip||!Object.isExtensible(e)?0:jc(bc(e))}function ar(e){return cn(e)?e:oo(e,!1,Fc,Wc,Ri)}function xi(e){return oo(e,!1,Uc,Bc,Ci)}function Mi(e){return oo(e,!0,$c,Kc,Ni)}function oo(e,t,n,r,s){if(!ve(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const o=s.get(e);if(o)return o;const a=qc(e);if(a===0)return e;const i=new Proxy(e,a===2?r:n);return s.set(e,i),i}function Ut(e){return cn(e)?Ut(e.__v_raw):!!(e&&e.__v_isReactive)}function cn(e){return!!(e&&e.__v_isReadonly)}function at(e){return!!(e&&e.__v_isShallow)}function ao(e){return e?!!e.__v_raw:!1}function ie(e){const t=e&&e.__v_raw;return t?ie(t):e}function io(e){return!he(e,"__v_skip")&&Object.isExtensible(e)&&hi(e,"__v_skip",!0),e}const He=e=>ve(e)?ar(e):e,Ls=e=>ve(e)?Mi(e):e;function Oe(e){return e?e.__v_isRef===!0:!1}function ge(e){return Di(e,!1)}function lo(e){return Di(e,!0)}function Di(e,t){return Oe(e)?e:new Yc(e,t)}class Yc{constructor(t,n){this.dep=new ro,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=n?t:ie(t),this._value=n?t:He(t),this.__v_isShallow=n}get value(){return this.dep.track(),this._value}set value(t){const n=this._rawValue,r=this.__v_isShallow||at(t)||cn(t);t=r?t:ie(t),$t(t,n)&&(this._rawValue=t,this._value=r?t:He(t),this.dep.trigger())}}function le(e){return Oe(e)?e.value:e}const Jc={get:(e,t,n)=>t==="__v_raw"?e:le(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const s=e[t];return Oe(s)&&!Oe(n)?(s.value=n,!0):Reflect.set(e,t,n,r)}};function Fi(e){return Ut(e)?e:new Proxy(e,Jc)}function Xc(e){const t=J(e)?new Array(e.length):{};for(const n in e)t[n]=Qc(e,n);return t}class zc{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0,this._value=void 0}get value(){const t=this._object[this._key];return this._value=t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return Rc(ie(this._object),this._key)}}function Qc(e,t,n){const r=e[t];return Oe(r)?r:new zc(e,t,n)}class Zc{constructor(t,n,r){this.fn=t,this.setter=n,this._value=void 0,this.dep=new ro(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=jn-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!n,this.isSSR=r}notify(){if(this.flags|=16,!(this.flags&8)&&be!==this)return ki(this,!0),!0}get value(){const t=this.dep.track();return Si(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function eu(e,t,n=!1){let r,s;return te(e)?r=e:(r=e.get,s=e.set),new Zc(r,s,n)}const hr={},wr=new WeakMap;let sn;function tu(e,t=!1,n=sn){if(n){let r=wr.get(n);r||wr.set(n,r=[]),r.push(e)}}function nu(e,t,n=_e){const{immediate:r,deep:s,once:o,scheduler:a,augmentJob:i,call:l}=n,c=v=>s?v:at(v)||s===!1||s===0?It(v,1):It(v);let u,f,m,_,S=!1,T=!1;if(Oe(e)?(f=()=>e.value,S=at(e)):Ut(e)?(f=()=>c(e),S=!0):J(e)?(T=!0,S=e.some(v=>Ut(v)||at(v)),f=()=>e.map(v=>{if(Oe(v))return v.value;if(Ut(v))return c(v);if(te(v))return l?l(v,2):v()})):te(e)?t?f=l?()=>l(e,2):e:f=()=>{if(m){Gt();try{m()}finally{jt()}}const v=sn;sn=u;try{return l?l(e,3,[_]):e(_)}finally{sn=v}}:f=vt,t&&s){const v=f,L=s===!0?1/0:s;f=()=>It(v(),L)}const I=yi(),P=()=>{u.stop(),I&&I.active&&Xs(I.effects,u)};if(o&&t){const v=t;t=(...L)=>{v(...L),P()}}let F=T?new Array(e.length).fill(hr):hr;const y=v=>{if(!(!(u.flags&1)||!u.dirty&&!v))if(t){const L=u.run();if(s||S||(T?L.some((R,D)=>$t(R,F[D])):$t(L,F))){m&&m();const R=sn;sn=u;try{const D=[L,F===hr?void 0:T&&F[0]===hr?[]:F,_];l?l(t,3,D):t(...D),F=L}finally{sn=R}}}else u.run()};return i&&i(y),u=new vi(f),u.scheduler=a?()=>a(y,!1):y,_=v=>tu(v,!1,u),m=u.onStop=()=>{const v=wr.get(u);if(v){if(l)l(v,4);else for(const L of v)L();wr.delete(u)}},t?r?y(!0):F=u.run():a?a(y.bind(null,!0),!0):u.run(),P.pause=u.pause.bind(u),P.resume=u.resume.bind(u),P.stop=P,P}function It(e,t=1/0,n){if(t<=0||!ve(e)||e.__v_skip||(n=n||new Set,n.has(e)))return e;if(n.add(e),t--,Oe(e))It(e.value,t,n);else if(J(e))for(let r=0;r{It(r,t,n)});else if(fi(e)){for(const r in e)It(e[r],t,n);for(const r of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,r)&&It(e[r],t,n)}return e}/** +* @vue/runtime-core v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function ir(e,t,n,r){try{return r?e(...r):e()}catch(s){Hr(s,t,n)}}function Et(e,t,n,r){if(te(e)){const s=ir(e,t,n,r);return s&&ci(s)&&s.catch(o=>{Hr(o,t,n)}),s}if(J(e)){const s=[];for(let o=0;o>>1,s=Ye[r],o=Yn(s);o=Yn(n)?Ye.push(e):Ye.splice(su(t),0,e),e.flags|=1,Ui()}}function Ui(){Sr||(Sr=$i.then(Hi))}function ou(e){J(e)?vn.push(...e):Mt&&e.id===-1?Mt.splice(mn+1,0,e):e.flags&1||(vn.push(e),e.flags|=1),Ui()}function Po(e,t,n=_t+1){for(;nYn(n)-Yn(r));if(vn.length=0,Mt){Mt.push(...t);return}for(Mt=t,mn=0;mne.id==null?e.flags&2?-1:1/0:e.id;function Hi(e){try{for(_t=0;_t{r._d&&Vo(-1);const o=Lr(t);let a;try{a=e(...s)}finally{Lr(o),r._d&&Vo(1)}return a};return r._n=!0,r._c=!0,r._d=!0,r}function ls(e,t){if(Me===null)return e;const n=Kr(Me),r=e.dirs||(e.dirs=[]);for(let s=0;se.__isTeleport;function fo(e,t){e.shapeFlag&6&&e.component?(e.transition=t,fo(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}/*! #__NO_SIDE_EFFECTS__ */function it(e,t){return te(e)?We({name:e.name},t,{setup:e}):e}function Bi(e){e.ids=[e.ids[0]+e.ids[2]+++"-",0,0]}function Or(e,t,n,r,s=!1){if(J(e)){e.forEach((S,T)=>Or(S,t&&(J(t)?t[T]:t),n,r,s));return}if(kn(r)&&!s){r.shapeFlag&512&&r.type.__asyncResolved&&r.component.subTree.component&&Or(e,t,n,r.component.subTree);return}const o=r.shapeFlag&4?Kr(r.component):r.el,a=s?null:o,{i,r:l}=e,c=t&&t.r,u=i.refs===_e?i.refs={}:i.refs,f=i.setupState,m=ie(f),_=f===_e?()=>!1:S=>he(m,S);if(c!=null&&c!==l&&(Re(c)?(u[c]=null,_(c)&&(f[c]=null)):Oe(c)&&(c.value=null)),te(l))ir(l,i,12,[a,u]);else{const S=Re(l),T=Oe(l);if(S||T){const I=()=>{if(e.f){const P=S?_(l)?f[l]:u[l]:l.value;s?J(P)&&Xs(P,o):J(P)?P.includes(o)||P.push(o):S?(u[l]=[o],_(l)&&(f[l]=u[l])):(l.value=[o],e.k&&(u[e.k]=l.value))}else S?(u[l]=a,_(l)&&(f[l]=a)):T&&(l.value=a,e.k&&(u[e.k]=a))};a?(I.id=-1,tt(I,n)):I()}}}Fr().requestIdleCallback;Fr().cancelIdleCallback;const kn=e=>!!e.type.__asyncLoader,Ki=e=>e.type.__isKeepAlive;function lu(e,t){Gi(e,"a",t)}function cu(e,t){Gi(e,"da",t)}function Gi(e,t,n=$e){const r=e.__wdc||(e.__wdc=()=>{let s=n;for(;s;){if(s.isDeactivated)return;s=s.parent}return e()});if(Wr(t,r,n),n){let s=n.parent;for(;s&&s.parent;)Ki(s.parent.vnode)&&uu(r,t,n,s),s=s.parent}}function uu(e,t,n,r){const s=Wr(t,e,r,!0);Rn(()=>{Xs(r[t],s)},n)}function Wr(e,t,n=$e,r=!1){if(n){const s=n[e]||(n[e]=[]),o=t.__weh||(t.__weh=(...a)=>{Gt();const i=cr(n),l=Et(t,n,e,a);return i(),jt(),l});return r?s.unshift(o):s.push(o),o}}const Pt=e=>(t,n=$e)=>{(!Zn||e==="sp")&&Wr(e,(...r)=>t(...r),n)},ji=Pt("bm"),qt=Pt("m"),fu=Pt("bu"),du=Pt("u"),hu=Pt("bum"),Rn=Pt("um"),mu=Pt("sp"),gu=Pt("rtg"),pu=Pt("rtc");function _u(e,t=$e){Wr("ec",e,t)}const bu=Symbol.for("v-ndc");function Jn(e,t,n,r){let s;const o=n,a=J(e);if(a||Re(e)){const i=a&&Ut(e);let l=!1;i&&(l=!at(e),e=Vr(e)),s=new Array(e.length);for(let c=0,u=e.length;ct(i,l,void 0,o));else{const i=Object.keys(e);s=new Array(i.length);for(let l=0,c=i.length;lzn(t)?!(t.type===Bt||t.type===Ce&&!qi(t.children)):!0)?e:null}const Os=e=>e?gl(e)?Kr(e):Os(e.parent):null,Hn=We(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Os(e.parent),$root:e=>Os(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Ji(e),$forceUpdate:e=>e.f||(e.f=()=>{uo(e.update)}),$nextTick:e=>e.n||(e.n=co.bind(e.proxy)),$watch:e=>Vu.bind(e)}),cs=(e,t)=>e!==_e&&!e.__isScriptSetup&&he(e,t),yu={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:n,setupState:r,data:s,props:o,accessCache:a,type:i,appContext:l}=e;let c;if(t[0]!=="$"){const _=a[t];if(_!==void 0)switch(_){case 1:return r[t];case 2:return s[t];case 4:return n[t];case 3:return o[t]}else{if(cs(r,t))return a[t]=1,r[t];if(s!==_e&&he(s,t))return a[t]=2,s[t];if((c=e.propsOptions[0])&&he(c,t))return a[t]=3,o[t];if(n!==_e&&he(n,t))return a[t]=4,n[t];Is&&(a[t]=0)}}const u=Hn[t];let f,m;if(u)return t==="$attrs"&&Ve(e.attrs,"get",""),u(e);if((f=i.__cssModules)&&(f=f[t]))return f;if(n!==_e&&he(n,t))return a[t]=4,n[t];if(m=l.config.globalProperties,he(m,t))return m[t]},set({_:e},t,n){const{data:r,setupState:s,ctx:o}=e;return cs(s,t)?(s[t]=n,!0):r!==_e&&he(r,t)?(r[t]=n,!0):he(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:s,propsOptions:o}},a){let i;return!!n[a]||e!==_e&&he(e,a)||cs(t,a)||(i=o[0])&&he(i,a)||he(r,a)||he(Hn,a)||he(s.config.globalProperties,a)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:he(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Co(e){return J(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let Is=!0;function vu(e){const t=Ji(e),n=e.proxy,r=e.ctx;Is=!1,t.beforeCreate&&No(t.beforeCreate,e,"bc");const{data:s,computed:o,methods:a,watch:i,provide:l,inject:c,created:u,beforeMount:f,mounted:m,beforeUpdate:_,updated:S,activated:T,deactivated:I,beforeDestroy:P,beforeUnmount:F,destroyed:y,unmounted:v,render:L,renderTracked:R,renderTriggered:D,errorCaptured:$,serverPrefetch:M,expose:ee,inheritAttrs:me,components:z,directives:ue,filters:Qe}=t;if(c&&Eu(c,r,null),a)for(const Q in a){const re=a[Q];te(re)&&(r[Q]=re.bind(n))}if(s){const Q=s.call(n,n);ve(Q)&&(e.data=ar(Q))}if(Is=!0,o)for(const Q in o){const re=o[Q],Be=te(re)?re.bind(n,n):te(re.get)?re.get.bind(n,n):vt,Xe=!te(re)&&te(re.set)?re.set.bind(n):vt,Se=we({get:Be,set:Xe});Object.defineProperty(r,Q,{enumerable:!0,configurable:!0,get:()=>Se.value,set:Le=>Se.value=Le})}if(i)for(const Q in i)Yi(i[Q],r,n,Q);if(l){const Q=te(l)?l.call(n):l;Reflect.ownKeys(Q).forEach(re=>{br(re,Q[re])})}u&&No(u,e,"c");function se(Q,re){J(re)?re.forEach(Be=>Q(Be.bind(n))):re&&Q(re.bind(n))}if(se(ji,f),se(qt,m),se(fu,_),se(du,S),se(lu,T),se(cu,I),se(_u,$),se(pu,R),se(gu,D),se(hu,F),se(Rn,v),se(mu,M),J(ee))if(ee.length){const Q=e.exposed||(e.exposed={});ee.forEach(re=>{Object.defineProperty(Q,re,{get:()=>n[re],set:Be=>n[re]=Be})})}else e.exposed||(e.exposed={});L&&e.render===vt&&(e.render=L),me!=null&&(e.inheritAttrs=me),z&&(e.components=z),ue&&(e.directives=ue),M&&Bi(e)}function Eu(e,t,n=vt){J(e)&&(e=As(e));for(const r in e){const s=e[r];let o;ve(s)?"default"in s?o=rt(s.from||r,s.default,!0):o=rt(s.from||r):o=rt(s),Oe(o)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>o.value,set:a=>o.value=a}):t[r]=o}}function No(e,t,n){Et(J(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function Yi(e,t,n,r){let s=r.includes(".")?cl(n,r):()=>n[r];if(Re(e)){const o=t[e];te(o)&&Vt(s,o)}else if(te(e))Vt(s,e.bind(n));else if(ve(e))if(J(e))e.forEach(o=>Yi(o,t,n,r));else{const o=te(e.handler)?e.handler.bind(n):t[e.handler];te(o)&&Vt(s,o,e)}}function Ji(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:s,optionsCache:o,config:{optionMergeStrategies:a}}=e.appContext,i=o.get(t);let l;return i?l=i:!s.length&&!n&&!r?l=t:(l={},s.length&&s.forEach(c=>Ir(l,c,a,!0)),Ir(l,t,a)),ve(t)&&o.set(t,l),l}function Ir(e,t,n,r=!1){const{mixins:s,extends:o}=t;o&&Ir(e,o,n,!0),s&&s.forEach(a=>Ir(e,a,n,!0));for(const a in t)if(!(r&&a==="expose")){const i=ku[a]||n&&n[a];e[a]=i?i(e[a],t[a]):t[a]}return e}const ku={data:xo,props:Mo,emits:Mo,methods:Fn,computed:Fn,beforeCreate:Ge,created:Ge,beforeMount:Ge,mounted:Ge,beforeUpdate:Ge,updated:Ge,beforeDestroy:Ge,beforeUnmount:Ge,destroyed:Ge,unmounted:Ge,activated:Ge,deactivated:Ge,errorCaptured:Ge,serverPrefetch:Ge,components:Fn,directives:Fn,watch:wu,provide:xo,inject:Tu};function xo(e,t){return t?e?function(){return We(te(e)?e.call(this,this):e,te(t)?t.call(this,this):t)}:t:e}function Tu(e,t){return Fn(As(e),As(t))}function As(e){if(J(e)){const t={};for(let n=0;n1)return n&&te(t)?t.call(r&&r.proxy):t}}function Ou(){return!!($e||Me||ln)}const zi={},Qi=()=>Object.create(zi),Zi=e=>Object.getPrototypeOf(e)===zi;function Iu(e,t,n,r=!1){const s={},o=Qi();e.propsDefaults=Object.create(null),el(e,t,s,o);for(const a in e.propsOptions[0])a in s||(s[a]=void 0);n?e.props=r?s:xi(s):e.type.props?e.props=s:e.props=o,e.attrs=o}function Au(e,t,n,r){const{props:s,attrs:o,vnode:{patchFlag:a}}=e,i=ie(s),[l]=e.propsOptions;let c=!1;if((r||a>0)&&!(a&16)){if(a&8){const u=e.vnode.dynamicProps;for(let f=0;f{l=!0;const[m,_]=tl(f,t,!0);We(a,m),_&&i.push(..._)};!n&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!o&&!l)return ve(e)&&r.set(e,bn),bn;if(J(o))for(let u=0;ue[0]==="_"||e==="$stable",ho=e=>J(e)?e.map(bt):[bt(e)],Ru=(e,t,n)=>{if(t._n)return t;const r=En((...s)=>ho(t(...s)),n);return r._c=!1,r},rl=(e,t,n)=>{const r=e._ctx;for(const s in e){if(nl(s))continue;const o=e[s];if(te(o))t[s]=Ru(s,o,r);else if(o!=null){const a=ho(o);t[s]=()=>a}}},sl=(e,t)=>{const n=ho(t);e.slots.default=()=>n},ol=(e,t,n)=>{for(const r in t)(n||r!=="_")&&(e[r]=t[r])},Cu=(e,t,n)=>{const r=e.slots=Qi();if(e.vnode.shapeFlag&32){const s=t._;s?(ol(r,t,n),n&&hi(r,"_",s,!0)):rl(t,r)}else t&&sl(e,t)},Nu=(e,t,n)=>{const{vnode:r,slots:s}=e;let o=!0,a=_e;if(r.shapeFlag&32){const i=t._;i?n&&i===1?o=!1:ol(s,t,n):(o=!t.$stable,rl(t,s)),a=t}else t&&(sl(e,t),a={default:1});if(o)for(const i in s)!nl(i)&&a[i]==null&&delete s[i]},tt=qu;function xu(e){return Mu(e)}function Mu(e,t){const n=Fr();n.__VUE__=!0;const{insert:r,remove:s,patchProp:o,createElement:a,createText:i,createComment:l,setText:c,setElementText:u,parentNode:f,nextSibling:m,setScopeId:_=vt,insertStaticContent:S}=e,T=(p,b,g,w=null,x=null,N=null,W=void 0,V=null,d=!!b.dynamicChildren)=>{if(p===b)return;p&&!xn(p,b)&&(w=C(p),Le(p,x,N,!0),p=null),b.patchFlag===-2&&(d=!1,b.dynamicChildren=null);const{type:h,ref:k,shapeFlag:O}=b;switch(h){case lr:I(p,b,g,w);break;case Bt:P(p,b,g,w);break;case fs:p==null&&F(b,g,w,W);break;case Ce:z(p,b,g,w,x,N,W,V,d);break;default:O&1?L(p,b,g,w,x,N,W,V,d):O&6?ue(p,b,g,w,x,N,W,V,d):(O&64||O&128)&&h.process(p,b,g,w,x,N,W,V,d,q)}k!=null&&x&&Or(k,p&&p.ref,N,b||p,!b)},I=(p,b,g,w)=>{if(p==null)r(b.el=i(b.children),g,w);else{const x=b.el=p.el;b.children!==p.children&&c(x,b.children)}},P=(p,b,g,w)=>{p==null?r(b.el=l(b.children||""),g,w):b.el=p.el},F=(p,b,g,w)=>{[p.el,p.anchor]=S(p.children,b,g,w,p.el,p.anchor)},y=({el:p,anchor:b},g,w)=>{let x;for(;p&&p!==b;)x=m(p),r(p,g,w),p=x;r(b,g,w)},v=({el:p,anchor:b})=>{let g;for(;p&&p!==b;)g=m(p),s(p),p=g;s(b)},L=(p,b,g,w,x,N,W,V,d)=>{b.type==="svg"?W="svg":b.type==="math"&&(W="mathml"),p==null?R(b,g,w,x,N,W,V,d):M(p,b,x,N,W,V,d)},R=(p,b,g,w,x,N,W,V)=>{let d,h;const{props:k,shapeFlag:O,transition:K,dirs:U}=p;if(d=p.el=a(p.type,N,k&&k.is,k),O&8?u(d,p.children):O&16&&$(p.children,d,null,w,x,us(p,N),W,V),U&&Zt(p,null,w,"created"),D(d,p,p.scopeId,W,w),k){for(const A in k)A!=="value"&&!$n(A)&&o(d,A,null,k[A],N,w);"value"in k&&o(d,"value",null,k.value,N),(h=k.onVnodeBeforeMount)&&pt(h,w,p)}U&&Zt(p,null,w,"beforeMount");const E=Du(x,K);E&&K.beforeEnter(d),r(d,b,g),((h=k&&k.onVnodeMounted)||E||U)&&tt(()=>{h&&pt(h,w,p),E&&K.enter(d),U&&Zt(p,null,w,"mounted")},x)},D=(p,b,g,w,x)=>{if(g&&_(p,g),w)for(let N=0;N{for(let h=d;h{const V=b.el=p.el;let{patchFlag:d,dynamicChildren:h,dirs:k}=b;d|=p.patchFlag&16;const O=p.props||_e,K=b.props||_e;let U;if(g&&en(g,!1),(U=K.onVnodeBeforeUpdate)&&pt(U,g,b,p),k&&Zt(b,p,g,"beforeUpdate"),g&&en(g,!0),(O.innerHTML&&K.innerHTML==null||O.textContent&&K.textContent==null)&&u(V,""),h?ee(p.dynamicChildren,h,V,g,w,us(b,x),N):W||re(p,b,V,null,g,w,us(b,x),N,!1),d>0){if(d&16)me(V,O,K,g,x);else if(d&2&&O.class!==K.class&&o(V,"class",null,K.class,x),d&4&&o(V,"style",O.style,K.style,x),d&8){const E=b.dynamicProps;for(let A=0;A{U&&pt(U,g,b,p),k&&Zt(b,p,g,"updated")},w)},ee=(p,b,g,w,x,N,W)=>{for(let V=0;V{if(b!==g){if(b!==_e)for(const N in b)!$n(N)&&!(N in g)&&o(p,N,b[N],null,x,w);for(const N in g){if($n(N))continue;const W=g[N],V=b[N];W!==V&&N!=="value"&&o(p,N,V,W,x,w)}"value"in g&&o(p,"value",b.value,g.value,x)}},z=(p,b,g,w,x,N,W,V,d)=>{const h=b.el=p?p.el:i(""),k=b.anchor=p?p.anchor:i("");let{patchFlag:O,dynamicChildren:K,slotScopeIds:U}=b;U&&(V=V?V.concat(U):U),p==null?(r(h,g,w),r(k,g,w),$(b.children||[],g,k,x,N,W,V,d)):O>0&&O&64&&K&&p.dynamicChildren?(ee(p.dynamicChildren,K,g,x,N,W,V),(b.key!=null||x&&b===x.subTree)&&al(p,b,!0)):re(p,b,g,k,x,N,W,V,d)},ue=(p,b,g,w,x,N,W,V,d)=>{b.slotScopeIds=V,p==null?b.shapeFlag&512?x.ctx.activate(b,g,w,W,d):Qe(b,g,w,x,N,W,d):Je(p,b,d)},Qe=(p,b,g,w,x,N,W)=>{const V=p.component=ef(p,w,x);if(Ki(p)&&(V.ctx.renderer=q),tf(V,!1,W),V.asyncDep){if(x&&x.registerDep(V,se,W),!p.el){const d=V.subTree=Pe(Bt);P(null,d,b,g)}}else se(V,p,b,g,x,N,W)},Je=(p,b,g)=>{const w=b.component=p.component;if(Gu(p,b,g))if(w.asyncDep&&!w.asyncResolved){Q(w,b,g);return}else w.next=b,w.update();else b.el=p.el,w.vnode=b},se=(p,b,g,w,x,N,W)=>{const V=()=>{if(p.isMounted){let{next:O,bu:K,u:U,parent:E,vnode:A}=p;{const Fe=il(p);if(Fe){O&&(O.el=A.el,Q(p,O,W)),Fe.asyncDep.then(()=>{p.isUnmounted||V()});return}}let Y=O,oe;en(p,!1),O?(O.el=A.el,Q(p,O,W)):O=A,K&&_r(K),(oe=O.props&&O.props.onVnodeBeforeUpdate)&&pt(oe,E,O,A),en(p,!0);const Te=$o(p),Ke=p.subTree;p.subTree=Te,T(Ke,Te,f(Ke.el),C(Ke),p,x,N),O.el=Te.el,Y===null&&ju(p,Te.el),U&&tt(U,x),(oe=O.props&&O.props.onVnodeUpdated)&&tt(()=>pt(oe,E,O,A),x)}else{let O;const{el:K,props:U}=b,{bm:E,m:A,parent:Y,root:oe,type:Te}=p,Ke=kn(b);en(p,!1),E&&_r(E),!Ke&&(O=U&&U.onVnodeBeforeMount)&&pt(O,Y,b),en(p,!0);{oe.ce&&oe.ce._injectChildStyle(Te);const Fe=p.subTree=$o(p);T(null,Fe,g,w,p,x,N),b.el=Fe.el}if(A&&tt(A,x),!Ke&&(O=U&&U.onVnodeMounted)){const Fe=b;tt(()=>pt(O,Y,Fe),x)}(b.shapeFlag&256||Y&&kn(Y.vnode)&&Y.vnode.shapeFlag&256)&&p.a&&tt(p.a,x),p.isMounted=!0,b=g=w=null}};p.scope.on();const d=p.effect=new vi(V);p.scope.off();const h=p.update=d.run.bind(d),k=p.job=d.runIfDirty.bind(d);k.i=p,k.id=p.uid,d.scheduler=()=>uo(k),en(p,!0),h()},Q=(p,b,g)=>{b.component=p;const w=p.vnode.props;p.vnode=b,p.next=null,Au(p,b.props,w,g),Nu(p,b.children,g),Gt(),Po(p),jt()},re=(p,b,g,w,x,N,W,V,d=!1)=>{const h=p&&p.children,k=p?p.shapeFlag:0,O=b.children,{patchFlag:K,shapeFlag:U}=b;if(K>0){if(K&128){Xe(h,O,g,w,x,N,W,V,d);return}else if(K&256){Be(h,O,g,w,x,N,W,V,d);return}}U&8?(k&16&&xe(h,x,N),O!==h&&u(g,O)):k&16?U&16?Xe(h,O,g,w,x,N,W,V,d):xe(h,x,N,!0):(k&8&&u(g,""),U&16&&$(O,g,w,x,N,W,V,d))},Be=(p,b,g,w,x,N,W,V,d)=>{p=p||bn,b=b||bn;const h=p.length,k=b.length,O=Math.min(h,k);let K;for(K=0;Kk?xe(p,x,N,!0,!1,O):$(b,g,w,x,N,W,V,d,O)},Xe=(p,b,g,w,x,N,W,V,d)=>{let h=0;const k=b.length;let O=p.length-1,K=k-1;for(;h<=O&&h<=K;){const U=p[h],E=b[h]=d?Dt(b[h]):bt(b[h]);if(xn(U,E))T(U,E,g,null,x,N,W,V,d);else break;h++}for(;h<=O&&h<=K;){const U=p[O],E=b[K]=d?Dt(b[K]):bt(b[K]);if(xn(U,E))T(U,E,g,null,x,N,W,V,d);else break;O--,K--}if(h>O){if(h<=K){const U=K+1,E=UK)for(;h<=O;)Le(p[h],x,N,!0),h++;else{const U=h,E=h,A=new Map;for(h=E;h<=K;h++){const et=b[h]=d?Dt(b[h]):bt(b[h]);et.key!=null&&A.set(et.key,h)}let Y,oe=0;const Te=K-E+1;let Ke=!1,Fe=0;const zt=new Array(Te);for(h=0;h=Te){Le(et,x,N,!0);continue}let gt;if(et.key!=null)gt=A.get(et.key);else for(Y=E;Y<=K;Y++)if(zt[Y-E]===0&&xn(et,b[Y])){gt=Y;break}gt===void 0?Le(et,x,N,!0):(zt[gt-E]=h+1,gt>=Fe?Fe=gt:Ke=!0,T(et,b[gt],g,null,x,N,W,V,d),oe++)}const ns=Ke?Fu(zt):bn;for(Y=ns.length-1,h=Te-1;h>=0;h--){const et=E+h,gt=b[et],So=et+1{const{el:N,type:W,transition:V,children:d,shapeFlag:h}=p;if(h&6){Se(p.component.subTree,b,g,w);return}if(h&128){p.suspense.move(b,g,w);return}if(h&64){W.move(p,b,g,q);return}if(W===Ce){r(N,b,g);for(let O=0;OV.enter(N),x);else{const{leave:O,delayLeave:K,afterLeave:U}=V,E=()=>r(N,b,g),A=()=>{O(N,()=>{E(),U&&U()})};K?K(N,E,A):A()}else r(N,b,g)},Le=(p,b,g,w=!1,x=!1)=>{const{type:N,props:W,ref:V,children:d,dynamicChildren:h,shapeFlag:k,patchFlag:O,dirs:K,cacheIndex:U}=p;if(O===-2&&(x=!1),V!=null&&Or(V,null,g,p,!0),U!=null&&(b.renderCache[U]=void 0),k&256){b.ctx.deactivate(p);return}const E=k&1&&K,A=!kn(p);let Y;if(A&&(Y=W&&W.onVnodeBeforeUnmount)&&pt(Y,b,p),k&6)mt(p.component,g,w);else{if(k&128){p.suspense.unmount(g,w);return}E&&Zt(p,null,b,"beforeUnmount"),k&64?p.type.remove(p,b,g,q,w):h&&!h.hasOnce&&(N!==Ce||O>0&&O&64)?xe(h,b,g,!1,!0):(N===Ce&&O&384||!x&&k&16)&&xe(d,b,g),w&&st(p)}(A&&(Y=W&&W.onVnodeUnmounted)||E)&&tt(()=>{Y&&pt(Y,b,p),E&&Zt(p,null,b,"unmounted")},g)},st=p=>{const{type:b,el:g,anchor:w,transition:x}=p;if(b===Ce){Ze(g,w);return}if(b===fs){v(p);return}const N=()=>{s(g),x&&!x.persisted&&x.afterLeave&&x.afterLeave()};if(p.shapeFlag&1&&x&&!x.persisted){const{leave:W,delayLeave:V}=x,d=()=>W(g,N);V?V(p.el,N,d):d()}else N()},Ze=(p,b)=>{let g;for(;p!==b;)g=m(p),s(p),p=g;s(b)},mt=(p,b,g)=>{const{bum:w,scope:x,job:N,subTree:W,um:V,m:d,a:h}=p;Fo(d),Fo(h),w&&_r(w),x.stop(),N&&(N.flags|=8,Le(W,p,b,g)),V&&tt(V,b),tt(()=>{p.isUnmounted=!0},b),b&&b.pendingBranch&&!b.isUnmounted&&p.asyncDep&&!p.asyncResolved&&p.suspenseId===b.pendingId&&(b.deps--,b.deps===0&&b.resolve())},xe=(p,b,g,w=!1,x=!1,N=0)=>{for(let W=N;W{if(p.shapeFlag&6)return C(p.component.subTree);if(p.shapeFlag&128)return p.suspense.next();const b=m(p.anchor||p.el),g=b&&b[au];return g?m(g):b};let j=!1;const B=(p,b,g)=>{p==null?b._vnode&&Le(b._vnode,null,null,!0):T(b._vnode||null,p,b,null,null,null,g),b._vnode=p,j||(j=!0,Po(),Vi(),j=!1)},q={p:T,um:Le,m:Se,r:st,mt:Qe,mc:$,pc:re,pbc:ee,n:C,o:e};return{render:B,hydrate:void 0,createApp:Lu(B)}}function us({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function en({effect:e,job:t},n){n?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Du(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function al(e,t,n=!1){const r=e.children,s=t.children;if(J(r)&&J(s))for(let o=0;o>1,e[n[i]]0&&(t[r]=n[o-1]),n[o]=r)}}for(o=n.length,a=n[o-1];o-- >0;)n[o]=a,a=t[a];return n}function il(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:il(t)}function Fo(e){if(e)for(let t=0;trt($u);function Vt(e,t,n){return ll(e,t,n)}function ll(e,t,n=_e){const{immediate:r,deep:s,flush:o,once:a}=n,i=We({},n),l=t&&r||!t&&o!=="post";let c;if(Zn){if(o==="sync"){const _=Uu();c=_.__watcherHandles||(_.__watcherHandles=[])}else if(!l){const _=()=>{};return _.stop=vt,_.resume=vt,_.pause=vt,_}}const u=$e;i.call=(_,S,T)=>Et(_,u,S,T);let f=!1;o==="post"?i.scheduler=_=>{tt(_,u&&u.suspense)}:o!=="sync"&&(f=!0,i.scheduler=(_,S)=>{S?_():uo(_)}),i.augmentJob=_=>{t&&(_.flags|=4),f&&(_.flags|=2,u&&(_.id=u.uid,_.i=u))};const m=nu(e,t,i);return Zn&&(c?c.push(m):l&&m()),m}function Vu(e,t,n){const r=this.proxy,s=Re(e)?e.includes(".")?cl(r,e):()=>r[e]:e.bind(r,r);let o;te(t)?o=t:(o=t.handler,n=t);const a=cr(this),i=ll(s,o.bind(r),n);return a(),i}function cl(e,t){const n=t.split(".");return()=>{let r=e;for(let s=0;st==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${Wt(t)}Modifiers`]||e[`${un(t)}Modifiers`];function Wu(e,t,...n){if(e.isUnmounted)return;const r=e.vnode.props||_e;let s=n;const o=t.startsWith("update:"),a=o&&Hu(r,t.slice(7));a&&(a.trim&&(s=n.map(u=>Re(u)?u.trim():u)),a.number&&(s=n.map(ks)));let i,l=r[i=rs(t)]||r[i=rs(Wt(t))];!l&&o&&(l=r[i=rs(un(t))]),l&&Et(l,e,6,s);const c=r[i+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[i])return;e.emitted[i]=!0,Et(c,e,6,s)}}function ul(e,t,n=!1){const r=t.emitsCache,s=r.get(e);if(s!==void 0)return s;const o=e.emits;let a={},i=!1;if(!te(e)){const l=c=>{const u=ul(c,t,!0);u&&(i=!0,We(a,u))};!n&&t.mixins.length&&t.mixins.forEach(l),e.extends&&l(e.extends),e.mixins&&e.mixins.forEach(l)}return!o&&!i?(ve(e)&&r.set(e,null),null):(J(o)?o.forEach(l=>a[l]=null):We(a,o),ve(e)&&r.set(e,a),a)}function Br(e,t){return!e||!xr(t)?!1:(t=t.slice(2).replace(/Once$/,""),he(e,t[0].toLowerCase()+t.slice(1))||he(e,un(t))||he(e,t))}function $o(e){const{type:t,vnode:n,proxy:r,withProxy:s,propsOptions:[o],slots:a,attrs:i,emit:l,render:c,renderCache:u,props:f,data:m,setupState:_,ctx:S,inheritAttrs:T}=e,I=Lr(e);let P,F;try{if(n.shapeFlag&4){const v=s||r,L=v;P=bt(c.call(L,v,u,f,_,m,S)),F=i}else{const v=t;P=bt(v.length>1?v(f,{attrs:i,slots:a,emit:l}):v(f,null)),F=t.props?i:Bu(i)}}catch(v){Wn.length=0,Hr(v,e,1),P=Pe(Bt)}let y=P;if(F&&T!==!1){const v=Object.keys(F),{shapeFlag:L}=y;v.length&&L&7&&(o&&v.some(Js)&&(F=Ku(F,o)),y=Sn(y,F,!1,!0))}return n.dirs&&(y=Sn(y,null,!1,!0),y.dirs=y.dirs?y.dirs.concat(n.dirs):n.dirs),n.transition&&fo(y,n.transition),P=y,Lr(I),P}const Bu=e=>{let t;for(const n in e)(n==="class"||n==="style"||xr(n))&&((t||(t={}))[n]=e[n]);return t},Ku=(e,t)=>{const n={};for(const r in e)(!Js(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function Gu(e,t,n){const{props:r,children:s,component:o}=e,{props:a,children:i,patchFlag:l}=t,c=o.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&l>=0){if(l&1024)return!0;if(l&16)return r?Uo(r,a,c):!!a;if(l&8){const u=t.dynamicProps;for(let f=0;fe.__isSuspense;function qu(e,t){t&&t.pendingBranch?J(e)?t.effects.push(...e):t.effects.push(e):ou(e)}const Ce=Symbol.for("v-fgt"),lr=Symbol.for("v-txt"),Bt=Symbol.for("v-cmt"),fs=Symbol.for("v-stc"),Wn=[];let nt=null;function fe(e=!1){Wn.push(nt=e?null:[])}function Yu(){Wn.pop(),nt=Wn[Wn.length-1]||null}let Xn=1;function Vo(e,t=!1){Xn+=e,e<0&&nt&&t&&(nt.hasOnce=!0)}function dl(e){return e.dynamicChildren=Xn>0?nt||bn:null,Yu(),Xn>0&&nt&&nt.push(e),e}function Ee(e,t,n,r,s,o){return dl(H(e,t,n,r,s,o,!0))}function wn(e,t,n,r,s){return dl(Pe(e,t,n,r,s,!0))}function zn(e){return e?e.__v_isVNode===!0:!1}function xn(e,t){return e.type===t.type&&e.key===t.key}const hl=({key:e})=>e??null,yr=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?Re(e)||Oe(e)||te(e)?{i:Me,r:e,k:t,f:!!n}:e:null);function H(e,t=null,n=null,r=0,s=null,o=e===Ce?0:1,a=!1,i=!1){const l={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&hl(t),ref:t&&yr(t),scopeId:Wi,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:r,dynamicProps:s,dynamicChildren:null,appContext:null,ctx:Me};return i?(mo(l,n),o&128&&e.normalize(l)):n&&(l.shapeFlag|=Re(n)?8:16),Xn>0&&!a&&nt&&(l.patchFlag>0||o&6)&&l.patchFlag!==32&&nt.push(l),l}const Pe=Ju;function Ju(e,t=null,n=null,r=0,s=null,o=!1){if((!e||e===bu)&&(e=Bt),zn(e)){const i=Sn(e,t,!0);return n&&mo(i,n),Xn>0&&!o&&nt&&(i.shapeFlag&6?nt[nt.indexOf(e)]=i:nt.push(i)),i.patchFlag=-2,i}if(of(e)&&(e=e.__vccOpts),t){t=Xu(t);let{class:i,style:l}=t;i&&!Re(i)&&(t.class=$r(i)),ve(l)&&(ao(l)&&!J(l)&&(l=We({},l)),t.style=Qs(l))}const a=Re(e)?1:fl(e)?128:iu(e)?64:ve(e)?4:te(e)?2:0;return H(e,t,n,r,s,a,o,!0)}function Xu(e){return e?ao(e)||Zi(e)?We({},e):e:null}function Sn(e,t,n=!1,r=!1){const{props:s,ref:o,patchFlag:a,children:i,transition:l}=e,c=t?zu(s||{},t):s,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&hl(c),ref:t&&t.ref?n&&o?J(o)?o.concat(yr(t)):[o,yr(t)]:yr(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Ce?a===-1?16:a|16:a,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:l,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Sn(e.ssContent),ssFallback:e.ssFallback&&Sn(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return l&&r&&fo(u,l.clone(u)),u}function ml(e=" ",t=0){return Pe(lr,null,e,t)}function Ht(e="",t=!1){return t?(fe(),wn(Bt,null,e)):Pe(Bt,null,e)}function bt(e){return e==null||typeof e=="boolean"?Pe(Bt):J(e)?Pe(Ce,null,e.slice()):zn(e)?Dt(e):Pe(lr,null,String(e))}function Dt(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Sn(e)}function mo(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(J(t))n=16;else if(typeof t=="object")if(r&65){const s=t.default;s&&(s._c&&(s._d=!1),mo(e,s()),s._c&&(s._d=!0));return}else{n=32;const s=t._;!s&&!Zi(t)?t._ctx=Me:s===3&&Me&&(Me.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else te(t)?(t={default:t,_ctx:Me},n=32):(t=String(t),r&64?(n=16,t=[ml(t)]):n=8);e.children=t,e.shapeFlag|=n}function zu(...e){const t={};for(let n=0;n$e||Me;let Ar,Rs;{const e=Fr(),t=(n,r)=>{let s;return(s=e[n])||(s=e[n]=[]),s.push(r),o=>{s.length>1?s.forEach(a=>a(o)):s[0](o)}};Ar=t("__VUE_INSTANCE_SETTERS__",n=>$e=n),Rs=t("__VUE_SSR_SETTERS__",n=>Zn=n)}const cr=e=>{const t=$e;return Ar(e),e.scope.on(),()=>{e.scope.off(),Ar(t)}},Ho=()=>{$e&&$e.scope.off(),Ar(null)};function gl(e){return e.vnode.shapeFlag&4}let Zn=!1;function tf(e,t=!1,n=!1){t&&Rs(t);const{props:r,children:s}=e.vnode,o=gl(e);Iu(e,r,o,t),Cu(e,s,n);const a=o?nf(e,t):void 0;return t&&Rs(!1),a}function nf(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,yu);const{setup:r}=n;if(r){Gt();const s=e.setupContext=r.length>1?sf(e):null,o=cr(e),a=ir(r,e,0,[e.props,s]),i=ci(a);if(jt(),o(),(i||e.sp)&&!kn(e)&&Bi(e),i){if(a.then(Ho,Ho),t)return a.then(l=>{Wo(e,l)}).catch(l=>{Hr(l,e,0)});e.asyncDep=a}else Wo(e,a)}else pl(e)}function Wo(e,t,n){te(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ve(t)&&(e.setupState=Fi(t)),pl(e)}function pl(e,t,n){const r=e.type;e.render||(e.render=r.render||vt);{const s=cr(e);Gt();try{vu(e)}finally{jt(),s()}}}const rf={get(e,t){return Ve(e,"get",""),e[t]}};function sf(e){const t=n=>{e.exposed=n||{}};return{attrs:new Proxy(e.attrs,rf),slots:e.slots,emit:e.emit,expose:t}}function Kr(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(Fi(io(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Hn)return Hn[n](e)},has(t,n){return n in t||n in Hn}})):e.proxy}function of(e){return te(e)&&"__vccOpts"in e}const we=(e,t)=>eu(e,t,Zn);function Gr(e,t,n){const r=arguments.length;return r===2?ve(t)&&!J(t)?zn(t)?Pe(e,null,[t]):Pe(e,t):Pe(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&zn(n)&&(n=[n]),Pe(e,t,n))}const af="3.5.13";/** +* @vue/runtime-dom v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Cs;const Bo=typeof window<"u"&&window.trustedTypes;if(Bo)try{Cs=Bo.createPolicy("vue",{createHTML:e=>e})}catch{}const _l=Cs?e=>Cs.createHTML(e):e=>e,lf="http://www.w3.org/2000/svg",cf="http://www.w3.org/1998/Math/MathML",Lt=typeof document<"u"?document:null,Ko=Lt&&Lt.createElement("template"),uf={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const s=t==="svg"?Lt.createElementNS(lf,e):t==="mathml"?Lt.createElementNS(cf,e):n?Lt.createElement(e,{is:n}):Lt.createElement(e);return e==="select"&&r&&r.multiple!=null&&s.setAttribute("multiple",r.multiple),s},createText:e=>Lt.createTextNode(e),createComment:e=>Lt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Lt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,s,o){const a=n?n.previousSibling:t.lastChild;if(s&&(s===o||s.nextSibling))for(;t.insertBefore(s.cloneNode(!0),n),!(s===o||!(s=s.nextSibling)););else{Ko.innerHTML=_l(r==="svg"?`${e}`:r==="mathml"?`${e}`:e);const i=Ko.content;if(r==="svg"||r==="mathml"){const l=i.firstChild;for(;l.firstChild;)i.appendChild(l.firstChild);i.removeChild(l)}t.insertBefore(i,n)}return[a?a.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},ff=Symbol("_vtc");function df(e,t,n){const r=e[ff];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Go=Symbol("_vod"),hf=Symbol("_vsh"),mf=Symbol(""),gf=/(^|;)\s*display\s*:/;function pf(e,t,n){const r=e.style,s=Re(n);let o=!1;if(n&&!s){if(t)if(Re(t))for(const a of t.split(";")){const i=a.slice(0,a.indexOf(":")).trim();n[i]==null&&vr(r,i,"")}else for(const a in t)n[a]==null&&vr(r,a,"");for(const a in n)a==="display"&&(o=!0),vr(r,a,n[a])}else if(s){if(t!==n){const a=r[mf];a&&(n+=";"+a),r.cssText=n,o=gf.test(n)}}else t&&e.removeAttribute("style");Go in e&&(e[Go]=o?r.display:"",e[hf]&&(r.display="none"))}const jo=/\s*!important$/;function vr(e,t,n){if(J(n))n.forEach(r=>vr(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=_f(e,t);jo.test(n)?e.setProperty(un(r),n.replace(jo,""),"important"):e[r]=n}}const qo=["Webkit","Moz","ms"],ds={};function _f(e,t){const n=ds[t];if(n)return n;let r=Wt(t);if(r!=="filter"&&r in e)return ds[t]=r;r=di(r);for(let s=0;shs||(Ef.then(()=>hs=0),hs=Date.now());function Tf(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;Et(wf(r,n.value),t,5,[r])};return n.value=e,n.attached=kf(),n}function wf(e,t){if(J(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>s=>!s._stopped&&r&&r(s))}else return t}const Zo=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Sf=(e,t,n,r,s,o)=>{const a=s==="svg";t==="class"?df(e,r,a):t==="style"?pf(e,n,r):xr(t)?Js(t)||yf(e,t,n,r,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Lf(e,t,r,a))?(Xo(e,t,r),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&Jo(e,t,r,a,o,t!=="value")):e._isVueCE&&(/[A-Z]/.test(t)||!Re(r))?Xo(e,Wt(t),r,o,t):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),Jo(e,t,r,a))};function Lf(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&Zo(t)&&te(n));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const s=e.tagName;if(s==="IMG"||s==="VIDEO"||s==="CANVAS"||s==="SOURCE")return!1}return Zo(t)&&Re(n)?!1:t in e}const Pr=e=>{const t=e.props["onUpdate:modelValue"]||!1;return J(t)?n=>_r(t,n):t};function Of(e){e.target.composing=!0}function ea(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const Tn=Symbol("_assign"),ta={created(e,{modifiers:{lazy:t,trim:n,number:r}},s){e[Tn]=Pr(s);const o=r||s.props&&s.props.type==="number";on(e,t?"change":"input",a=>{if(a.target.composing)return;let i=e.value;n&&(i=i.trim()),o&&(i=ks(i)),e[Tn](i)}),n&&on(e,"change",()=>{e.value=e.value.trim()}),t||(on(e,"compositionstart",Of),on(e,"compositionend",ea),on(e,"change",ea))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:r,trim:s,number:o}},a){if(e[Tn]=Pr(a),e.composing)return;const i=(o||e.type==="number")&&!/^0\d/.test(e.value)?ks(e.value):e.value,l=t??"";i!==l&&(document.activeElement===e&&e.type!=="range"&&(r&&t===n||s&&e.value.trim()===l)||(e.value=l))}},If={deep:!0,created(e,t,n){e[Tn]=Pr(n),on(e,"change",()=>{const r=e._modelValue,s=Af(e),o=e.checked,a=e[Tn];if(J(r)){const i=gi(r,s),l=i!==-1;if(o&&!l)a(r.concat(s));else if(!o&&l){const c=[...r];c.splice(i,1),a(c)}}else if(Mr(r)){const i=new Set(r);o?i.add(s):i.delete(s),a(i)}else a(bl(e,o))})},mounted:na,beforeUpdate(e,t,n){e[Tn]=Pr(n),na(e,t,n)}};function na(e,{value:t,oldValue:n},r){e._modelValue=t;let s;if(J(t))s=gi(t,r.props.value)>-1;else if(Mr(t))s=t.has(r.props.value);else{if(t===n)return;s=Ur(t,bl(e,!0))}e.checked!==s&&(e.checked=s)}function Af(e){return"_value"in e?e._value:e.value}function bl(e,t){const n=t?"_trueValue":"_falseValue";return n in e?e[n]:t}const Pf=["ctrl","shift","alt","meta"],Rf={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Pf.some(n=>e[`${n}Key`]&&!t.includes(n))},jr=(e,t)=>{const n=e._withMods||(e._withMods={}),r=t.join(".");return n[r]||(n[r]=(s,...o)=>{for(let a=0;a{const t=Nf().createApp(...e),{mount:n}=t;return t.mount=r=>{const s=Df(r);if(!s)return;const o=t._component;!te(o)&&!o.render&&!o.template&&(o.template=s.innerHTML),s.nodeType===1&&(s.textContent="");const a=n(s,!1,Mf(s));return s instanceof Element&&(s.removeAttribute("v-cloak"),s.setAttribute("data-v-app","")),a},t};function Mf(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function Df(e){return Re(e)?document.querySelector(e):e}/*! + * pinia v3.0.1 + * (c) 2025 Eduardo San Martin Morote + * @license MIT + */let yl;const qr=e=>yl=e,vl=Symbol();function Ns(e){return e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"&&typeof e.toJSON!="function"}var Bn;(function(e){e.direct="direct",e.patchObject="patch object",e.patchFunction="patch function"})(Bn||(Bn={}));function Ff(){const e=Zs(!0),t=e.run(()=>ge({}));let n=[],r=[];const s=io({install(o){qr(s),s._a=o,o.provide(vl,s),o.config.globalProperties.$pinia=s,r.forEach(a=>n.push(a)),r=[]},use(o){return this._a?n.push(o):r.push(o),this},_p:n,_a:null,_e:e,_s:new Map,state:t});return s}const El=()=>{};function sa(e,t,n,r=El){e.push(t);const s=()=>{const o=e.indexOf(t);o>-1&&(e.splice(o,1),r())};return!n&&yi()&&Ic(s),s}function hn(e,...t){e.slice().forEach(n=>{n(...t)})}const $f=e=>e(),oa=Symbol(),ms=Symbol();function xs(e,t){e instanceof Map&&t instanceof Map?t.forEach((n,r)=>e.set(r,n)):e instanceof Set&&t instanceof Set&&t.forEach(e.add,e);for(const n in t){if(!t.hasOwnProperty(n))continue;const r=t[n],s=e[n];Ns(s)&&Ns(r)&&e.hasOwnProperty(n)&&!Oe(r)&&!Ut(r)?e[n]=xs(s,r):e[n]=r}return e}const Uf=Symbol();function Vf(e){return!Ns(e)||!e.hasOwnProperty(Uf)}const{assign:xt}=Object;function Hf(e){return!!(Oe(e)&&e.effect)}function Wf(e,t,n,r){const{state:s,actions:o,getters:a}=t,i=n.state.value[e];let l;function c(){i||(n.state.value[e]=s?s():{});const u=Xc(n.state.value[e]);return xt(u,o,Object.keys(a||{}).reduce((f,m)=>(f[m]=io(we(()=>{qr(n);const _=n._s.get(e);return a[m].call(_,_)})),f),{}))}return l=kl(e,c,t,n,r,!0),l}function kl(e,t,n={},r,s,o){let a;const i=xt({actions:{}},n),l={deep:!0};let c,u,f=[],m=[],_;const S=r.state.value[e];!o&&!S&&(r.state.value[e]={}),ge({});let T;function I($){let M;c=u=!1,typeof $=="function"?($(r.state.value[e]),M={type:Bn.patchFunction,storeId:e,events:_}):(xs(r.state.value[e],$),M={type:Bn.patchObject,payload:$,storeId:e,events:_});const ee=T=Symbol();co().then(()=>{T===ee&&(c=!0)}),u=!0,hn(f,M,r.state.value[e])}const P=o?function(){const{state:M}=n,ee=M?M():{};this.$patch(me=>{xt(me,ee)})}:El;function F(){a.stop(),f=[],m=[],r._s.delete(e)}const y=($,M="")=>{if(oa in $)return $[ms]=M,$;const ee=function(){qr(r);const me=Array.from(arguments),z=[],ue=[];function Qe(Q){z.push(Q)}function Je(Q){ue.push(Q)}hn(m,{args:me,name:ee[ms],store:L,after:Qe,onError:Je});let se;try{se=$.apply(this&&this.$id===e?this:L,me)}catch(Q){throw hn(ue,Q),Q}return se instanceof Promise?se.then(Q=>(hn(z,Q),Q)).catch(Q=>(hn(ue,Q),Promise.reject(Q))):(hn(z,se),se)};return ee[oa]=!0,ee[ms]=M,ee},v={_p:r,$id:e,$onAction:sa.bind(null,m),$patch:I,$reset:P,$subscribe($,M={}){const ee=sa(f,$,M.detached,()=>me()),me=a.run(()=>Vt(()=>r.state.value[e],z=>{(M.flush==="sync"?u:c)&&$({storeId:e,type:Bn.direct,events:_},z)},xt({},l,M)));return ee},$dispose:F},L=ar(v);r._s.set(e,L);const D=(r._a&&r._a.runWithContext||$f)(()=>r._e.run(()=>(a=Zs()).run(()=>t({action:y}))));for(const $ in D){const M=D[$];if(Oe(M)&&!Hf(M)||Ut(M))o||(S&&Vf(M)&&(Oe(M)?M.value=S[$]:xs(M,S[$])),r.state.value[e][$]=M);else if(typeof M=="function"){const ee=y(M,$);D[$]=ee,i.actions[$]=M}}return xt(L,D),xt(ie(L),D),Object.defineProperty(L,"$state",{get:()=>r.state.value[e],set:$=>{I(M=>{xt(M,$)})}}),r._p.forEach($=>{xt(L,a.run(()=>$({store:L,app:r._a,pinia:r,options:i})))}),S&&o&&n.hydrate&&n.hydrate(L.$state,S),c=!0,u=!0,L}/*! #__NO_SIDE_EFFECTS__ */function Bf(e,t,n){let r;const s=typeof t=="function";r=s?n:t;function o(a,i){const l=Ou();return a=a||(l?rt(vl,null):null),a&&qr(a),a=yl,a._s.has(e)||(s?kl(e,t,r,a):Wf(e,r,a)),a._s.get(e)}return o.$id=e,o}/*! + * vue-router v4.5.0 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */const gn=typeof document<"u";function Tl(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function Kf(e){return e.__esModule||e[Symbol.toStringTag]==="Module"||e.default&&Tl(e.default)}const de=Object.assign;function gs(e,t){const n={};for(const r in t){const s=t[r];n[r]=ht(s)?s.map(e):e(s)}return n}const Kn=()=>{},ht=Array.isArray,wl=/#/g,Gf=/&/g,jf=/\//g,qf=/=/g,Yf=/\?/g,Sl=/\+/g,Jf=/%5B/g,Xf=/%5D/g,Ll=/%5E/g,zf=/%60/g,Ol=/%7B/g,Qf=/%7C/g,Il=/%7D/g,Zf=/%20/g;function go(e){return encodeURI(""+e).replace(Qf,"|").replace(Jf,"[").replace(Xf,"]")}function ed(e){return go(e).replace(Ol,"{").replace(Il,"}").replace(Ll,"^")}function Ms(e){return go(e).replace(Sl,"%2B").replace(Zf,"+").replace(wl,"%23").replace(Gf,"%26").replace(zf,"`").replace(Ol,"{").replace(Il,"}").replace(Ll,"^")}function td(e){return Ms(e).replace(qf,"%3D")}function nd(e){return go(e).replace(wl,"%23").replace(Yf,"%3F")}function rd(e){return e==null?"":nd(e).replace(jf,"%2F")}function er(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const sd=/\/$/,od=e=>e.replace(sd,"");function ps(e,t,n="/"){let r,s={},o="",a="";const i=t.indexOf("#");let l=t.indexOf("?");return i=0&&(l=-1),l>-1&&(r=t.slice(0,l),o=t.slice(l+1,i>-1?i:t.length),s=e(o)),i>-1&&(r=r||t.slice(0,i),a=t.slice(i,t.length)),r=cd(r??t,n),{fullPath:r+(o&&"?")+o+a,path:r,query:s,hash:er(a)}}function ad(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function aa(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function id(e,t,n){const r=t.matched.length-1,s=n.matched.length-1;return r>-1&&r===s&&Ln(t.matched[r],n.matched[s])&&Al(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Ln(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Al(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!ld(e[n],t[n]))return!1;return!0}function ld(e,t){return ht(e)?ia(e,t):ht(t)?ia(t,e):e===t}function ia(e,t){return ht(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function cd(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/"),s=r[r.length-1];(s===".."||s===".")&&r.push("");let o=n.length-1,a,i;for(a=0;a1&&o--;else break;return n.slice(0,o).join("/")+"/"+r.slice(a).join("/")}const Rt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};var tr;(function(e){e.pop="pop",e.push="push"})(tr||(tr={}));var Gn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Gn||(Gn={}));function ud(e){if(!e)if(gn){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),od(e)}const fd=/^[^#]+#/;function dd(e,t){return e.replace(fd,"#")+t}function hd(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const Yr=()=>({left:window.scrollX,top:window.scrollY});function md(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),s=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!s)return;t=hd(s,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function la(e,t){return(history.state?history.state.position-t:-1)+e}const Ds=new Map;function gd(e,t){Ds.set(e,t)}function pd(e){const t=Ds.get(e);return Ds.delete(e),t}let _d=()=>location.protocol+"//"+location.host;function Pl(e,t){const{pathname:n,search:r,hash:s}=t,o=e.indexOf("#");if(o>-1){let i=s.includes(e.slice(o))?e.slice(o).length:1,l=s.slice(i);return l[0]!=="/"&&(l="/"+l),aa(l,"")}return aa(n,e)+r+s}function bd(e,t,n,r){let s=[],o=[],a=null;const i=({state:m})=>{const _=Pl(e,location),S=n.value,T=t.value;let I=0;if(m){if(n.value=_,t.value=m,a&&a===S){a=null;return}I=T?m.position-T.position:0}else r(_);s.forEach(P=>{P(n.value,S,{delta:I,type:tr.pop,direction:I?I>0?Gn.forward:Gn.back:Gn.unknown})})};function l(){a=n.value}function c(m){s.push(m);const _=()=>{const S=s.indexOf(m);S>-1&&s.splice(S,1)};return o.push(_),_}function u(){const{history:m}=window;m.state&&m.replaceState(de({},m.state,{scroll:Yr()}),"")}function f(){for(const m of o)m();o=[],window.removeEventListener("popstate",i),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",i),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:l,listen:c,destroy:f}}function ca(e,t,n,r=!1,s=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:s?Yr():null}}function yd(e){const{history:t,location:n}=window,r={value:Pl(e,n)},s={value:t.state};s.value||o(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function o(l,c,u){const f=e.indexOf("#"),m=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+l:_d()+e+l;try{t[u?"replaceState":"pushState"](c,"",m),s.value=c}catch(_){console.error(_),n[u?"replace":"assign"](m)}}function a(l,c){const u=de({},t.state,ca(s.value.back,l,s.value.forward,!0),c,{position:s.value.position});o(l,u,!0),r.value=l}function i(l,c){const u=de({},s.value,t.state,{forward:l,scroll:Yr()});o(u.current,u,!0);const f=de({},ca(r.value,l,null),{position:u.position+1},c);o(l,f,!1),r.value=l}return{location:r,state:s,push:i,replace:a}}function vd(e){e=ud(e);const t=yd(e),n=bd(e,t.state,t.location,t.replace);function r(o,a=!0){a||n.pauseListeners(),history.go(o)}const s=de({location:"",base:e,go:r,createHref:dd.bind(null,e)},t,n);return Object.defineProperty(s,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(s,"state",{enumerable:!0,get:()=>t.state.value}),s}function Ed(e){return typeof e=="string"||e&&typeof e=="object"}function Rl(e){return typeof e=="string"||typeof e=="symbol"}const Cl=Symbol("");var ua;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(ua||(ua={}));function On(e,t){return de(new Error,{type:e,[Cl]:!0},t)}function Tt(e,t){return e instanceof Error&&Cl in e&&(t==null||!!(e.type&t))}const fa="[^/]+?",kd={sensitive:!1,strict:!1,start:!0,end:!0},Td=/[.+*?^${}()[\]/\\]/g;function wd(e,t){const n=de({},kd,t),r=[];let s=n.start?"^":"";const o=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(s+="/");for(let f=0;ft.length?t.length===1&&t[0]===80?1:-1:0}function Nl(e,t){let n=0;const r=e.score,s=t.score;for(;n0&&t[t.length-1]<0}const Ld={type:0,value:""},Od=/[a-zA-Z0-9_]/;function Id(e){if(!e)return[[]];if(e==="/")return[[Ld]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(_){throw new Error(`ERR (${n})/"${c}": ${_}`)}let n=0,r=n;const s=[];let o;function a(){o&&s.push(o),o=[]}let i=0,l,c="",u="";function f(){c&&(n===0?o.push({type:0,value:c}):n===1||n===2||n===3?(o.length>1&&(l==="*"||l==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),o.push({type:1,value:c,regexp:u,repeatable:l==="*"||l==="+",optional:l==="*"||l==="?"})):t("Invalid state to consume buffer"),c="")}function m(){c+=l}for(;i{a(y)}:Kn}function a(f){if(Rl(f)){const m=r.get(f);m&&(r.delete(f),n.splice(n.indexOf(m),1),m.children.forEach(a),m.alias.forEach(a))}else{const m=n.indexOf(f);m>-1&&(n.splice(m,1),f.record.name&&r.delete(f.record.name),f.children.forEach(a),f.alias.forEach(a))}}function i(){return n}function l(f){const m=Nd(f,n);n.splice(m,0,f),f.record.name&&!ga(f)&&r.set(f.record.name,f)}function c(f,m){let _,S={},T,I;if("name"in f&&f.name){if(_=r.get(f.name),!_)throw On(1,{location:f});I=_.record.name,S=de(ha(m.params,_.keys.filter(y=>!y.optional).concat(_.parent?_.parent.keys.filter(y=>y.optional):[]).map(y=>y.name)),f.params&&ha(f.params,_.keys.map(y=>y.name))),T=_.stringify(S)}else if(f.path!=null)T=f.path,_=n.find(y=>y.re.test(T)),_&&(S=_.parse(T),I=_.record.name);else{if(_=m.name?r.get(m.name):n.find(y=>y.re.test(m.path)),!_)throw On(1,{location:f,currentLocation:m});I=_.record.name,S=de({},m.params,f.params),T=_.stringify(S)}const P=[];let F=_;for(;F;)P.unshift(F.record),F=F.parent;return{name:I,path:T,params:S,matched:P,meta:Cd(P)}}e.forEach(f=>o(f));function u(){n.length=0,r.clear()}return{addRoute:o,resolve:c,removeRoute:a,clearRoutes:u,getRoutes:i,getRecordMatcher:s}}function ha(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function ma(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:Rd(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function Rd(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="object"?n[r]:n;return t}function ga(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Cd(e){return e.reduce((t,n)=>de(t,n.meta),{})}function pa(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function Nd(e,t){let n=0,r=t.length;for(;n!==r;){const o=n+r>>1;Nl(e,t[o])<0?r=o:n=o+1}const s=xd(e);return s&&(r=t.lastIndexOf(s,r-1)),r}function xd(e){let t=e;for(;t=t.parent;)if(xl(t)&&Nl(e,t)===0)return t}function xl({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function Md(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let s=0;so&&Ms(o)):[r&&Ms(r)]).forEach(o=>{o!==void 0&&(t+=(t.length?"&":"")+n,o!=null&&(t+="="+o))})}return t}function Dd(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=ht(r)?r.map(s=>s==null?null:""+s):r==null?r:""+r)}return t}const Fd=Symbol(""),ba=Symbol(""),Jr=Symbol(""),po=Symbol(""),Fs=Symbol("");function Mn(){let e=[];function t(r){return e.push(r),()=>{const s=e.indexOf(r);s>-1&&e.splice(s,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Ft(e,t,n,r,s,o=a=>a()){const a=r&&(r.enterCallbacks[s]=r.enterCallbacks[s]||[]);return()=>new Promise((i,l)=>{const c=m=>{m===!1?l(On(4,{from:n,to:t})):m instanceof Error?l(m):Ed(m)?l(On(2,{from:t,to:m})):(a&&r.enterCallbacks[s]===a&&typeof m=="function"&&a.push(m),i())},u=o(()=>e.call(r&&r.instances[s],t,n,c));let f=Promise.resolve(u);e.length<3&&(f=f.then(c)),f.catch(m=>l(m))})}function _s(e,t,n,r,s=o=>o()){const o=[];for(const a of e)for(const i in a.components){let l=a.components[i];if(!(t!=="beforeRouteEnter"&&!a.instances[i]))if(Tl(l)){const u=(l.__vccOpts||l)[t];u&&o.push(Ft(u,n,r,a,i,s))}else{let c=l();o.push(()=>c.then(u=>{if(!u)throw new Error(`Couldn't resolve component "${i}" at "${a.path}"`);const f=Kf(u)?u.default:u;a.mods[i]=u,a.components[i]=f;const _=(f.__vccOpts||f)[t];return _&&Ft(_,n,r,a,i,s)()}))}}return o}function ya(e){const t=rt(Jr),n=rt(po),r=we(()=>{const l=le(e.to);return t.resolve(l)}),s=we(()=>{const{matched:l}=r.value,{length:c}=l,u=l[c-1],f=n.matched;if(!u||!f.length)return-1;const m=f.findIndex(Ln.bind(null,u));if(m>-1)return m;const _=va(l[c-2]);return c>1&&va(u)===_&&f[f.length-1].path!==_?f.findIndex(Ln.bind(null,l[c-2])):m}),o=we(()=>s.value>-1&&Hd(n.params,r.value.params)),a=we(()=>s.value>-1&&s.value===n.matched.length-1&&Al(n.params,r.value.params));function i(l={}){if(Vd(l)){const c=t[le(e.replace)?"replace":"push"](le(e.to)).catch(Kn);return e.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>c),c}return Promise.resolve()}return{route:r,href:we(()=>r.value.href),isActive:o,isExactActive:a,navigate:i}}function $d(e){return e.length===1?e[0]:e}const Ud=it({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:ya,setup(e,{slots:t}){const n=ar(ya(e)),{options:r}=rt(Jr),s=we(()=>({[Ea(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[Ea(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const o=t.default&&$d(t.default(n));return e.custom?o:Gr("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:s.value},o)}}}),Er=Ud;function Vd(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Hd(e,t){for(const n in t){const r=t[n],s=e[n];if(typeof r=="string"){if(r!==s)return!1}else if(!ht(s)||s.length!==r.length||r.some((o,a)=>o!==s[a]))return!1}return!0}function va(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Ea=(e,t,n)=>e??t??n,Wd=it({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=rt(Fs),s=we(()=>e.route||r.value),o=rt(ba,0),a=we(()=>{let c=le(o);const{matched:u}=s.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),i=we(()=>s.value.matched[a.value]);br(ba,we(()=>a.value+1)),br(Fd,i),br(Fs,s);const l=ge();return Vt(()=>[l.value,i.value,e.name],([c,u,f],[m,_,S])=>{u&&(u.instances[f]=c,_&&_!==u&&c&&c===m&&(u.leaveGuards.size||(u.leaveGuards=_.leaveGuards),u.updateGuards.size||(u.updateGuards=_.updateGuards))),c&&u&&(!_||!Ln(u,_)||!m)&&(u.enterCallbacks[f]||[]).forEach(T=>T(c))},{flush:"post"}),()=>{const c=s.value,u=e.name,f=i.value,m=f&&f.components[u];if(!m)return ka(n.default,{Component:m,route:c});const _=f.props[u],S=_?_===!0?c.params:typeof _=="function"?_(c):_:null,I=Gr(m,de({},S,t,{onVnodeUnmounted:P=>{P.component.isUnmounted&&(f.instances[u]=null)},ref:l}));return ka(n.default,{Component:I,route:c})||I}}});function ka(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Ml=Wd;function Bd(e){const t=Pd(e.routes,e),n=e.parseQuery||Md,r=e.stringifyQuery||_a,s=e.history,o=Mn(),a=Mn(),i=Mn(),l=lo(Rt);let c=Rt;gn&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=gs.bind(null,C=>""+C),f=gs.bind(null,rd),m=gs.bind(null,er);function _(C,j){let B,q;return Rl(C)?(B=t.getRecordMatcher(C),q=j):q=C,t.addRoute(q,B)}function S(C){const j=t.getRecordMatcher(C);j&&t.removeRoute(j)}function T(){return t.getRoutes().map(C=>C.record)}function I(C){return!!t.getRecordMatcher(C)}function P(C,j){if(j=de({},j||l.value),typeof C=="string"){const g=ps(n,C,j.path),w=t.resolve({path:g.path},j),x=s.createHref(g.fullPath);return de(g,w,{params:m(w.params),hash:er(g.hash),redirectedFrom:void 0,href:x})}let B;if(C.path!=null)B=de({},C,{path:ps(n,C.path,j.path).path});else{const g=de({},C.params);for(const w in g)g[w]==null&&delete g[w];B=de({},C,{params:f(g)}),j.params=f(j.params)}const q=t.resolve(B,j),ae=C.hash||"";q.params=u(m(q.params));const p=ad(r,de({},C,{hash:ed(ae),path:q.path})),b=s.createHref(p);return de({fullPath:p,hash:ae,query:r===_a?Dd(C.query):C.query||{}},q,{redirectedFrom:void 0,href:b})}function F(C){return typeof C=="string"?ps(n,C,l.value.path):de({},C)}function y(C,j){if(c!==C)return On(8,{from:j,to:C})}function v(C){return D(C)}function L(C){return v(de(F(C),{replace:!0}))}function R(C){const j=C.matched[C.matched.length-1];if(j&&j.redirect){const{redirect:B}=j;let q=typeof B=="function"?B(C):B;return typeof q=="string"&&(q=q.includes("?")||q.includes("#")?q=F(q):{path:q},q.params={}),de({query:C.query,hash:C.hash,params:q.path!=null?{}:C.params},q)}}function D(C,j){const B=c=P(C),q=l.value,ae=C.state,p=C.force,b=C.replace===!0,g=R(B);if(g)return D(de(F(g),{state:typeof g=="object"?de({},ae,g.state):ae,force:p,replace:b}),j||B);const w=B;w.redirectedFrom=j;let x;return!p&&id(r,q,B)&&(x=On(16,{to:w,from:q}),Se(q,q,!0,!1)),(x?Promise.resolve(x):ee(w,q)).catch(N=>Tt(N)?Tt(N,2)?N:Xe(N):re(N,w,q)).then(N=>{if(N){if(Tt(N,2))return D(de({replace:b},F(N.to),{state:typeof N.to=="object"?de({},ae,N.to.state):ae,force:p}),j||w)}else N=z(w,q,!0,b,ae);return me(w,q,N),N})}function $(C,j){const B=y(C,j);return B?Promise.reject(B):Promise.resolve()}function M(C){const j=Ze.values().next().value;return j&&typeof j.runWithContext=="function"?j.runWithContext(C):C()}function ee(C,j){let B;const[q,ae,p]=Kd(C,j);B=_s(q.reverse(),"beforeRouteLeave",C,j);for(const g of q)g.leaveGuards.forEach(w=>{B.push(Ft(w,C,j))});const b=$.bind(null,C,j);return B.push(b),xe(B).then(()=>{B=[];for(const g of o.list())B.push(Ft(g,C,j));return B.push(b),xe(B)}).then(()=>{B=_s(ae,"beforeRouteUpdate",C,j);for(const g of ae)g.updateGuards.forEach(w=>{B.push(Ft(w,C,j))});return B.push(b),xe(B)}).then(()=>{B=[];for(const g of p)if(g.beforeEnter)if(ht(g.beforeEnter))for(const w of g.beforeEnter)B.push(Ft(w,C,j));else B.push(Ft(g.beforeEnter,C,j));return B.push(b),xe(B)}).then(()=>(C.matched.forEach(g=>g.enterCallbacks={}),B=_s(p,"beforeRouteEnter",C,j,M),B.push(b),xe(B))).then(()=>{B=[];for(const g of a.list())B.push(Ft(g,C,j));return B.push(b),xe(B)}).catch(g=>Tt(g,8)?g:Promise.reject(g))}function me(C,j,B){i.list().forEach(q=>M(()=>q(C,j,B)))}function z(C,j,B,q,ae){const p=y(C,j);if(p)return p;const b=j===Rt,g=gn?history.state:{};B&&(q||b?s.replace(C.fullPath,de({scroll:b&&g&&g.scroll},ae)):s.push(C.fullPath,ae)),l.value=C,Se(C,j,B,b),Xe()}let ue;function Qe(){ue||(ue=s.listen((C,j,B)=>{if(!mt.listening)return;const q=P(C),ae=R(q);if(ae){D(de(ae,{replace:!0,force:!0}),q).catch(Kn);return}c=q;const p=l.value;gn&&gd(la(p.fullPath,B.delta),Yr()),ee(q,p).catch(b=>Tt(b,12)?b:Tt(b,2)?(D(de(F(b.to),{force:!0}),q).then(g=>{Tt(g,20)&&!B.delta&&B.type===tr.pop&&s.go(-1,!1)}).catch(Kn),Promise.reject()):(B.delta&&s.go(-B.delta,!1),re(b,q,p))).then(b=>{b=b||z(q,p,!1),b&&(B.delta&&!Tt(b,8)?s.go(-B.delta,!1):B.type===tr.pop&&Tt(b,20)&&s.go(-1,!1)),me(q,p,b)}).catch(Kn)}))}let Je=Mn(),se=Mn(),Q;function re(C,j,B){Xe(C);const q=se.list();return q.length?q.forEach(ae=>ae(C,j,B)):console.error(C),Promise.reject(C)}function Be(){return Q&&l.value!==Rt?Promise.resolve():new Promise((C,j)=>{Je.add([C,j])})}function Xe(C){return Q||(Q=!C,Qe(),Je.list().forEach(([j,B])=>C?B(C):j()),Je.reset()),C}function Se(C,j,B,q){const{scrollBehavior:ae}=e;if(!gn||!ae)return Promise.resolve();const p=!B&&pd(la(C.fullPath,0))||(q||!B)&&history.state&&history.state.scroll||null;return co().then(()=>ae(C,j,p)).then(b=>b&&md(b)).catch(b=>re(b,C,j))}const Le=C=>s.go(C);let st;const Ze=new Set,mt={currentRoute:l,listening:!0,addRoute:_,removeRoute:S,clearRoutes:t.clearRoutes,hasRoute:I,getRoutes:T,resolve:P,options:e,push:v,replace:L,go:Le,back:()=>Le(-1),forward:()=>Le(1),beforeEach:o.add,beforeResolve:a.add,afterEach:i.add,onError:se.add,isReady:Be,install(C){const j=this;C.component("RouterLink",Er),C.component("RouterView",Ml),C.config.globalProperties.$router=j,Object.defineProperty(C.config.globalProperties,"$route",{enumerable:!0,get:()=>le(l)}),gn&&!st&&l.value===Rt&&(st=!0,v(s.location).catch(ae=>{}));const B={};for(const ae in Rt)Object.defineProperty(B,ae,{get:()=>l.value[ae],enumerable:!0});C.provide(Jr,j),C.provide(po,xi(B)),C.provide(Fs,l);const q=C.unmount;Ze.add(C),C.unmount=function(){Ze.delete(C),Ze.size<1&&(c=Rt,ue&&ue(),ue=null,l.value=Rt,st=!1,Q=!1),q()}}};function xe(C){return C.reduce((j,B)=>j.then(()=>M(B)),Promise.resolve())}return mt}function Kd(e,t){const n=[],r=[],s=[],o=Math.max(t.matched.length,e.matched.length);for(let a=0;aLn(c,i))?r.push(i):n.push(i));const l=e.matched[a];l&&(t.matched.find(c=>Ln(c,l))||s.push(l))}return[n,r,s]}function Xr(){return rt(Jr)}function Gd(e){return rt(po)}const jd="http://localhost".replace(/\/+$/,"");class Dl{constructor(t={}){this.configuration=t}set config(t){this.configuration=t}get basePath(){return this.configuration.basePath!=null?this.configuration.basePath:jd}get fetchApi(){return this.configuration.fetchApi}get middleware(){return this.configuration.middleware||[]}get queryParamsStringify(){return this.configuration.queryParamsStringify||Fl}get username(){return this.configuration.username}get password(){return this.configuration.password}get apiKey(){const t=this.configuration.apiKey;if(t)return typeof t=="function"?t:()=>t}get accessToken(){const t=this.configuration.accessToken;if(t)return typeof t=="function"?t:async()=>t}get headers(){return this.configuration.headers}get credentials(){return this.configuration.credentials}}const qd=new Dl,Nr=class Nr{constructor(t=qd){Qt(this,"middleware");Qt(this,"fetchApi",async(t,n)=>{let r={url:t,init:n};for(const o of this.middleware)o.pre&&(r=await o.pre({fetch:this.fetchApi,...r})||r);let s;try{s=await(this.configuration.fetchApi||fetch)(r.url,r.init)}catch(o){for(const a of this.middleware)a.onError&&(s=await a.onError({fetch:this.fetchApi,url:r.url,init:r.init,error:o,response:s?s.clone():void 0})||s);if(s===void 0)throw o instanceof Error?new zd(o,"The request failed and the interceptors did not return an alternative response"):o}for(const o of this.middleware)o.post&&(s=await o.post({fetch:this.fetchApi,url:r.url,init:r.init,response:s.clone()})||s);return s});this.configuration=t,this.middleware=t.middleware}withMiddleware(...t){const n=this.clone();return n.middleware=n.middleware.concat(...t),n}withPreMiddleware(...t){const n=t.map(r=>({pre:r}));return this.withMiddleware(...n)}withPostMiddleware(...t){const n=t.map(r=>({post:r}));return this.withMiddleware(...n)}isJsonMime(t){return t?Nr.jsonRegex.test(t):!1}async request(t,n){const{url:r,init:s}=await this.createFetchParams(t,n),o=await this.fetchApi(r,s);if(o&&o.status>=200&&o.status<300)return o;throw new Xd(o,"Response returned an error code")}async createFetchParams(t,n){let r=this.configuration.basePath+t.path;t.query!==void 0&&Object.keys(t.query).length!==0&&(r+="?"+this.configuration.queryParamsStringify(t.query));const s=Object.assign({},this.configuration.headers,t.headers);Object.keys(s).forEach(u=>s[u]===void 0?delete s[u]:{});const o=typeof n=="function"?n:async()=>n,a={method:t.method,headers:s,body:t.body,credentials:this.configuration.credentials},i={...a,...await o({init:a,context:t})};let l;Jd(i.body)||i.body instanceof URLSearchParams||Yd(i.body)?l=i.body:this.isJsonMime(s["Content-Type"])?l=JSON.stringify(i.body):l=i.body;const c={...i,body:l};return{url:r,init:c}}clone(){const t=this.constructor,n=new t(this.configuration);return n.middleware=this.middleware.slice(),n}};Qt(Nr,"jsonRegex",new RegExp("^(:?application/json|[^;/ ]+/[^;/ ]+[+]json)[ ]*(:?;.*)?$","i"));let $s=Nr;function Yd(e){return typeof Blob<"u"&&e instanceof Blob}function Jd(e){return typeof FormData<"u"&&e instanceof FormData}class Xd extends Error{constructor(n,r){super(r);Qt(this,"name","ResponseError");this.response=n}}class zd extends Error{constructor(n,r){super(r);Qt(this,"name","FetchError");this.cause=n}}class tn extends Error{constructor(n,r){super(r);Qt(this,"name","RequiredError");this.field=n}}function Fl(e,t=""){return Object.keys(e).map(n=>$l(n,e[n],t)).filter(n=>n.length>0).join("&")}function $l(e,t,n=""){const r=n+(n.length?`[${e}]`:e);if(t instanceof Array){const s=t.map(o=>encodeURIComponent(String(o))).join(`&${encodeURIComponent(r)}=`);return`${encodeURIComponent(r)}=${s}`}if(t instanceof Set){const s=Array.from(t);return $l(e,s,n)}return t instanceof Date?`${encodeURIComponent(r)}=${encodeURIComponent(t.toISOString())}`:t instanceof Object?Fl(t,r):`${encodeURIComponent(r)}=${encodeURIComponent(String(t))}`}class Ct{constructor(t,n=r=>r){this.raw=t,this.transformer=n}async value(){return this.transformer(await this.raw.json())}}class bs{constructor(t){this.raw=t}async value(){}}function Ta(e){return Qd(e,!1)}function Qd(e,t=!1){return e==null?e:{tag_id:e.tagId}}function Zd(e){return eh(e,!1)}function eh(e,t=!1){return e==null?e:{bookmark_ids:e.bookmarkIds,tag_ids:e.tagIds}}function th(e){return nh(e,!1)}function nh(e,t=!1){return e==null?e:{password:e.password,remember_me:e.rememberMe,username:e.username}}function wa(e){return rh(e)}function rh(e,t){return e==null?e:{expires:e.expires==null?void 0:e.expires,token:e.token==null?void 0:e.token}}function sh(e){return oh(e)}function oh(e,t){return e==null?e:{content:e.content==null?void 0:e.content,html:e.html==null?void 0:e.html}}function ah(e){return ih(e)}function ih(e,t){return e==null?e:{createEbook:e.createEbook==null?void 0:e.createEbook,hideExcerpt:e.hideExcerpt==null?void 0:e.hideExcerpt,hideThumbnail:e.hideThumbnail==null?void 0:e.hideThumbnail,keepMetadata:e.keepMetadata==null?void 0:e.keepMetadata,listMode:e.listMode==null?void 0:e.listMode,makePublic:e.makePublic==null?void 0:e.makePublic,showId:e.showId==null?void 0:e.showId,theme:e.theme==null?void 0:e.theme,useArchive:e.useArchive==null?void 0:e.useArchive}}function lh(e){return ch(e,!1)}function ch(e,t=!1){return e==null?e:{createEbook:e.createEbook,hideExcerpt:e.hideExcerpt,hideThumbnail:e.hideThumbnail,keepMetadata:e.keepMetadata,listMode:e.listMode,makePublic:e.makePublic,showId:e.showId,theme:e.theme,useArchive:e.useArchive}}function uh(e){return fh(e,!1)}function fh(e,t=!1){return e==null?e:{config:lh(e.config),new_password:e.newPassword,old_password:e.oldPassword,owner:e.owner,username:e.username}}function dh(e){return hh(e,!1)}function hh(e,t=!1){return e==null?e:{create_archive:e.createArchive,create_ebook:e.createEbook,ids:e.ids,keep_metadata:e.keepMetadata,skip_exist:e.skipExist}}function Sa(e){return mh(e)}function mh(e,t){return e==null?e:{config:e.config==null?void 0:ah(e.config),id:e.id==null?void 0:e.id,owner:e.owner==null?void 0:e.owner,password:e.password==null?void 0:e.password,username:e.username==null?void 0:e.username}}function Ul(e){return gh(e)}function gh(e,t){return e==null?e:{bookmarkCount:e.bookmark_count==null?void 0:e.bookmark_count,deleted:e.deleted==null?void 0:e.deleted,id:e.id==null?void 0:e.id,name:e.name==null?void 0:e.name}}function $_(e){return ph(e,!1)}function ph(e,t=!1){return e==null?e:{bookmark_count:e.bookmarkCount,deleted:e.deleted,id:e.id,name:e.name}}function La(e){return _h(e)}function _h(e,t){return e==null?e:{author:e.author==null?void 0:e.author,createArchive:e.create_archive==null?void 0:e.create_archive,createEbook:e.create_ebook==null?void 0:e.create_ebook,createdAt:e.createdAt==null?void 0:e.createdAt,excerpt:e.excerpt==null?void 0:e.excerpt,hasArchive:e.hasArchive==null?void 0:e.hasArchive,hasContent:e.hasContent==null?void 0:e.hasContent,hasEbook:e.hasEbook==null?void 0:e.hasEbook,html:e.html==null?void 0:e.html,id:e.id==null?void 0:e.id,imageURL:e.imageURL==null?void 0:e.imageURL,modifiedAt:e.modifiedAt==null?void 0:e.modifiedAt,_public:e.public==null?void 0:e.public,tags:e.tags==null?void 0:e.tags.map(Ul),title:e.title==null?void 0:e.title,url:e.url==null?void 0:e.url}}class bh extends $s{async apiV1AuthAccountPatchRaw(t,n){const r={},s={};s["Content-Type"]="application/json";const o=await this.request({path:"/api/v1/auth/account",method:"PATCH",headers:s,query:r,body:uh(t.payload)},n);return new Ct(o,a=>Sa(a))}async apiV1AuthAccountPatch(t={},n){return await(await this.apiV1AuthAccountPatchRaw(t,n)).value()}async apiV1AuthLoginPostRaw(t,n){const r={},s={};s["Content-Type"]="application/json";const o=await this.request({path:"/api/v1/auth/login",method:"POST",headers:s,query:r,body:th(t.payload)},n);return new Ct(o,a=>wa(a))}async apiV1AuthLoginPost(t={},n){return await(await this.apiV1AuthLoginPostRaw(t,n)).value()}async apiV1AuthLogoutPostRaw(t){const n={},r={},s=await this.request({path:"/api/v1/auth/logout",method:"POST",headers:r,query:n},t);return new bs(s)}async apiV1AuthLogoutPost(t){await this.apiV1AuthLogoutPostRaw(t)}async apiV1AuthMeGetRaw(t){const n={},r={},s=await this.request({path:"/api/v1/auth/me",method:"GET",headers:r,query:n},t);return new Ct(s,o=>Sa(o))}async apiV1AuthMeGet(t){return await(await this.apiV1AuthMeGetRaw(t)).value()}async apiV1AuthRefreshPostRaw(t){const n={},r={},s=await this.request({path:"/api/v1/auth/refresh",method:"POST",headers:r,query:n},t);return new Ct(s,o=>wa(o))}async apiV1AuthRefreshPost(t){return await(await this.apiV1AuthRefreshPostRaw(t)).value()}async apiV1BookmarksBulkTagsPutRaw(t,n){if(t.payload==null)throw new tn("payload",'Required parameter "payload" was null or undefined when calling apiV1BookmarksBulkTagsPut().');const r={},s={};s["Content-Type"]="application/json";const o=await this.request({path:"/api/v1/bookmarks/bulk/tags",method:"PUT",headers:s,query:r,body:Zd(t.payload)},n);return new Ct(o,a=>a.map(La))}async apiV1BookmarksBulkTagsPut(t,n){return await(await this.apiV1BookmarksBulkTagsPutRaw(t,n)).value()}async apiV1BookmarksCachePutRaw(t,n){if(t.payload==null)throw new tn("payload",'Required parameter "payload" was null or undefined when calling apiV1BookmarksCachePut().');const r={},s={};s["Content-Type"]="application/json";const o=await this.request({path:"/api/v1/bookmarks/cache",method:"PUT",headers:s,query:r,body:dh(t.payload)},n);return new Ct(o,a=>La(a))}async apiV1BookmarksCachePut(t,n){return await(await this.apiV1BookmarksCachePutRaw(t,n)).value()}async apiV1BookmarksIdReadableGetRaw(t){const n={},r={},s=await this.request({path:"/api/v1/bookmarks/id/readable",method:"GET",headers:r,query:n},t);return new Ct(s,o=>sh(o))}async apiV1BookmarksIdReadableGet(t){return await(await this.apiV1BookmarksIdReadableGetRaw(t)).value()}async apiV1BookmarksIdTagsDeleteRaw(t,n){if(t.id==null)throw new tn("id",'Required parameter "id" was null or undefined when calling apiV1BookmarksIdTagsDelete().');if(t.payload==null)throw new tn("payload",'Required parameter "payload" was null or undefined when calling apiV1BookmarksIdTagsDelete().');const r={},s={};s["Content-Type"]="application/json";const o=await this.request({path:"/api/v1/bookmarks/{id}/tags".replace("{id}",encodeURIComponent(String(t.id))),method:"DELETE",headers:s,query:r,body:Ta(t.payload)},n);return new bs(o)}async apiV1BookmarksIdTagsDelete(t,n){await this.apiV1BookmarksIdTagsDeleteRaw(t,n)}async apiV1BookmarksIdTagsGetRaw(t,n){if(t.id==null)throw new tn("id",'Required parameter "id" was null or undefined when calling apiV1BookmarksIdTagsGet().');const r={},s={},o=await this.request({path:"/api/v1/bookmarks/{id}/tags".replace("{id}",encodeURIComponent(String(t.id))),method:"GET",headers:s,query:r},n);return new Ct(o,a=>a.map(Ul))}async apiV1BookmarksIdTagsGet(t,n){return await(await this.apiV1BookmarksIdTagsGetRaw(t,n)).value()}async apiV1BookmarksIdTagsPostRaw(t,n){if(t.id==null)throw new tn("id",'Required parameter "id" was null or undefined when calling apiV1BookmarksIdTagsPost().');if(t.payload==null)throw new tn("payload",'Required parameter "payload" was null or undefined when calling apiV1BookmarksIdTagsPost().');const r={},s={};s["Content-Type"]="application/json";const o=await this.request({path:"/api/v1/bookmarks/{id}/tags".replace("{id}",encodeURIComponent(String(t.id))),method:"POST",headers:s,query:r,body:Ta(t.payload)},n);return new bs(o)}async apiV1BookmarksIdTagsPost(t,n){await this.apiV1BookmarksIdTagsPostRaw(t,n)}}const ur=Bf("auth",()=>{const e=ge(localStorage.getItem("token")),t=ge(Number(localStorage.getItem("expires"))||null),n=ge(null),r=ge(!1),s=ge(null),o=ge(null),a=we(()=>!e.value||!t.value?!1:t.value>Date.now()),i=()=>{const I=new Dl({basePath:"http://localhost:8080",accessToken:e.value||void 0,headers:e.value?{Authorization:`Bearer ${e.value}`}:void 0});return new bh(I)},l=async()=>{if(!e.value)return!1;r.value=!0;try{const I=await u();return r.value=!1,!!I}catch{return r.value=!1,!1}},c=async(I,P,F=!1)=>{r.value=!0,s.value=null;try{const y={username:I,password:P,rememberMe:F},L=await i().apiV1AuthLoginPost({payload:y});if(L.token)return e.value=L.token,t.value=L.expires||0,localStorage.setItem("token",L.token),localStorage.setItem("expires",String(L.expires)),await u(),!0;throw new Error("Invalid response from server")}catch(y){if(console.error("Login error:",y),y.response)try{const v=await y.response.json();v&&v.message?s.value=v.message:v&&v.error?s.value=v.error:typeof v=="string"?s.value=v:s.value=`Server error: ${y.response.status}`}catch{s.value=y.response.statusText||`Server error: ${y.response.status}`}else s.value=y.message||"Failed to login";return!1}finally{r.value=!1}},u=async()=>{if(!e.value)return null;try{const P=await i().apiV1AuthMeGet();if(P)return n.value=P,n.value;throw new Error("Failed to fetch user info")}catch(I){return console.error("Error fetching user info:",I),I.response&&I.response.status===401&&f(),null}},f=()=>{e.value=null,t.value=null,n.value=null,localStorage.removeItem("token"),localStorage.removeItem("expires")};return{token:e,expires:t,user:n,loading:r,error:s,isAuthenticated:a,login:c,logout:async()=>{r.value=!0;try{e.value&&await i().apiV1AuthLogoutPost()}catch(I){console.error("Logout error:",I)}finally{f(),r.value=!1}},fetchUserInfo:u,refreshToken:async()=>{if(!e.value)return!1;try{const P=await i().apiV1AuthRefreshPost();return P.token?(e.value=P.token,t.value=P.expires||0,localStorage.setItem("token",P.token),localStorage.setItem("expires",String(P.expires)),!0):!1}catch(I){return console.error("Token refresh error:",I),!1}},validateToken:l,setRedirectDestination:I=>{o.value=I},getAndClearRedirectDestination:()=>{const I=o.value;return o.value=null,I},clearAuth:f}}),yh={class:"min-h-screen h-full flex flex-col bg-[var(--background-color)] text-[var(--text-color)]"},vh={key:0,class:"fixed inset-0 flex items-center justify-center bg-white dark:bg-gray-900 bg-opacity-80 dark:bg-opacity-80 z-50"},Eh=it({__name:"App",setup(e){const t=ur();Xr();const n=ge(!0);return qt(async()=>{if(t.token)try{await t.validateToken()}catch(r){console.error("Failed to validate token:",r)}n.value=!1}),(r,s)=>(fe(),Ee("div",yh,[n.value?(fe(),Ee("div",vh,s[0]||(s[0]=[H("div",{class:"text-center"},[H("div",{class:"animate-spin rounded-full h-12 w-12 border-b-2 border-red-500 mx-auto mb-2"}),H("p",{class:"text-gray-700 dark:text-gray-300"},"Loading...")],-1)]))):(fe(),wn(le(Ml),{key:1,class:"flex-1"}))]))}}),kh="modulepreload",Th=function(e){return"/"+e},Oa={},mr=function(t,n,r){let s=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const a=document.querySelector("meta[property=csp-nonce]"),i=(a==null?void 0:a.nonce)||(a==null?void 0:a.getAttribute("nonce"));s=Promise.allSettled(n.map(l=>{if(l=Th(l),l in Oa)return;Oa[l]=!0;const c=l.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${u}`))return;const f=document.createElement("link");if(f.rel=c?"stylesheet":kh,c||(f.as="script"),f.crossOrigin="",f.href=l,i&&f.setAttribute("nonce",i),document.head.appendChild(f),c)return new Promise((m,_)=>{f.addEventListener("load",m),f.addEventListener("error",()=>_(new Error(`Unable to preload CSS for ${l}`)))})}))}function o(a){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=a,window.dispatchEvent(i),!i.defaultPrevented)throw a}return s.then(a=>{for(const i of a||[])i.status==="rejected"&&o(i.reason);return t().catch(o)})};/*! + * shared v9.14.3 + * (c) 2025 kazuya kawaguchi + * Released under the MIT License. + */const Rr=typeof window<"u",Yt=(e,t=!1)=>t?Symbol.for(e):Symbol(e),wh=(e,t,n)=>Sh({l:e,k:t,s:n}),Sh=e=>JSON.stringify(e).replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029").replace(/\u0027/g,"\\u0027"),Ae=e=>typeof e=="number"&&isFinite(e),Lh=e=>Hl(e)==="[object Date]",Kt=e=>Hl(e)==="[object RegExp]",zr=e=>Z(e)&&Object.keys(e).length===0,Ue=Object.assign,Oh=Object.create,pe=(e=null)=>Oh(e);let Ia;const At=()=>Ia||(Ia=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:pe());function Aa(e){return e.replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}const Ih=Object.prototype.hasOwnProperty;function ct(e,t){return Ih.call(e,t)}const ke=Array.isArray,ye=e=>typeof e=="function",G=e=>typeof e=="string",ne=e=>typeof e=="boolean",ce=e=>e!==null&&typeof e=="object",Ah=e=>ce(e)&&ye(e.then)&&ye(e.catch),Vl=Object.prototype.toString,Hl=e=>Vl.call(e),Z=e=>{if(!ce(e))return!1;const t=Object.getPrototypeOf(e);return t===null||t.constructor===Object},Ph=e=>e==null?"":ke(e)||Z(e)&&e.toString===Vl?JSON.stringify(e,null,2):String(e);function Rh(e,t=""){return e.reduce((n,r,s)=>s===0?n+r:n+t+r,"")}function Qr(e){let t=e;return()=>++t}function Ch(e,t){typeof console<"u"&&(console.warn("[intlify] "+e),t&&console.warn(t.stack))}const gr=e=>!ce(e)||ke(e);function kr(e,t){if(gr(e)||gr(t))throw new Error("Invalid value");const n=[{src:e,des:t}];for(;n.length;){const{src:r,des:s}=n.pop();Object.keys(r).forEach(o=>{o!=="__proto__"&&(ce(r[o])&&!ce(s[o])&&(s[o]=Array.isArray(r[o])?[]:pe()),gr(s[o])||gr(r[o])?s[o]=r[o]:n.push({src:r[o],des:s[o]}))})}}/*! + * message-compiler v9.14.3 + * (c) 2025 kazuya kawaguchi + * Released under the MIT License. + */function Nh(e,t,n){return{line:e,column:t,offset:n}}function Cr(e,t,n){return{start:e,end:t}}const xh=/\{([0-9a-zA-Z]+)\}/g;function Wl(e,...t){return t.length===1&&Mh(t[0])&&(t=t[0]),(!t||!t.hasOwnProperty)&&(t={}),e.replace(xh,(n,r)=>t.hasOwnProperty(r)?t[r]:"")}const Bl=Object.assign,Pa=e=>typeof e=="string",Mh=e=>e!==null&&typeof e=="object";function Kl(e,t=""){return e.reduce((n,r,s)=>s===0?n+r:n+t+r,"")}const _o={USE_MODULO_SYNTAX:1,__EXTEND_POINT__:2},Dh={[_o.USE_MODULO_SYNTAX]:"Use modulo before '{{0}}'."};function Fh(e,t,...n){const r=Wl(Dh[e],...n||[]),s={message:String(r),code:e};return t&&(s.location=t),s}const X={EXPECTED_TOKEN:1,INVALID_TOKEN_IN_PLACEHOLDER:2,UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER:3,UNKNOWN_ESCAPE_SEQUENCE:4,INVALID_UNICODE_ESCAPE_SEQUENCE:5,UNBALANCED_CLOSING_BRACE:6,UNTERMINATED_CLOSING_BRACE:7,EMPTY_PLACEHOLDER:8,NOT_ALLOW_NEST_PLACEHOLDER:9,INVALID_LINKED_FORMAT:10,MUST_HAVE_MESSAGES_IN_PLURAL:11,UNEXPECTED_EMPTY_LINKED_MODIFIER:12,UNEXPECTED_EMPTY_LINKED_KEY:13,UNEXPECTED_LEXICAL_ANALYSIS:14,UNHANDLED_CODEGEN_NODE_TYPE:15,UNHANDLED_MINIFIER_NODE_TYPE:16,__EXTEND_POINT__:17},$h={[X.EXPECTED_TOKEN]:"Expected token: '{0}'",[X.INVALID_TOKEN_IN_PLACEHOLDER]:"Invalid token in placeholder: '{0}'",[X.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER]:"Unterminated single quote in placeholder",[X.UNKNOWN_ESCAPE_SEQUENCE]:"Unknown escape sequence: \\{0}",[X.INVALID_UNICODE_ESCAPE_SEQUENCE]:"Invalid unicode escape sequence: {0}",[X.UNBALANCED_CLOSING_BRACE]:"Unbalanced closing brace",[X.UNTERMINATED_CLOSING_BRACE]:"Unterminated closing brace",[X.EMPTY_PLACEHOLDER]:"Empty placeholder",[X.NOT_ALLOW_NEST_PLACEHOLDER]:"Not allowed nest placeholder",[X.INVALID_LINKED_FORMAT]:"Invalid linked format",[X.MUST_HAVE_MESSAGES_IN_PLURAL]:"Plural must have messages",[X.UNEXPECTED_EMPTY_LINKED_MODIFIER]:"Unexpected empty linked modifier",[X.UNEXPECTED_EMPTY_LINKED_KEY]:"Unexpected empty linked key",[X.UNEXPECTED_LEXICAL_ANALYSIS]:"Unexpected lexical analysis in token: '{0}'",[X.UNHANDLED_CODEGEN_NODE_TYPE]:"unhandled codegen node type: '{0}'",[X.UNHANDLED_MINIFIER_NODE_TYPE]:"unhandled mimifier node type: '{0}'"};function Cn(e,t,n={}){const{domain:r,messages:s,args:o}=n,a=Wl((s||$h)[e]||"",...o||[]),i=new SyntaxError(String(a));return i.code=e,t&&(i.location=t),i.domain=r,i}function Uh(e){throw e}const wt=" ",Vh="\r",je=` +`,Hh="\u2028",Wh="\u2029";function Bh(e){const t=e;let n=0,r=1,s=1,o=0;const a=D=>t[D]===Vh&&t[D+1]===je,i=D=>t[D]===je,l=D=>t[D]===Wh,c=D=>t[D]===Hh,u=D=>a(D)||i(D)||l(D)||c(D),f=()=>n,m=()=>r,_=()=>s,S=()=>o,T=D=>a(D)||l(D)||c(D)?je:t[D],I=()=>T(n),P=()=>T(n+o);function F(){return o=0,u(n)&&(r++,s=0),a(n)&&n++,n++,s++,t[n]}function y(){return a(n+o)&&o++,o++,t[n+o]}function v(){n=0,r=1,s=1,o=0}function L(D=0){o=D}function R(){const D=n+o;for(;D!==n;)F();o=0}return{index:f,line:m,column:_,peekOffset:S,charAt:T,currentChar:I,currentPeek:P,next:F,peek:y,reset:v,resetPeek:L,skipToPeek:R}}const Nt=void 0,Kh=".",Ra="'",Gh="tokenizer";function jh(e,t={}){const n=t.location!==!1,r=Bh(e),s=()=>r.index(),o=()=>Nh(r.line(),r.column(),r.index()),a=o(),i=s(),l={currentType:14,offset:i,startLoc:a,endLoc:a,lastType:14,lastOffset:i,lastStartLoc:a,lastEndLoc:a,braceNest:0,inLinked:!1,text:""},c=()=>l,{onError:u}=t;function f(d,h,k,...O){const K=c();if(h.column+=k,h.offset+=k,u){const U=n?Cr(K.startLoc,h):null,E=Cn(d,U,{domain:Gh,args:O});u(E)}}function m(d,h,k){d.endLoc=o(),d.currentType=h;const O={type:h};return n&&(O.loc=Cr(d.startLoc,d.endLoc)),k!=null&&(O.value=k),O}const _=d=>m(d,14);function S(d,h){return d.currentChar()===h?(d.next(),h):(f(X.EXPECTED_TOKEN,o(),0,h),"")}function T(d){let h="";for(;d.currentPeek()===wt||d.currentPeek()===je;)h+=d.currentPeek(),d.peek();return h}function I(d){const h=T(d);return d.skipToPeek(),h}function P(d){if(d===Nt)return!1;const h=d.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h===95}function F(d){if(d===Nt)return!1;const h=d.charCodeAt(0);return h>=48&&h<=57}function y(d,h){const{currentType:k}=h;if(k!==2)return!1;T(d);const O=P(d.currentPeek());return d.resetPeek(),O}function v(d,h){const{currentType:k}=h;if(k!==2)return!1;T(d);const O=d.currentPeek()==="-"?d.peek():d.currentPeek(),K=F(O);return d.resetPeek(),K}function L(d,h){const{currentType:k}=h;if(k!==2)return!1;T(d);const O=d.currentPeek()===Ra;return d.resetPeek(),O}function R(d,h){const{currentType:k}=h;if(k!==8)return!1;T(d);const O=d.currentPeek()===".";return d.resetPeek(),O}function D(d,h){const{currentType:k}=h;if(k!==9)return!1;T(d);const O=P(d.currentPeek());return d.resetPeek(),O}function $(d,h){const{currentType:k}=h;if(!(k===8||k===12))return!1;T(d);const O=d.currentPeek()===":";return d.resetPeek(),O}function M(d,h){const{currentType:k}=h;if(k!==10)return!1;const O=()=>{const U=d.currentPeek();return U==="{"?P(d.peek()):U==="@"||U==="%"||U==="|"||U===":"||U==="."||U===wt||!U?!1:U===je?(d.peek(),O()):z(d,!1)},K=O();return d.resetPeek(),K}function ee(d){T(d);const h=d.currentPeek()==="|";return d.resetPeek(),h}function me(d){const h=T(d),k=d.currentPeek()==="%"&&d.peek()==="{";return d.resetPeek(),{isModulo:k,hasSpace:h.length>0}}function z(d,h=!0){const k=(K=!1,U="",E=!1)=>{const A=d.currentPeek();return A==="{"?U==="%"?!1:K:A==="@"||!A?U==="%"?!0:K:A==="%"?(d.peek(),k(K,"%",!0)):A==="|"?U==="%"||E?!0:!(U===wt||U===je):A===wt?(d.peek(),k(!0,wt,E)):A===je?(d.peek(),k(!0,je,E)):!0},O=k();return h&&d.resetPeek(),O}function ue(d,h){const k=d.currentChar();return k===Nt?Nt:h(k)?(d.next(),k):null}function Qe(d){const h=d.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h>=48&&h<=57||h===95||h===36}function Je(d){return ue(d,Qe)}function se(d){const h=d.charCodeAt(0);return h>=97&&h<=122||h>=65&&h<=90||h>=48&&h<=57||h===95||h===36||h===45}function Q(d){return ue(d,se)}function re(d){const h=d.charCodeAt(0);return h>=48&&h<=57}function Be(d){return ue(d,re)}function Xe(d){const h=d.charCodeAt(0);return h>=48&&h<=57||h>=65&&h<=70||h>=97&&h<=102}function Se(d){return ue(d,Xe)}function Le(d){let h="",k="";for(;h=Be(d);)k+=h;return k}function st(d){I(d);const h=d.currentChar();return h!=="%"&&f(X.EXPECTED_TOKEN,o(),0,h),d.next(),"%"}function Ze(d){let h="";for(;;){const k=d.currentChar();if(k==="{"||k==="}"||k==="@"||k==="|"||!k)break;if(k==="%")if(z(d))h+=k,d.next();else break;else if(k===wt||k===je)if(z(d))h+=k,d.next();else{if(ee(d))break;h+=k,d.next()}else h+=k,d.next()}return h}function mt(d){I(d);let h="",k="";for(;h=Q(d);)k+=h;return d.currentChar()===Nt&&f(X.UNTERMINATED_CLOSING_BRACE,o(),0),k}function xe(d){I(d);let h="";return d.currentChar()==="-"?(d.next(),h+=`-${Le(d)}`):h+=Le(d),d.currentChar()===Nt&&f(X.UNTERMINATED_CLOSING_BRACE,o(),0),h}function C(d){return d!==Ra&&d!==je}function j(d){I(d),S(d,"'");let h="",k="";for(;h=ue(d,C);)h==="\\"?k+=B(d):k+=h;const O=d.currentChar();return O===je||O===Nt?(f(X.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER,o(),0),O===je&&(d.next(),S(d,"'")),k):(S(d,"'"),k)}function B(d){const h=d.currentChar();switch(h){case"\\":case"'":return d.next(),`\\${h}`;case"u":return q(d,h,4);case"U":return q(d,h,6);default:return f(X.UNKNOWN_ESCAPE_SEQUENCE,o(),0,h),""}}function q(d,h,k){S(d,h);let O="";for(let K=0;K{const O=d.currentChar();return O==="{"||O==="%"||O==="@"||O==="|"||O==="("||O===")"||!O||O===wt?k:(k+=O,d.next(),h(k))};return h("")}function w(d){I(d);const h=S(d,"|");return I(d),h}function x(d,h){let k=null;switch(d.currentChar()){case"{":return h.braceNest>=1&&f(X.NOT_ALLOW_NEST_PLACEHOLDER,o(),0),d.next(),k=m(h,2,"{"),I(d),h.braceNest++,k;case"}":return h.braceNest>0&&h.currentType===2&&f(X.EMPTY_PLACEHOLDER,o(),0),d.next(),k=m(h,3,"}"),h.braceNest--,h.braceNest>0&&I(d),h.inLinked&&h.braceNest===0&&(h.inLinked=!1),k;case"@":return h.braceNest>0&&f(X.UNTERMINATED_CLOSING_BRACE,o(),0),k=N(d,h)||_(h),h.braceNest=0,k;default:{let K=!0,U=!0,E=!0;if(ee(d))return h.braceNest>0&&f(X.UNTERMINATED_CLOSING_BRACE,o(),0),k=m(h,1,w(d)),h.braceNest=0,h.inLinked=!1,k;if(h.braceNest>0&&(h.currentType===5||h.currentType===6||h.currentType===7))return f(X.UNTERMINATED_CLOSING_BRACE,o(),0),h.braceNest=0,W(d,h);if(K=y(d,h))return k=m(h,5,mt(d)),I(d),k;if(U=v(d,h))return k=m(h,6,xe(d)),I(d),k;if(E=L(d,h))return k=m(h,7,j(d)),I(d),k;if(!K&&!U&&!E)return k=m(h,13,p(d)),f(X.INVALID_TOKEN_IN_PLACEHOLDER,o(),0,k.value),I(d),k;break}}return k}function N(d,h){const{currentType:k}=h;let O=null;const K=d.currentChar();switch((k===8||k===9||k===12||k===10)&&(K===je||K===wt)&&f(X.INVALID_LINKED_FORMAT,o(),0),K){case"@":return d.next(),O=m(h,8,"@"),h.inLinked=!0,O;case".":return I(d),d.next(),m(h,9,".");case":":return I(d),d.next(),m(h,10,":");default:return ee(d)?(O=m(h,1,w(d)),h.braceNest=0,h.inLinked=!1,O):R(d,h)||$(d,h)?(I(d),N(d,h)):D(d,h)?(I(d),m(h,12,b(d))):M(d,h)?(I(d),K==="{"?x(d,h)||O:m(h,11,g(d))):(k===8&&f(X.INVALID_LINKED_FORMAT,o(),0),h.braceNest=0,h.inLinked=!1,W(d,h))}}function W(d,h){let k={type:14};if(h.braceNest>0)return x(d,h)||_(h);if(h.inLinked)return N(d,h)||_(h);switch(d.currentChar()){case"{":return x(d,h)||_(h);case"}":return f(X.UNBALANCED_CLOSING_BRACE,o(),0),d.next(),m(h,3,"}");case"@":return N(d,h)||_(h);default:{if(ee(d))return k=m(h,1,w(d)),h.braceNest=0,h.inLinked=!1,k;const{isModulo:K,hasSpace:U}=me(d);if(K)return U?m(h,0,Ze(d)):m(h,4,st(d));if(z(d))return m(h,0,Ze(d));break}}return k}function V(){const{currentType:d,offset:h,startLoc:k,endLoc:O}=l;return l.lastType=d,l.lastOffset=h,l.lastStartLoc=k,l.lastEndLoc=O,l.offset=s(),l.startLoc=o(),r.currentChar()===Nt?m(l,14):W(r,l)}return{nextToken:V,currentOffset:s,currentPosition:o,context:c}}const qh="parser",Yh=/(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;function Jh(e,t,n){switch(e){case"\\\\":return"\\";case"\\'":return"'";default:{const r=parseInt(t||n,16);return r<=55295||r>=57344?String.fromCodePoint(r):"�"}}}function Xh(e={}){const t=e.location!==!1,{onError:n,onWarn:r}=e;function s(y,v,L,R,...D){const $=y.currentPosition();if($.offset+=R,$.column+=R,n){const M=t?Cr(L,$):null,ee=Cn(v,M,{domain:qh,args:D});n(ee)}}function o(y,v,L,R,...D){const $=y.currentPosition();if($.offset+=R,$.column+=R,r){const M=t?Cr(L,$):null;r(Fh(v,M,D))}}function a(y,v,L){const R={type:y};return t&&(R.start=v,R.end=v,R.loc={start:L,end:L}),R}function i(y,v,L,R){t&&(y.end=v,y.loc&&(y.loc.end=L))}function l(y,v){const L=y.context(),R=a(3,L.offset,L.startLoc);return R.value=v,i(R,y.currentOffset(),y.currentPosition()),R}function c(y,v){const L=y.context(),{lastOffset:R,lastStartLoc:D}=L,$=a(5,R,D);return $.index=parseInt(v,10),y.nextToken(),i($,y.currentOffset(),y.currentPosition()),$}function u(y,v,L){const R=y.context(),{lastOffset:D,lastStartLoc:$}=R,M=a(4,D,$);return M.key=v,L===!0&&(M.modulo=!0),y.nextToken(),i(M,y.currentOffset(),y.currentPosition()),M}function f(y,v){const L=y.context(),{lastOffset:R,lastStartLoc:D}=L,$=a(9,R,D);return $.value=v.replace(Yh,Jh),y.nextToken(),i($,y.currentOffset(),y.currentPosition()),$}function m(y){const v=y.nextToken(),L=y.context(),{lastOffset:R,lastStartLoc:D}=L,$=a(8,R,D);return v.type!==12?(s(y,X.UNEXPECTED_EMPTY_LINKED_MODIFIER,L.lastStartLoc,0),$.value="",i($,R,D),{nextConsumeToken:v,node:$}):(v.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,L.lastStartLoc,0,lt(v)),$.value=v.value||"",i($,y.currentOffset(),y.currentPosition()),{node:$})}function _(y,v){const L=y.context(),R=a(7,L.offset,L.startLoc);return R.value=v,i(R,y.currentOffset(),y.currentPosition()),R}function S(y){const v=y.context(),L=a(6,v.offset,v.startLoc);let R=y.nextToken();if(R.type===9){const D=m(y);L.modifier=D.node,R=D.nextConsumeToken||y.nextToken()}switch(R.type!==10&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(R)),R=y.nextToken(),R.type===2&&(R=y.nextToken()),R.type){case 11:R.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(R)),L.key=_(y,R.value||"");break;case 5:R.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(R)),L.key=u(y,R.value||"");break;case 6:R.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(R)),L.key=c(y,R.value||"");break;case 7:R.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(R)),L.key=f(y,R.value||"");break;default:{s(y,X.UNEXPECTED_EMPTY_LINKED_KEY,v.lastStartLoc,0);const D=y.context(),$=a(7,D.offset,D.startLoc);return $.value="",i($,D.offset,D.startLoc),L.key=$,i(L,D.offset,D.startLoc),{nextConsumeToken:R,node:L}}}return i(L,y.currentOffset(),y.currentPosition()),{node:L}}function T(y){const v=y.context(),L=v.currentType===1?y.currentOffset():v.offset,R=v.currentType===1?v.endLoc:v.startLoc,D=a(2,L,R);D.items=[];let $=null,M=null;do{const z=$||y.nextToken();switch($=null,z.type){case 0:z.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(z)),D.items.push(l(y,z.value||""));break;case 6:z.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(z)),D.items.push(c(y,z.value||""));break;case 4:M=!0;break;case 5:z.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(z)),D.items.push(u(y,z.value||"",!!M)),M&&(o(y,_o.USE_MODULO_SYNTAX,v.lastStartLoc,0,lt(z)),M=null);break;case 7:z.value==null&&s(y,X.UNEXPECTED_LEXICAL_ANALYSIS,v.lastStartLoc,0,lt(z)),D.items.push(f(y,z.value||""));break;case 8:{const ue=S(y);D.items.push(ue.node),$=ue.nextConsumeToken||null;break}}}while(v.currentType!==14&&v.currentType!==1);const ee=v.currentType===1?v.lastOffset:y.currentOffset(),me=v.currentType===1?v.lastEndLoc:y.currentPosition();return i(D,ee,me),D}function I(y,v,L,R){const D=y.context();let $=R.items.length===0;const M=a(1,v,L);M.cases=[],M.cases.push(R);do{const ee=T(y);$||($=ee.items.length===0),M.cases.push(ee)}while(D.currentType!==14);return $&&s(y,X.MUST_HAVE_MESSAGES_IN_PLURAL,L,0),i(M,y.currentOffset(),y.currentPosition()),M}function P(y){const v=y.context(),{offset:L,startLoc:R}=v,D=T(y);return v.currentType===14?D:I(y,L,R,D)}function F(y){const v=jh(y,Bl({},e)),L=v.context(),R=a(0,L.offset,L.startLoc);return t&&R.loc&&(R.loc.source=y),R.body=P(v),e.onCacheKey&&(R.cacheKey=e.onCacheKey(y)),L.currentType!==14&&s(v,X.UNEXPECTED_LEXICAL_ANALYSIS,L.lastStartLoc,0,y[L.offset]||""),i(R,v.currentOffset(),v.currentPosition()),R}return{parse:F}}function lt(e){if(e.type===14)return"EOF";const t=(e.value||"").replace(/\r?\n/gu,"\\n");return t.length>10?t.slice(0,9)+"…":t}function zh(e,t={}){const n={ast:e,helpers:new Set};return{context:()=>n,helper:o=>(n.helpers.add(o),o)}}function Ca(e,t){for(let n=0;nNa(n)),e}function Na(e){if(e.items.length===1){const t=e.items[0];(t.type===3||t.type===9)&&(e.static=t.value,delete t.value)}else{const t=[];for(let n=0;na;function l(T,I){a.code+=T}function c(T,I=!0){const P=I?r:"";l(s?P+" ".repeat(T):P)}function u(T=!0){const I=++a.indentLevel;T&&c(I)}function f(T=!0){const I=--a.indentLevel;T&&c(I)}function m(){c(a.indentLevel)}return{context:i,push:l,indent:u,deindent:f,newline:m,helper:T=>`_${T}`,needIndent:()=>a.needIndent}}function rm(e,t){const{helper:n}=e;e.push(`${n("linked")}(`),In(e,t.key),t.modifier?(e.push(", "),In(e,t.modifier),e.push(", _type")):e.push(", undefined, _type"),e.push(")")}function sm(e,t){const{helper:n,needIndent:r}=e;e.push(`${n("normalize")}([`),e.indent(r());const s=t.items.length;for(let o=0;o1){e.push(`${n("plural")}([`),e.indent(r());const s=t.cases.length;for(let o=0;o{const n=Pa(t.mode)?t.mode:"normal",r=Pa(t.filename)?t.filename:"message.intl";t.sourceMap;const s=t.breakLineCode!=null?t.breakLineCode:n==="arrow"?";":` +`,o=t.needIndent?t.needIndent:n!=="arrow",a=e.helpers||[],i=nm(e,{filename:r,breakLineCode:s,needIndent:o});i.push(n==="normal"?"function __msg__ (ctx) {":"(ctx) => {"),i.indent(o),a.length>0&&(i.push(`const { ${Kl(a.map(u=>`${u}: _${u}`),", ")} } = ctx`),i.newline()),i.push("return "),In(i,e),i.deindent(o),i.push("}"),delete e.helpers;const{code:l,map:c}=i.context();return{ast:e,code:l,map:c?c.toJSON():void 0}};function lm(e,t={}){const n=Bl({},t),r=!!n.jit,s=!!n.minify,o=n.optimize==null?!0:n.optimize,i=Xh(n).parse(e);return r?(o&&Zh(i),s&&pn(i),{ast:i,code:""}):(Qh(i,n),im(i,n))}/*! + * core-base v9.14.3 + * (c) 2025 kazuya kawaguchi + * Released under the MIT License. + */function cm(){typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(At().__INTLIFY_PROD_DEVTOOLS__=!1),typeof __INTLIFY_JIT_COMPILATION__!="boolean"&&(At().__INTLIFY_JIT_COMPILATION__=!1),typeof __INTLIFY_DROP_MESSAGE_COMPILER__!="boolean"&&(At().__INTLIFY_DROP_MESSAGE_COMPILER__=!1)}const Jt=[];Jt[0]={w:[0],i:[3,0],"[":[4],o:[7]};Jt[1]={w:[1],".":[2],"[":[4],o:[7]};Jt[2]={w:[2],i:[3,0],0:[3,0]};Jt[3]={i:[3,0],0:[3,0],w:[1,1],".":[2,1],"[":[4,1],o:[7,1]};Jt[4]={"'":[5,0],'"':[6,0],"[":[4,2],"]":[1,3],o:8,l:[4,0]};Jt[5]={"'":[4,0],o:8,l:[5,0]};Jt[6]={'"':[4,0],o:8,l:[6,0]};const um=/^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;function fm(e){return um.test(e)}function dm(e){const t=e.charCodeAt(0),n=e.charCodeAt(e.length-1);return t===n&&(t===34||t===39)?e.slice(1,-1):e}function hm(e){if(e==null)return"o";switch(e.charCodeAt(0)){case 91:case 93:case 46:case 34:case 39:return e;case 95:case 36:case 45:return"i";case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"w"}return"i"}function mm(e){const t=e.trim();return e.charAt(0)==="0"&&isNaN(parseInt(e))?!1:fm(t)?dm(t):"*"+t}function gm(e){const t=[];let n=-1,r=0,s=0,o,a,i,l,c,u,f;const m=[];m[0]=()=>{a===void 0?a=i:a+=i},m[1]=()=>{a!==void 0&&(t.push(a),a=void 0)},m[2]=()=>{m[0](),s++},m[3]=()=>{if(s>0)s--,r=4,m[0]();else{if(s=0,a===void 0||(a=mm(a),a===!1))return!1;m[1]()}};function _(){const S=e[n+1];if(r===5&&S==="'"||r===6&&S==='"')return n++,i="\\"+S,m[0](),!0}for(;r!==null;)if(n++,o=e[n],!(o==="\\"&&_())){if(l=hm(o),f=Jt[r],c=f[l]||f.l||8,c===8||(r=c[0],c[1]!==void 0&&(u=m[c[1]],u&&(i=o,u()===!1))))return;if(r===7)return t}}const xa=new Map;function pm(e,t){return ce(e)?e[t]:null}function _m(e,t){if(!ce(e))return null;let n=xa.get(t);if(n||(n=gm(t),n&&xa.set(t,n)),!n)return null;const r=n.length;let s=e,o=0;for(;oe,ym=e=>"",vm="text",Em=e=>e.length===0?"":Rh(e),km=Ph;function Ma(e,t){return e=Math.abs(e),t===2?e?e>1?1:0:1:e?Math.min(e,2):0}function Tm(e){const t=Ae(e.pluralIndex)?e.pluralIndex:-1;return e.named&&(Ae(e.named.count)||Ae(e.named.n))?Ae(e.named.count)?e.named.count:Ae(e.named.n)?e.named.n:t:t}function wm(e,t){t.count||(t.count=e),t.n||(t.n=e)}function Sm(e={}){const t=e.locale,n=Tm(e),r=ce(e.pluralRules)&&G(t)&&ye(e.pluralRules[t])?e.pluralRules[t]:Ma,s=ce(e.pluralRules)&&G(t)&&ye(e.pluralRules[t])?Ma:void 0,o=P=>P[r(n,P.length,s)],a=e.list||[],i=P=>a[P],l=e.named||pe();Ae(e.pluralIndex)&&wm(n,l);const c=P=>l[P];function u(P){const F=ye(e.messages)?e.messages(P):ce(e.messages)?e.messages[P]:!1;return F||(e.parent?e.parent.message(P):ym)}const f=P=>e.modifiers?e.modifiers[P]:bm,m=Z(e.processor)&&ye(e.processor.normalize)?e.processor.normalize:Em,_=Z(e.processor)&&ye(e.processor.interpolate)?e.processor.interpolate:km,S=Z(e.processor)&&G(e.processor.type)?e.processor.type:vm,I={list:i,named:c,plural:o,linked:(P,...F)=>{const[y,v]=F;let L="text",R="";F.length===1?ce(y)?(R=y.modifier||R,L=y.type||L):G(y)&&(R=y||R):F.length===2&&(G(y)&&(R=y||R),G(v)&&(L=v||L));const D=u(P)(I),$=L==="vnode"&&ke(D)&&R?D[0]:D;return R?f(R)($,L):$},message:u,type:S,interpolate:_,normalize:m,values:Ue(pe(),a,l)};return I}let nr=null;function Lm(e){nr=e}function Om(e,t,n){nr&&nr.emit("i18n:init",{timestamp:Date.now(),i18n:e,version:t,meta:n})}const Im=Am("function:translate");function Am(e){return t=>nr&&nr.emit(e,t)}const Pm=_o.__EXTEND_POINT__,nn=Qr(Pm),Rm={FALLBACK_TO_TRANSLATE:nn(),CANNOT_FORMAT_NUMBER:nn(),FALLBACK_TO_NUMBER_FORMAT:nn(),CANNOT_FORMAT_DATE:nn(),FALLBACK_TO_DATE_FORMAT:nn(),EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER:nn(),__EXTEND_POINT__:nn()},Gl=X.__EXTEND_POINT__,rn=Qr(Gl),ut={INVALID_ARGUMENT:Gl,INVALID_DATE_ARGUMENT:rn(),INVALID_ISO_DATE_ARGUMENT:rn(),NOT_SUPPORT_NON_STRING_MESSAGE:rn(),NOT_SUPPORT_LOCALE_PROMISE_VALUE:rn(),NOT_SUPPORT_LOCALE_ASYNC_FUNCTION:rn(),NOT_SUPPORT_LOCALE_TYPE:rn(),__EXTEND_POINT__:rn()};function yt(e){return Cn(e,null,void 0)}function yo(e,t){return t.locale!=null?Da(t.locale):Da(e.locale)}let ys;function Da(e){if(G(e))return e;if(ye(e)){if(e.resolvedOnce&&ys!=null)return ys;if(e.constructor.name==="Function"){const t=e();if(Ah(t))throw yt(ut.NOT_SUPPORT_LOCALE_PROMISE_VALUE);return ys=t}else throw yt(ut.NOT_SUPPORT_LOCALE_ASYNC_FUNCTION)}else throw yt(ut.NOT_SUPPORT_LOCALE_TYPE)}function Cm(e,t,n){return[...new Set([n,...ke(t)?t:ce(t)?Object.keys(t):G(t)?[t]:[n]])]}function jl(e,t,n){const r=G(n)?n:An,s=e;s.__localeChainCache||(s.__localeChainCache=new Map);let o=s.__localeChainCache.get(r);if(!o){o=[];let a=[n];for(;ke(a);)a=Fa(o,a,t);const i=ke(t)||!Z(t)?t:t.default?t.default:null;a=G(i)?[i]:i,ke(a)&&Fa(o,a,!1),s.__localeChainCache.set(r,o)}return o}function Fa(e,t,n){let r=!0;for(let s=0;s`${e.charAt(0).toLocaleUpperCase()}${e.substr(1)}`;function Dm(){return{upper:(e,t)=>t==="text"&&G(e)?e.toUpperCase():t==="vnode"&&ce(e)&&"__v_isVNode"in e?e.children.toUpperCase():e,lower:(e,t)=>t==="text"&&G(e)?e.toLowerCase():t==="vnode"&&ce(e)&&"__v_isVNode"in e?e.children.toLowerCase():e,capitalize:(e,t)=>t==="text"&&G(e)?Ua(e):t==="vnode"&&ce(e)&&"__v_isVNode"in e?Ua(e.children):e}}let ql;function Va(e){ql=e}let Yl;function Fm(e){Yl=e}let Jl;function $m(e){Jl=e}let Xl=null;const Um=e=>{Xl=e},Vm=()=>Xl;let zl=null;const Ha=e=>{zl=e},Hm=()=>zl;let Wa=0;function Wm(e={}){const t=ye(e.onWarn)?e.onWarn:Ch,n=G(e.version)?e.version:Mm,r=G(e.locale)||ye(e.locale)?e.locale:An,s=ye(r)?An:r,o=ke(e.fallbackLocale)||Z(e.fallbackLocale)||G(e.fallbackLocale)||e.fallbackLocale===!1?e.fallbackLocale:s,a=Z(e.messages)?e.messages:vs(s),i=Z(e.datetimeFormats)?e.datetimeFormats:vs(s),l=Z(e.numberFormats)?e.numberFormats:vs(s),c=Ue(pe(),e.modifiers,Dm()),u=e.pluralRules||pe(),f=ye(e.missing)?e.missing:null,m=ne(e.missingWarn)||Kt(e.missingWarn)?e.missingWarn:!0,_=ne(e.fallbackWarn)||Kt(e.fallbackWarn)?e.fallbackWarn:!0,S=!!e.fallbackFormat,T=!!e.unresolving,I=ye(e.postTranslation)?e.postTranslation:null,P=Z(e.processor)?e.processor:null,F=ne(e.warnHtmlMessage)?e.warnHtmlMessage:!0,y=!!e.escapeParameter,v=ye(e.messageCompiler)?e.messageCompiler:ql,L=ye(e.messageResolver)?e.messageResolver:Yl||pm,R=ye(e.localeFallbacker)?e.localeFallbacker:Jl||Cm,D=ce(e.fallbackContext)?e.fallbackContext:void 0,$=e,M=ce($.__datetimeFormatters)?$.__datetimeFormatters:new Map,ee=ce($.__numberFormatters)?$.__numberFormatters:new Map,me=ce($.__meta)?$.__meta:{};Wa++;const z={version:n,cid:Wa,locale:r,fallbackLocale:o,messages:a,modifiers:c,pluralRules:u,missing:f,missingWarn:m,fallbackWarn:_,fallbackFormat:S,unresolving:T,postTranslation:I,processor:P,warnHtmlMessage:F,escapeParameter:y,messageCompiler:v,messageResolver:L,localeFallbacker:R,fallbackContext:D,onWarn:t,__meta:me};return z.datetimeFormats=i,z.numberFormats=l,z.__datetimeFormatters=M,z.__numberFormatters=ee,__INTLIFY_PROD_DEVTOOLS__&&Om(z,n,me),z}const vs=e=>({[e]:pe()});function vo(e,t,n,r,s){const{missing:o,onWarn:a}=e;if(o!==null){const i=o(e,n,t,s);return G(i)?i:t}else return t}function Dn(e,t,n){const r=e;r.__localeChainCache=new Map,e.localeFallbacker(e,n,t)}function Bm(e,t){return e===t?!1:e.split("-")[0]===t.split("-")[0]}function Km(e,t){const n=t.indexOf(e);if(n===-1)return!1;for(let r=n+1;rGm(n,e)}function Gm(e,t){const n=qm(t);if(n==null)throw rr(0);if(Eo(n)===1){const o=Jm(n);return e.plural(o.reduce((a,i)=>[...a,Ba(e,i)],[]))}else return Ba(e,n)}const jm=["b","body"];function qm(e){return Xt(e,jm)}const Ym=["c","cases"];function Jm(e){return Xt(e,Ym,[])}function Ba(e,t){const n=zm(t);if(n!=null)return e.type==="text"?n:e.normalize([n]);{const r=Zm(t).reduce((s,o)=>[...s,Us(e,o)],[]);return e.normalize(r)}}const Xm=["s","static"];function zm(e){return Xt(e,Xm)}const Qm=["i","items"];function Zm(e){return Xt(e,Qm,[])}function Us(e,t){const n=Eo(t);switch(n){case 3:return pr(t,n);case 9:return pr(t,n);case 4:{const r=t;if(ct(r,"k")&&r.k)return e.interpolate(e.named(r.k));if(ct(r,"key")&&r.key)return e.interpolate(e.named(r.key));throw rr(n)}case 5:{const r=t;if(ct(r,"i")&&Ae(r.i))return e.interpolate(e.list(r.i));if(ct(r,"index")&&Ae(r.index))return e.interpolate(e.list(r.index));throw rr(n)}case 6:{const r=t,s=rg(r),o=og(r);return e.linked(Us(e,o),s?Us(e,s):void 0,e.type)}case 7:return pr(t,n);case 8:return pr(t,n);default:throw new Error(`unhandled node on format message part: ${n}`)}}const eg=["t","type"];function Eo(e){return Xt(e,eg)}const tg=["v","value"];function pr(e,t){const n=Xt(e,tg);if(n)return n;throw rr(t)}const ng=["m","modifier"];function rg(e){return Xt(e,ng)}const sg=["k","key"];function og(e){const t=Xt(e,sg);if(t)return t;throw rr(6)}function Xt(e,t,n){for(let r=0;re;let _n=pe();function Pn(e){return ce(e)&&Eo(e)===0&&(ct(e,"b")||ct(e,"body"))}function Zl(e,t={}){let n=!1;const r=t.onError||Uh;return t.onError=s=>{n=!0,r(s)},{...lm(e,t),detectError:n}}const ag=(e,t)=>{if(!G(e))throw yt(ut.NOT_SUPPORT_NON_STRING_MESSAGE);{ne(t.warnHtmlMessage)&&t.warnHtmlMessage;const r=(t.onCacheKey||Ql)(e),s=_n[r];if(s)return s;const{code:o,detectError:a}=Zl(e,t),i=new Function(`return ${o}`)();return a?i:_n[r]=i}};function ig(e,t){if(__INTLIFY_JIT_COMPILATION__&&!__INTLIFY_DROP_MESSAGE_COMPILER__&&G(e)){ne(t.warnHtmlMessage)&&t.warnHtmlMessage;const r=(t.onCacheKey||Ql)(e),s=_n[r];if(s)return s;const{ast:o,detectError:a}=Zl(e,{...t,location:!1,jit:!0}),i=Es(o);return a?i:_n[r]=i}else{const n=e.cacheKey;if(n){const r=_n[n];return r||(_n[n]=Es(e))}else return Es(e)}}const Ka=()=>"",ot=e=>ye(e);function Ga(e,...t){const{fallbackFormat:n,postTranslation:r,unresolving:s,messageCompiler:o,fallbackLocale:a,messages:i}=e,[l,c]=Vs(...t),u=ne(c.missingWarn)?c.missingWarn:e.missingWarn,f=ne(c.fallbackWarn)?c.fallbackWarn:e.fallbackWarn,m=ne(c.escapeParameter)?c.escapeParameter:e.escapeParameter,_=!!c.resolvedMessage,S=G(c.default)||ne(c.default)?ne(c.default)?o?l:()=>l:c.default:n?o?l:()=>l:"",T=n||S!=="",I=yo(e,c);m&&lg(c);let[P,F,y]=_?[l,I,i[I]||pe()]:ec(e,l,I,a,f,u),v=P,L=l;if(!_&&!(G(v)||Pn(v)||ot(v))&&T&&(v=S,L=v),!_&&(!(G(v)||Pn(v)||ot(v))||!G(F)))return s?Zr:l;let R=!1;const D=()=>{R=!0},$=ot(v)?v:tc(e,l,F,v,L,D);if(R)return v;const M=fg(e,F,y,c),ee=Sm(M),me=cg(e,$,ee),z=r?r(me,l):me;if(__INTLIFY_PROD_DEVTOOLS__){const ue={timestamp:Date.now(),key:G(l)?l:ot(v)?v.key:"",locale:F||(ot(v)?v.locale:""),format:G(v)?v:ot(v)?v.source:"",message:z};ue.meta=Ue({},e.__meta,Vm()||{}),Im(ue)}return z}function lg(e){ke(e.list)?e.list=e.list.map(t=>G(t)?Aa(t):t):ce(e.named)&&Object.keys(e.named).forEach(t=>{G(e.named[t])&&(e.named[t]=Aa(e.named[t]))})}function ec(e,t,n,r,s,o){const{messages:a,onWarn:i,messageResolver:l,localeFallbacker:c}=e,u=c(e,r,n);let f=pe(),m,_=null;const S="translate";for(let T=0;Tr;return c.locale=n,c.key=t,c}const l=a(r,ug(e,n,s,r,i,o));return l.locale=n,l.key=t,l.source=r,l}function cg(e,t,n){return t(n)}function Vs(...e){const[t,n,r]=e,s=pe();if(!G(t)&&!Ae(t)&&!ot(t)&&!Pn(t))throw yt(ut.INVALID_ARGUMENT);const o=Ae(t)?String(t):(ot(t),t);return Ae(n)?s.plural=n:G(n)?s.default=n:Z(n)&&!zr(n)?s.named=n:ke(n)&&(s.list=n),Ae(r)?s.plural=r:G(r)?s.default=r:Z(r)&&Ue(s,r),[o,s]}function ug(e,t,n,r,s,o){return{locale:t,key:n,warnHtmlMessage:s,onError:a=>{throw o&&o(a),a},onCacheKey:a=>wh(t,n,a)}}function fg(e,t,n,r){const{modifiers:s,pluralRules:o,messageResolver:a,fallbackLocale:i,fallbackWarn:l,missingWarn:c,fallbackContext:u}=e,m={locale:t,modifiers:s,pluralRules:o,messages:_=>{let S=a(n,_);if(S==null&&u){const[,,T]=ec(u,_,t,i,l,c);S=a(T,_)}if(G(S)||Pn(S)){let T=!1;const P=tc(e,_,t,S,_,()=>{T=!0});return T?Ka:P}else return ot(S)?S:Ka}};return e.processor&&(m.processor=e.processor),r.list&&(m.list=r.list),r.named&&(m.named=r.named),Ae(r.plural)&&(m.pluralIndex=r.plural),m}function ja(e,...t){const{datetimeFormats:n,unresolving:r,fallbackLocale:s,onWarn:o,localeFallbacker:a}=e,{__datetimeFormatters:i}=e,[l,c,u,f]=Hs(...t),m=ne(u.missingWarn)?u.missingWarn:e.missingWarn;ne(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn;const _=!!u.part,S=yo(e,u),T=a(e,s,S);if(!G(l)||l==="")return new Intl.DateTimeFormat(S,f).format(c);let I={},P,F=null;const y="datetime format";for(let R=0;R{nc.includes(l)?a[l]=n[l]:o[l]=n[l]}),G(r)?o.locale=r:Z(r)&&(a=r),Z(s)&&(a=s),[o.key||"",i,o,a]}function qa(e,t,n){const r=e;for(const s in n){const o=`${t}__${s}`;r.__datetimeFormatters.has(o)&&r.__datetimeFormatters.delete(o)}}function Ya(e,...t){const{numberFormats:n,unresolving:r,fallbackLocale:s,onWarn:o,localeFallbacker:a}=e,{__numberFormatters:i}=e,[l,c,u,f]=Ws(...t),m=ne(u.missingWarn)?u.missingWarn:e.missingWarn;ne(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn;const _=!!u.part,S=yo(e,u),T=a(e,s,S);if(!G(l)||l==="")return new Intl.NumberFormat(S,f).format(c);let I={},P,F=null;const y="number format";for(let R=0;R{rc.includes(l)?a[l]=n[l]:o[l]=n[l]}),G(r)?o.locale=r:Z(r)&&(a=r),Z(s)&&(a=s),[o.key||"",i,o,a]}function Ja(e,t,n){const r=e;for(const s in n){const o=`${t}__${s}`;r.__numberFormatters.has(o)&&r.__numberFormatters.delete(o)}}cm();/*! + * vue-i18n v9.14.3 + * (c) 2025 kazuya kawaguchi + * Released under the MIT License. + */const dg="9.14.3";function hg(){typeof __VUE_I18N_FULL_INSTALL__!="boolean"&&(At().__VUE_I18N_FULL_INSTALL__=!0),typeof __VUE_I18N_LEGACY_API__!="boolean"&&(At().__VUE_I18N_LEGACY_API__=!0),typeof __INTLIFY_JIT_COMPILATION__!="boolean"&&(At().__INTLIFY_JIT_COMPILATION__=!1),typeof __INTLIFY_DROP_MESSAGE_COMPILER__!="boolean"&&(At().__INTLIFY_DROP_MESSAGE_COMPILER__=!1),typeof __INTLIFY_PROD_DEVTOOLS__!="boolean"&&(At().__INTLIFY_PROD_DEVTOOLS__=!1)}const mg=Rm.__EXTEND_POINT__,St=Qr(mg);St(),St(),St(),St(),St(),St(),St(),St(),St();const sc=ut.__EXTEND_POINT__,ze=Qr(sc),Ne={UNEXPECTED_RETURN_TYPE:sc,INVALID_ARGUMENT:ze(),MUST_BE_CALL_SETUP_TOP:ze(),NOT_INSTALLED:ze(),NOT_AVAILABLE_IN_LEGACY_MODE:ze(),REQUIRED_VALUE:ze(),INVALID_VALUE:ze(),CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN:ze(),NOT_INSTALLED_WITH_PROVIDE:ze(),UNEXPECTED_ERROR:ze(),NOT_COMPATIBLE_LEGACY_VUE_I18N:ze(),BRIDGE_SUPPORT_VUE_2_ONLY:ze(),MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION:ze(),NOT_AVAILABLE_COMPOSITION_IN_LEGACY:ze(),__EXTEND_POINT__:ze()};function De(e,...t){return Cn(e,null,void 0)}const Bs=Yt("__translateVNode"),Ks=Yt("__datetimeParts"),Gs=Yt("__numberParts"),oc=Yt("__setPluralRules"),ac=Yt("__injectWithOption"),js=Yt("__dispose");function sr(e){if(!ce(e))return e;for(const t in e)if(ct(e,t))if(!t.includes("."))ce(e[t])&&sr(e[t]);else{const n=t.split("."),r=n.length-1;let s=e,o=!1;for(let a=0;a{if("locale"in i&&"resource"in i){const{locale:l,resource:c}=i;l?(a[l]=a[l]||pe(),kr(c,a[l])):kr(c,a)}else G(i)&&kr(JSON.parse(i),a)}),s==null&&o)for(const i in a)ct(a,i)&&sr(a[i]);return a}function ic(e){return e.type}function lc(e,t,n){let r=ce(t.messages)?t.messages:pe();"__i18nGlobal"in n&&(r=es(e.locale.value,{messages:r,__i18n:n.__i18nGlobal}));const s=Object.keys(r);s.length&&s.forEach(o=>{e.mergeLocaleMessage(o,r[o])});{if(ce(t.datetimeFormats)){const o=Object.keys(t.datetimeFormats);o.length&&o.forEach(a=>{e.mergeDateTimeFormat(a,t.datetimeFormats[a])})}if(ce(t.numberFormats)){const o=Object.keys(t.numberFormats);o.length&&o.forEach(a=>{e.mergeNumberFormat(a,t.numberFormats[a])})}}}function Xa(e){return Pe(lr,null,e,0)}const za="__INTLIFY_META__",Qa=()=>[],gg=()=>!1;let Za=0;function ei(e){return(t,n,r,s)=>e(n,r,Qn()||void 0,s)}const pg=()=>{const e=Qn();let t=null;return e&&(t=ic(e)[za])?{[za]:t}:null};function ko(e={},t){const{__root:n,__injectWithOption:r}=e,s=n===void 0,o=e.flatJson,a=Rr?ge:lo,i=!!e.translateExistCompatible;let l=ne(e.inheritLocale)?e.inheritLocale:!0;const c=a(n&&l?n.locale.value:G(e.locale)?e.locale:An),u=a(n&&l?n.fallbackLocale.value:G(e.fallbackLocale)||ke(e.fallbackLocale)||Z(e.fallbackLocale)||e.fallbackLocale===!1?e.fallbackLocale:c.value),f=a(es(c.value,e)),m=a(Z(e.datetimeFormats)?e.datetimeFormats:{[c.value]:{}}),_=a(Z(e.numberFormats)?e.numberFormats:{[c.value]:{}});let S=n?n.missingWarn:ne(e.missingWarn)||Kt(e.missingWarn)?e.missingWarn:!0,T=n?n.fallbackWarn:ne(e.fallbackWarn)||Kt(e.fallbackWarn)?e.fallbackWarn:!0,I=n?n.fallbackRoot:ne(e.fallbackRoot)?e.fallbackRoot:!0,P=!!e.fallbackFormat,F=ye(e.missing)?e.missing:null,y=ye(e.missing)?ei(e.missing):null,v=ye(e.postTranslation)?e.postTranslation:null,L=n?n.warnHtmlMessage:ne(e.warnHtmlMessage)?e.warnHtmlMessage:!0,R=!!e.escapeParameter;const D=n?n.modifiers:Z(e.modifiers)?e.modifiers:{};let $=e.pluralRules||n&&n.pluralRules,M;M=(()=>{s&&Ha(null);const E={version:dg,locale:c.value,fallbackLocale:u.value,messages:f.value,modifiers:D,pluralRules:$,missing:y===null?void 0:y,missingWarn:S,fallbackWarn:T,fallbackFormat:P,unresolving:!0,postTranslation:v===null?void 0:v,warnHtmlMessage:L,escapeParameter:R,messageResolver:e.messageResolver,messageCompiler:e.messageCompiler,__meta:{framework:"vue"}};E.datetimeFormats=m.value,E.numberFormats=_.value,E.__datetimeFormatters=Z(M)?M.__datetimeFormatters:void 0,E.__numberFormatters=Z(M)?M.__numberFormatters:void 0;const A=Wm(E);return s&&Ha(A),A})(),Dn(M,c.value,u.value);function me(){return[c.value,u.value,f.value,m.value,_.value]}const z=we({get:()=>c.value,set:E=>{c.value=E,M.locale=c.value}}),ue=we({get:()=>u.value,set:E=>{u.value=E,M.fallbackLocale=u.value,Dn(M,c.value,E)}}),Qe=we(()=>f.value),Je=we(()=>m.value),se=we(()=>_.value);function Q(){return ye(v)?v:null}function re(E){v=E,M.postTranslation=E}function Be(){return F}function Xe(E){E!==null&&(y=ei(E)),F=E,M.missing=y}const Se=(E,A,Y,oe,Te,Ke)=>{me();let Fe;try{__INTLIFY_PROD_DEVTOOLS__,s||(M.fallbackContext=n?Hm():void 0),Fe=E(M)}finally{__INTLIFY_PROD_DEVTOOLS__,s||(M.fallbackContext=void 0)}if(Y!=="translate exists"&&Ae(Fe)&&Fe===Zr||Y==="translate exists"&&!Fe){const[zt,ns]=A();return n&&I?oe(n):Te(zt)}else{if(Ke(Fe))return Fe;throw De(Ne.UNEXPECTED_RETURN_TYPE)}};function Le(...E){return Se(A=>Reflect.apply(Ga,null,[A,...E]),()=>Vs(...E),"translate",A=>Reflect.apply(A.t,A,[...E]),A=>A,A=>G(A))}function st(...E){const[A,Y,oe]=E;if(oe&&!ce(oe))throw De(Ne.INVALID_ARGUMENT);return Le(A,Y,Ue({resolvedMessage:!0},oe||{}))}function Ze(...E){return Se(A=>Reflect.apply(ja,null,[A,...E]),()=>Hs(...E),"datetime format",A=>Reflect.apply(A.d,A,[...E]),()=>$a,A=>G(A))}function mt(...E){return Se(A=>Reflect.apply(Ya,null,[A,...E]),()=>Ws(...E),"number format",A=>Reflect.apply(A.n,A,[...E]),()=>$a,A=>G(A))}function xe(E){return E.map(A=>G(A)||Ae(A)||ne(A)?Xa(String(A)):A)}const j={normalize:xe,interpolate:E=>E,type:"vnode"};function B(...E){return Se(A=>{let Y;const oe=A;try{oe.processor=j,Y=Reflect.apply(Ga,null,[oe,...E])}finally{oe.processor=null}return Y},()=>Vs(...E),"translate",A=>A[Bs](...E),A=>[Xa(A)],A=>ke(A))}function q(...E){return Se(A=>Reflect.apply(Ya,null,[A,...E]),()=>Ws(...E),"number format",A=>A[Gs](...E),Qa,A=>G(A)||ke(A))}function ae(...E){return Se(A=>Reflect.apply(ja,null,[A,...E]),()=>Hs(...E),"datetime format",A=>A[Ks](...E),Qa,A=>G(A)||ke(A))}function p(E){$=E,M.pluralRules=$}function b(E,A){return Se(()=>{if(!E)return!1;const Y=G(A)?A:c.value,oe=x(Y),Te=M.messageResolver(oe,E);return i?Te!=null:Pn(Te)||ot(Te)||G(Te)},()=>[E],"translate exists",Y=>Reflect.apply(Y.te,Y,[E,A]),gg,Y=>ne(Y))}function g(E){let A=null;const Y=jl(M,u.value,c.value);for(let oe=0;oe{l&&(c.value=E,M.locale=E,Dn(M,c.value,u.value))}),Vt(n.fallbackLocale,E=>{l&&(u.value=E,M.fallbackLocale=E,Dn(M,c.value,u.value))}));const U={id:Za,locale:z,fallbackLocale:ue,get inheritLocale(){return l},set inheritLocale(E){l=E,E&&n&&(c.value=n.locale.value,u.value=n.fallbackLocale.value,Dn(M,c.value,u.value))},get availableLocales(){return Object.keys(f.value).sort()},messages:Qe,get modifiers(){return D},get pluralRules(){return $||{}},get isGlobal(){return s},get missingWarn(){return S},set missingWarn(E){S=E,M.missingWarn=S},get fallbackWarn(){return T},set fallbackWarn(E){T=E,M.fallbackWarn=T},get fallbackRoot(){return I},set fallbackRoot(E){I=E},get fallbackFormat(){return P},set fallbackFormat(E){P=E,M.fallbackFormat=P},get warnHtmlMessage(){return L},set warnHtmlMessage(E){L=E,M.warnHtmlMessage=E},get escapeParameter(){return R},set escapeParameter(E){R=E,M.escapeParameter=E},t:Le,getLocaleMessage:x,setLocaleMessage:N,mergeLocaleMessage:W,getPostTranslationHandler:Q,setPostTranslationHandler:re,getMissingHandler:Be,setMissingHandler:Xe,[oc]:p};return U.datetimeFormats=Je,U.numberFormats=se,U.rt=st,U.te=b,U.tm=w,U.d=Ze,U.n=mt,U.getDateTimeFormat=V,U.setDateTimeFormat=d,U.mergeDateTimeFormat=h,U.getNumberFormat=k,U.setNumberFormat=O,U.mergeNumberFormat=K,U[ac]=r,U[Bs]=B,U[Ks]=ae,U[Gs]=q,U}function _g(e){const t=G(e.locale)?e.locale:An,n=G(e.fallbackLocale)||ke(e.fallbackLocale)||Z(e.fallbackLocale)||e.fallbackLocale===!1?e.fallbackLocale:t,r=ye(e.missing)?e.missing:void 0,s=ne(e.silentTranslationWarn)||Kt(e.silentTranslationWarn)?!e.silentTranslationWarn:!0,o=ne(e.silentFallbackWarn)||Kt(e.silentFallbackWarn)?!e.silentFallbackWarn:!0,a=ne(e.fallbackRoot)?e.fallbackRoot:!0,i=!!e.formatFallbackMessages,l=Z(e.modifiers)?e.modifiers:{},c=e.pluralizationRules,u=ye(e.postTranslation)?e.postTranslation:void 0,f=G(e.warnHtmlInMessage)?e.warnHtmlInMessage!=="off":!0,m=!!e.escapeParameterHtml,_=ne(e.sync)?e.sync:!0;let S=e.messages;if(Z(e.sharedMessages)){const R=e.sharedMessages;S=Object.keys(R).reduce(($,M)=>{const ee=$[M]||($[M]={});return Ue(ee,R[M]),$},S||{})}const{__i18n:T,__root:I,__injectWithOption:P}=e,F=e.datetimeFormats,y=e.numberFormats,v=e.flatJson,L=e.translateExistCompatible;return{locale:t,fallbackLocale:n,messages:S,flatJson:v,datetimeFormats:F,numberFormats:y,missing:r,missingWarn:s,fallbackWarn:o,fallbackRoot:a,fallbackFormat:i,modifiers:l,pluralRules:c,postTranslation:u,warnHtmlMessage:f,escapeParameter:m,messageResolver:e.messageResolver,inheritLocale:_,translateExistCompatible:L,__i18n:T,__root:I,__injectWithOption:P}}function qs(e={},t){{const n=ko(_g(e)),{__extender:r}=e,s={id:n.id,get locale(){return n.locale.value},set locale(o){n.locale.value=o},get fallbackLocale(){return n.fallbackLocale.value},set fallbackLocale(o){n.fallbackLocale.value=o},get messages(){return n.messages.value},get datetimeFormats(){return n.datetimeFormats.value},get numberFormats(){return n.numberFormats.value},get availableLocales(){return n.availableLocales},get formatter(){return{interpolate(){return[]}}},set formatter(o){},get missing(){return n.getMissingHandler()},set missing(o){n.setMissingHandler(o)},get silentTranslationWarn(){return ne(n.missingWarn)?!n.missingWarn:n.missingWarn},set silentTranslationWarn(o){n.missingWarn=ne(o)?!o:o},get silentFallbackWarn(){return ne(n.fallbackWarn)?!n.fallbackWarn:n.fallbackWarn},set silentFallbackWarn(o){n.fallbackWarn=ne(o)?!o:o},get modifiers(){return n.modifiers},get formatFallbackMessages(){return n.fallbackFormat},set formatFallbackMessages(o){n.fallbackFormat=o},get postTranslation(){return n.getPostTranslationHandler()},set postTranslation(o){n.setPostTranslationHandler(o)},get sync(){return n.inheritLocale},set sync(o){n.inheritLocale=o},get warnHtmlInMessage(){return n.warnHtmlMessage?"warn":"off"},set warnHtmlInMessage(o){n.warnHtmlMessage=o!=="off"},get escapeParameterHtml(){return n.escapeParameter},set escapeParameterHtml(o){n.escapeParameter=o},get preserveDirectiveContent(){return!0},set preserveDirectiveContent(o){},get pluralizationRules(){return n.pluralRules||{}},__composer:n,t(...o){const[a,i,l]=o,c={};let u=null,f=null;if(!G(a))throw De(Ne.INVALID_ARGUMENT);const m=a;return G(i)?c.locale=i:ke(i)?u=i:Z(i)&&(f=i),ke(l)?u=l:Z(l)&&(f=l),Reflect.apply(n.t,n,[m,u||f||{},c])},rt(...o){return Reflect.apply(n.rt,n,[...o])},tc(...o){const[a,i,l]=o,c={plural:1};let u=null,f=null;if(!G(a))throw De(Ne.INVALID_ARGUMENT);const m=a;return G(i)?c.locale=i:Ae(i)?c.plural=i:ke(i)?u=i:Z(i)&&(f=i),G(l)?c.locale=l:ke(l)?u=l:Z(l)&&(f=l),Reflect.apply(n.t,n,[m,u||f||{},c])},te(o,a){return n.te(o,a)},tm(o){return n.tm(o)},getLocaleMessage(o){return n.getLocaleMessage(o)},setLocaleMessage(o,a){n.setLocaleMessage(o,a)},mergeLocaleMessage(o,a){n.mergeLocaleMessage(o,a)},d(...o){return Reflect.apply(n.d,n,[...o])},getDateTimeFormat(o){return n.getDateTimeFormat(o)},setDateTimeFormat(o,a){n.setDateTimeFormat(o,a)},mergeDateTimeFormat(o,a){n.mergeDateTimeFormat(o,a)},n(...o){return Reflect.apply(n.n,n,[...o])},getNumberFormat(o){return n.getNumberFormat(o)},setNumberFormat(o,a){n.setNumberFormat(o,a)},mergeNumberFormat(o,a){n.mergeNumberFormat(o,a)},getChoiceIndex(o,a){return-1}};return s.__extender=r,s}}const To={tag:{type:[String,Object]},locale:{type:String},scope:{type:String,validator:e=>e==="parent"||e==="global",default:"parent"},i18n:{type:Object}};function bg({slots:e},t){return t.length===1&&t[0]==="default"?(e.default?e.default():[]).reduce((r,s)=>[...r,...s.type===Ce?s.children:[s]],[]):t.reduce((n,r)=>{const s=e[r];return s&&(n[r]=s()),n},pe())}function cc(e){return Ce}const yg=it({name:"i18n-t",props:Ue({keypath:{type:String,required:!0},plural:{type:[Number,String],validator:e=>Ae(e)||!isNaN(e)}},To),setup(e,t){const{slots:n,attrs:r}=t,s=e.i18n||fn({useScope:e.scope,__useComponent:!0});return()=>{const o=Object.keys(n).filter(f=>f!=="_"),a=pe();e.locale&&(a.locale=e.locale),e.plural!==void 0&&(a.plural=G(e.plural)?+e.plural:e.plural);const i=bg(t,o),l=s[Bs](e.keypath,i,a),c=Ue(pe(),r),u=G(e.tag)||ce(e.tag)?e.tag:cc();return Gr(u,c,l)}}}),ti=yg;function vg(e){return ke(e)&&!G(e[0])}function uc(e,t,n,r){const{slots:s,attrs:o}=t;return()=>{const a={part:!0};let i=pe();e.locale&&(a.locale=e.locale),G(e.format)?a.key=e.format:ce(e.format)&&(G(e.format.key)&&(a.key=e.format.key),i=Object.keys(e.format).reduce((m,_)=>n.includes(_)?Ue(pe(),m,{[_]:e.format[_]}):m,pe()));const l=r(e.value,a,i);let c=[a.key];ke(l)?c=l.map((m,_)=>{const S=s[m.type],T=S?S({[m.type]:m.value,index:_,parts:l}):[m.value];return vg(T)&&(T[0].key=`${m.type}-${_}`),T}):G(l)&&(c=[l]);const u=Ue(pe(),o),f=G(e.tag)||ce(e.tag)?e.tag:cc();return Gr(f,u,c)}}const Eg=it({name:"i18n-n",props:Ue({value:{type:Number,required:!0},format:{type:[String,Object]}},To),setup(e,t){const n=e.i18n||fn({useScope:e.scope,__useComponent:!0});return uc(e,t,rc,(...r)=>n[Gs](...r))}}),ni=Eg,kg=it({name:"i18n-d",props:Ue({value:{type:[Number,Date],required:!0},format:{type:[String,Object]}},To),setup(e,t){const n=e.i18n||fn({useScope:e.scope,__useComponent:!0});return uc(e,t,nc,(...r)=>n[Ks](...r))}}),ri=kg;function Tg(e,t){const n=e;if(e.mode==="composition")return n.__getInstance(t)||e.global;{const r=n.__getInstance(t);return r!=null?r.__composer:e.global.__composer}}function wg(e){const t=a=>{const{instance:i,modifiers:l,value:c}=a;if(!i||!i.$)throw De(Ne.UNEXPECTED_ERROR);const u=Tg(e,i.$),f=si(c);return[Reflect.apply(u.t,u,[...oi(f)]),u]};return{created:(a,i)=>{const[l,c]=t(i);Rr&&e.global===c&&(a.__i18nWatcher=Vt(c.locale,()=>{i.instance&&i.instance.$forceUpdate()})),a.__composer=c,a.textContent=l},unmounted:a=>{Rr&&a.__i18nWatcher&&(a.__i18nWatcher(),a.__i18nWatcher=void 0,delete a.__i18nWatcher),a.__composer&&(a.__composer=void 0,delete a.__composer)},beforeUpdate:(a,{value:i})=>{if(a.__composer){const l=a.__composer,c=si(i);a.textContent=Reflect.apply(l.t,l,[...oi(c)])}},getSSRProps:a=>{const[i]=t(a);return{textContent:i}}}}function si(e){if(G(e))return{path:e};if(Z(e)){if(!("path"in e))throw De(Ne.REQUIRED_VALUE,"path");return e}else throw De(Ne.INVALID_VALUE)}function oi(e){const{path:t,locale:n,args:r,choice:s,plural:o}=e,a={},i=r||{};return G(n)&&(a.locale=n),Ae(s)&&(a.plural=s),Ae(o)&&(a.plural=o),[t,i,a]}function Sg(e,t,...n){const r=Z(n[0])?n[0]:{},s=!!r.useI18nComponentName;(ne(r.globalInstall)?r.globalInstall:!0)&&([s?"i18n":ti.name,"I18nT"].forEach(a=>e.component(a,ti)),[ni.name,"I18nN"].forEach(a=>e.component(a,ni)),[ri.name,"I18nD"].forEach(a=>e.component(a,ri))),e.directive("t",wg(t))}function Lg(e,t,n){return{beforeCreate(){const r=Qn();if(!r)throw De(Ne.UNEXPECTED_ERROR);const s=this.$options;if(s.i18n){const o=s.i18n;if(s.__i18n&&(o.__i18n=s.__i18n),o.__root=t,this===this.$root)this.$i18n=ai(e,o);else{o.__injectWithOption=!0,o.__extender=n.__vueI18nExtend,this.$i18n=qs(o);const a=this.$i18n;a.__extender&&(a.__disposer=a.__extender(this.$i18n))}}else if(s.__i18n)if(this===this.$root)this.$i18n=ai(e,s);else{this.$i18n=qs({__i18n:s.__i18n,__injectWithOption:!0,__extender:n.__vueI18nExtend,__root:t});const o=this.$i18n;o.__extender&&(o.__disposer=o.__extender(this.$i18n))}else this.$i18n=e;s.__i18nGlobal&&lc(t,s,s),this.$t=(...o)=>this.$i18n.t(...o),this.$rt=(...o)=>this.$i18n.rt(...o),this.$tc=(...o)=>this.$i18n.tc(...o),this.$te=(o,a)=>this.$i18n.te(o,a),this.$d=(...o)=>this.$i18n.d(...o),this.$n=(...o)=>this.$i18n.n(...o),this.$tm=o=>this.$i18n.tm(o),n.__setInstance(r,this.$i18n)},mounted(){},unmounted(){const r=Qn();if(!r)throw De(Ne.UNEXPECTED_ERROR);const s=this.$i18n;delete this.$t,delete this.$rt,delete this.$tc,delete this.$te,delete this.$d,delete this.$n,delete this.$tm,s.__disposer&&(s.__disposer(),delete s.__disposer,delete s.__extender),n.__deleteInstance(r),delete this.$i18n}}}function ai(e,t){e.locale=t.locale||e.locale,e.fallbackLocale=t.fallbackLocale||e.fallbackLocale,e.missing=t.missing||e.missing,e.silentTranslationWarn=t.silentTranslationWarn||e.silentFallbackWarn,e.silentFallbackWarn=t.silentFallbackWarn||e.silentFallbackWarn,e.formatFallbackMessages=t.formatFallbackMessages||e.formatFallbackMessages,e.postTranslation=t.postTranslation||e.postTranslation,e.warnHtmlInMessage=t.warnHtmlInMessage||e.warnHtmlInMessage,e.escapeParameterHtml=t.escapeParameterHtml||e.escapeParameterHtml,e.sync=t.sync||e.sync,e.__composer[oc](t.pluralizationRules||e.pluralizationRules);const n=es(e.locale,{messages:t.messages,__i18n:t.__i18n});return Object.keys(n).forEach(r=>e.mergeLocaleMessage(r,n[r])),t.datetimeFormats&&Object.keys(t.datetimeFormats).forEach(r=>e.mergeDateTimeFormat(r,t.datetimeFormats[r])),t.numberFormats&&Object.keys(t.numberFormats).forEach(r=>e.mergeNumberFormat(r,t.numberFormats[r])),e}const Og=Yt("global-vue-i18n");function Ig(e={},t){const n=__VUE_I18N_LEGACY_API__&&ne(e.legacy)?e.legacy:__VUE_I18N_LEGACY_API__,r=ne(e.globalInjection)?e.globalInjection:!0,s=__VUE_I18N_LEGACY_API__&&n?!!e.allowComposition:!0,o=new Map,[a,i]=Ag(e,n),l=Yt("");function c(m){return o.get(m)||null}function u(m,_){o.set(m,_)}function f(m){o.delete(m)}{const m={get mode(){return __VUE_I18N_LEGACY_API__&&n?"legacy":"composition"},get allowComposition(){return s},async install(_,...S){if(_.__VUE_I18N_SYMBOL__=l,_.provide(_.__VUE_I18N_SYMBOL__,m),Z(S[0])){const P=S[0];m.__composerExtend=P.__composerExtend,m.__vueI18nExtend=P.__vueI18nExtend}let T=null;!n&&r&&(T=$g(_,m.global)),__VUE_I18N_FULL_INSTALL__&&Sg(_,m,...S),__VUE_I18N_LEGACY_API__&&n&&_.mixin(Lg(i,i.__composer,m));const I=_.unmount;_.unmount=()=>{T&&T(),m.dispose(),I()}},get global(){return i},dispose(){a.stop()},__instances:o,__getInstance:c,__setInstance:u,__deleteInstance:f};return m}}function fn(e={}){const t=Qn();if(t==null)throw De(Ne.MUST_BE_CALL_SETUP_TOP);if(!t.isCE&&t.appContext.app!=null&&!t.appContext.app.__VUE_I18N_SYMBOL__)throw De(Ne.NOT_INSTALLED);const n=Pg(t),r=Cg(n),s=ic(t),o=Rg(e,s);if(__VUE_I18N_LEGACY_API__&&n.mode==="legacy"&&!e.__useComponent){if(!n.allowComposition)throw De(Ne.NOT_AVAILABLE_IN_LEGACY_MODE);return Dg(t,o,r,e)}if(o==="global")return lc(r,e,s),r;if(o==="parent"){let l=Ng(n,t,e.__useComponent);return l==null&&(l=r),l}const a=n;let i=a.__getInstance(t);if(i==null){const l=Ue({},e);"__i18n"in s&&(l.__i18n=s.__i18n),r&&(l.__root=r),i=ko(l),a.__composerExtend&&(i[js]=a.__composerExtend(i)),Mg(a,t,i),a.__setInstance(t,i)}return i}function Ag(e,t,n){const r=Zs();{const s=__VUE_I18N_LEGACY_API__&&t?r.run(()=>qs(e)):r.run(()=>ko(e));if(s==null)throw De(Ne.UNEXPECTED_ERROR);return[r,s]}}function Pg(e){{const t=rt(e.isCE?Og:e.appContext.app.__VUE_I18N_SYMBOL__);if(!t)throw De(e.isCE?Ne.NOT_INSTALLED_WITH_PROVIDE:Ne.UNEXPECTED_ERROR);return t}}function Rg(e,t){return zr(e)?"__i18n"in t?"local":"global":e.useScope?e.useScope:"local"}function Cg(e){return e.mode==="composition"?e.global:e.global.__composer}function Ng(e,t,n=!1){let r=null;const s=t.root;let o=xg(t,n);for(;o!=null;){const a=e;if(e.mode==="composition")r=a.__getInstance(o);else if(__VUE_I18N_LEGACY_API__){const i=a.__getInstance(o);i!=null&&(r=i.__composer,n&&r&&!r[ac]&&(r=null))}if(r!=null||s===o)break;o=o.parent}return r}function xg(e,t=!1){return e==null?null:t&&e.vnode.ctx||e.parent}function Mg(e,t,n){qt(()=>{},t),Rn(()=>{const r=n;e.__deleteInstance(t);const s=r[js];s&&(s(),delete r[js])},t)}function Dg(e,t,n,r={}){const s=t==="local",o=lo(null);if(s&&e.proxy&&!(e.proxy.$options.i18n||e.proxy.$options.__i18n))throw De(Ne.MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION);const a=ne(r.inheritLocale)?r.inheritLocale:!G(r.locale),i=ge(!s||a?n.locale.value:G(r.locale)?r.locale:An),l=ge(!s||a?n.fallbackLocale.value:G(r.fallbackLocale)||ke(r.fallbackLocale)||Z(r.fallbackLocale)||r.fallbackLocale===!1?r.fallbackLocale:i.value),c=ge(es(i.value,r)),u=ge(Z(r.datetimeFormats)?r.datetimeFormats:{[i.value]:{}}),f=ge(Z(r.numberFormats)?r.numberFormats:{[i.value]:{}}),m=s?n.missingWarn:ne(r.missingWarn)||Kt(r.missingWarn)?r.missingWarn:!0,_=s?n.fallbackWarn:ne(r.fallbackWarn)||Kt(r.fallbackWarn)?r.fallbackWarn:!0,S=s?n.fallbackRoot:ne(r.fallbackRoot)?r.fallbackRoot:!0,T=!!r.fallbackFormat,I=ye(r.missing)?r.missing:null,P=ye(r.postTranslation)?r.postTranslation:null,F=s?n.warnHtmlMessage:ne(r.warnHtmlMessage)?r.warnHtmlMessage:!0,y=!!r.escapeParameter,v=s?n.modifiers:Z(r.modifiers)?r.modifiers:{},L=r.pluralRules||s&&n.pluralRules;function R(){return[i.value,l.value,c.value,u.value,f.value]}const D=we({get:()=>o.value?o.value.locale.value:i.value,set:g=>{o.value&&(o.value.locale.value=g),i.value=g}}),$=we({get:()=>o.value?o.value.fallbackLocale.value:l.value,set:g=>{o.value&&(o.value.fallbackLocale.value=g),l.value=g}}),M=we(()=>o.value?o.value.messages.value:c.value),ee=we(()=>u.value),me=we(()=>f.value);function z(){return o.value?o.value.getPostTranslationHandler():P}function ue(g){o.value&&o.value.setPostTranslationHandler(g)}function Qe(){return o.value?o.value.getMissingHandler():I}function Je(g){o.value&&o.value.setMissingHandler(g)}function se(g){return R(),g()}function Q(...g){return o.value?se(()=>Reflect.apply(o.value.t,null,[...g])):se(()=>"")}function re(...g){return o.value?Reflect.apply(o.value.rt,null,[...g]):""}function Be(...g){return o.value?se(()=>Reflect.apply(o.value.d,null,[...g])):se(()=>"")}function Xe(...g){return o.value?se(()=>Reflect.apply(o.value.n,null,[...g])):se(()=>"")}function Se(g){return o.value?o.value.tm(g):{}}function Le(g,w){return o.value?o.value.te(g,w):!1}function st(g){return o.value?o.value.getLocaleMessage(g):{}}function Ze(g,w){o.value&&(o.value.setLocaleMessage(g,w),c.value[g]=w)}function mt(g,w){o.value&&o.value.mergeLocaleMessage(g,w)}function xe(g){return o.value?o.value.getDateTimeFormat(g):{}}function C(g,w){o.value&&(o.value.setDateTimeFormat(g,w),u.value[g]=w)}function j(g,w){o.value&&o.value.mergeDateTimeFormat(g,w)}function B(g){return o.value?o.value.getNumberFormat(g):{}}function q(g,w){o.value&&(o.value.setNumberFormat(g,w),f.value[g]=w)}function ae(g,w){o.value&&o.value.mergeNumberFormat(g,w)}const p={get id(){return o.value?o.value.id:-1},locale:D,fallbackLocale:$,messages:M,datetimeFormats:ee,numberFormats:me,get inheritLocale(){return o.value?o.value.inheritLocale:a},set inheritLocale(g){o.value&&(o.value.inheritLocale=g)},get availableLocales(){return o.value?o.value.availableLocales:Object.keys(c.value)},get modifiers(){return o.value?o.value.modifiers:v},get pluralRules(){return o.value?o.value.pluralRules:L},get isGlobal(){return o.value?o.value.isGlobal:!1},get missingWarn(){return o.value?o.value.missingWarn:m},set missingWarn(g){o.value&&(o.value.missingWarn=g)},get fallbackWarn(){return o.value?o.value.fallbackWarn:_},set fallbackWarn(g){o.value&&(o.value.missingWarn=g)},get fallbackRoot(){return o.value?o.value.fallbackRoot:S},set fallbackRoot(g){o.value&&(o.value.fallbackRoot=g)},get fallbackFormat(){return o.value?o.value.fallbackFormat:T},set fallbackFormat(g){o.value&&(o.value.fallbackFormat=g)},get warnHtmlMessage(){return o.value?o.value.warnHtmlMessage:F},set warnHtmlMessage(g){o.value&&(o.value.warnHtmlMessage=g)},get escapeParameter(){return o.value?o.value.escapeParameter:y},set escapeParameter(g){o.value&&(o.value.escapeParameter=g)},t:Q,getPostTranslationHandler:z,setPostTranslationHandler:ue,getMissingHandler:Qe,setMissingHandler:Je,rt:re,d:Be,n:Xe,tm:Se,te:Le,getLocaleMessage:st,setLocaleMessage:Ze,mergeLocaleMessage:mt,getDateTimeFormat:xe,setDateTimeFormat:C,mergeDateTimeFormat:j,getNumberFormat:B,setNumberFormat:q,mergeNumberFormat:ae};function b(g){g.locale.value=i.value,g.fallbackLocale.value=l.value,Object.keys(c.value).forEach(w=>{g.mergeLocaleMessage(w,c.value[w])}),Object.keys(u.value).forEach(w=>{g.mergeDateTimeFormat(w,u.value[w])}),Object.keys(f.value).forEach(w=>{g.mergeNumberFormat(w,f.value[w])}),g.escapeParameter=y,g.fallbackFormat=T,g.fallbackRoot=S,g.fallbackWarn=_,g.missingWarn=m,g.warnHtmlMessage=F}return ji(()=>{if(e.proxy==null||e.proxy.$i18n==null)throw De(Ne.NOT_AVAILABLE_COMPOSITION_IN_LEGACY);const g=o.value=e.proxy.$i18n.__composer;t==="global"?(i.value=g.locale.value,l.value=g.fallbackLocale.value,c.value=g.messages.value,u.value=g.datetimeFormats.value,f.value=g.numberFormats.value):s&&b(g)}),p}const Fg=["locale","fallbackLocale","availableLocales"],ii=["t","rt","d","n","tm","te"];function $g(e,t){const n=Object.create(null);return Fg.forEach(s=>{const o=Object.getOwnPropertyDescriptor(t,s);if(!o)throw De(Ne.UNEXPECTED_ERROR);const a=Oe(o.value)?{get(){return o.value.value},set(i){o.value.value=i}}:{get(){return o.get&&o.get()}};Object.defineProperty(n,s,a)}),e.config.globalProperties.$i18n=n,ii.forEach(s=>{const o=Object.getOwnPropertyDescriptor(t,s);if(!o||!o.value)throw De(Ne.UNEXPECTED_ERROR);Object.defineProperty(e.config.globalProperties,`$${s}`,o)}),()=>{delete e.config.globalProperties.$i18n,ii.forEach(s=>{delete e.config.globalProperties[`$${s}`]})}}hg();__INTLIFY_JIT_COMPILATION__?Va(ig):Va(ag);Fm(_m);$m(jl);if(__INTLIFY_PROD_DEVTOOLS__){const e=At();e.__INTLIFY__=!0,Lm(e.__INTLIFY_DEVTOOLS_GLOBAL_HOOK__)}const Ug={key:0,class:"w-20 h-screen bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 flex flex-col items-center py-6 sticky top-0"},Vg={class:"flex flex-col items-center space-y-6 flex-1"},Hg=["innerHTML"],Wg={class:"text-xs mt-1 dark:text-gray-300"},Bg=["title"],Kg=["innerHTML"],Gg={class:"text-xs mt-1 dark:text-gray-300"},jg={key:0,class:"absolute left-20 bottom-0 w-48 bg-white dark:bg-gray-800 rounded-md shadow-lg py-1 z-50 border border-gray-200 dark:border-gray-700"},qg={class:"px-4 py-2 text-sm text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700"},Yg={class:"font-medium dark:text-gray-300"},Jg={key:1,class:"fixed bottom-0 left-0 right-0 bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 flex justify-around py-2 z-10"},Xg=["innerHTML"],zg={class:"text-xs mt-1 dark:text-gray-300"},Qg=it({__name:"Sidebar",props:{isMobile:{type:Boolean}},setup(e){const{t}=fn(),n=ur(),r=Xr(),s=ge(!1),o=ge(null),a=[{nameKey:"navigation.home",icon:"home",route:"/home"},{nameKey:"navigation.tags",icon:"tag",route:"/tags"},{nameKey:"navigation.folders",icon:"folder",route:"/folders"},{nameKey:"navigation.archive",icon:"archive",route:"/archive"},{nameKey:"navigation.settings",icon:"settings",route:"/settings"}],i=()=>{s.value=!s.value},l=async()=>{await n.logout(),s.value=!1,r.push("/login")},c=f=>{o.value&&!o.value.contains(f.target)&&(s.value=!1)};qt(()=>{document.addEventListener("click",c)}),Rn(()=>{document.removeEventListener("click",c)});const u={home:` + + `,tag:` + + `,folder:` + + `,archive:` + + `,settings:` + + + `,user:` + + `};return(f,m)=>{var _,S;return fe(),Ee("div",null,[f.isMobile?(fe(),Ee("nav",Jg,[(fe(),Ee(Ce,null,Jn(a,T=>Pe(le(Er),{key:T.nameKey,to:T.route,class:"text-gray-500 dark:text-gray-400 hover:text-red-500 p-2 flex flex-col items-center"},{default:En(()=>[H("div",{innerHTML:u[T.icon]},null,8,Xg),H("span",zg,Ie(le(t)(T.nameKey)),1)]),_:2},1032,["to"])),64))])):(fe(),Ee("aside",Ug,[m[1]||(m[1]=H("div",{class:"mb-8 flex flex-col items-center"},[H("div",{class:"text-red-500 font-bold text-2xl"},"栞"),H("span",{class:"text-xs mt-1 dark:text-gray-300"},"shiori")],-1)),H("nav",Vg,[(fe(),Ee(Ce,null,Jn(a,T=>Pe(le(Er),{key:T.nameKey,to:T.route,class:"text-gray-500 dark:text-gray-400 hover:text-red-500 p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors flex flex-col items-center",title:le(t)(T.nameKey)},{default:En(()=>[H("div",{innerHTML:u[T.icon]},null,8,Hg),H("span",Wg,Ie(le(t)(T.nameKey)),1)]),_:2},1032,["to","title"])),64)),m[0]||(m[0]=H("div",{class:"flex-1"},null,-1)),H("div",{class:"relative mt-auto",ref_key:"menuRef",ref:o},[H("button",{onClick:jr(i,["stop"]),class:"text-gray-500 dark:text-gray-400 hover:text-red-500 p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors flex flex-col items-center",title:le(t)("auth.user")},[H("div",{innerHTML:u.user},null,8,Kg),H("span",Gg,Ie(((_=le(n).user)==null?void 0:_.username)||le(t)("auth.user")),1)],8,Bg),s.value?(fe(),Ee("div",jg,[H("div",qg,[H("div",Yg,Ie(((S=le(n).user)==null?void 0:S.username)||le(t)("auth.user")),1)]),Pe(le(Er),{to:"/settings",class:"block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"},{default:En(()=>[ml(Ie(le(t)("navigation.settings")),1)]),_:1}),H("button",{onClick:l,class:"block w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"},Ie(le(t)("auth.logout")),1)])):Ht("",!0)],512)])]))])}}}),wo=(e,t)=>{const n=e.__vccOpts||e;for(const[r,s]of t)n[r]=s;return n},Zg=wo(Qg,[["__scopeId","data-v-4fa04b46"]]),ep={loading:"Loading...",save:"Save",cancel:"Cancel",delete:"Delete",edit:"Edit",search:"Search",add:"Add",remove:"Remove",close:"Close"},tp={login:"Login",logout:"Logout",username:"Username",password:"Password",remember_me:"Remember me",login_failed:"Login failed. Please check your credentials."},np={home:"Home",archive:"Archive",tags:"Tags",folders:"Folders",settings:"Settings",about:"About"},rp={title:"Title",add_bookmark:"Add Bookmark",edit_bookmark:"Edit Bookmark",delete_bookmark:"Delete Bookmark",url:"URL",excerpt:"Excerpt",tags:"Tags",add_tag:"Add Tag",no_bookmarks:"No bookmarks found"},sp={title:"Tags",add_tag:"Add Tag",edit_tag:"Edit Tag",delete_tag:"Delete Tag",name:"Name",no_tags:"No tags found"},op={title:"Folders",add_folder:"Add Folder",edit_folder:"Edit Folder",delete_folder:"Delete Folder",name:"Name",no_folders:"No folders found"},ap={title:"Settings",language:"Language",theme:"Theme",light:"Light",dark:"Dark",system:"System"},ip={common:ep,auth:tp,navigation:np,bookmarks:rp,tags:sp,folders:op,settings:ap},lp={loading:"Cargando...",save:"Guardar",cancel:"Cancelar",delete:"Eliminar",edit:"Editar",search:"Buscar",add:"Añadir",remove:"Eliminar",close:"Cerrar"},cp={login:"Iniciar sesión",logout:"Cerrar sesión",username:"Usuario",password:"Contraseña",remember_me:"Recordarme",login_failed:"Error al iniciar sesión. Por favor, verifica tus credenciales."},up={home:"Inicio",archive:"Archivo",tags:"Etiquetas",folders:"Carpetas",settings:"Configuración",about:"Acerca de"},fp={title:"Título",add_bookmark:"Añadir marcador",edit_bookmark:"Editar marcador",delete_bookmark:"Eliminar marcador",url:"URL",excerpt:"Extracto",tags:"Etiquetas",add_tag:"Añadir etiqueta",no_bookmarks:"No se encontraron marcadores"},dp={title:"Etiquetas",add_tag:"Añadir etiqueta",edit_tag:"Editar etiqueta",delete_tag:"Eliminar etiqueta",name:"Nombre",no_tags:"No se encontraron etiquetas"},hp={title:"Carpetas",add_folder:"Añadir carpeta",edit_folder:"Editar carpeta",delete_folder:"Eliminar carpeta",name:"Nombre",no_folders:"No se encontraron carpetas"},mp={title:"Configuración",language:"Idioma",theme:"Tema",light:"Claro",dark:"Oscuro",system:"Sistema"},gp={common:lp,auth:cp,navigation:up,bookmarks:fp,tags:dp,folders:hp,settings:mp},pp={loading:"Chargement...",save:"Enregistrer",cancel:"Annuler",delete:"Supprimer",edit:"Modifier",search:"Rechercher",add:"Ajouter",remove:"Supprimer",close:"Fermer"},_p={login:"Connexion",logout:"Déconnexion",username:"Nom d'utilisateur",password:"Mot de passe",remember_me:"Se souvenir de moi",login_failed:"Échec de la connexion. Veuillez vérifier vos identifiants."},bp={home:"Accueil",archive:"Archives",tags:"Tags",folders:"Dossiers",settings:"Paramètres",about:"À propos"},yp={title:"Titre",add_bookmark:"Ajouter un favori",edit_bookmark:"Modifier le favori",delete_bookmark:"Supprimer le favori",url:"URL",excerpt:"Extrait",tags:"Tags",add_tag:"Ajouter un tag",no_bookmarks:"Aucun favori trouvé"},vp={title:"Tags",add_tag:"Ajouter un tag",edit_tag:"Modifier le tag",delete_tag:"Supprimer le tag",name:"Nom",no_tags:"Aucun tag trouvé"},Ep={title:"Dossiers",add_folder:"Ajouter un dossier",edit_folder:"Modifier le dossier",delete_folder:"Supprimer le dossier",name:"Nom",no_folders:"Aucun dossier trouvé"},kp={title:"Paramètres",language:"Langue",theme:"Thème",light:"Clair",dark:"Sombre",system:"Système"},Tp={common:pp,auth:_p,navigation:bp,bookmarks:yp,tags:vp,folders:Ep,settings:kp},wp={loading:"Wird geladen...",save:"Speichern",cancel:"Abbrechen",delete:"Löschen",edit:"Bearbeiten",search:"Suchen",add:"Hinzufügen",remove:"Entfernen",close:"Schließen"},Sp={login:"Anmelden",logout:"Abmelden",username:"Benutzername",password:"Passwort",remember_me:"Angemeldet bleiben",login_failed:"Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihre Anmeldedaten."},Lp={home:"Startseite",archive:"Archiv",tags:"Tags",folders:"Ordner",settings:"Einstellungen",about:"Über"},Op={title:"Titel",add_bookmark:"Lesezeichen hinzufügen",edit_bookmark:"Lesezeichen bearbeiten",delete_bookmark:"Lesezeichen löschen",url:"URL",excerpt:"Auszug",tags:"Tags",add_tag:"Tag hinzufügen",no_bookmarks:"Keine Lesezeichen gefunden"},Ip={title:"Tags",add_tag:"Tag hinzufügen",edit_tag:"Tag bearbeiten",delete_tag:"Tag löschen",name:"Name",no_tags:"Keine Tags gefunden"},Ap={title:"Ordner",add_folder:"Ordner hinzufügen",edit_folder:"Ordner bearbeiten",delete_folder:"Ordner löschen",name:"Name",no_folders:"Keine Ordner gefunden"},Pp={title:"Einstellungen",language:"Sprache",theme:"Thema",light:"Hell",dark:"Dunkel",system:"System"},Rp={common:wp,auth:Sp,navigation:Lp,bookmarks:Op,tags:Ip,folders:Ap,settings:Pp},Cp={loading:"読み込み中...",save:"保存",cancel:"キャンセル",delete:"削除",edit:"編集",search:"検索",add:"追加",remove:"削除",close:"閉じる"},Np={login:"ログイン",logout:"ログアウト",username:"ユーザー名",password:"パスワード",remember_me:"ログイン状態を保持する",login_failed:"ログインに失敗しました。認証情報を確認してください。"},xp={home:"ホーム",archive:"アーカイブ",tags:"タグ",folders:"フォルダ",settings:"設定",about:"概要"},Mp={title:"タイトル",add_bookmark:"ブックマークを追加",edit_bookmark:"ブックマークを編集",delete_bookmark:"ブックマークを削除",url:"URL",excerpt:"抜粋",tags:"タグ",add_tag:"タグを追加",no_bookmarks:"ブックマークが見つかりません"},Dp={title:"タグ",add_tag:"タグを追加",edit_tag:"タグを編集",delete_tag:"タグを削除",name:"名前",no_tags:"タグが見つかりません"},Fp={title:"フォルダ",add_folder:"フォルダを追加",edit_folder:"フォルダを編集",delete_folder:"フォルダを削除",name:"名前",no_folders:"フォルダが見つかりません"},$p={title:"設定",language:"言語",theme:"テーマ",light:"ライト",dark:"ダーク",system:"システム"},Up={common:Cp,auth:Np,navigation:xp,bookmarks:Mp,tags:Dp,folders:Fp,settings:$p},Vp=()=>{const e=navigator.language.split("-")[0];return["en","es","fr","de","ja"].includes(e)?e:"en"},fc=()=>{const e=localStorage.getItem("shiori-language");return e&&["en","es","fr","de","ja"].includes(e)?e:Vp()},dc=Ig({legacy:!1,locale:fc(),fallbackLocale:"en",messages:{en:ip,es:gp,fr:Tp,de:Rp,ja:Up}}),Hp=e=>{var t;dc.global.locale.value=e,localStorage.setItem("shiori-language",e),(t=document.querySelector("html"))==null||t.setAttribute("lang",e)};var li;(li=document.querySelector("html"))==null||li.setAttribute("lang",fc());const Wp={class:"language-selector relative"},Bp=["aria-expanded"],Kp={class:"mr-1"},Gp={key:0,class:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-md shadow-lg z-10 py-1"},jp=["onClick"],qp=it({__name:"LanguageSelector",setup(e){const{t,locale:n}=fn(),r=[{code:"en",name:"English"},{code:"es",name:"Español"},{code:"fr",name:"Français"},{code:"de",name:"Deutsch"},{code:"ja",name:"日本語"}],s=ge(!1),o=ge(n.value),a=()=>{s.value=!s.value},i=()=>{s.value=!1},l=c=>{o.value=c,Hp(c),i()};return qt(()=>{document.addEventListener("click",c=>{c.target.closest(".language-selector")||i()})}),(c,u)=>{var f;return fe(),Ee("div",Wp,[H("button",{onClick:jr(a,["stop"]),class:"flex items-center px-3 py-2 text-sm rounded-md hover:bg-gray-100 dark:hover:bg-gray-700","aria-haspopup":"true","aria-expanded":s.value},[H("span",Kp,Ie((f=r.find(m=>m.code===o.value))==null?void 0:f.name),1),u[0]||(u[0]=H("svg",{class:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},[H("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 9l-7 7-7-7"})],-1))],8,Bp),s.value?(fe(),Ee("div",Gp,[(fe(),Ee(Ce,null,Jn(r,m=>H("button",{key:m.code,onClick:_=>l(m.code),class:$r(["block w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700",{"bg-gray-100 dark:bg-gray-700":o.value===m.code}])},Ie(m.name),11,jp)),64))])):Ht("",!0)])}}}),Yp={class:"bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-4 py-3 flex items-center justify-between"},Jp={class:"flex-1 mx-4"},Xp={class:"relative"},zp=["placeholder"],Qp={class:"flex items-center space-x-2"},Zp={key:0,class:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-md shadow-lg py-1 z-10 border border-gray-200 dark:border-gray-700"},e_={class:"px-4 py-2 text-sm text-gray-700 dark:text-gray-300"},t_=it({__name:"TopBar",setup(e){const{t}=fn(),n=ge(!1),r=ur(),s=Xr(),o=ge(null),a=c=>{c.stopPropagation(),n.value=!n.value},i=async()=>{await r.logout(),n.value=!1,s.push("/login")},l=c=>{o.value&&!o.value.contains(c.target)&&(n.value=!1)};return qt(()=>{document.addEventListener("click",l)}),Rn(()=>{document.removeEventListener("click",l)}),(c,u)=>{var f;return fe(),Ee("header",Yp,[u[2]||(u[2]=H("div",{class:"flex items-center"},[H("div",{class:"text-red-500 font-bold text-xl"},"栞")],-1)),H("div",Jp,[H("div",Xp,[H("input",{type:"text",placeholder:le(t)("common.search"),class:"w-full border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white rounded-md px-3 py-1 focus:outline-none focus:ring-2 focus:ring-red-500"},null,8,zp)])]),H("div",Qp,[Pe(qp),H("div",{class:"relative",ref_key:"menuRef",ref:o},[H("button",{onClick:a,class:"text-gray-500 dark:text-gray-300 p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full"},u[0]||(u[0]=[H("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-6 w-6",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor"},[H("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z"})],-1)])),n.value?(fe(),Ee("div",Zp,[H("div",e_,Ie(((f=le(r).user)==null?void 0:f.username)||"User"),1),u[1]||(u[1]=H("hr",{class:"border-gray-200 dark:border-gray-700"},null,-1)),H("a",{href:"#",onClick:jr(i,["prevent"]),class:"block px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"},Ie(le(t)("auth.logout")),1)])):Ht("",!0)],512)])])}}}),n_=wo(t_,[["__scopeId","data-v-37e7f8fb"]]),r_={class:"min-h-screen flex flex-col bg-gray-100 dark:bg-gray-900"},s_={class:"flex flex-1"},o_={class:"flex-1 p-6 pb-24 md:pb-6 overflow-auto"},a_={key:0,class:"mb-6"},i_={key:1,class:"bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 fixed bottom-0 left-0 right-0 z-10"},l_=it({__name:"AppLayout",setup(e){const t=ge(!1),n=()=>{t.value=window.innerWidth<768};return qt(()=>{n(),window.addEventListener("resize",n)}),Rn(()=>{window.removeEventListener("resize",n)}),(r,s)=>(fe(),Ee("div",r_,[t.value?(fe(),wn(n_,{key:0})):Ht("",!0),H("div",s_,[Pe(Zg,{"is-mobile":t.value},null,8,["is-mobile"]),H("main",o_,[r.$slots.header?(fe(),Ee("header",a_,[Ro(r.$slots,"header")])):Ht("",!0),Ro(r.$slots,"default")])]),t.value?(fe(),Ee("nav",i_)):Ht("",!0)]))}}),c_={class:"mt-6"},u_={class:"space-y-4"},f_={class:"flex justify-between"},d_=["href"],h_={class:"text-gray-500 dark:text-gray-400 text-sm mt-1 truncate"},m_={class:"mt-2 flex flex-wrap gap-1"},g_=it({__name:"HomeView",setup(e){const t=ge([{id:1,title:"Example Bookmark 1",url:"https://example.com/1",tags:["example","first"]},{id:2,title:"Example Bookmark 2",url:"https://example.com/2",tags:["example","second"]},{id:3,title:"Example Bookmark 3",url:"https://example.com/3",tags:["example","third"]}]);return(n,r)=>(fe(),wn(l_,null,{header:En(()=>r[0]||(r[0]=[H("div",{class:"flex justify-between items-center"},[H("h1",{class:"text-xl font-bold text-gray-800 dark:text-white"},"My Bookmarks"),H("div",{class:"flex space-x-2"},[H("button",{class:"bg-red-500 text-white px-3 py-1 rounded-md hover:bg-red-600"}," Add Bookmark "),H("div",{class:"relative"},[H("input",{type:"text",placeholder:"Search...",class:"border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white rounded-md px-3 py-1 focus:outline-none focus:ring-2 focus:ring-red-500"})])])],-1)])),default:En(()=>[H("div",c_,[H("ul",u_,[(fe(!0),Ee(Ce,null,Jn(t.value,s=>(fe(),Ee("li",{key:s.id,class:"bg-white dark:bg-gray-800 p-4 rounded-md shadow-sm hover:shadow-md transition-shadow"},[H("div",f_,[H("a",{href:s.url,target:"_blank",class:"text-blue-600 dark:text-blue-400 hover:underline font-medium"},Ie(s.title),9,d_),r[1]||(r[1]=H("div",{class:"flex space-x-2"},[H("button",{class:"text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"},[H("span",{class:"sr-only"},"Edit"),H("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[H("path",{d:"M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"})])]),H("button",{class:"text-gray-500 dark:text-gray-400 hover:text-red-500"},[H("span",{class:"sr-only"},"Delete"),H("svg",{xmlns:"http://www.w3.org/2000/svg",class:"h-5 w-5",viewBox:"0 0 20 20",fill:"currentColor"},[H("path",{"fill-rule":"evenodd",d:"M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z","clip-rule":"evenodd"})])])],-1))]),H("div",h_,Ie(s.url),1),H("div",m_,[(fe(!0),Ee(Ce,null,Jn(s.tags,o=>(fe(),Ee("span",{key:o,class:"bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs px-2 py-1 rounded-full"},Ie(o),1))),128))])]))),128))])])]),_:1}))}}),p_={class:"min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900"},__={class:"w-full max-w-md bg-white dark:bg-gray-800 shadow-lg rounded-md overflow-hidden"},b_={class:"p-8"},y_={key:0,class:"mb-4 p-3 bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 rounded-md text-sm text-center"},v_={key:1,class:"mb-4 p-3 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded-md text-sm text-center"},E_={class:"mb-6"},k_={class:"flex items-center mb-4"},T_={class:"w-28 text-right mr-4 text-gray-700 dark:text-gray-300"},w_=["placeholder"],S_={class:"flex items-center"},L_={class:"w-28 text-right mr-4 text-gray-700 dark:text-gray-300"},O_=["placeholder"],I_={class:"flex justify-center items-center mb-6"},A_={for:"remember-me",class:"ml-2 block text-sm text-gray-700 dark:text-gray-300"},P_={class:"flex justify-center"},R_=["disabled"],C_={key:0},N_={key:1},x_=it({__name:"LoginView",props:{dst:{}},setup(e){const t=e,{t:n}=fn(),r=ge(""),s=ge(""),o=ge(!1),a=ge(""),i=ge(!1),l=Xr(),c=Gd(),u=ur();qt(async()=>{if(u.token){i.value=!0;const _=await u.validateToken();i.value=!1,_&&m()}});const f=async()=>{if(!r.value||!s.value){a.value=n("auth.login_failed");return}i.value=!0,a.value="";try{await u.login(r.value,s.value,o.value)?m():a.value=u.error||n("auth.login_failed")}catch(_){console.error("Login error:",_),a.value=_.message||n("auth.login_failed")}finally{i.value=!1}},m=()=>{let _=u.getAndClearRedirectDestination();_||(_=t.dst||c.query.dst||"/home"),l.push(_)};return(_,S)=>(fe(),Ee("div",p_,[H("div",__,[S[3]||(S[3]=H("div",{class:"bg-red-500 text-white py-6 px-4 text-center"},[H("div",{class:"text-4xl font-bold mb-1"},"栞 shiori"),H("div",{class:"text-sm"},"simple bookmark manager")],-1)),H("div",b_,[a.value?(fe(),Ee("div",y_,Ie(a.value),1)):Ht("",!0),i.value&&le(u).token?(fe(),Ee("div",v_,Ie(le(n)("common.loading")),1)):Ht("",!0),H("form",{onSubmit:jr(f,["prevent"])},[H("div",E_,[H("div",k_,[H("div",T_,Ie(le(n)("auth.username"))+":",1),ls(H("input",{"onUpdate:modelValue":S[0]||(S[0]=T=>r.value=T),type:"text",class:"flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-red-500",placeholder:le(n)("auth.username"),required:""},null,8,w_),[[ta,r.value]])]),H("div",S_,[H("div",L_,Ie(le(n)("auth.password"))+":",1),ls(H("input",{"onUpdate:modelValue":S[1]||(S[1]=T=>s.value=T),type:"password",class:"flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-red-500",placeholder:le(n)("auth.password"),required:""},null,8,O_),[[ta,s.value]])])]),H("div",I_,[ls(H("input",{id:"remember-me","onUpdate:modelValue":S[2]||(S[2]=T=>o.value=T),type:"checkbox",class:"h-4 w-4 text-red-500 focus:ring-red-500 border-gray-300 dark:border-gray-600 rounded"},null,512),[[If,o.value]]),H("label",A_,Ie(le(n)("auth.remember_me")),1)]),H("div",P_,[H("button",{type:"submit",class:"w-full bg-gray-800 dark:bg-gray-700 text-white py-2 px-4 rounded-md hover:bg-gray-700 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 uppercase font-medium",disabled:i.value},[i.value?(fe(),Ee("span",C_,Ie(le(n)("common.loading")),1)):(fe(),Ee("span",N_,Ie(le(n)("auth.login")),1))],8,R_)])],32)])])]))}}),M_=wo(x_,[["__scopeId","data-v-0227928c"]]),D_=[{path:"/",redirect:"/home"},{path:"/home",name:"home",component:g_,meta:{requiresAuth:!0}},{path:"/login",name:"login",component:M_,props:e=>({dst:e.query.dst})},{path:"/tags",name:"tags",component:()=>mr(()=>import("./TagsView-C_xWTaWR.js"),[]),meta:{requiresAuth:!0}},{path:"/folders",name:"folders",component:()=>mr(()=>import("./FoldersView-MT8bk8jx.js"),__vite__mapDeps([0,1])),meta:{requiresAuth:!0}},{path:"/archive",name:"archive",component:()=>mr(()=>import("./ArchiveView-DU4ueUTk.js"),[]),meta:{requiresAuth:!0}},{path:"/settings",name:"settings",component:()=>mr(()=>import("./SettingsView-BN1uxgdS.js"),[]),meta:{requiresAuth:!0}},{path:"/:pathMatch(.*)*",redirect:"/home"}],hc=Bd({history:vd("/"),routes:D_});hc.beforeEach(async(e,t,n)=>{const r=ur();if(e.matched.some(s=>s.meta.requiresAuth))if(r.token)if(await r.validateToken())n();else{const o=e.fullPath;r.setRedirectDestination(o),n({name:"login",query:{dst:o}})}else{const s=e.fullPath;r.setRedirectDestination(s),n({name:"login",query:{dst:s}})}else n()});const ts=xf(Eh);ts.use(Ff());ts.use(hc);ts.use(dc);ts.mount("#app");export{$s as B,Dl as C,Ce as F,Ct as J,Ul as M,tn as R,bs as V,l_ as _,$_ as a,it as b,Xr as c,Bf as d,wn as e,fe as f,Ee as g,Ht as h,H as i,ls as j,jr as k,le as l,Jn as m,wo as n,qt as o,fn as p,$r as q,ge as r,Hp as s,Ie as t,ur as u,ta as v,En as w}; diff --git a/webapp/dist/assets/index-BsglCM2z.css b/webapp/dist/assets/index-BsglCM2z.css new file mode 100644 index 000000000..ca0ff1182 --- /dev/null +++ b/webapp/dist/assets/index-BsglCM2z.css @@ -0,0 +1 @@ +/*! tailwindcss v4.0.14 | MIT License | https://tailwindcss.com */@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(.971 .013 17.38);--color-red-100:oklch(.936 .032 17.717);--color-red-200:oklch(.885 .062 18.334);--color-red-300:oklch(.808 .114 19.571);--color-red-500:oklch(.637 .237 25.331);--color-red-600:oklch(.577 .245 27.325);--color-red-700:oklch(.505 .213 27.518);--color-red-900:oklch(.396 .141 25.723);--color-blue-100:oklch(.932 .032 255.585);--color-blue-300:oklch(.809 .105 251.813);--color-blue-400:oklch(.707 .165 254.624);--color-blue-500:oklch(.623 .214 259.815);--color-blue-600:oklch(.546 .245 262.881);--color-blue-700:oklch(.488 .243 264.376);--color-blue-900:oklch(.379 .146 265.522);--color-gray-50:oklch(.985 .002 247.839);--color-gray-100:oklch(.967 .003 264.542);--color-gray-200:oklch(.928 .006 264.531);--color-gray-300:oklch(.872 .01 258.338);--color-gray-400:oklch(.707 .022 261.325);--color-gray-500:oklch(.551 .027 264.364);--color-gray-600:oklch(.446 .03 256.802);--color-gray-700:oklch(.373 .034 259.733);--color-gray-800:oklch(.278 .033 256.848);--color-gray-900:oklch(.21 .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-font-feature-settings:var(--font-sans--font-feature-settings);--default-font-variation-settings:var(--font-sans--font-variation-settings);--default-mono-font-family:var(--font-mono);--default-mono-font-feature-settings:var(--font-mono--font-feature-settings);--default-mono-font-variation-settings:var(--font-mono--font-variation-settings)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1;color:color-mix(in oklab,currentColor 50%,transparent)}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.right-0{right:calc(var(--spacing)*0)}.bottom-0{bottom:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.left-20{left:calc(var(--spacing)*20)}.z-10{z-index:10}.z-50{z-index:50}.mx-4{margin-inline:calc(var(--spacing)*4)}.mx-auto{margin-inline:auto}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-auto{margin-top:auto}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-3{margin-right:calc(var(--spacing)*3)}.mr-4{margin-right:calc(var(--spacing)*4)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-2{margin-left:calc(var(--spacing)*2)}.block{display:block}.flex{display:flex}.grid{display:grid}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-12{height:calc(var(--spacing)*12)}.h-full{height:100%}.h-screen{height:100vh}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-12{width:calc(var(--spacing)*12)}.w-20{width:calc(var(--spacing)*20)}.w-28{width:calc(var(--spacing)*28)}.w-48{width:calc(var(--spacing)*48)}.w-full{width:100%}.max-w-md{max-width:var(--container-md)}.flex-1{flex:1}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing)*1)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-red-200{border-color:var(--color-red-200)}.border-red-500{border-color:var(--color-red-500)}.bg-\[var\(--background-color\)\]{background-color:var(--background-color)}.bg-black{background-color:var(--color-black)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500{background-color:var(--color-red-500)}.bg-white{background-color:var(--color-white)}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-1{padding-block:calc(var(--spacing)*1)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.pb-24{padding-bottom:calc(var(--spacing)*24)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-\[var\(--text-color\)\]{color:var(--text-color)}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentColor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:ring-gray-500:focus{--tw-ring-color:var(--color-gray-500)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width>=48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:pb-6{padding-bottom:calc(var(--spacing)*6)}}@media (width>=64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (prefers-color-scheme:dark){.dark\:border-gray-600{border-color:var(--color-gray-600)}.dark\:border-gray-700{border-color:var(--color-gray-700)}.dark\:bg-blue-900\/30{background-color:color-mix(in oklab,var(--color-blue-900)30%,transparent)}.dark\:bg-gray-700{background-color:var(--color-gray-700)}.dark\:bg-gray-800{background-color:var(--color-gray-800)}.dark\:bg-gray-900{background-color:var(--color-gray-900)}.dark\:bg-red-900\/20{background-color:color-mix(in oklab,var(--color-red-900)20%,transparent)}.dark\:bg-red-900\/30{background-color:color-mix(in oklab,var(--color-red-900)30%,transparent)}.dark\:text-blue-300{color:var(--color-blue-300)}.dark\:text-blue-400{color:var(--color-blue-400)}.dark\:text-gray-100{color:var(--color-gray-100)}.dark\:text-gray-300{color:var(--color-gray-300)}.dark\:text-gray-400{color:var(--color-gray-400)}.dark\:text-red-300{color:var(--color-red-300)}.dark\:text-white{color:var(--color-white)}@media (hover:hover){.dark\:hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.dark\:hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.dark\:hover\:text-gray-300:hover{color:var(--color-gray-300)}}}}:root{--primary-color:#f44336;--secondary-color:#fff;--text-color:#333;--background-color:#f5f5f5;--card-background:#fff;--border-color:#e0e0e0}@media (prefers-color-scheme:dark){:root{--primary-color:#f44336;--secondary-color:#1f1f1f;--text-color:#f5f5f5;--background-color:#121212;--card-background:#1e1e1e;--border-color:#333}}html,body{height:100%;margin:0;padding:0}body{color:var(--text-color);background-color:var(--background-color);min-height:100vh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif}#app{flex-direction:column;min-height:100vh;display:flex}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}html,body,#app{height:100%;min-height:100vh;margin:0;padding:0}body{font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;color:var(--text-color);background-color:var(--background-color)}.relative[data-v-4fa04b46],.relative[data-v-37e7f8fb]{position:relative}html,body,#app{height:100%;min-height:100vh} diff --git a/webapp/dist/favicon.ico b/webapp/dist/favicon.ico new file mode 100644 index 000000000..df36fcfb7 Binary files /dev/null and b/webapp/dist/favicon.ico differ diff --git a/webapp/dist/index.html b/webapp/dist/index.html new file mode 100644 index 000000000..70b6c8a73 --- /dev/null +++ b/webapp/dist/index.html @@ -0,0 +1,15 @@ + + + + + + + Shiori - Simple Bookmark Manager + + + + + +
+ + diff --git a/webapp/embed.go b/webapp/embed.go new file mode 100644 index 000000000..d57aacd76 --- /dev/null +++ b/webapp/embed.go @@ -0,0 +1,11 @@ +package webapp + +import ( + "embed" +) + +//go:embed dist/index.html +var Templates embed.FS + +//go:embed dist/assets dist/*.ico +var Assets embed.FS diff --git a/webapp/env.d.ts b/webapp/env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/webapp/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/webapp/eslint.config.ts b/webapp/eslint.config.ts new file mode 100644 index 000000000..84b2cefa0 --- /dev/null +++ b/webapp/eslint.config.ts @@ -0,0 +1,30 @@ +import pluginVue from 'eslint-plugin-vue' +import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' +import pluginVitest from '@vitest/eslint-plugin' +import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' + +// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: +// import { configureVueProject } from '@vue/eslint-config-typescript' +// configureVueProject({ scriptLangs: ['ts', 'tsx'] }) +// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup + +export default defineConfigWithVueTs( + { + name: 'app/files-to-lint', + files: ['**/*.{ts,mts,tsx,vue}'], + }, + + { + name: 'app/files-to-ignore', + ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'], + }, + + pluginVue.configs['flat/essential'], + vueTsConfigs.recommended, + + { + ...pluginVitest.configs.recommended, + files: ['src/**/__tests__/*'], + }, + skipFormatting, +) diff --git a/webapp/index.html b/webapp/index.html new file mode 100644 index 000000000..1c5814f3c --- /dev/null +++ b/webapp/index.html @@ -0,0 +1,14 @@ + + + + + + + Shiori - Simple Bookmark Manager + + + +
+ + + diff --git a/webapp/package-lock.json b/webapp/package-lock.json new file mode 100644 index 000000000..3304df39f --- /dev/null +++ b/webapp/package-lock.json @@ -0,0 +1,5371 @@ +{ + "name": "shiori", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "shiori", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.0.14", + "@vueuse/core": "^13.0.0", + "pinia": "^3.0.1", + "vue": "^3.5.13", + "vue-i18n": "^9.14.3", + "vue-router": "4" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.0", + "@types/jsdom": "^21.1.7", + "@types/node": "^22.13.9", + "@types/vue-router": "^2.0.0", + "@vitejs/plugin-vue": "^5.2.1", + "@vitest/eslint-plugin": "^1.1.36", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.5.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.7.0", + "eslint": "^9.21.0", + "eslint-plugin-vue": "~10.0.0", + "jiti": "^2.4.2", + "jsdom": "^26.0.0", + "npm-run-all2": "^7.0.2", + "prettier": "3.5.3", + "tailwindcss": "^4.0.14", + "typescript": "~5.8.0", + "vite": "^6.2.1", + "vite-plugin-vue-devtools": "^7.7.2", + "vitest": "^3.0.8", + "vue-tsc": "^2.2.8" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.2", + "@csstools/css-color-parser": "^3.0.8", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.26.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.26.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.26.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.10", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.10" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-decorators": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.26.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-syntax-typescript": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.26.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.10", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.2", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.8", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.22.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@intlify/core-base": { + "version": "9.14.3", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.3.tgz", + "integrity": "sha512-nbJ7pKTlXFnaXPblyfiH6awAx1C0PWNNuqXAR74yRwgi5A/Re/8/5fErLY0pv4R8+EHj3ZaThMHdnuC/5OBa6g==", + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "9.14.3", + "@intlify/shared": "9.14.3" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.14.3", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.3.tgz", + "integrity": "sha512-ANwC226BQdd+MpJ36rOYkChSESfPwu3Ss2Faw0RHTOknYLoHTX6V6e/JjIKVDMbzs0/H/df/rO6yU0SPiWHqNg==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "9.14.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "9.14.3", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.3.tgz", + "integrity": "sha512-hJXz9LA5VG7qNE00t50bdzDv8Z4q9fpcL81wj4y4duKavrv0KM8YNLTwXNEFINHjTsfrG9TXvPuEjVaAvZ7yWg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.35.0", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.0.14", + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "tailwindcss": "4.0.14" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.0.14", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.0.14", + "@tailwindcss/oxide-darwin-arm64": "4.0.14", + "@tailwindcss/oxide-darwin-x64": "4.0.14", + "@tailwindcss/oxide-freebsd-x64": "4.0.14", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.14", + "@tailwindcss/oxide-linux-arm64-gnu": "4.0.14", + "@tailwindcss/oxide-linux-arm64-musl": "4.0.14", + "@tailwindcss/oxide-linux-x64-gnu": "4.0.14", + "@tailwindcss/oxide-linux-x64-musl": "4.0.14", + "@tailwindcss/oxide-win32-arm64-msvc": "4.0.14", + "@tailwindcss/oxide-win32-x64-msvc": "4.0.14" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.0.14", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.0.14", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.0.14", + "@tailwindcss/oxide": "4.0.14", + "lightningcss": "1.29.2", + "tailwindcss": "4.0.14" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@tsconfig/node22": { + "version": "22.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/@types/jsdom": { + "version": "21.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.13.10", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vue-router": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "vue-router": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/type-utils": "8.26.1", + "@typescript-eslint/utils": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.26.1", + "@typescript-eslint/utils": "8.26.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/eslint-plugin": { + "version": "1.1.37", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/utils": "^8.24.0", + "eslint": ">= 8.57.0", + "typescript": ">= 5.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.8", + "@vitest/utils": "3.0.8", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.8", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.8", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.8", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.12" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.12", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.12", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", + "@vue/babel-helper-vue-transform-on": "1.4.0", + "@vue/babel-plugin-resolve-type": "1.4.0", + "@vue/shared": "^3.5.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/parser": "^7.26.9", + "@vue/compiler-sfc": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.2", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.2" + } + }, + "node_modules/@vue/devtools-core": { + "version": "7.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.2", + "@vue/devtools-shared": "^7.7.2", + "mitt": "^3.0.1", + "nanoid": "^5.0.9", + "pathe": "^2.0.2", + "vite-hot-client": "^0.2.4" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-core/node_modules/nanoid": { + "version": "5.1.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.2", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.2", + "birpc": "^0.2.19", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.1" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.2", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "10.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2" + }, + "peerDependencies": { + "eslint": ">= 8.21.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.26.0", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.26.0", + "vue-eslint-parser": "^10.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0", + "eslint-plugin-vue": "^9.28.0 || ^10.0.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "2.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.11", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vue/language-core/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "vue": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "license": "MIT" + }, + "node_modules/@vue/test-utils": { + "version": "2.4.6", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, + "node_modules/@vue/tsconfig": { + "version": "0.7.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core": { + "version": "13.0.0", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "13.0.0", + "@vueuse/shared": "13.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "13.0.0", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "13.0.0", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alien-signals": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/birpc": { + "version": "0.2.19", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001704", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.1.1", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/editorconfig/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.118", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "0.1.5", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.1", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.22.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/config-helpers": "^0.1.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.0", + "@eslint/js": "9.22.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "10.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "vue-eslint-parser": "^10.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "9.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expect-type": { + "version": "1.2.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "8.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "26.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.1", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.9", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "7.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0", + "npm": ">= 9" + } + }, + "node_modules/npm-run-all2/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-run-all2/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.18", + "dev": true, + "license": "MIT" + }, + "node_modules/open": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pinia": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.35.0", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.35.0", + "@rollup/rollup-android-arm64": "4.35.0", + "@rollup/rollup-darwin-arm64": "4.35.0", + "@rollup/rollup-darwin-x64": "4.35.0", + "@rollup/rollup-freebsd-arm64": "4.35.0", + "@rollup/rollup-freebsd-x64": "4.35.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", + "@rollup/rollup-linux-arm-musleabihf": "4.35.0", + "@rollup/rollup-linux-arm64-gnu": "4.35.0", + "@rollup/rollup-linux-arm64-musl": "4.35.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", + "@rollup/rollup-linux-riscv64-gnu": "4.35.0", + "@rollup/rollup-linux-s390x-gnu": "4.35.0", + "@rollup/rollup-linux-x64-gnu": "4.35.0", + "@rollup/rollup-linux-x64-musl": "4.35.0", + "@rollup/rollup-win32-arm64-msvc": "4.35.0", + "@rollup/rollup-win32-ia32-msvc": "4.35.0", + "@rollup/rollup-win32-x64-msvc": "4.35.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.8.1", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tailwindcss": { + "version": "4.0.14", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.84", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.84" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.84", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ts-api-utils": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.26.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.26.1", + "@typescript-eslint/parser": "8.26.1", + "@typescript-eslint/utils": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.2.2", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-hot-client": { + "version": "0.2.4", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0" + } + }, + "node_modules/vite-node": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "0.8.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^0.7.10", + "@rollup/pluginutils": "^5.1.3", + "debug": "^4.3.7", + "error-stack-parser-es": "^0.1.5", + "fs-extra": "^11.2.0", + "open": "^10.1.0", + "perfect-debounce": "^1.0.0", + "picocolors": "^1.1.1", + "sirv": "^3.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "7.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^7.7.2", + "@vue/devtools-kit": "^7.7.2", + "@vue/devtools-shared": "^7.7.2", + "execa": "^9.5.1", + "sirv": "^3.0.0", + "vite-plugin-inspect": "0.8.9", + "vite-plugin-vue-inspector": "^5.3.1" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0" + } + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0" + } + }, + "node_modules/vitest": { + "version": "3.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.8", + "@vitest/mocker": "3.0.8", + "@vitest/pretty-format": "^3.0.8", + "@vitest/runner": "3.0.8", + "@vitest/snapshot": "3.0.8", + "@vitest/spy": "3.0.8", + "@vitest/utils": "3.0.8", + "chai": "^5.2.0", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.8", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.8", + "@vitest/ui": "3.0.8", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.13", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "2.2.8", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-eslint-parser": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.6.0", + "lodash": "^4.17.21", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/vue-i18n": { + "version": "9.14.3", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.3.tgz", + "integrity": "sha512-C+E0KE8ihKjdYCQx8oUkXX+8tBItrYNMnGJuzEPevBARQFUN2tKez6ZVOvBrWH0+KT5wEk3vOWjNk7ygb2u9ig==", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "9.14.3", + "@intlify/shared": "9.14.3", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-i18n/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/vue-router": { + "version": "4.5.0", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "license": "MIT" + }, + "node_modules/vue-tsc": { + "version": "2.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~2.4.11", + "@vue/language-core": "2.2.8" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/webapp/package.json b/webapp/package.json new file mode 100644 index 000000000..16eb0e4c0 --- /dev/null +++ b/webapp/package.json @@ -0,0 +1,48 @@ +{ + "name": "shiori", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "test:unit": "vitest", + "build-only": "vite build", + "type-check": "vue-tsc --build", + "lint": "eslint . --fix", + "format": "prettier --write src/" + }, + "dependencies": { + "@tailwindcss/vite": "^4.0.14", + "@vueuse/core": "^13.0.0", + "pinia": "^3.0.1", + "vue": "^3.5.13", + "vue-i18n": "^9.14.3", + "vue-router": "4" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.0", + "@types/jsdom": "^21.1.7", + "@types/node": "^22.13.9", + "@types/vue-router": "^2.0.0", + "@vitejs/plugin-vue": "^5.2.1", + "@vitest/eslint-plugin": "^1.1.36", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.5.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.7.0", + "eslint": "^9.21.0", + "eslint-plugin-vue": "~10.0.0", + "jiti": "^2.4.2", + "jsdom": "^26.0.0", + "npm-run-all2": "^7.0.2", + "prettier": "3.5.3", + "tailwindcss": "^4.0.14", + "typescript": "~5.8.0", + "vite": "^6.2.1", + "vite-plugin-vue-devtools": "^7.7.2", + "vitest": "^3.0.8", + "vue-tsc": "^2.2.8" + } +} diff --git a/webapp/public/favicon.ico b/webapp/public/favicon.ico new file mode 100644 index 000000000..df36fcfb7 Binary files /dev/null and b/webapp/public/favicon.ico differ diff --git a/webapp/src/App.vue b/webapp/src/App.vue new file mode 100644 index 000000000..1a5e08626 --- /dev/null +++ b/webapp/src/App.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/webapp/src/assets/main.css b/webapp/src/assets/main.css new file mode 100644 index 000000000..b63a1e9b3 --- /dev/null +++ b/webapp/src/assets/main.css @@ -0,0 +1,43 @@ +@import "tailwindcss"; + +/* Custom styles */ +:root { + --primary-color: #f44336; + --secondary-color: #ffffff; + --text-color: #333333; + --background-color: #f5f5f5; + --card-background: #ffffff; + --border-color: #e0e0e0; +} + +/* Dark mode variables */ +@media (prefers-color-scheme: dark) { + :root { + --primary-color: #f44336; + --secondary-color: #1f1f1f; + --text-color: #f5f5f5; + --background-color: #121212; + --card-background: #1e1e1e; + --border-color: #333333; + } +} + +/* Base styles for full height layout */ +html, body { + height: 100%; + margin: 0; + padding: 0; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + color: var(--text-color); + background-color: var(--background-color); + min-height: 100vh; +} + +#app { + display: flex; + flex-direction: column; + min-height: 100vh; +} diff --git a/webapp/src/client/.openapi-generator-ignore b/webapp/src/client/.openapi-generator-ignore new file mode 100644 index 000000000..7484ee590 --- /dev/null +++ b/webapp/src/client/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/webapp/src/client/.openapi-generator/FILES b/webapp/src/client/.openapi-generator/FILES new file mode 100644 index 000000000..63019d02a --- /dev/null +++ b/webapp/src/client/.openapi-generator/FILES @@ -0,0 +1,23 @@ +.openapi-generator-ignore +apis/AccountsApi.ts +apis/AuthApi.ts +apis/SystemApi.ts +apis/TagsApi.ts +apis/index.ts +index.ts +models/ApiV1BookmarkTagPayload.ts +models/ApiV1BulkUpdateBookmarkTagsPayload.ts +models/ApiV1InfoResponse.ts +models/ApiV1InfoResponseVersion.ts +models/ApiV1LoginRequestPayload.ts +models/ApiV1LoginResponseMessage.ts +models/ApiV1ReadableResponseMessage.ts +models/ApiV1UpdateAccountPayload.ts +models/ApiV1UpdateCachePayload.ts +models/ModelAccount.ts +models/ModelAccountDTO.ts +models/ModelBookmarkDTO.ts +models/ModelTagDTO.ts +models/ModelUserConfig.ts +models/index.ts +runtime.ts diff --git a/webapp/src/client/.openapi-generator/VERSION b/webapp/src/client/.openapi-generator/VERSION new file mode 100644 index 000000000..5f84a81db --- /dev/null +++ b/webapp/src/client/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.12.0 diff --git a/webapp/src/client/apis/AccountsApi.ts b/webapp/src/client/apis/AccountsApi.ts new file mode 100644 index 000000000..77fec6a0e --- /dev/null +++ b/webapp/src/client/apis/AccountsApi.ts @@ -0,0 +1,171 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + ApiV1UpdateAccountPayload, + ModelAccountDTO, +} from '../models/index'; +import { + ApiV1UpdateAccountPayloadFromJSON, + ApiV1UpdateAccountPayloadToJSON, + ModelAccountDTOFromJSON, + ModelAccountDTOToJSON, +} from '../models/index'; + +export interface ApiV1AccountsIdDeleteRequest { + id: number; +} + +export interface ApiV1AccountsIdPatchRequest { + id: number; + account: ApiV1UpdateAccountPayload; +} + +/** + * + */ +export class AccountsApi extends runtime.BaseAPI { + + /** + * List accounts + * List accounts + */ + async apiV1AccountsGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/accounts`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(ModelAccountDTOFromJSON)); + } + + /** + * List accounts + * List accounts + */ + async apiV1AccountsGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.apiV1AccountsGetRaw(initOverrides); + return await response.value(); + } + + /** + * Delete an account + */ + async apiV1AccountsIdDeleteRaw(requestParameters: ApiV1AccountsIdDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1AccountsIdDelete().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/accounts/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Delete an account + */ + async apiV1AccountsIdDelete(requestParameters: ApiV1AccountsIdDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.apiV1AccountsIdDeleteRaw(requestParameters, initOverrides); + } + + /** + * Update an account + */ + async apiV1AccountsIdPatchRaw(requestParameters: ApiV1AccountsIdPatchRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1AccountsIdPatch().' + ); + } + + if (requestParameters['account'] == null) { + throw new runtime.RequiredError( + 'account', + 'Required parameter "account" was null or undefined when calling apiV1AccountsIdPatch().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/accounts/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: ApiV1UpdateAccountPayloadToJSON(requestParameters['account']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelAccountDTOFromJSON(jsonValue)); + } + + /** + * Update an account + */ + async apiV1AccountsIdPatch(requestParameters: ApiV1AccountsIdPatchRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1AccountsIdPatchRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Create an account + */ + async apiV1AccountsPostRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/accounts`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelAccountDTOFromJSON(jsonValue)); + } + + /** + * Create an account + */ + async apiV1AccountsPost(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1AccountsPostRaw(initOverrides); + return await response.value(); + } + +} diff --git a/webapp/src/client/apis/AuthApi.ts b/webapp/src/client/apis/AuthApi.ts new file mode 100644 index 000000000..ac956b6b1 --- /dev/null +++ b/webapp/src/client/apis/AuthApi.ts @@ -0,0 +1,437 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + ApiV1BookmarkTagPayload, + ApiV1BulkUpdateBookmarkTagsPayload, + ApiV1LoginRequestPayload, + ApiV1LoginResponseMessage, + ApiV1ReadableResponseMessage, + ApiV1UpdateAccountPayload, + ApiV1UpdateCachePayload, + ModelAccount, + ModelBookmarkDTO, + ModelTagDTO, +} from '../models/index'; +import { + ApiV1BookmarkTagPayloadFromJSON, + ApiV1BookmarkTagPayloadToJSON, + ApiV1BulkUpdateBookmarkTagsPayloadFromJSON, + ApiV1BulkUpdateBookmarkTagsPayloadToJSON, + ApiV1LoginRequestPayloadFromJSON, + ApiV1LoginRequestPayloadToJSON, + ApiV1LoginResponseMessageFromJSON, + ApiV1LoginResponseMessageToJSON, + ApiV1ReadableResponseMessageFromJSON, + ApiV1ReadableResponseMessageToJSON, + ApiV1UpdateAccountPayloadFromJSON, + ApiV1UpdateAccountPayloadToJSON, + ApiV1UpdateCachePayloadFromJSON, + ApiV1UpdateCachePayloadToJSON, + ModelAccountFromJSON, + ModelAccountToJSON, + ModelBookmarkDTOFromJSON, + ModelBookmarkDTOToJSON, + ModelTagDTOFromJSON, + ModelTagDTOToJSON, +} from '../models/index'; + +export interface ApiV1AuthAccountPatchRequest { + payload?: ApiV1UpdateAccountPayload; +} + +export interface ApiV1AuthLoginPostRequest { + payload?: ApiV1LoginRequestPayload; +} + +export interface ApiV1BookmarksBulkTagsPutRequest { + payload: ApiV1BulkUpdateBookmarkTagsPayload; +} + +export interface ApiV1BookmarksCachePutRequest { + payload: ApiV1UpdateCachePayload; +} + +export interface ApiV1BookmarksIdTagsDeleteRequest { + id: number; + payload: ApiV1BookmarkTagPayload; +} + +export interface ApiV1BookmarksIdTagsGetRequest { + id: number; +} + +export interface ApiV1BookmarksIdTagsPostRequest { + id: number; + payload: ApiV1BookmarkTagPayload; +} + +/** + * + */ +export class AuthApi extends runtime.BaseAPI { + + /** + * Update account information + */ + async apiV1AuthAccountPatchRaw(requestParameters: ApiV1AuthAccountPatchRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/auth/account`, + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: ApiV1UpdateAccountPayloadToJSON(requestParameters['payload']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelAccountFromJSON(jsonValue)); + } + + /** + * Update account information + */ + async apiV1AuthAccountPatch(requestParameters: ApiV1AuthAccountPatchRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1AuthAccountPatchRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Login to an account using username and password + */ + async apiV1AuthLoginPostRaw(requestParameters: ApiV1AuthLoginPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/auth/login`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ApiV1LoginRequestPayloadToJSON(requestParameters['payload']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ApiV1LoginResponseMessageFromJSON(jsonValue)); + } + + /** + * Login to an account using username and password + */ + async apiV1AuthLoginPost(requestParameters: ApiV1AuthLoginPostRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1AuthLoginPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Logout from the current session + */ + async apiV1AuthLogoutPostRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/auth/logout`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Logout from the current session + */ + async apiV1AuthLogoutPost(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.apiV1AuthLogoutPostRaw(initOverrides); + } + + /** + * Get information for the current logged in user + */ + async apiV1AuthMeGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/auth/me`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelAccountFromJSON(jsonValue)); + } + + /** + * Get information for the current logged in user + */ + async apiV1AuthMeGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1AuthMeGetRaw(initOverrides); + return await response.value(); + } + + /** + * Refresh a token for an account + */ + async apiV1AuthRefreshPostRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/auth/refresh`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ApiV1LoginResponseMessageFromJSON(jsonValue)); + } + + /** + * Refresh a token for an account + */ + async apiV1AuthRefreshPost(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1AuthRefreshPostRaw(initOverrides); + return await response.value(); + } + + /** + * Bulk update tags for multiple bookmarks. + */ + async apiV1BookmarksBulkTagsPutRaw(requestParameters: ApiV1BookmarksBulkTagsPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + if (requestParameters['payload'] == null) { + throw new runtime.RequiredError( + 'payload', + 'Required parameter "payload" was null or undefined when calling apiV1BookmarksBulkTagsPut().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/bookmarks/bulk/tags`, + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: ApiV1BulkUpdateBookmarkTagsPayloadToJSON(requestParameters['payload']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(ModelBookmarkDTOFromJSON)); + } + + /** + * Bulk update tags for multiple bookmarks. + */ + async apiV1BookmarksBulkTagsPut(requestParameters: ApiV1BookmarksBulkTagsPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.apiV1BookmarksBulkTagsPutRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update Cache and Ebook on server. + */ + async apiV1BookmarksCachePutRaw(requestParameters: ApiV1BookmarksCachePutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['payload'] == null) { + throw new runtime.RequiredError( + 'payload', + 'Required parameter "payload" was null or undefined when calling apiV1BookmarksCachePut().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/bookmarks/cache`, + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: ApiV1UpdateCachePayloadToJSON(requestParameters['payload']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelBookmarkDTOFromJSON(jsonValue)); + } + + /** + * Update Cache and Ebook on server. + */ + async apiV1BookmarksCachePut(requestParameters: ApiV1BookmarksCachePutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1BookmarksCachePutRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Get readable version of bookmark. + */ + async apiV1BookmarksIdReadableGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/bookmarks/id/readable`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ApiV1ReadableResponseMessageFromJSON(jsonValue)); + } + + /** + * Get readable version of bookmark. + */ + async apiV1BookmarksIdReadableGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1BookmarksIdReadableGetRaw(initOverrides); + return await response.value(); + } + + /** + * Remove a tag from a bookmark. + */ + async apiV1BookmarksIdTagsDeleteRaw(requestParameters: ApiV1BookmarksIdTagsDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1BookmarksIdTagsDelete().' + ); + } + + if (requestParameters['payload'] == null) { + throw new runtime.RequiredError( + 'payload', + 'Required parameter "payload" was null or undefined when calling apiV1BookmarksIdTagsDelete().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/bookmarks/{id}/tags`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + body: ApiV1BookmarkTagPayloadToJSON(requestParameters['payload']), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Remove a tag from a bookmark. + */ + async apiV1BookmarksIdTagsDelete(requestParameters: ApiV1BookmarksIdTagsDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.apiV1BookmarksIdTagsDeleteRaw(requestParameters, initOverrides); + } + + /** + * Get tags for a bookmark. + */ + async apiV1BookmarksIdTagsGetRaw(requestParameters: ApiV1BookmarksIdTagsGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1BookmarksIdTagsGet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/bookmarks/{id}/tags`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(ModelTagDTOFromJSON)); + } + + /** + * Get tags for a bookmark. + */ + async apiV1BookmarksIdTagsGet(requestParameters: ApiV1BookmarksIdTagsGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.apiV1BookmarksIdTagsGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Add a tag to a bookmark. + */ + async apiV1BookmarksIdTagsPostRaw(requestParameters: ApiV1BookmarksIdTagsPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1BookmarksIdTagsPost().' + ); + } + + if (requestParameters['payload'] == null) { + throw new runtime.RequiredError( + 'payload', + 'Required parameter "payload" was null or undefined when calling apiV1BookmarksIdTagsPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/bookmarks/{id}/tags`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ApiV1BookmarkTagPayloadToJSON(requestParameters['payload']), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Add a tag to a bookmark. + */ + async apiV1BookmarksIdTagsPost(requestParameters: ApiV1BookmarksIdTagsPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.apiV1BookmarksIdTagsPostRaw(requestParameters, initOverrides); + } + +} diff --git a/webapp/src/client/apis/SystemApi.ts b/webapp/src/client/apis/SystemApi.ts new file mode 100644 index 000000000..a727b8d94 --- /dev/null +++ b/webapp/src/client/apis/SystemApi.ts @@ -0,0 +1,58 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + ApiV1InfoResponse, +} from '../models/index'; +import { + ApiV1InfoResponseFromJSON, + ApiV1InfoResponseToJSON, +} from '../models/index'; + +/** + * + */ +export class SystemApi extends runtime.BaseAPI { + + /** + * Get general system information like Shiori version, database, and OS + * Get general system information + */ + async apiV1SystemInfoGetRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/system/info`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ApiV1InfoResponseFromJSON(jsonValue)); + } + + /** + * Get general system information like Shiori version, database, and OS + * Get general system information + */ + async apiV1SystemInfoGet(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1SystemInfoGetRaw(initOverrides); + return await response.value(); + } + +} diff --git a/webapp/src/client/apis/TagsApi.ts b/webapp/src/client/apis/TagsApi.ts new file mode 100644 index 000000000..b1ecf541e --- /dev/null +++ b/webapp/src/client/apis/TagsApi.ts @@ -0,0 +1,245 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + ModelTagDTO, +} from '../models/index'; +import { + ModelTagDTOFromJSON, + ModelTagDTOToJSON, +} from '../models/index'; + +export interface ApiV1TagsGetRequest { + withBookmarkCount?: boolean; + bookmarkId?: number; + search?: string; +} + +export interface ApiV1TagsIdDeleteRequest { + id: number; +} + +export interface ApiV1TagsIdGetRequest { + id: number; +} + +export interface ApiV1TagsIdPutRequest { + id: number; + tag: ModelTagDTO; +} + +export interface ApiV1TagsPostRequest { + tag: ModelTagDTO; +} + +/** + * + */ +export class TagsApi extends runtime.BaseAPI { + + /** + * List all tags + * List tags + */ + async apiV1TagsGetRaw(requestParameters: ApiV1TagsGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + const queryParameters: any = {}; + + if (requestParameters['withBookmarkCount'] != null) { + queryParameters['with_bookmark_count'] = requestParameters['withBookmarkCount']; + } + + if (requestParameters['bookmarkId'] != null) { + queryParameters['bookmark_id'] = requestParameters['bookmarkId']; + } + + if (requestParameters['search'] != null) { + queryParameters['search'] = requestParameters['search']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/tags`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(ModelTagDTOFromJSON)); + } + + /** + * List all tags + * List tags + */ + async apiV1TagsGet(requestParameters: ApiV1TagsGetRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.apiV1TagsGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Delete a tag + * Delete tag + */ + async apiV1TagsIdDeleteRaw(requestParameters: ApiV1TagsIdDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1TagsIdDelete().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/tags/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * Delete a tag + * Delete tag + */ + async apiV1TagsIdDelete(requestParameters: ApiV1TagsIdDeleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.apiV1TagsIdDeleteRaw(requestParameters, initOverrides); + } + + /** + * Get a tag by ID + * Get tag + */ + async apiV1TagsIdGetRaw(requestParameters: ApiV1TagsIdGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1TagsIdGet().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/api/v1/tags/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelTagDTOFromJSON(jsonValue)); + } + + /** + * Get a tag by ID + * Get tag + */ + async apiV1TagsIdGet(requestParameters: ApiV1TagsIdGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1TagsIdGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update an existing tag + * Update tag + */ + async apiV1TagsIdPutRaw(requestParameters: ApiV1TagsIdPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['id'] == null) { + throw new runtime.RequiredError( + 'id', + 'Required parameter "id" was null or undefined when calling apiV1TagsIdPut().' + ); + } + + if (requestParameters['tag'] == null) { + throw new runtime.RequiredError( + 'tag', + 'Required parameter "tag" was null or undefined when calling apiV1TagsIdPut().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/tags/{id}`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))), + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: ModelTagDTOToJSON(requestParameters['tag']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelTagDTOFromJSON(jsonValue)); + } + + /** + * Update an existing tag + * Update tag + */ + async apiV1TagsIdPut(requestParameters: ApiV1TagsIdPutRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1TagsIdPutRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Create a new tag + * Create tag + */ + async apiV1TagsPostRaw(requestParameters: ApiV1TagsPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['tag'] == null) { + throw new runtime.RequiredError( + 'tag', + 'Required parameter "tag" was null or undefined when calling apiV1TagsPost().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + const response = await this.request({ + path: `/api/v1/tags`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ModelTagDTOToJSON(requestParameters['tag']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelTagDTOFromJSON(jsonValue)); + } + + /** + * Create a new tag + * Create tag + */ + async apiV1TagsPost(requestParameters: ApiV1TagsPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.apiV1TagsPostRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/webapp/src/client/apis/index.ts b/webapp/src/client/apis/index.ts new file mode 100644 index 000000000..9692ddb30 --- /dev/null +++ b/webapp/src/client/apis/index.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './AccountsApi'; +export * from './AuthApi'; +export * from './SystemApi'; +export * from './TagsApi'; diff --git a/webapp/src/client/index.ts b/webapp/src/client/index.ts new file mode 100644 index 000000000..bebe8bbbe --- /dev/null +++ b/webapp/src/client/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis/index'; +export * from './models/index'; diff --git a/webapp/src/client/models/ApiV1BookmarkTagPayload.ts b/webapp/src/client/models/ApiV1BookmarkTagPayload.ts new file mode 100644 index 000000000..95454e9b4 --- /dev/null +++ b/webapp/src/client/models/ApiV1BookmarkTagPayload.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ApiV1BookmarkTagPayload + */ +export interface ApiV1BookmarkTagPayload { + /** + * + * @type {number} + * @memberof ApiV1BookmarkTagPayload + */ + tagId: number; +} + +/** + * Check if a given object implements the ApiV1BookmarkTagPayload interface. + */ +export function instanceOfApiV1BookmarkTagPayload(value: object): value is ApiV1BookmarkTagPayload { + if (!('tagId' in value) || value['tagId'] === undefined) return false; + return true; +} + +export function ApiV1BookmarkTagPayloadFromJSON(json: any): ApiV1BookmarkTagPayload { + return ApiV1BookmarkTagPayloadFromJSONTyped(json, false); +} + +export function ApiV1BookmarkTagPayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1BookmarkTagPayload { + if (json == null) { + return json; + } + return { + + 'tagId': json['tag_id'], + }; +} + +export function ApiV1BookmarkTagPayloadToJSON(json: any): ApiV1BookmarkTagPayload { + return ApiV1BookmarkTagPayloadToJSONTyped(json, false); +} + +export function ApiV1BookmarkTagPayloadToJSONTyped(value?: ApiV1BookmarkTagPayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'tag_id': value['tagId'], + }; +} + diff --git a/webapp/src/client/models/ApiV1BulkUpdateBookmarkTagsPayload.ts b/webapp/src/client/models/ApiV1BulkUpdateBookmarkTagsPayload.ts new file mode 100644 index 000000000..e182ca473 --- /dev/null +++ b/webapp/src/client/models/ApiV1BulkUpdateBookmarkTagsPayload.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ApiV1BulkUpdateBookmarkTagsPayload + */ +export interface ApiV1BulkUpdateBookmarkTagsPayload { + /** + * + * @type {Array} + * @memberof ApiV1BulkUpdateBookmarkTagsPayload + */ + bookmarkIds: Array; + /** + * + * @type {Array} + * @memberof ApiV1BulkUpdateBookmarkTagsPayload + */ + tagIds: Array; +} + +/** + * Check if a given object implements the ApiV1BulkUpdateBookmarkTagsPayload interface. + */ +export function instanceOfApiV1BulkUpdateBookmarkTagsPayload(value: object): value is ApiV1BulkUpdateBookmarkTagsPayload { + if (!('bookmarkIds' in value) || value['bookmarkIds'] === undefined) return false; + if (!('tagIds' in value) || value['tagIds'] === undefined) return false; + return true; +} + +export function ApiV1BulkUpdateBookmarkTagsPayloadFromJSON(json: any): ApiV1BulkUpdateBookmarkTagsPayload { + return ApiV1BulkUpdateBookmarkTagsPayloadFromJSONTyped(json, false); +} + +export function ApiV1BulkUpdateBookmarkTagsPayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1BulkUpdateBookmarkTagsPayload { + if (json == null) { + return json; + } + return { + + 'bookmarkIds': json['bookmark_ids'], + 'tagIds': json['tag_ids'], + }; +} + +export function ApiV1BulkUpdateBookmarkTagsPayloadToJSON(json: any): ApiV1BulkUpdateBookmarkTagsPayload { + return ApiV1BulkUpdateBookmarkTagsPayloadToJSONTyped(json, false); +} + +export function ApiV1BulkUpdateBookmarkTagsPayloadToJSONTyped(value?: ApiV1BulkUpdateBookmarkTagsPayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'bookmark_ids': value['bookmarkIds'], + 'tag_ids': value['tagIds'], + }; +} + diff --git a/webapp/src/client/models/ApiV1InfoResponse.ts b/webapp/src/client/models/ApiV1InfoResponse.ts new file mode 100644 index 000000000..73a1e54a2 --- /dev/null +++ b/webapp/src/client/models/ApiV1InfoResponse.ts @@ -0,0 +1,89 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ApiV1InfoResponseVersion } from './ApiV1InfoResponseVersion'; +import { + ApiV1InfoResponseVersionFromJSON, + ApiV1InfoResponseVersionFromJSONTyped, + ApiV1InfoResponseVersionToJSON, + ApiV1InfoResponseVersionToJSONTyped, +} from './ApiV1InfoResponseVersion'; + +/** + * + * @export + * @interface ApiV1InfoResponse + */ +export interface ApiV1InfoResponse { + /** + * + * @type {string} + * @memberof ApiV1InfoResponse + */ + database?: string; + /** + * + * @type {string} + * @memberof ApiV1InfoResponse + */ + os?: string; + /** + * + * @type {ApiV1InfoResponseVersion} + * @memberof ApiV1InfoResponse + */ + version?: ApiV1InfoResponseVersion; +} + +/** + * Check if a given object implements the ApiV1InfoResponse interface. + */ +export function instanceOfApiV1InfoResponse(value: object): value is ApiV1InfoResponse { + return true; +} + +export function ApiV1InfoResponseFromJSON(json: any): ApiV1InfoResponse { + return ApiV1InfoResponseFromJSONTyped(json, false); +} + +export function ApiV1InfoResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1InfoResponse { + if (json == null) { + return json; + } + return { + + 'database': json['database'] == null ? undefined : json['database'], + 'os': json['os'] == null ? undefined : json['os'], + 'version': json['version'] == null ? undefined : ApiV1InfoResponseVersionFromJSON(json['version']), + }; +} + +export function ApiV1InfoResponseToJSON(json: any): ApiV1InfoResponse { + return ApiV1InfoResponseToJSONTyped(json, false); +} + +export function ApiV1InfoResponseToJSONTyped(value?: ApiV1InfoResponse | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'database': value['database'], + 'os': value['os'], + 'version': ApiV1InfoResponseVersionToJSON(value['version']), + }; +} + diff --git a/webapp/src/client/models/ApiV1InfoResponseVersion.ts b/webapp/src/client/models/ApiV1InfoResponseVersion.ts new file mode 100644 index 000000000..b91ab7633 --- /dev/null +++ b/webapp/src/client/models/ApiV1InfoResponseVersion.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ApiV1InfoResponseVersion + */ +export interface ApiV1InfoResponseVersion { + /** + * + * @type {string} + * @memberof ApiV1InfoResponseVersion + */ + commit?: string; + /** + * + * @type {string} + * @memberof ApiV1InfoResponseVersion + */ + date?: string; + /** + * + * @type {string} + * @memberof ApiV1InfoResponseVersion + */ + tag?: string; +} + +/** + * Check if a given object implements the ApiV1InfoResponseVersion interface. + */ +export function instanceOfApiV1InfoResponseVersion(value: object): value is ApiV1InfoResponseVersion { + return true; +} + +export function ApiV1InfoResponseVersionFromJSON(json: any): ApiV1InfoResponseVersion { + return ApiV1InfoResponseVersionFromJSONTyped(json, false); +} + +export function ApiV1InfoResponseVersionFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1InfoResponseVersion { + if (json == null) { + return json; + } + return { + + 'commit': json['commit'] == null ? undefined : json['commit'], + 'date': json['date'] == null ? undefined : json['date'], + 'tag': json['tag'] == null ? undefined : json['tag'], + }; +} + +export function ApiV1InfoResponseVersionToJSON(json: any): ApiV1InfoResponseVersion { + return ApiV1InfoResponseVersionToJSONTyped(json, false); +} + +export function ApiV1InfoResponseVersionToJSONTyped(value?: ApiV1InfoResponseVersion | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'commit': value['commit'], + 'date': value['date'], + 'tag': value['tag'], + }; +} + diff --git a/webapp/src/client/models/ApiV1LoginRequestPayload.ts b/webapp/src/client/models/ApiV1LoginRequestPayload.ts new file mode 100644 index 000000000..91cc321c8 --- /dev/null +++ b/webapp/src/client/models/ApiV1LoginRequestPayload.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ApiV1LoginRequestPayload + */ +export interface ApiV1LoginRequestPayload { + /** + * + * @type {string} + * @memberof ApiV1LoginRequestPayload + */ + password?: string; + /** + * + * @type {boolean} + * @memberof ApiV1LoginRequestPayload + */ + rememberMe?: boolean; + /** + * + * @type {string} + * @memberof ApiV1LoginRequestPayload + */ + username?: string; +} + +/** + * Check if a given object implements the ApiV1LoginRequestPayload interface. + */ +export function instanceOfApiV1LoginRequestPayload(value: object): value is ApiV1LoginRequestPayload { + return true; +} + +export function ApiV1LoginRequestPayloadFromJSON(json: any): ApiV1LoginRequestPayload { + return ApiV1LoginRequestPayloadFromJSONTyped(json, false); +} + +export function ApiV1LoginRequestPayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1LoginRequestPayload { + if (json == null) { + return json; + } + return { + + 'password': json['password'] == null ? undefined : json['password'], + 'rememberMe': json['remember_me'] == null ? undefined : json['remember_me'], + 'username': json['username'] == null ? undefined : json['username'], + }; +} + +export function ApiV1LoginRequestPayloadToJSON(json: any): ApiV1LoginRequestPayload { + return ApiV1LoginRequestPayloadToJSONTyped(json, false); +} + +export function ApiV1LoginRequestPayloadToJSONTyped(value?: ApiV1LoginRequestPayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'password': value['password'], + 'remember_me': value['rememberMe'], + 'username': value['username'], + }; +} + diff --git a/webapp/src/client/models/ApiV1LoginResponseMessage.ts b/webapp/src/client/models/ApiV1LoginResponseMessage.ts new file mode 100644 index 000000000..075df7bad --- /dev/null +++ b/webapp/src/client/models/ApiV1LoginResponseMessage.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ApiV1LoginResponseMessage + */ +export interface ApiV1LoginResponseMessage { + /** + * + * @type {number} + * @memberof ApiV1LoginResponseMessage + */ + expires?: number; + /** + * + * @type {string} + * @memberof ApiV1LoginResponseMessage + */ + token?: string; +} + +/** + * Check if a given object implements the ApiV1LoginResponseMessage interface. + */ +export function instanceOfApiV1LoginResponseMessage(value: object): value is ApiV1LoginResponseMessage { + return true; +} + +export function ApiV1LoginResponseMessageFromJSON(json: any): ApiV1LoginResponseMessage { + return ApiV1LoginResponseMessageFromJSONTyped(json, false); +} + +export function ApiV1LoginResponseMessageFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1LoginResponseMessage { + if (json == null) { + return json; + } + return { + + 'expires': json['expires'] == null ? undefined : json['expires'], + 'token': json['token'] == null ? undefined : json['token'], + }; +} + +export function ApiV1LoginResponseMessageToJSON(json: any): ApiV1LoginResponseMessage { + return ApiV1LoginResponseMessageToJSONTyped(json, false); +} + +export function ApiV1LoginResponseMessageToJSONTyped(value?: ApiV1LoginResponseMessage | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'expires': value['expires'], + 'token': value['token'], + }; +} + diff --git a/webapp/src/client/models/ApiV1ReadableResponseMessage.ts b/webapp/src/client/models/ApiV1ReadableResponseMessage.ts new file mode 100644 index 000000000..84f0a0e49 --- /dev/null +++ b/webapp/src/client/models/ApiV1ReadableResponseMessage.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ApiV1ReadableResponseMessage + */ +export interface ApiV1ReadableResponseMessage { + /** + * + * @type {string} + * @memberof ApiV1ReadableResponseMessage + */ + content?: string; + /** + * + * @type {string} + * @memberof ApiV1ReadableResponseMessage + */ + html?: string; +} + +/** + * Check if a given object implements the ApiV1ReadableResponseMessage interface. + */ +export function instanceOfApiV1ReadableResponseMessage(value: object): value is ApiV1ReadableResponseMessage { + return true; +} + +export function ApiV1ReadableResponseMessageFromJSON(json: any): ApiV1ReadableResponseMessage { + return ApiV1ReadableResponseMessageFromJSONTyped(json, false); +} + +export function ApiV1ReadableResponseMessageFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1ReadableResponseMessage { + if (json == null) { + return json; + } + return { + + 'content': json['content'] == null ? undefined : json['content'], + 'html': json['html'] == null ? undefined : json['html'], + }; +} + +export function ApiV1ReadableResponseMessageToJSON(json: any): ApiV1ReadableResponseMessage { + return ApiV1ReadableResponseMessageToJSONTyped(json, false); +} + +export function ApiV1ReadableResponseMessageToJSONTyped(value?: ApiV1ReadableResponseMessage | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'content': value['content'], + 'html': value['html'], + }; +} + diff --git a/webapp/src/client/models/ApiV1UpdateAccountPayload.ts b/webapp/src/client/models/ApiV1UpdateAccountPayload.ts new file mode 100644 index 000000000..525243ed7 --- /dev/null +++ b/webapp/src/client/models/ApiV1UpdateAccountPayload.ts @@ -0,0 +1,105 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelUserConfig } from './ModelUserConfig'; +import { + ModelUserConfigFromJSON, + ModelUserConfigFromJSONTyped, + ModelUserConfigToJSON, + ModelUserConfigToJSONTyped, +} from './ModelUserConfig'; + +/** + * + * @export + * @interface ApiV1UpdateAccountPayload + */ +export interface ApiV1UpdateAccountPayload { + /** + * + * @type {ModelUserConfig} + * @memberof ApiV1UpdateAccountPayload + */ + config?: ModelUserConfig; + /** + * + * @type {string} + * @memberof ApiV1UpdateAccountPayload + */ + newPassword?: string; + /** + * + * @type {string} + * @memberof ApiV1UpdateAccountPayload + */ + oldPassword?: string; + /** + * + * @type {boolean} + * @memberof ApiV1UpdateAccountPayload + */ + owner?: boolean; + /** + * + * @type {string} + * @memberof ApiV1UpdateAccountPayload + */ + username?: string; +} + +/** + * Check if a given object implements the ApiV1UpdateAccountPayload interface. + */ +export function instanceOfApiV1UpdateAccountPayload(value: object): value is ApiV1UpdateAccountPayload { + return true; +} + +export function ApiV1UpdateAccountPayloadFromJSON(json: any): ApiV1UpdateAccountPayload { + return ApiV1UpdateAccountPayloadFromJSONTyped(json, false); +} + +export function ApiV1UpdateAccountPayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1UpdateAccountPayload { + if (json == null) { + return json; + } + return { + + 'config': json['config'] == null ? undefined : ModelUserConfigFromJSON(json['config']), + 'newPassword': json['new_password'] == null ? undefined : json['new_password'], + 'oldPassword': json['old_password'] == null ? undefined : json['old_password'], + 'owner': json['owner'] == null ? undefined : json['owner'], + 'username': json['username'] == null ? undefined : json['username'], + }; +} + +export function ApiV1UpdateAccountPayloadToJSON(json: any): ApiV1UpdateAccountPayload { + return ApiV1UpdateAccountPayloadToJSONTyped(json, false); +} + +export function ApiV1UpdateAccountPayloadToJSONTyped(value?: ApiV1UpdateAccountPayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'config': ModelUserConfigToJSON(value['config']), + 'new_password': value['newPassword'], + 'old_password': value['oldPassword'], + 'owner': value['owner'], + 'username': value['username'], + }; +} + diff --git a/webapp/src/client/models/ApiV1UpdateCachePayload.ts b/webapp/src/client/models/ApiV1UpdateCachePayload.ts new file mode 100644 index 000000000..4caf71ffe --- /dev/null +++ b/webapp/src/client/models/ApiV1UpdateCachePayload.ts @@ -0,0 +1,98 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ApiV1UpdateCachePayload + */ +export interface ApiV1UpdateCachePayload { + /** + * + * @type {boolean} + * @memberof ApiV1UpdateCachePayload + */ + createArchive?: boolean; + /** + * + * @type {boolean} + * @memberof ApiV1UpdateCachePayload + */ + createEbook?: boolean; + /** + * + * @type {Array} + * @memberof ApiV1UpdateCachePayload + */ + ids: Array; + /** + * + * @type {boolean} + * @memberof ApiV1UpdateCachePayload + */ + keepMetadata?: boolean; + /** + * + * @type {boolean} + * @memberof ApiV1UpdateCachePayload + */ + skipExist?: boolean; +} + +/** + * Check if a given object implements the ApiV1UpdateCachePayload interface. + */ +export function instanceOfApiV1UpdateCachePayload(value: object): value is ApiV1UpdateCachePayload { + if (!('ids' in value) || value['ids'] === undefined) return false; + return true; +} + +export function ApiV1UpdateCachePayloadFromJSON(json: any): ApiV1UpdateCachePayload { + return ApiV1UpdateCachePayloadFromJSONTyped(json, false); +} + +export function ApiV1UpdateCachePayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): ApiV1UpdateCachePayload { + if (json == null) { + return json; + } + return { + + 'createArchive': json['create_archive'] == null ? undefined : json['create_archive'], + 'createEbook': json['create_ebook'] == null ? undefined : json['create_ebook'], + 'ids': json['ids'], + 'keepMetadata': json['keep_metadata'] == null ? undefined : json['keep_metadata'], + 'skipExist': json['skip_exist'] == null ? undefined : json['skip_exist'], + }; +} + +export function ApiV1UpdateCachePayloadToJSON(json: any): ApiV1UpdateCachePayload { + return ApiV1UpdateCachePayloadToJSONTyped(json, false); +} + +export function ApiV1UpdateCachePayloadToJSONTyped(value?: ApiV1UpdateCachePayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'create_archive': value['createArchive'], + 'create_ebook': value['createEbook'], + 'ids': value['ids'], + 'keep_metadata': value['keepMetadata'], + 'skip_exist': value['skipExist'], + }; +} + diff --git a/webapp/src/client/models/ModelAccount.ts b/webapp/src/client/models/ModelAccount.ts new file mode 100644 index 000000000..90d704b83 --- /dev/null +++ b/webapp/src/client/models/ModelAccount.ts @@ -0,0 +1,105 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelUserConfig } from './ModelUserConfig'; +import { + ModelUserConfigFromJSON, + ModelUserConfigFromJSONTyped, + ModelUserConfigToJSON, + ModelUserConfigToJSONTyped, +} from './ModelUserConfig'; + +/** + * + * @export + * @interface ModelAccount + */ +export interface ModelAccount { + /** + * + * @type {ModelUserConfig} + * @memberof ModelAccount + */ + config?: ModelUserConfig; + /** + * + * @type {number} + * @memberof ModelAccount + */ + id?: number; + /** + * + * @type {boolean} + * @memberof ModelAccount + */ + owner?: boolean; + /** + * + * @type {string} + * @memberof ModelAccount + */ + password?: string; + /** + * + * @type {string} + * @memberof ModelAccount + */ + username?: string; +} + +/** + * Check if a given object implements the ModelAccount interface. + */ +export function instanceOfModelAccount(value: object): value is ModelAccount { + return true; +} + +export function ModelAccountFromJSON(json: any): ModelAccount { + return ModelAccountFromJSONTyped(json, false); +} + +export function ModelAccountFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelAccount { + if (json == null) { + return json; + } + return { + + 'config': json['config'] == null ? undefined : ModelUserConfigFromJSON(json['config']), + 'id': json['id'] == null ? undefined : json['id'], + 'owner': json['owner'] == null ? undefined : json['owner'], + 'password': json['password'] == null ? undefined : json['password'], + 'username': json['username'] == null ? undefined : json['username'], + }; +} + +export function ModelAccountToJSON(json: any): ModelAccount { + return ModelAccountToJSONTyped(json, false); +} + +export function ModelAccountToJSONTyped(value?: ModelAccount | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'config': ModelUserConfigToJSON(value['config']), + 'id': value['id'], + 'owner': value['owner'], + 'password': value['password'], + 'username': value['username'], + }; +} + diff --git a/webapp/src/client/models/ModelAccountDTO.ts b/webapp/src/client/models/ModelAccountDTO.ts new file mode 100644 index 000000000..d630c7b1e --- /dev/null +++ b/webapp/src/client/models/ModelAccountDTO.ts @@ -0,0 +1,105 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelUserConfig } from './ModelUserConfig'; +import { + ModelUserConfigFromJSON, + ModelUserConfigFromJSONTyped, + ModelUserConfigToJSON, + ModelUserConfigToJSONTyped, +} from './ModelUserConfig'; + +/** + * + * @export + * @interface ModelAccountDTO + */ +export interface ModelAccountDTO { + /** + * + * @type {ModelUserConfig} + * @memberof ModelAccountDTO + */ + config?: ModelUserConfig; + /** + * + * @type {number} + * @memberof ModelAccountDTO + */ + id?: number; + /** + * + * @type {boolean} + * @memberof ModelAccountDTO + */ + owner?: boolean; + /** + * Used only to store, not to retrieve + * @type {string} + * @memberof ModelAccountDTO + */ + passowrd?: string; + /** + * + * @type {string} + * @memberof ModelAccountDTO + */ + username?: string; +} + +/** + * Check if a given object implements the ModelAccountDTO interface. + */ +export function instanceOfModelAccountDTO(value: object): value is ModelAccountDTO { + return true; +} + +export function ModelAccountDTOFromJSON(json: any): ModelAccountDTO { + return ModelAccountDTOFromJSONTyped(json, false); +} + +export function ModelAccountDTOFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelAccountDTO { + if (json == null) { + return json; + } + return { + + 'config': json['config'] == null ? undefined : ModelUserConfigFromJSON(json['config']), + 'id': json['id'] == null ? undefined : json['id'], + 'owner': json['owner'] == null ? undefined : json['owner'], + 'passowrd': json['passowrd'] == null ? undefined : json['passowrd'], + 'username': json['username'] == null ? undefined : json['username'], + }; +} + +export function ModelAccountDTOToJSON(json: any): ModelAccountDTO { + return ModelAccountDTOToJSONTyped(json, false); +} + +export function ModelAccountDTOToJSONTyped(value?: ModelAccountDTO | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'config': ModelUserConfigToJSON(value['config']), + 'id': value['id'], + 'owner': value['owner'], + 'passowrd': value['passowrd'], + 'username': value['username'], + }; +} + diff --git a/webapp/src/client/models/ModelBookmarkDTO.ts b/webapp/src/client/models/ModelBookmarkDTO.ts new file mode 100644 index 000000000..1cc9ff0a6 --- /dev/null +++ b/webapp/src/client/models/ModelBookmarkDTO.ts @@ -0,0 +1,193 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ModelTagDTO } from './ModelTagDTO'; +import { + ModelTagDTOFromJSON, + ModelTagDTOFromJSONTyped, + ModelTagDTOToJSON, + ModelTagDTOToJSONTyped, +} from './ModelTagDTO'; + +/** + * + * @export + * @interface ModelBookmarkDTO + */ +export interface ModelBookmarkDTO { + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + author?: string; + /** + * TODO: migrate outside the DTO + * @type {boolean} + * @memberof ModelBookmarkDTO + */ + createArchive?: boolean; + /** + * TODO: migrate outside the DTO + * @type {boolean} + * @memberof ModelBookmarkDTO + */ + createEbook?: boolean; + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + createdAt?: string; + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + excerpt?: string; + /** + * + * @type {boolean} + * @memberof ModelBookmarkDTO + */ + hasArchive?: boolean; + /** + * + * @type {boolean} + * @memberof ModelBookmarkDTO + */ + hasContent?: boolean; + /** + * + * @type {boolean} + * @memberof ModelBookmarkDTO + */ + hasEbook?: boolean; + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + html?: string; + /** + * + * @type {number} + * @memberof ModelBookmarkDTO + */ + id?: number; + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + imageURL?: string; + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + modifiedAt?: string; + /** + * + * @type {number} + * @memberof ModelBookmarkDTO + */ + _public?: number; + /** + * + * @type {Array} + * @memberof ModelBookmarkDTO + */ + tags?: Array; + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + title?: string; + /** + * + * @type {string} + * @memberof ModelBookmarkDTO + */ + url?: string; +} + +/** + * Check if a given object implements the ModelBookmarkDTO interface. + */ +export function instanceOfModelBookmarkDTO(value: object): value is ModelBookmarkDTO { + return true; +} + +export function ModelBookmarkDTOFromJSON(json: any): ModelBookmarkDTO { + return ModelBookmarkDTOFromJSONTyped(json, false); +} + +export function ModelBookmarkDTOFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelBookmarkDTO { + if (json == null) { + return json; + } + return { + + 'author': json['author'] == null ? undefined : json['author'], + 'createArchive': json['create_archive'] == null ? undefined : json['create_archive'], + 'createEbook': json['create_ebook'] == null ? undefined : json['create_ebook'], + 'createdAt': json['createdAt'] == null ? undefined : json['createdAt'], + 'excerpt': json['excerpt'] == null ? undefined : json['excerpt'], + 'hasArchive': json['hasArchive'] == null ? undefined : json['hasArchive'], + 'hasContent': json['hasContent'] == null ? undefined : json['hasContent'], + 'hasEbook': json['hasEbook'] == null ? undefined : json['hasEbook'], + 'html': json['html'] == null ? undefined : json['html'], + 'id': json['id'] == null ? undefined : json['id'], + 'imageURL': json['imageURL'] == null ? undefined : json['imageURL'], + 'modifiedAt': json['modifiedAt'] == null ? undefined : json['modifiedAt'], + '_public': json['public'] == null ? undefined : json['public'], + 'tags': json['tags'] == null ? undefined : ((json['tags'] as Array).map(ModelTagDTOFromJSON)), + 'title': json['title'] == null ? undefined : json['title'], + 'url': json['url'] == null ? undefined : json['url'], + }; +} + +export function ModelBookmarkDTOToJSON(json: any): ModelBookmarkDTO { + return ModelBookmarkDTOToJSONTyped(json, false); +} + +export function ModelBookmarkDTOToJSONTyped(value?: ModelBookmarkDTO | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'author': value['author'], + 'create_archive': value['createArchive'], + 'create_ebook': value['createEbook'], + 'createdAt': value['createdAt'], + 'excerpt': value['excerpt'], + 'hasArchive': value['hasArchive'], + 'hasContent': value['hasContent'], + 'hasEbook': value['hasEbook'], + 'html': value['html'], + 'id': value['id'], + 'imageURL': value['imageURL'], + 'modifiedAt': value['modifiedAt'], + 'public': value['_public'], + 'tags': value['tags'] == null ? undefined : ((value['tags'] as Array).map(ModelTagDTOToJSON)), + 'title': value['title'], + 'url': value['url'], + }; +} + diff --git a/webapp/src/client/models/ModelTagDTO.ts b/webapp/src/client/models/ModelTagDTO.ts new file mode 100644 index 000000000..efb088d1e --- /dev/null +++ b/webapp/src/client/models/ModelTagDTO.ts @@ -0,0 +1,89 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelTagDTO + */ +export interface ModelTagDTO { + /** + * Number of bookmarks with this tag + * @type {number} + * @memberof ModelTagDTO + */ + bookmarkCount?: number; + /** + * Marks when a tag is deleted from a bookmark + * @type {boolean} + * @memberof ModelTagDTO + */ + deleted?: boolean; + /** + * + * @type {number} + * @memberof ModelTagDTO + */ + id?: number; + /** + * + * @type {string} + * @memberof ModelTagDTO + */ + name?: string; +} + +/** + * Check if a given object implements the ModelTagDTO interface. + */ +export function instanceOfModelTagDTO(value: object): value is ModelTagDTO { + return true; +} + +export function ModelTagDTOFromJSON(json: any): ModelTagDTO { + return ModelTagDTOFromJSONTyped(json, false); +} + +export function ModelTagDTOFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelTagDTO { + if (json == null) { + return json; + } + return { + + 'bookmarkCount': json['bookmark_count'] == null ? undefined : json['bookmark_count'], + 'deleted': json['deleted'] == null ? undefined : json['deleted'], + 'id': json['id'] == null ? undefined : json['id'], + 'name': json['name'] == null ? undefined : json['name'], + }; +} + +export function ModelTagDTOToJSON(json: any): ModelTagDTO { + return ModelTagDTOToJSONTyped(json, false); +} + +export function ModelTagDTOToJSONTyped(value?: ModelTagDTO | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'bookmark_count': value['bookmarkCount'], + 'deleted': value['deleted'], + 'id': value['id'], + 'name': value['name'], + }; +} + diff --git a/webapp/src/client/models/ModelUserConfig.ts b/webapp/src/client/models/ModelUserConfig.ts new file mode 100644 index 000000000..d8c0c9da5 --- /dev/null +++ b/webapp/src/client/models/ModelUserConfig.ts @@ -0,0 +1,129 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelUserConfig + */ +export interface ModelUserConfig { + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + createEbook?: boolean; + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + hideExcerpt?: boolean; + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + hideThumbnail?: boolean; + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + keepMetadata?: boolean; + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + listMode?: boolean; + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + makePublic?: boolean; + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + showId?: boolean; + /** + * + * @type {string} + * @memberof ModelUserConfig + */ + theme?: string; + /** + * + * @type {boolean} + * @memberof ModelUserConfig + */ + useArchive?: boolean; +} + +/** + * Check if a given object implements the ModelUserConfig interface. + */ +export function instanceOfModelUserConfig(value: object): value is ModelUserConfig { + return true; +} + +export function ModelUserConfigFromJSON(json: any): ModelUserConfig { + return ModelUserConfigFromJSONTyped(json, false); +} + +export function ModelUserConfigFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelUserConfig { + if (json == null) { + return json; + } + return { + + 'createEbook': json['createEbook'] == null ? undefined : json['createEbook'], + 'hideExcerpt': json['hideExcerpt'] == null ? undefined : json['hideExcerpt'], + 'hideThumbnail': json['hideThumbnail'] == null ? undefined : json['hideThumbnail'], + 'keepMetadata': json['keepMetadata'] == null ? undefined : json['keepMetadata'], + 'listMode': json['listMode'] == null ? undefined : json['listMode'], + 'makePublic': json['makePublic'] == null ? undefined : json['makePublic'], + 'showId': json['showId'] == null ? undefined : json['showId'], + 'theme': json['theme'] == null ? undefined : json['theme'], + 'useArchive': json['useArchive'] == null ? undefined : json['useArchive'], + }; +} + +export function ModelUserConfigToJSON(json: any): ModelUserConfig { + return ModelUserConfigToJSONTyped(json, false); +} + +export function ModelUserConfigToJSONTyped(value?: ModelUserConfig | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'createEbook': value['createEbook'], + 'hideExcerpt': value['hideExcerpt'], + 'hideThumbnail': value['hideThumbnail'], + 'keepMetadata': value['keepMetadata'], + 'listMode': value['listMode'], + 'makePublic': value['makePublic'], + 'showId': value['showId'], + 'theme': value['theme'], + 'useArchive': value['useArchive'], + }; +} + diff --git a/webapp/src/client/models/index.ts b/webapp/src/client/models/index.ts new file mode 100644 index 000000000..b332e9f1f --- /dev/null +++ b/webapp/src/client/models/index.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './ApiV1BookmarkTagPayload'; +export * from './ApiV1BulkUpdateBookmarkTagsPayload'; +export * from './ApiV1InfoResponse'; +export * from './ApiV1InfoResponseVersion'; +export * from './ApiV1LoginRequestPayload'; +export * from './ApiV1LoginResponseMessage'; +export * from './ApiV1ReadableResponseMessage'; +export * from './ApiV1UpdateAccountPayload'; +export * from './ApiV1UpdateCachePayload'; +export * from './ModelAccount'; +export * from './ModelAccountDTO'; +export * from './ModelBookmarkDTO'; +export * from './ModelTagDTO'; +export * from './ModelUserConfig'; diff --git a/webapp/src/client/runtime.ts b/webapp/src/client/runtime.ts new file mode 100644 index 000000000..8bcc03488 --- /dev/null +++ b/webapp/src/client/runtime.ts @@ -0,0 +1,431 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Shiori API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "http://localhost".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function exists(json: any, key: string) { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/webapp/src/components/layout/AppLayout.vue b/webapp/src/components/layout/AppLayout.vue new file mode 100644 index 000000000..fc1db9c74 --- /dev/null +++ b/webapp/src/components/layout/AppLayout.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/webapp/src/components/layout/LanguageSelector.vue b/webapp/src/components/layout/LanguageSelector.vue new file mode 100644 index 000000000..b596723e9 --- /dev/null +++ b/webapp/src/components/layout/LanguageSelector.vue @@ -0,0 +1,65 @@ + + + diff --git a/webapp/src/components/layout/Sidebar.vue b/webapp/src/components/layout/Sidebar.vue new file mode 100644 index 000000000..31c9a6785 --- /dev/null +++ b/webapp/src/components/layout/Sidebar.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/webapp/src/components/layout/TopBar.vue b/webapp/src/components/layout/TopBar.vue new file mode 100644 index 000000000..26b1df4b1 --- /dev/null +++ b/webapp/src/components/layout/TopBar.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json new file mode 100644 index 000000000..150f6acc6 --- /dev/null +++ b/webapp/src/locales/de.json @@ -0,0 +1,65 @@ +{ + "common": { + "loading": "Wird geladen...", + "save": "Speichern", + "cancel": "Abbrechen", + "delete": "Löschen", + "edit": "Bearbeiten", + "search": "Suchen", + "add": "Hinzufügen", + "remove": "Entfernen", + "close": "Schließen" + }, + "auth": { + "login": "Anmelden", + "logout": "Abmelden", + "username": "Benutzername", + "password": "Passwort", + "remember_me": "Angemeldet bleiben", + "login_failed": "Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihre Anmeldedaten." + }, + "navigation": { + "home": "Startseite", + "archive": "Archiv", + "tags": "Tags", + "folders": "Ordner", + "settings": "Einstellungen", + "about": "Über" + }, + "bookmarks": { + "title": "Lesezeichen", + "add_bookmark": "Lesezeichen hinzufügen", + "edit_bookmark": "Lesezeichen bearbeiten", + "delete_bookmark": "Lesezeichen löschen", + "url": "URL", + "title": "Titel", + "excerpt": "Auszug", + "tags": "Tags", + "add_tag": "Tag hinzufügen", + "no_bookmarks": "Keine Lesezeichen gefunden" + }, + "tags": { + "title": "Tags", + "add_tag": "Tag hinzufügen", + "edit_tag": "Tag bearbeiten", + "delete_tag": "Tag löschen", + "name": "Name", + "no_tags": "Keine Tags gefunden" + }, + "folders": { + "title": "Ordner", + "add_folder": "Ordner hinzufügen", + "edit_folder": "Ordner bearbeiten", + "delete_folder": "Ordner löschen", + "name": "Name", + "no_folders": "Keine Ordner gefunden" + }, + "settings": { + "title": "Einstellungen", + "language": "Sprache", + "theme": "Thema", + "light": "Hell", + "dark": "Dunkel", + "system": "System" + } +} diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json new file mode 100644 index 000000000..4849f9915 --- /dev/null +++ b/webapp/src/locales/en.json @@ -0,0 +1,65 @@ +{ + "common": { + "loading": "Loading...", + "save": "Save", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "search": "Search", + "add": "Add", + "remove": "Remove", + "close": "Close" + }, + "auth": { + "login": "Login", + "logout": "Logout", + "username": "Username", + "password": "Password", + "remember_me": "Remember me", + "login_failed": "Login failed. Please check your credentials." + }, + "navigation": { + "home": "Home", + "archive": "Archive", + "tags": "Tags", + "folders": "Folders", + "settings": "Settings", + "about": "About" + }, + "bookmarks": { + "title": "Bookmarks", + "add_bookmark": "Add Bookmark", + "edit_bookmark": "Edit Bookmark", + "delete_bookmark": "Delete Bookmark", + "url": "URL", + "title": "Title", + "excerpt": "Excerpt", + "tags": "Tags", + "add_tag": "Add Tag", + "no_bookmarks": "No bookmarks found" + }, + "tags": { + "title": "Tags", + "add_tag": "Add Tag", + "edit_tag": "Edit Tag", + "delete_tag": "Delete Tag", + "name": "Name", + "no_tags": "No tags found" + }, + "folders": { + "title": "Folders", + "add_folder": "Add Folder", + "edit_folder": "Edit Folder", + "delete_folder": "Delete Folder", + "name": "Name", + "no_folders": "No folders found" + }, + "settings": { + "title": "Settings", + "language": "Language", + "theme": "Theme", + "light": "Light", + "dark": "Dark", + "system": "System" + } +} diff --git a/webapp/src/locales/es.json b/webapp/src/locales/es.json new file mode 100644 index 000000000..c96977cb9 --- /dev/null +++ b/webapp/src/locales/es.json @@ -0,0 +1,65 @@ +{ + "common": { + "loading": "Cargando...", + "save": "Guardar", + "cancel": "Cancelar", + "delete": "Eliminar", + "edit": "Editar", + "search": "Buscar", + "add": "Añadir", + "remove": "Eliminar", + "close": "Cerrar" + }, + "auth": { + "login": "Iniciar sesión", + "logout": "Cerrar sesión", + "username": "Usuario", + "password": "Contraseña", + "remember_me": "Recordarme", + "login_failed": "Error al iniciar sesión. Por favor, verifica tus credenciales." + }, + "navigation": { + "home": "Inicio", + "archive": "Archivo", + "tags": "Etiquetas", + "folders": "Carpetas", + "settings": "Configuración", + "about": "Acerca de" + }, + "bookmarks": { + "title": "Marcadores", + "add_bookmark": "Añadir marcador", + "edit_bookmark": "Editar marcador", + "delete_bookmark": "Eliminar marcador", + "url": "URL", + "title": "Título", + "excerpt": "Extracto", + "tags": "Etiquetas", + "add_tag": "Añadir etiqueta", + "no_bookmarks": "No se encontraron marcadores" + }, + "tags": { + "title": "Etiquetas", + "add_tag": "Añadir etiqueta", + "edit_tag": "Editar etiqueta", + "delete_tag": "Eliminar etiqueta", + "name": "Nombre", + "no_tags": "No se encontraron etiquetas" + }, + "folders": { + "title": "Carpetas", + "add_folder": "Añadir carpeta", + "edit_folder": "Editar carpeta", + "delete_folder": "Eliminar carpeta", + "name": "Nombre", + "no_folders": "No se encontraron carpetas" + }, + "settings": { + "title": "Configuración", + "language": "Idioma", + "theme": "Tema", + "light": "Claro", + "dark": "Oscuro", + "system": "Sistema" + } +} diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json new file mode 100644 index 000000000..ee6e3d51f --- /dev/null +++ b/webapp/src/locales/fr.json @@ -0,0 +1,65 @@ +{ + "common": { + "loading": "Chargement...", + "save": "Enregistrer", + "cancel": "Annuler", + "delete": "Supprimer", + "edit": "Modifier", + "search": "Rechercher", + "add": "Ajouter", + "remove": "Supprimer", + "close": "Fermer" + }, + "auth": { + "login": "Connexion", + "logout": "Déconnexion", + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "remember_me": "Se souvenir de moi", + "login_failed": "Échec de la connexion. Veuillez vérifier vos identifiants." + }, + "navigation": { + "home": "Accueil", + "archive": "Archives", + "tags": "Tags", + "folders": "Dossiers", + "settings": "Paramètres", + "about": "À propos" + }, + "bookmarks": { + "title": "Favoris", + "add_bookmark": "Ajouter un favori", + "edit_bookmark": "Modifier le favori", + "delete_bookmark": "Supprimer le favori", + "url": "URL", + "title": "Titre", + "excerpt": "Extrait", + "tags": "Tags", + "add_tag": "Ajouter un tag", + "no_bookmarks": "Aucun favori trouvé" + }, + "tags": { + "title": "Tags", + "add_tag": "Ajouter un tag", + "edit_tag": "Modifier le tag", + "delete_tag": "Supprimer le tag", + "name": "Nom", + "no_tags": "Aucun tag trouvé" + }, + "folders": { + "title": "Dossiers", + "add_folder": "Ajouter un dossier", + "edit_folder": "Modifier le dossier", + "delete_folder": "Supprimer le dossier", + "name": "Nom", + "no_folders": "Aucun dossier trouvé" + }, + "settings": { + "title": "Paramètres", + "language": "Langue", + "theme": "Thème", + "light": "Clair", + "dark": "Sombre", + "system": "Système" + } +} diff --git a/webapp/src/locales/ja.json b/webapp/src/locales/ja.json new file mode 100644 index 000000000..b44f0ad2c --- /dev/null +++ b/webapp/src/locales/ja.json @@ -0,0 +1,65 @@ +{ + "common": { + "loading": "読み込み中...", + "save": "保存", + "cancel": "キャンセル", + "delete": "削除", + "edit": "編集", + "search": "検索", + "add": "追加", + "remove": "削除", + "close": "閉じる" + }, + "auth": { + "login": "ログイン", + "logout": "ログアウト", + "username": "ユーザー名", + "password": "パスワード", + "remember_me": "ログイン状態を保持する", + "login_failed": "ログインに失敗しました。認証情報を確認してください。" + }, + "navigation": { + "home": "ホーム", + "archive": "アーカイブ", + "tags": "タグ", + "folders": "フォルダ", + "settings": "設定", + "about": "概要" + }, + "bookmarks": { + "title": "ブックマーク", + "add_bookmark": "ブックマークを追加", + "edit_bookmark": "ブックマークを編集", + "delete_bookmark": "ブックマークを削除", + "url": "URL", + "title": "タイトル", + "excerpt": "抜粋", + "tags": "タグ", + "add_tag": "タグを追加", + "no_bookmarks": "ブックマークが見つかりません" + }, + "tags": { + "title": "タグ", + "add_tag": "タグを追加", + "edit_tag": "タグを編集", + "delete_tag": "タグを削除", + "name": "名前", + "no_tags": "タグが見つかりません" + }, + "folders": { + "title": "フォルダ", + "add_folder": "フォルダを追加", + "edit_folder": "フォルダを編集", + "delete_folder": "フォルダを削除", + "name": "名前", + "no_folders": "フォルダが見つかりません" + }, + "settings": { + "title": "設定", + "language": "言語", + "theme": "テーマ", + "light": "ライト", + "dark": "ダーク", + "system": "システム" + } +} diff --git a/webapp/src/main.ts b/webapp/src/main.ts new file mode 100644 index 000000000..fc8f69143 --- /dev/null +++ b/webapp/src/main.ts @@ -0,0 +1,16 @@ +import './assets/main.css' + +import { createApp } from 'vue' +import { createPinia } from 'pinia' + +import App from './App.vue' +import router from './router' +import i18n from './utils/i18n' + +const app = createApp(App) + +app.use(createPinia()) +app.use(router) +app.use(i18n) + +app.mount('#app') diff --git a/webapp/src/router/index.ts b/webapp/src/router/index.ts new file mode 100644 index 000000000..09ea1510f --- /dev/null +++ b/webapp/src/router/index.ts @@ -0,0 +1,97 @@ +import { createRouter, createWebHistory } from 'vue-router' +import type { RouteRecordRaw, NavigationGuardNext as NavigationGuard, RouteLocationNormalized } from 'vue-router' +import HomeView from '../views/HomeView.vue' +import LoginView from '../views/LoginView.vue' +import { useAuthStore } from '@/stores/auth' + +const routes: Array = [ + { + path: '/', + redirect: '/home' + }, + { + path: '/home', + name: 'home', + component: HomeView, + meta: { requiresAuth: true } + }, + { + path: '/login', + name: 'login', + component: LoginView, + props: (route) => ({ dst: route.query.dst }) + }, + { + path: '/tags', + name: 'tags', + component: () => import('../views/TagsView.vue'), + meta: { requiresAuth: true } + }, + { + path: '/folders', + name: 'folders', + component: () => import('../views/FoldersView.vue'), + meta: { requiresAuth: true } + }, + { + path: '/archive', + name: 'archive', + component: () => import('../views/ArchiveView.vue'), + meta: { requiresAuth: true } + }, + { + path: '/settings', + name: 'settings', + component: () => import('../views/SettingsView.vue'), + meta: { requiresAuth: true } + }, + // Redirect any unmatched routes to home (which will redirect to login if not authenticated) + { + path: '/:pathMatch(.*)*', + redirect: '/home' + } +] + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes +}) + +// Navigation guard +router.beforeEach(async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuard) => { + const authStore = useAuthStore() + + // Check if the route requires authentication + if (to.matched.some((record) => record.meta.requiresAuth)) { + // If we have a token, validate it + if (authStore.token) { + const isValid = await authStore.validateToken() + + if (isValid) { + // Token is valid, proceed to the requested route + next() + } else { + // Token is invalid, redirect to login with destination + const destination = to.fullPath + authStore.setRedirectDestination(destination) + next({ + name: 'login', + query: { dst: destination } + }) + } + } else { + // No token, redirect to login with destination + const destination = to.fullPath + authStore.setRedirectDestination(destination) + next({ + name: 'login', + query: { dst: destination } + }) + } + } else { + // Route doesn't require auth, proceed + next() + } +}) + +export default router diff --git a/webapp/src/stores/auth.ts b/webapp/src/stores/auth.ts new file mode 100644 index 000000000..8c1614c48 --- /dev/null +++ b/webapp/src/stores/auth.ts @@ -0,0 +1,217 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { AuthApi } from '@/client/apis/AuthApi' +import type { ApiV1LoginRequestPayload } from '@/client/models/ApiV1LoginRequestPayload' +import { Configuration } from '@/client/runtime' + +export const useAuthStore = defineStore('auth', () => { + const token = ref(localStorage.getItem('token')) + const expires = ref(Number(localStorage.getItem('expires')) || null) + const user = ref(null) + const loading = ref(false) + const error = ref(null) + const redirectDestination = ref(null) + + const isAuthenticated = computed(() => { + if (!token.value) return false + if (!expires.value) return false + return expires.value > Date.now() + }) + + // Create API client with auth token + const getApiClient = () => { + const config = new Configuration({ + basePath: 'http://localhost:8080', + accessToken: token.value || undefined, + headers: token.value ? { + 'Authorization': `Bearer ${token.value}` + } : undefined + }) + return new AuthApi(config) + } + + // Validate token by fetching user info + const validateToken = async (): Promise => { + if (!token.value) return false + + loading.value = true + try { + const result = await fetchUserInfo() + loading.value = false + return !!result + } catch (err) { + loading.value = false + return false + } + } + + // Login function + const login = async (username: string, password: string, rememberMe: boolean = false) => { + loading.value = true + error.value = null + + try { + const payload: ApiV1LoginRequestPayload = { + username, + password, + rememberMe, + } + + const api = getApiClient() + const response = await api.apiV1AuthLoginPost({ payload }) + + if (response.token) { + token.value = response.token + expires.value = response.expires || 0 + + // Store in localStorage + localStorage.setItem('token', response.token) + localStorage.setItem('expires', String(response.expires)) + + // Get user info + await fetchUserInfo() + return true + } else { + throw new Error('Invalid response from server') + } + } catch (err: any) { + console.error('Login error:', err) + + // Extract error message from response if available + if (err.response) { + try { + // Try to parse the response body as JSON + const responseBody = await err.response.json() + if (responseBody && responseBody.message) { + error.value = responseBody.message + } else if (responseBody && responseBody.error) { + error.value = responseBody.error + } else if (typeof responseBody === 'string') { + error.value = responseBody + } else { + error.value = `Server error: ${err.response.status}` + } + } catch (jsonError) { + // If response is not JSON, use status text + error.value = err.response.statusText || `Server error: ${err.response.status}` + } + } else { + // If no response object, use the error message + error.value = err.message || 'Failed to login' + } + + return false + } finally { + loading.value = false + } + } + + // Fetch user info + const fetchUserInfo = async () => { + if (!token.value) return null + + try { + // Create a new API client with the current token + const api = getApiClient() + + // Make the API request with the token in the headers + const response = await api.apiV1AuthMeGet() + + if (response) { + user.value = response + return user.value + } else { + throw new Error('Failed to fetch user info') + } + } catch (err: any) { + console.error('Error fetching user info:', err) + + // If we get a 401 Unauthorized, the token is invalid + if (err.response && err.response.status === 401) { + // Clear the invalid token + clearAuth() + } + + return null + } + } + + // Clear authentication data + const clearAuth = () => { + token.value = null + expires.value = null + user.value = null + localStorage.removeItem('token') + localStorage.removeItem('expires') + } + + // Logout function + const logout = async () => { + loading.value = true + + try { + if (token.value) { + const api = getApiClient() + await api.apiV1AuthLogoutPost() + } + } catch (err) { + console.error('Logout error:', err) + } finally { + // Clear state regardless of API success + clearAuth() + loading.value = false + } + } + + // Refresh token + const refreshToken = async () => { + if (!token.value) return false + + try { + const api = getApiClient() + const response = await api.apiV1AuthRefreshPost() + + if (response.token) { + token.value = response.token + expires.value = response.expires || 0 + + localStorage.setItem('token', response.token) + localStorage.setItem('expires', String(response.expires)) + return true + } + return false + } catch (err) { + console.error('Token refresh error:', err) + return false + } + } + + // Set redirect destination + const setRedirectDestination = (destination: string | null) => { + redirectDestination.value = destination + } + + // Get and clear redirect destination + const getAndClearRedirectDestination = () => { + const destination = redirectDestination.value + redirectDestination.value = null + return destination + } + + return { + token, + expires, + user, + loading, + error, + isAuthenticated, + login, + logout, + fetchUserInfo, + refreshToken, + validateToken, + setRedirectDestination, + getAndClearRedirectDestination, + clearAuth + } +}) diff --git a/webapp/src/stores/counter.ts b/webapp/src/stores/counter.ts new file mode 100644 index 000000000..b6757ba57 --- /dev/null +++ b/webapp/src/stores/counter.ts @@ -0,0 +1,12 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const doubleCount = computed(() => count.value * 2) + function increment() { + count.value++ + } + + return { count, doubleCount, increment } +}) diff --git a/webapp/src/stores/tags.ts b/webapp/src/stores/tags.ts new file mode 100644 index 000000000..088b280b3 --- /dev/null +++ b/webapp/src/stores/tags.ts @@ -0,0 +1,141 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { Configuration, TagsApi } from '@/client' +import type { ModelTagDTO } from '@/client/models' +import { useAuthStore } from './auth' + +export const useTagsStore = defineStore('tags', () => { + const tags = ref([]) + const isLoading = ref(false) + const error = ref(null) + + // API client + const getTagsApi = () => { + const authStore = useAuthStore() + const token = authStore.token + + const config = new Configuration({ + basePath: 'http://localhost:8080', + accessToken: token || undefined, + headers: token ? { + 'Authorization': `Bearer ${token}` + } : undefined + }) + return new TagsApi(config) + } + + // Get all tags + const fetchTags = async (withBookmarkCount = true) => { + isLoading.value = true + error.value = null + + try { + const api = getTagsApi() + const response = await api.apiV1TagsGet({ withBookmarkCount }) + + // Ensure response is an array before assigning + if (Array.isArray(response)) { + tags.value = response + } else { + console.error('Expected array response but got:', typeof response) + tags.value = [] + } + + return tags.value + } catch (err) { + console.error('Failed to fetch tags:', err) + if (err instanceof Error && err.message.includes('401')) { + error.value = 'Authentication error. Please log in again.' + } else { + error.value = 'Failed to load tags. Please try again.' + } + throw err + } finally { + isLoading.value = false + } + + } + + // Create a new tag + const createTag = async (name: string) => { + isLoading.value = true + error.value = null + + try { + const api = getTagsApi() + const newTag = await api.apiV1TagsPost({ tag: { name } }) + tags.value.push(newTag) + return newTag + } catch (err) { + console.error('Failed to create tag:', err) + if (err instanceof Error && err.message.includes('401')) { + error.value = 'Authentication error. Please log in again.' + } else { + error.value = 'Failed to create tag. Please try again.' + } + throw err + } finally { + isLoading.value = false + } + } + + // Update a tag + const updateTag = async (id: number, name: string) => { + isLoading.value = true + error.value = null + + try { + const api = getTagsApi() + const updatedTag = await api.apiV1TagsIdPut({ id, tag: { id, name } }) + + const index = tags.value.findIndex(tag => tag.id === id) + if (index !== -1) { + tags.value[index] = updatedTag + } + + return updatedTag + } catch (err) { + console.error('Failed to update tag:', err) + if (err instanceof Error && err.message.includes('401')) { + error.value = 'Authentication error. Please log in again.' + } else { + error.value = 'Failed to update tag. Please try again.' + } + throw err + } finally { + isLoading.value = false + } + } + + // Delete a tag + const deleteTag = async (id: number) => { + isLoading.value = true + error.value = null + + try { + const api = getTagsApi() + await api.apiV1TagsIdDelete({ id }) + tags.value = tags.value.filter(tag => tag.id !== id) + } catch (err) { + console.error('Failed to delete tag:', err) + if (err instanceof Error && err.message.includes('401')) { + error.value = 'Authentication error. Please log in again.' + } else { + error.value = 'Failed to delete tag. Please try again.' + } + throw err + } finally { + isLoading.value = false + } + } + + return { + tags, + isLoading, + error, + fetchTags, + createTag, + updateTag, + deleteTag + } +}) diff --git a/webapp/src/utils/i18n.ts b/webapp/src/utils/i18n.ts new file mode 100644 index 000000000..d54080c8a --- /dev/null +++ b/webapp/src/utils/i18n.ts @@ -0,0 +1,49 @@ +import { createI18n } from 'vue-i18n' +import en from '@/locales/en.json' +import es from '@/locales/es.json' +import fr from '@/locales/fr.json' +import de from '@/locales/de.json' +import ja from '@/locales/ja.json' + +// Define supported languages +export type SupportedLocale = 'en' | 'es' | 'fr' | 'de' | 'ja'; + +// Get the browser language or use English as fallback +const getBrowserLanguage = (): SupportedLocale => { + const browserLang = navigator.language.split('-')[0] + return ['en', 'es', 'fr', 'de', 'ja'].includes(browserLang) ? browserLang as SupportedLocale : 'en' +} + +// Get the stored language preference or use browser language +const getStoredLanguage = (): SupportedLocale => { + const storedLang = localStorage.getItem('shiori-language') + return (storedLang && ['en', 'es', 'fr', 'de', 'ja'].includes(storedLang)) + ? storedLang as SupportedLocale + : getBrowserLanguage() +} + +// Create the i18n instance +const i18n = createI18n({ + legacy: false, // Use Composition API + locale: getStoredLanguage(), + fallbackLocale: 'en', + messages: { + en, + es, + fr, + de, + ja + } +}) + +// Function to change the language +export const setLanguage = (lang: SupportedLocale): void => { + i18n.global.locale.value = lang + localStorage.setItem('shiori-language', lang) + document.querySelector('html')?.setAttribute('lang', lang) +} + +// Initialize HTML lang attribute +document.querySelector('html')?.setAttribute('lang', getStoredLanguage()) + +export default i18n diff --git a/webapp/src/views/AboutView.vue b/webapp/src/views/AboutView.vue new file mode 100644 index 000000000..756ad2a17 --- /dev/null +++ b/webapp/src/views/AboutView.vue @@ -0,0 +1,15 @@ + + + diff --git a/webapp/src/views/ArchiveView.vue b/webapp/src/views/ArchiveView.vue new file mode 100644 index 000000000..76ef8a9ef --- /dev/null +++ b/webapp/src/views/ArchiveView.vue @@ -0,0 +1,17 @@ + + + diff --git a/webapp/src/views/FoldersView.vue b/webapp/src/views/FoldersView.vue new file mode 100644 index 000000000..dc3f35db4 --- /dev/null +++ b/webapp/src/views/FoldersView.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/webapp/src/views/HomeView.vue b/webapp/src/views/HomeView.vue new file mode 100644 index 000000000..a63980965 --- /dev/null +++ b/webapp/src/views/HomeView.vue @@ -0,0 +1,66 @@ + + + diff --git a/webapp/src/views/LoginView.vue b/webapp/src/views/LoginView.vue new file mode 100644 index 000000000..b3e82a47a --- /dev/null +++ b/webapp/src/views/LoginView.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/webapp/src/views/SettingsView.vue b/webapp/src/views/SettingsView.vue new file mode 100644 index 000000000..f76be7cbe --- /dev/null +++ b/webapp/src/views/SettingsView.vue @@ -0,0 +1,59 @@ + + + diff --git a/webapp/src/views/TagsView.vue b/webapp/src/views/TagsView.vue new file mode 100644 index 000000000..ac7e6efbc --- /dev/null +++ b/webapp/src/views/TagsView.vue @@ -0,0 +1,267 @@ + + + diff --git a/webapp/tailwind.config.js b/webapp/tailwind.config.js new file mode 100644 index 000000000..2f90ec32b --- /dev/null +++ b/webapp/tailwind.config.js @@ -0,0 +1,13 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], + darkMode: 'media', + theme: { + extend: { + colors: { + primary: '#f44336', + }, + }, + }, + plugins: [], +} diff --git a/webapp/tsconfig.app.json b/webapp/tsconfig.app.json new file mode 100644 index 000000000..913b8f279 --- /dev/null +++ b/webapp/tsconfig.app.json @@ -0,0 +1,12 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/webapp/tsconfig.json b/webapp/tsconfig.json new file mode 100644 index 000000000..100cf6a8f --- /dev/null +++ b/webapp/tsconfig.json @@ -0,0 +1,14 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.vitest.json" + } + ] +} diff --git a/webapp/tsconfig.node.json b/webapp/tsconfig.node.json new file mode 100644 index 000000000..a83dfc9d4 --- /dev/null +++ b/webapp/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*", + "eslint.config.*" + ], + "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/webapp/tsconfig.vitest.json b/webapp/tsconfig.vitest.json new file mode 100644 index 000000000..7d1d8cef3 --- /dev/null +++ b/webapp/tsconfig.vitest.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.app.json", + "include": ["src/**/__tests__/*", "env.d.ts"], + "exclude": [], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", + + "lib": [], + "types": ["node", "jsdom"] + } +} diff --git a/webapp/vite.config.ts b/webapp/vite.config.ts new file mode 100644 index 000000000..7985e9dfe --- /dev/null +++ b/webapp/vite.config.ts @@ -0,0 +1,23 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import vueDevTools from 'vite-plugin-vue-devtools' +import tailwindcss from '@tailwindcss/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + vueDevTools(), + tailwindcss(), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + }, + }, + css: { + devSourcemap: true, + }, +}) diff --git a/webapp/vitest.config.ts b/webapp/vitest.config.ts new file mode 100644 index 000000000..c32871718 --- /dev/null +++ b/webapp/vitest.config.ts @@ -0,0 +1,14 @@ +import { fileURLToPath } from 'node:url' +import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' +import viteConfig from './vite.config' + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + environment: 'jsdom', + exclude: [...configDefaults.exclude, 'e2e/**'], + root: fileURLToPath(new URL('./', import.meta.url)), + }, + }), +)